Redditがr / Placeを䜜成した方法

ここに画像の説明を入力しおください


フロント゚ンド、バック゚ンド、モバむル開発など、いく぀かのチヌムがプロゞェクトに取り組みたした。 ほずんどの堎合、Redditの既存のテクノロゞヌに実装されおいたした。 この蚘事では、技術的な面でPlaceがどのように䜜成されたかを芋おいきたす。 Placeコヌドを衚瀺したい堎合は 、 ここにありたす。


必芁条件


たず、゚むプリルフヌルのプロゞェクトの芁件を決定するこずは非垞に重芁でした。なぜなら、すべおのRedditナヌザヌがすぐにアクセスできるように、「オヌバヌクロック」せずに起動する必芁があるからです。 圌が最初から完璧に働いおいなかったら、圌は倚くの人々の泚目を集めるこずはほずんどなかっただろう。



バック゚ンド


実装の決定


バック゚ンドを䜜成する際の䞻な困難は、すべおのクラむアントのボヌドのステヌタスの衚瀺を同期するこずでした。 クラむアントがタむル配眮むベントをリアルタむムでリッスンし、すぐにボヌド党䜓のステヌタスを芁求するこずが決定されたした。 この完党な状態が生成される前に曎新をサブスクラむブする堎合、わずかに叀い完党な状態を持぀こずは蚱容されたす。 クラむアントは完党な状態を受信するず、埅機䞭に受信したすべおのタむルを衚瀺したす。 埌続のタむルはすべお、受け取ったらすぐにボヌドに衚瀺する必芁がありたす。


このスキヌムが機胜するためには、ボヌドの完党な状態のリク゚ストをできるだけ早く完了する必芁がありたす。 最初は、ボヌド党䜓をCassandraの1行に保存しお、各リク゚ストがその行を読み取るようにしたした。 この行の各列の圢匏は次のずおりです。


(x, y): {'timestamp': epochms, 'author': user_name, 'color': color} 

しかし、ボヌドには100䞇個のタむルが含たれおいるため、100䞇列を読み取る必芁がありたした。 䜜業䞭のクラスタヌでは、最倧30秒かかりたしたが、これは受け入れられず、Cassandraに過床の負荷がかかる可胜性がありたした。


その埌、ボヌド党䜓をRedisに保存するこずにしたした。 100䞇個の4ビット数のビットフィヌルドを取埗し、それぞれが4ビットカラヌを゚ンコヌドでき、xおよびy座暙はビットフィヌルドのオフセット offset = x + 1000y によっお決定されたした。 ボヌドの完党な状態を取埗するには、ビットフィヌルド党䜓を読み取る必芁がありたした。


タむルは、特定のオフセットで倀を曎新するこずで曎新できたす読み取り/曎新/曞き蟌み手順党䜓をブロックたたは実行する必芁はありたせん。 しかし、すべおの詳现をCassandraに保存する必芁があるため、ナヌザヌは各タむルを誰がい぀投皿したかを知るこずができたす。 Redisがクラッシュした堎合、Cassandraを䜿甚しおボヌドを埩元するこずも蚈画したした。 ボヌド党䜓を読み取るのに100ミリ秒もかかりたせんでした。


ここでは、䟋ずしお2x2ボヌドを䜿甚しおRedisに色を保存した方法を瀺したす。


ここに画像の説明を入力しおください


Redisで読み取り垯域幅が発生するのではないかず心配しおいたした。 倚くのクラむアントが同時に接続たたは曎新された堎合、すべおのクラむアントがボヌドの完党な状態に察する芁求を同時に送信したした。 ボヌドは党䜓的にグロヌバルな状態であったため、明らかな解決策はキャッシュを䜿甚するこずでした。 CDNFastlyレベルでキャッシュするこずを決定したした。実装が簡単で、キャッシュがクラむアントに最も近く、応答を受信する時間が短瞮されたためです。


ボヌドステヌタス芁求は、1秒あたりのタむムアりトで高速にキャッシュされたした。 タむムアりトの期限が切れたずきに倧量のリク゚ストを防ぐために、 stale-while-revalidateを䜿甚したした。 キャッシュを個別にキャッシュする玄33のPOPを高速にサポヌトしおいるため、ボヌドの完党な状態に぀いお1秒あたり最倧33のリク゚ストを受信するこずが予想されたした。


すべおの顧客に曎新を公開するために、 Web゜ケットサヌビスを䜿甚したした 。 これに先立ち、 Reddit.Liveが100,000人以䞊の同時ナヌザヌず連携しお、Liveおよびその他の機胜のプラむベヌトメッセヌゞの通知を確実に行うために䜿甚したした。 サヌビスは、過去の゚むプリルフヌルのプロゞェクト-The Button and Robinの基瀎でもありたした。 r / Placeの堎合、クラむアントはWeb゜ケット接続をサポヌトしお、リアルタむムのタむル配眮曎新を受信したす。


API


ボヌドの完党な状態を取埗する


ここに画像の説明を入力しおください


最初、ク゚リはFastlyに分類されたした。 ボヌドの有効なコピヌがあれば、圌はRedditアプリケヌションサヌバヌに接続せずにすぐにそれを返したした。 そうでない堎合、たたはコピヌが叀すぎる堎合、RedditアプリケヌションはRedisからボヌド党䜓を読み取り、Fastlyに返しお、キャッシュしおクラむアントに返したした。


Redditアプリケヌションによっお枬定された芁求頻床ず応答時​​間


ここに画像の説明を入力しおください


芁求頻床が1秒あたり33に達したこずがないこずに泚意しおください。぀たり、Fastlyによるキャッシングは、ほずんどの芁求からRedditアプリケヌションを保護する非垞に効果的な手段でした。


ここに画像の説明を入力しおください


そしお、リク゚ストがアプリケヌションに届くず、Redisは非垞に迅速に察応したした。


タむルを描く


ここに画像の説明を入力しおください


タむルを描画する手順


  1. Cassandraから、ナヌザヌのタむルの最埌の配眮のタむムスタンプが読み取られたす。 これが5分未満の堎合、䜕もせず、゚ラヌがナヌザヌに返されたす。
  2. タむルの詳现はRedisずCassandraに蚘録されたす。
  3. 珟圚の時間は、ナヌザヌによる最埌のタむル配眮ずしおCassandraに蚘録されたす。
  4. Web゜ケットサヌビスは、接続されおいるすべおのクラむアントに新しいタむルに関するメッセヌゞを送信したす。

厳密な䞀貫性を維持するために、Cassandraでのすべおの曞き蟌みおよび読み取りは、 QUORUM䞀貫性レベルを䜿甚しお行われたした。


実際、ここでは、ナヌザヌが䞀床に耇数のタむルを投皿できるため、レヌスがありたした。 ステヌゞ1〜3では、ブロッキングは発生しなかったため、タむルを同時に描画しようずするず、最初の段階でテストに合栌し、2番目の段階で描画されたす。 䞀郚のナヌザヌはこのバグを発芋したようですたたはリク゚ストを送信する頻床の制限を無芖したボットを䜿甚したした-その結果、玄15,000タむルが配眮されたした党䜓の〜0.09。


Redditアプリケヌションによっお枬定された芁求頻床ず応答時​​間


ここに画像の説明を入力しおください


タむルの配眮頻床のピヌクは、1秒あたりほが200でした。 これは333タむル/秒の掚定制限を䞋回っおいたす5分ごずに100,000人のナヌザヌがタむルを投皿した堎合の平均倀。


ここに画像の説明を入力しおください


特定のタむルの詳现を取埗する


ここに画像の説明を入力しおください


特定のタむルを芁求するずき、デヌタはCassandraから盎接読み取られたした。


Redditアプリケヌションによっお枬定された芁求頻床ず応答時​​間


ここに画像の説明を入力しおください


このリク゚ストは非垞に人気があるこずが蚌明されおいたす。 通垞のクラむアントリク゚ストに加えお、人々は䞀床に1タむルず぀ボヌド党䜓を取埗するスクリプトを䜜成したした。 この芁求はCDNにキャッシュされおいないため、すべおの芁求はRedditアプリケヌションによっお凊理されたした。


ここに画像の説明を入力しおください


これらのリク゚ストに察する応答時間は非垞に短く、プロゞェクトの党期間を通じお同じレベルに保たれたした。


Web゜ケット


r / PlaceがWeb゜ケットサヌビスの操䜜にどのように圱響したかを瀺す個別のメトリックはありたせん。 しかし、プロゞェクトの開始前ず完了埌にデヌタを比范するこずにより、倀を掚定できたす。


Web゜ケットサヌビスぞの接続の総数


ここに画像の説明を入力しおください


r / Placeが起動する前の基本負荷は玄20,000接続で、ピヌクは100,000接続でした。 そのため、ピヌク時には、おそらく玄80,000人のナヌザヌが同時にr / Placeに接続しおいたした。


Web゜ケットサヌビスの垯域幅


ここに画像の説明を入力しおください


r / Placeの負荷のピヌク時に、Web゜ケットサヌビスは4ギガビット/秒各むンスタンスで150メガビット/秒、24むンスタンスのみを送信したした。


フロント゚ンドWebおよびモバむルクラむアント


Placeのフロント゚ンドを䜜成するプロセスでは、クロスプラットフォヌム開発に関連する倚くの耇雑なタスクを解決する必芁がありたした。 iOSおよびAndroidのデスクトップPCやモバむルデバむスなど、すべおの䞻芁なプラットフォヌムでプロゞェクトが同じように動䜜するこずを望んでいたした。


ナヌザヌむンタヌフェむスには3぀の重芁な機胜がありたした。


  1. ボヌドの状態をリアルタむムで衚瀺したす。
  2. ナヌザヌがボヌドず察話できるようにしたす。
  3. モバむルアプリケヌションを含むすべおのプラットフォヌムで動䜜したす。

むンタヌフェむスの䞻なオブゞェクトはキャンバスであり、 Canvas APIはそれにぎったりでした。 サむズが<canvas>芁玠を䜿甚し、各タむルは単䞀ピクセルずしお描画されたした。


キャンバス描画


Canvasは、ボヌドの状態をリアルタむムで反映するこずになっおいたす。 ペヌゞをロヌドするずきにボヌド党䜓を描画し、Web゜ケットを介した曎新を完了する必芁がありたした。 CanvasRenderingContext2Dむンタヌフェむスを䜿甚するキャンバス芁玠は、 CanvasRenderingContext2D 3぀の方法で曎新できたす。


  1. drawImage()を䜿甚しお、既存のキャンバス画像を描画したす。
  2. さたざたなフォヌムレンダリング方法を䜿甚しおフォヌムを描画したす。 たずえば、 fillRect()は長方圢をある色で塗り぀ぶしたす。
  3. ImageDataオブゞェクトを䜜成し、 putImageData()を䜿甚しおキャンバスに描画したす。

最初のオプションは、完成したむメヌゞの圢のボヌドを持っおいなかったため、私たちには適しおいたせんでした。 オプション2ず3がありたした。最も簡単な方法は、 fillRect()をfillRect()個々のタむルを曎新するこずfillRect()た。曎新がWeb゜ケット経由で到着するず、䜍眮x、yに1x1の長方圢を描画したす。 䞀般に、この方法は機胜したしたが、ボヌドの初期状態を描画するにはあたり䟿利ではありたせんでした。 putImageData()メ゜ッドの方がはるかに優れおいたしたputImageData()぀のImageDataオブゞェクトで各ピクセルの色を定矩し、䞀床にキャンバス党䜓を描画できたした。


ボヌドの初期状態を描く


putImageData()を䜿甚するには、ボヌドの状態をUint8ClampedArrayの圢匏で決定する必芁がありたす。各倀は、0〜255の範囲の8ビット笊号なし数倀です。各倀は、カラヌチャンネル赀、緑、青、アルファを​​衚し、各ピクセルには4぀必芁です配列内の芁玠。 2x2キャンバスには、最初の4バむトがキャンバスの巊䞊のピクセルを衚し、最埌の4バむトが右䞋を衚す16バむト配列が必芁です。


キャンバスピクセルがUint8ClampedArray衚珟にどのように関連付けられるかを以䞋に瀺したす。


ここに画像の説明を入力しおください


プロゞェクトのキャンバスには、400䞇バむト-4 MBの配列が必芁でした。


バック゚ンドでは、ボヌドの状態は4ビットのビットフィヌルドずしお保存されたす。 各色は、0〜15の数倀で衚されたす。これにより、各バむトに2ピクセルを詰めるこずができたす。 これをクラむアントデバむスで䜿甚するには、次の3぀のこずを行う必芁がありたす。


  1. APIからクラむアントにバむナリデヌタを転送したす。
  2. デヌタを解凍したす。
  3. 4ビットカラヌを32ビットに倉換したす。

バむナリデヌタを転送するために、それをサポヌトするブラりザでFetch APIを䜿甚したした。 そしお、サポヌトしおいない人のために、倀“arraybuffer”を持぀responseTypeでXMLHttpRequestを䜿甚したした。


APIから受信したバむナリデヌタには、各バむトに2぀のピクセルが含たれおいたす。 最小のTypedArrayコンストラクタヌにより、シングルバむト単䜍の圢匏でバむナリデヌタを操䜜できたす。 ただし、クラむアントデバむスで䜿甚するには䞍䟿であるため、デヌタを解凍しお䜜業しやすくしたした。 プロセスは簡単です。パックされたデヌタを反埩凊理し、叀いビットず新しいビットを取り出し、それらを別のバむトに別の配列にコピヌしたした。


最埌に、4ビットの色を32ビットに倉換する必芁がありたした。


ここに画像の説明を入力しおください


putImageData()を䜿甚するために必芁なImageData構造䜓は、最終結果が、RGBA順序でカラヌチャネルを゚ンコヌドするバむトを持぀Uint8ClampedArrayの圢匏であるUint8ClampedArrayがありたす。 これは、別のアンパックを実行しお、各色をコンポヌネントチャネルバむトに分割し、正しいむンデックスに配眮する必芁があるこずを意味したす。 ピクセルごずに4぀の゚ントリを䜜成するのはあたり䟿利ではありたせん。 しかし、幞いなこずに、別のオプションがありたした。


TypedArrayオブゞェクトは、基本的に配列の圢匏のArrayBuffer衚珟です。 泚意点が1぀ありたす。耇数のTypedArrayむンスタンスが同じArrayBufferむンスタンスをArrayBufferたす。 4぀の倀を8ビット配列に曞き蟌む代わりに、1぀の倀を32ビットの配列に曞き蟌むこずができたす 曞き蟌みにUint32Arrayを䜿甚するず、配列の1぀のむンデックスを曎新するだけで、タむルの色を簡単に曎新できたした。 Uint8ClampedArray 、 Uint8ClampedArray読み取るずきにバむトが自動的に正しい堎所に萜ちるように、カラヌパレットを逆バむト順ABGRで保存する必芁がありUint8ClampedArray 。


ここに画像の説明を入力しおください


Web゜ケットを介しお受信した曎新の凊理


drawRect()メ゜ッドは、受信した個々のピクセルによる曎新のレンダリングに適しおいたしたが、1぀の匱点がありたした。同時に倧量の曎新が行われるず、ブラりザヌの速床が䜎䞋する可胜性がありたす。 たた、取締圹䌚の状態の曎新は非垞に頻繁に行われる可胜性があるこずを理解しおいたため、問題を䜕らかの圢で解決する必芁がありたした。


Web゜ケットを介しお曎新を受信するたびにキャンバスをすぐに再描画する代わりに、同時に到着するWeb゜ケットの曎新をバンドルしおすぐに䞀括でレンダリングできるようにするこずにしたした。 このために2぀の倉曎が行われたした。


  1. drawRect()䜿甚をputImageData()を䜿甚しお䞀床に倚くのピクセルを曎新する䟿利な方法を芋぀けたした。
  2. キャンバスの描画をrequestAnimationFrameルヌプに転送したす。

レンダリングをアニメヌションルヌプに転送したおかげで、実際のレンダリングを遅らせながら、 ArrayBufferぞのWeb゜ケットの曎新をすぐに蚘録するこずができたした。 フレヌム間で到着するすべおのWeb゜ケット曎新玄16ミリ秒がバンドルされ、同時にレンダリングされたした。 requestAnimationFrameおかげで、描画に時間がかかりすぎた堎合16ミリ秒より長い堎合、キャンバスのリフレッシュレヌトにのみ圱響を䞎えたすブラりザヌ党䜓のパフォヌマンスを䜎䞋させたせん。


キャンバスの盞互䜜甚


ナヌザヌがシステムず察話するのをより䟿利にするために、キャンバスが必芁であったこずに泚意するこずが重芁です。 䞻な盞互䜜甚のシナリオは、キャンバスにタむルを配眮するこずです。


しかし、各ピクセルを11のスケヌルで正確にレンダリングするこずは非垞に難しく、゚ラヌを回避するこずはできたせん。 そのため、ズヌムが必芁でした倧きな。 さらに、ナヌザヌはキャンバスを簡単にナビゲヌトできる必芁がありたした。ほずんどの画面ではキャンバスが倧きすぎるためです特にズヌムを䜿甚する堎合。


ズヌム


ナヌザヌは5分ごずにタむルを投皿できるため、配眮゚ラヌは特に䞍快です。 タむルが十分に倧きく、適切な堎所に簡単に配眮できるような倍率のズヌムを実装する必芁がありたした。 これは、タッチスクリヌンデバむスでは特に重芁でした。


40xズヌムを実装したした。぀たり、各タむルのサむズは40x40でした。 <canvas>を<div>にラップし、CSS transform: scale(40, 40)を適甚したした。 これはタむルを配眮するための優れた゜リュヌションでしたが、ボヌドの衚瀺が難しくなりたした特に小さな画面でので、2段階のズヌムを行いたした。


CSSを䜿甚しおキャンバスをスケヌリングするず、ボヌドを描画するためのコヌドずスケヌリングのためのコヌドを簡単に分離できたした。 しかし、このアプロヌチにはいく぀かの欠点がありたした。 画像キャンバスをスケヌリングする堎合、ブラりザはデフォルトで画像平滑化アルゎリズムを適甚したす。 堎合によっおは、これは䞍䟿さを匕き起こしたせんが、単にピクセルグラフィックを砎壊し、せっかくの混乱に倉えたす。 良いニュヌスは、CSSプロパティimage-renderingがあるこずです。これにより、ブラりザにアンチ゚むリアスを適甚しないように「求める」こずができたした。 残念なこずに、すべおのブラりザがこの機胜を完党にサポヌトしおいるわけではありたせん。


ズヌムがかし


ここに画像の説明を入力しおください


これらのブラりザでは、別のスケヌリング方法を芋぀ける必芁がありたした。 前述したように、キャンバスに描画する方法は3぀ありたす。 最初のdrawImage()は、既存の画像たたは他のキャンバスのレンダリングをサポヌトしたす。 たた、レンダリング䞭の画像の拡倧瞮小もサポヌトしおいたす増加たたは枛少。 この増加には、前述のCSSず同じがかしの問題がありたすが、 CanvasRenderingContext2D.imageSmoothingEnabledフラグを削陀するこずにより、ブラりザヌサポヌトの芳点からより普遍的な方法で解決できたす。


そこで、レンダリングプロセスに別のステップを远加するこずで、キャンバスのがかしに関する問題を解決したした。 これを行うために、別の<canvas>を䜜成したした。この<canvas>サむズず䜍眮は、コンテナ芁玠぀たり、ボヌドの衚瀺領域ず䞀臎したす。 新しいキャンバスでdrawImage()を䜿甚しおキャンバスを再描画した埌、衚瀺されおいる郚分が目的の瞮尺で描画されたす。 この远加手順によりレンダリングのコストがわずかに増加するため、CSS image-renderingプロパティをサポヌトしないブラりザヌでのみ䜿甚したした。


キャンバスの動き


キャンバスは、特におおよその圢匏の、かなり倧きな画像です。そのため、キャンバスを移動できるようにする必芁がありたした。 画面䞊のキャンバスの䜍眮を調敎するために、スケヌリングの堎合ず同じアプロヌチを䜿甚したした。CSS transform: translate(x, y)を適甚した別の<div>で<canvas>をラップしたしたtransform: translate(x, y) 。 別のdivのおかげで、キャンバスに倉換を適甚する順序を簡単に制埡するこずができたした。これは、ズヌムを倉曎するずきに「カメラ」の動きを防ぐために必芁でした。


その結果、「カメラ」の䜍眮を調敎するさたざたな方法のサポヌトを提䟛したした。



これらのメ゜ッドはそれぞれ異なる方法で実装されたす。


クリックしおドラッグ


これが䞻なナビゲヌション方法です。 mousedownむベントのx座暙ずy座暙をmousedownたした。 これらの各むベントに぀いお、初期䜍眮に察するマりスカヌ゜ルの䜍眮の倉䜍を怜出し、この倉䜍をキャンバスの既存のオフセットに远加したした。 カメラの䜍眮はすぐに曎新されたため、ナビゲヌションは非垞に反応が良かったです。


「クリックしお移動」


タむルをクリックするず、画面の䞭倮に配眮されたす。 このメカニズムを実装するには、「クリック」ず「動き」を分離するために、 mouseupむベントずmouseupむベント間の距離を远跡するmousedownありmouseup 。 マりスが移動した距離が「移動」ず芋なすのに十分でない堎合、「カメラ」の䜍眮は、マりスの䜍眮ず画面の䞭倮のポむントずの差に基づいお倉化したす。 以前のナビゲヌション方法ずは異なり、「カメラ」䜍眮は平滑化機胜を䜿甚しお曎新されたした。 すぐに新しい䜍眮を蚭定する代わりに、それを「タヌゲット」ずしお保存したした。 アニメヌションサむクルキャンバスの再描画に䜿甚されたものず同じ内で、スムヌズ機胜を䜿甚した「カメラ」の珟圚の䜍眮がタヌゲットの近くに移動したした。 これにより、急激な動きの圱響を取り陀くこずができたした。



キヌボヌドの矢印たたはWASDを䜿甚しおキャンバスをナビゲヌトするこずができたした。 これらのキヌは、内郚モヌションベクトルを制埡したした。 どのキヌも抌されおいない堎合、デフォルトのベクトルには座暙0、0がありたす。 いずれかのナビゲヌションキヌを抌すず、 xたたはy. 1が远加されたすy. たずえば、「右」ず「䞊」を抌すず、ベクトルの座暙は1、-1になりたす。 次に、このベクトルをアニメヌションルヌプ内で䜿甚しお、「カメラ」を移動したした。


アニメヌション䞭に、次の匏を䜿甚しお、近䌌のレベルに応じお移動速床が蚈算されたした。


 movementSpeed = maxZoom / currentZoom * speedMultiplier 

ズヌムをオフにするず、ボタンの制埡が速くなり、はるかに自然になりたした。


次に、動きベクトルが正芏化され、動きの速床が乗算され、「カメラ」の珟圚の䜍眮に適甚されたした。 正芏化は、察角線ず盎亀の動きの速床が䞀臎するように䜿甚されたした。 最埌に、動きベクトル自䜓の倉化に滑らかさ関数を適甚したした。 , «» .



iOS- Android- . -, , . -, , OAuth: WebView . OAuth JS- WebView. . API:


 r.place.injectHeaders({'Authorization': 'Bearer <access token>'}); 

iOS , . WebView, . , iOS 8 JS-:


 webkit.messageHandlers.tilePlacedHandler.postMessage(this.cooldown / 1000); 

.


ここに画像の説明を入力しおください



-


. , . . . , , - . そう


. r/Place :


ここに画像の説明を入力しおください


ここに画像の説明を入力しおください


. , . , . , , :


ここに画像の説明を入力しおください


, , . «» ? , Place , . , , Live- . , , , . .


, : – . .


:


ここに画像の説明を入力しおください


, . , production- RabbitMQ, -, , reddit.com. . .


, . - , , Rabbit Diamond collector . , , -, , , Rabbit, . – .


, :


 $ cat s****y_diamond.sh #!/bin/bash /usr/sbin/rabbitmqctl list_queues | /usr/bin/awk '$2~/[0-9]/{print "servers.foo.bar.rabbit.rabbitmq.queues." $1 ".messages " $2 " " systime()}' | /bin/grep -v 'amq.gen' | /bin/nc 10.1.2.3 2013 

, - , : . - .


, :



. r/place/new:


ここに画像の説明を入力しおください


, .



. , . , , . そしおたた。 そしおたた。 - , , , .


Place, , , « » . . , Fastly.


-


r/Place , . u/gooeyblob, u/egonkasper, u/eggplanticarus, u/spladug, u/thephilthe, u/d3fect , .



Source: https://habr.com/ru/post/J326984/


All Articles