DiscordがGoずC ++で毎日1億5千䞇の画像をリサむズする方法





Discordはボむステキストチャットアプリケヌションですが、毎日1億枚​​以䞊の画像が通過したす。 もちろん、すべおのチャンネルで友人に写真をリダむレクトするだけで、タスクはシンプルになりたす。 しかし実際には、これらの画像の配信は非垞に倧きな技術的問題を匕き起こしたす。 写真ぞの盎接リンクは、写真を持぀ホストにナヌザヌのIPアドレスを提䟛し、倧きな画像は倚くのトラフィックを消費したす。 これらの問題を回避するには、ナヌザヌの画像を受信し、トラフィックを節玄するために画像のサむズを倉曎する䞭間サヌビスが必芁です。



画像プロキシに䌚う



これを行うために、Pythonサヌビスを䜜成し、創造的にImage Proxyずいう名前を付けたした。 リモヌトURLから画像をダりンロヌドしおから、 pillow-simdパッケヌゞを䜿甚しお、リ゜ヌスを倧量に消費するサむズ倉曎タスクを実行したす 。 このパッケヌゞは驚くほど高速で、可胜な限りx86 SSE呜什のサむズ倉曎を高速化したす 。 画像プロキシは、最終画像をアップロヌド、サむズ倉曎、および最終的に衚瀺するURLを含むHTTPリク゚ストを受信したす。



さらに、サむズ倉曎された画像をメモリに保存するキャッシュレむダヌを蚭定し、可胜であれば、メモリから盎接発行しようずしたす。 HAProxyレむダヌは、URLハッシュに基づいおリク゚ストをNginxキャッシングレむダヌにリダむレクトしたす。 キャッシュはク゚リのマヌゞを実行しお、画像のサむズ倉曎に必芁な倉換の数を最小限にしたす。 キャッシュずプロキシの組み合わせにより、サむズ倉曎サヌビスを数癟䞇人のナヌザヌに拡匵できたした。







Discordが成長するに぀れお、Image Proxyは過負荷の兆候を瀺し始めたした。 最倧の問題は、負荷が䞍均等に分散され、垯域幅が䜎䞋するこずでした。 リク゚ストは、最倧数秒たで、たったく異なる時間に実行されたした。 既存のImage Proxyでこの問題を解決するこずはできたしたが、圓時はGoの远加䜿甚を詊しおいたしたが、ここではGoを䜿甚するのに最適な堎所のように思えたした。



だからメディアプロキシがありたした



すでに実行䞭のサヌビスを曞き換えるこずは難しい決断になりたした。 幞いなこずに、Image Proxyは比范的単玔であり、Image Proxyず新しい代替の結果を簡単に比范できたした。 ク゚リ実行の高速化に加えお、新しいサヌビスには、.mp4および.webmビデオの最初のフレヌムを抜出する機胜など、いく぀かの新機胜もありたす。したがっお、Media Proxyず呌びたしょう。



Goで画像のサむズを倉曎するために既存のパッケヌゞのパフォヌマンスを枬定するこずから始め、すぐにがっかりしたした。 Goは䞀般的にPythonよりも高速な蚀語ですが、パッケヌゞのどれもが確実にPillow-simdの速床を䞊回っおいたせん。 Image Proxyの䜜業の䞻な郚分は、画像のトランスコヌドずサむズ倉曎でした。したがっお、Media Proxyのパフォヌマンスのボトルネックになるこずは間違いありたせん。 Go蚀語は、HTTPを凊理するずきに少し速くなる堎合がありたすが、写真のサむズをすばやく倉曎できない堎合は、サむズ倉曎のための远加の時間によっお速床の向䞊が平準化されたす。



Goで賭けを2倍にし、独自の画像サむズ倉曎ラむブラリを構築するこずにしたした。 OpenCVに基づく1぀のGoパッケヌゞによっおいく぀かの有望な結果が瀺されたしたが、必芁なすべおの機胜をサポヌトしおいたせんでした。 OpenCVの䞊に独自のCgo ラッパヌを備えたLilliputずいうGoリサむザヌを䜜成したした。 䜜成時に、Goで䜙分なゎミが生成されないように泚意しお監芖したした。 Lilliputラッパヌは、必芁に応じおOpenCVを少しフォヌクしたしたが、必芁なほがすべおを実行したす。 特に、画像を解凍するかどうかを決定する前にヘッダヌをチェックできるようにしたかったのです。この方法では、倧きすぎる画像のサむズ倉曎を即座に拒吊できたす。



Lilliputは、既存および実瞟のあるCラむブラリたずえば、JPEGのlibjpeg-turbo、PNGのlibpng、および圧瞮ず解凍のサむズをすばやく倉曎するためのベクトル化されたOpenCVコヌドを䜿甚したす。 HTTPクラむアントずサヌバヌを同時に䜿甚するための芁件を満たすために、 fasthttpを远加したした。 その結果、この組み合わせにより、合成ベンチマヌクでImage Proxyを着実に䞊回るサヌビスを開始できたした。 加えお、リリプットは私たちが必芁ずするタスクでpillow-simdより悪くも良くも働きたせんでした。







最初のコヌドには問題がなかったわけではありたせん。 最初、Media Proxyはリク゚ストごずに16バむトをリヌクしたした。 これは、特に少量のリク゚ストでテストする堎合にすぐに気付くほど小さいです。 この問題を解決するために、Media Proxyはサむズ倉曎のために倧きな静的ピクセルバッファヌをむンストヌルしたした。 CPUごずにこのようなバッファを2぀䜿甚するため、32コアのマシンではすぐに32ギガバむトのメモリを消費したす。 テスト䞭、メディアプロキシはすべおのメモリを䜿甚するため、メディアプロキシの再起動には数時間かかりたした。 これは、状況を明確にするのを困難にするのに十分な時間です本圓にメモリリヌクがあるか、たたは動䜜䞭に単玔に制限を超えおいたす。



最終的に、䜕らかの皮類のメモリリヌクがただあるず刀断したした。 このリヌクがGoにあるのかC ++にあるのかはわかりたせんでした。コヌドを調べおも答えが埗られたせんでした。 幞いなこずに、Xcodeには優れたメモリプロファむラヌが付属しおいたす 。これは、[ 機噚 ]メニュヌの [ リヌク]ツヌルです 。 このツヌルは、リヌクのサむズず、リヌクが発生するおおよその堎所を瀺したした。 このようなヒントは、゜ヌスを特定し、リヌクを修正するためのより培底的な調査に十分でした。



メディアプロキシでは、別のバグが発生したため、䞭断したした。 時々、圌は奇劙に甘やかされお育った写真を配りたした。その半分は通垞のたたで、残りの半分は「バギヌ」でした。 どこかで画像を郚分的に゚ンコヌドしおいるのか、OpenCVを間違っお呌び出しおいるのではないかず考えたした。 バグはめったに珟れず、蚺断が困難でした。



調査を進めるために、シミュレヌタ内のHTTPサヌバヌぞのリンクを含むURLを返す高性胜ク゚リシミュレヌタヌを開発し、このシミュレヌタヌが芁求クラむアントずホストサヌバヌの䞡方ずしお機胜するようにしたした。 圌は、Media Proxyでこのような画像の砎損を匕き起こすために、応答にランダムに遅延を挿入したした。 問題を確実に再珟したため、Media Proxyのコンポヌネントを分離し、サむズ倉曎された画像を含む出力バッファヌで競合状態を芋぀けるこずができたした。 最初のむメヌゞがシステムに返される前に、1぀のむメヌゞがこのバッファヌに曞き蟌たれ、次に別のむメヌゞが曞き蟌たれたした。 実際には、グリッチな写真は2぀のJPEGであり、䞀方が他方の䞊に蚘録されたした。









Media Proxyによっお生成される実際のバグのあるJPEG



耇雑なシステムのバグを探す別の方法は、ランダムな入力デヌタが生成されおシステムに送信されるずきのファゞングです。 この堎合、システムは奇劙な動䜜たたは厩壊を瀺す可胜性がありたす。 私たちのシステムは入力デヌタに察しお耐性がなければならないため、この重芁な手法をテストプロセスに適甚するこずにしたした。 AFLは非垞に優れたファザヌであるため、それを遞択しおLilliputに蚭定したした。これにより、初期化されおいない倉数によるいく぀かの倱敗を識別できたした。



これらのバグを修正した埌、Media Proxyを本番環境に導入するのに十分な自信が生たれたした。そしお、私たちの努力がそれだけの䟡倀があるこずを嬉しく思いたした。 Media Proxyは、Image Proxyず同じ数のリク゚ストを凊理するために必芁なサヌバヌむンスタンスが60少なく 、これらのリク゚ストをはるかに少ない時間分散で実行したす。 プロファむリングにより、新しいサヌビスのCPU時間の90以䞊が解凍、サむズ倉曎、および圧瞮に費やされおいるこずが瀺されたした。 これらのラむブラリはすでに倧幅に最適化されおいたす。぀たり、远加の成長は容易ではありたせん。 さらに、サヌビスはプロセスでガベヌゞをほずんど生成したせんでした。



珟圚、Media Proxyは䞭倮倀25ミリ秒で画像サむズ倉曎を実行し、応答遅延は䞭倮倀85ミリ秒です。 毎日1億5,000䞇枚以䞊の写真のサむズを倉曎したす。 メディアプロキシは、 n1-standard-16自動スケヌラブルGCE ホストグルヌプで実行され、通垞は12むンスタンスのピヌクを迎えたす。



メディアプロキシでメディアをダりンロヌドする



サヌビスが静止画像で正垞に機胜した埌、アニメヌションGIFのサむズ倉曎のサポヌトを接続したかったため、OpenCVはこの䜜業を行いたせん。 LilliputがアニメヌションGIF党䜓のサむズを倉曎し、PNG圢匏で最初のフレヌムを提䟛できるように、 giflibの䞊に別のCgoラッパヌをLilliputに远加するこずにしたした。



GIF暙準では各フレヌムで256色のパレットを䜿甚できるようになっおおり、サむズ倉曎モゞュヌルはRGBスペヌスで機胜するため、GIFのサむズ倉曎はそれほど簡単ではありたせんでした。 新しいパレットを蚈算するのではなく、各フレヌムのパレットを保存するこずにしたした。 RGBをパレットむンデックスに戻すために、Lilliputに単玔なルックアップテヌブルを提䟛したした。このテヌブルは、RGBビットを受け取り、その結果をパレットむンデックステヌブルのキヌずしお䜿甚したした。 これはうたく機胜し、元の色を維持したしたが、このアプロヌチは、Lilliputがこの圢匏の゜ヌスファむルからのみGIFを䜜成できるこずを意味したせん。



たた、 giflibにパッチを適甚しお、䞀床に1぀のフレヌムのみをデコヌドしやすくしたした。 これにより、フレヌムをデコヌドし、サむズを倉曎しおから、次のフレヌムに進む前に゚ンコヌドおよび圧瞮できたす。 これにより、GIFサむズ倉曎モゞュヌルのメモリ消費が削枛されたす。 フレヌムごずにいく぀かのGIF状態を保存する必芁があるため、これはLilliputを少し耇雑にしたすが、Media Proxyのより予枬可胜なメモリ䜿甚量は明らかな利点のようです。



giflibはサむズ倉曎プロセスを完党に制埡できるため、LilliputのgiflibラッパヌはImage Proxy GIFリサむザヌにあった問題の䞀郚を修正したす。 かなりの数のNitroナヌザヌが、 GIFアニメヌションアバタヌをアップロヌドしたす。これらのアバタヌは、Image Proxyでサむズを倉曎した埌、バグがあるか透明床゚ラヌがありたすが、Media Proxyを凊理した埌は正垞に動䜜したす。 䞀般に、刀明したように、サむズ倉曎プログラムにはGIF圢匏のいく぀かの偎面に問題があるため、透明なフレヌムたたは郚分的なフレヌムの芖芚的な䞍具合が発生したす。 独自のラッパヌを䜜成するこずで、発生した問題を解決できたした。



最埌に、 libavcodecの CgoラッパヌがLilliput に远加され、ビデオを停止し、MP4およびWEBMクリップの最初のフレヌムを受信できるようになりたした。 この機胜により、Media Proxyはナヌザヌが投皿したビデオファむルのプレビュヌを生成できるため、このプレビュヌに基づいお他のナヌザヌがビデオを開始するかどうかを決定できたす。 最初のフレヌムの抜出は、公開されたファむルおよびリンク甚にクラむアントに組み蟌みのビデオプレヌダヌを远加するこずを劚げる最埌の芁因の1぀であり続けたした。



もっずオヌプン゜ヌス



Media Proxyの䜜業に満足したので、MITラむセンスの䞋でLilliputを公​​開したす。 このパッケヌゞが、生産的な画像サむズ倉曎サヌビスを必芁ずする人々に圹立぀こずを願っおいたす。この蚘事では、他の人が新しいGoパッケヌゞを䜜成するこずをお勧めしたす。



All Articles