キューとロック。 理論と実践

HighLoad ++の後に呼気を吐き出し、過去数年間の最高のレポートを公開し続けています。 HighLoad ++は素晴らしい結果をもたらし、組織の改善の数はけいれん的に新しい製品品質に成長しました。 ちなみに、Habrは会議のテキスト放送を実施しました( 12 )。



アレクレンダダーカレダンダー



アレクサンダー・カレンダーレフakalend



こんにちは、同僚の皆さん! 私のレポートは、HighLoadプロジェクトでは不可能なことについてです。キューサーバーについてです。時間があれば、ロックについて説明します(復号化機能に注意してください。







レポートの内容は何ですか? キューの使用場所と使用方法、これらすべてが必要な理由、およびプロトコルについて少し説明します。







なぜなら 私たちの会議はHighLoad Juniorを呼び出します。私はJuniorプロジェクトから行きたいと思います。 典型的なジュニアプロジェクトがあります-これはデータベースにアクセスするある種のウェブページです。 たぶん、これは電子ストアかそこにある何かでしょう。 それで、ユーザーは私たちのところに行きましたが、ある段階でエラーが発生しました(別のエラーかもしれません):







私たちはオンラインになり、スケーリングの方法を探り始め、バックエンドを取得することにしました。







より多くのユーザーが行き、より多くのユーザーが行きましたが、別の間違いがありました。







次に、ログを調べて、SQLサーバーを拡張する方法を検討しました。 複製を見つけて作成しました。







しかし、ここMySQLではエラーが発生しました:







さて、ここで比fig的に示したように、エラーはより単純な構成で発生する可能性があります。



そしてこの瞬間から、私たちは建築について考え始めます。







「顕微鏡下」でアーキテクチャを調べ、2つのことを強調します。







1つ目は、実行する必要があるロジックの重要な要素です。 2番目-遅くまで延期できる、遅くて不必要なもの。 そして、このアーキテクチャを分離しようとしています:







それを2つの部分に分けました。



1つのサーバーに1つのパーツを配置し、別のサーバーに別のパーツを配置しようとします。 このパターンを「cな学生」と呼びます。







彼はかつてこれを「苦しめ」ました。彼は両親に私がすでにレッスンを終えて散歩に出かけたことを伝え、翌日これらのレッスンをクラスの前に読み、2分で教師に話しました。



このパターンには、より科学的な名前があります。







そして最終的に、Webサーバーとバックエンドサーバーがあるこのアーキテクチャに到達します。







彼らはどういうわけか彼ら自身の間で接続される必要があります。 これらが2つのサーバーである場合、これは簡単になります。 複数ある場合は、もう少し複雑です。 そして、それらをどのように接続するのでしょうか? そして、これらのサーバー間の通信のソリューションの1つはキューです。







キューとは何ですか? キューはリストです。







長くて退屈なものもありますが、これは要素を記述し、それらを読み取って消し、実行するだけのリストです。 リストが続き、要素が減り、キューが非常に規制されます。



2番目の部分に移ります-どこで、どのように使用されますか? 私はかつてこのようなプロジェクトで働いていました:







Yandex.Marketのこのアナログ。 このプロジェクトでは、さまざまなサービスが回転しています。 そして、これらのサービスはどういうわけか同期する必要がありました。 データベースを通じて同期されました。







キューはデータベースにどのように基づいていますか?







一定のカウンターがあります-MySQLでは自動インクリメントで、Postgressではサイケンを通じて実装されます。 いくつかのデータがあります。



データを書き込みます:







データを読む:







キューから削除しますが、完全に幸福にするにはロックが必要です。







それは良いですか悪いですか?



遅いです。 これに対する私の与えられたパターンは存在しませんが、場合によっては多くの人がデータベースを介してキューを作成します。







まず、データベースを介して履歴を保存できます。 次に、削除されたフィールドが追加され、フラグと書き込みタイムスタンプの両方になります。 私の同僚は、順番に、アフィリエイトネットワークを介して通信を行い、バナーを記録します。クリック数を記録し、Vertikaの分析でクラスターを見つけ、どのユーザーグループがどのバナーにさらに応答します。



これにはMongoDbを使用します。







原則として、すべてが同じであり、一部のコレクションのみが使用されます。 このコレクションに書き込み、このコレクションから読み取ります。 削除-アイテムを読み取り、自動的に削除します。







また、低速ですが、DBよりも高速です。 必要に応じて、統計でこれを使用します。これは正常です。



さらに、私はそのようなプロジェクトで働いていました、それは社会的なおもちゃでした-「片腕の盗賊」:







ボタンを押すと、ドラムが回転し、偶然の一致がなくなります。 このおもちゃはPHPで実装されましたが、データベースを処理できないため、リファクタリングするように頼まれました。







これにはTarantoolを使用しました。 簡単に言えば、Tarantoolは重要な価値のあるリポジトリであり、ドキュメント指向のデータベースにより近くなっています。 Tarantoolに運用キャッシュを実装しましたが、少ししか役に立ちませんでした。 これらすべてをキューで整理することにしました。 すべてが完璧に機能しましたが、一度すべてが落ちました。 バックエンドサーバーが落ちました。 また、Tarantoolでは、キューが蓄積され始め、ユーザーデータが蓄積され、メモリはメモリのみのストレージであるため、メモリがいっぱいになりました。







メモリがいっぱいで、すべてが落ち、ユーザーデータが半日失われました。 ユーザーは少し不幸で、どこかで遊んだり、失ったり、勝ったりします。 負けた人は勝った彼にとっては良いことです。 どんな結論? 監視を行う必要があります。



監視するものは何ですか? キューの長さ。 平均長を5倍、10倍、20倍超えていることがわかった場合は、SMSを送信する必要があります。そのようなサービスはTelegramで作成されています。 電報は無料で、SMSにはまだお金の価値があります。



Tarantoolは他に何を提供してくれますか? Tarantoolは優れたソリューションです。すぐに使えるシャーディング、すぐに使えるレプリケーションがあります。







Tarantoolには素晴らしいQueueパッケージもあります。







私が実装したのはまだ4〜5年前で、そのようなパッケージはまだありませんでした。 非常に優れたTarantool APIが登場しました。Pythonを使用している人は、一般的にキューに合わせたAPIを持っています。 私自身は、2002年から15年前からPHPを使用しています。 私はPHPでTarantoolのモジュールを開発したので、PHPは私に少し近づいています。



キューへの書き込みとキューからの読み取りの2つの操作があります。 この数値(スライド上の青色の0.1)に注意を払いたい-これがタイムアウトです。 また、一般に、キューを解析するバックエンドスクリプトの作成にアプローチする場合、同期と非同期の2つのアプローチがあります。







同期アプローチとは何ですか? これは、キューから読み取りを行い、データがある場合はそれを処理し、データがない場合はロックを取得して、データが到着するまで待機するときです。 データが来たので、さらに読みに行きました。







明らかなように、非同期アプローチでは、データがない場合、別のキューから読み取るか、他の操作を実行します。 必要に応じて、待ってください。 そして再び、サイクルの始まりに行きます。 誰もが理解している、すべてが非常に簡単です。







どのようなパンがキューパッケージを提供しますか? 優先順位のあるキューがありますが、他のキューサーバーの中で他のどこにも見られません。 そこでもキューの要素に命を吹き込むことができます-時には非常に便利です。 配達確認-それだけです。 私は同期と非同期について話しました。







レディス これは私たちの動物園であり、多くの異なるデータ構造があります。 キューはリストに実装されます。 良いリストとは何ですか? リストの最初の要素または最後の要素へのアクセス時間が一定時間で発生するという点で優れています。 キューはどのように実装されますか? リストの先頭から書き込み、リストの末尾から読み取ります。 あなたは反対を行うことができます、それは問題ではありません。







私はそのようなおもちゃで働いていました。 このおもちゃはVKontakteで書かれました。 従来の実装では、おもちゃはすぐに動作し、フラッシュドライブはWebサーバーと通信しました。







すべてが完璧でしたが、上から言われたら、「統計を使いましょう。パートナーは、購入したユニットの数、最も購入したユニットの数、残っている数、ユーザーのレベルなどを知りたいです。」 そして、彼らは我々が車輪を再発明せず、外部の統計スクリプトを使用することを提案した。 そしてすべてが素晴らしかった。







私のスクリプトだけが50ミリ秒動作し、外部スクリプトに目を向けると、ある種のアメリカがあり、少なくとも250ミリ秒、または2秒以上のpingがそこに行きました。 したがって、おもちゃ全体がクラッシュしました。



このスキームを使用しました。







そして、すべてが順調で、すべてが迅速に機能しました。 しかし、ある日、管理者が休暇に出かけました。 管理者は休暇を取り、最初の1週間はすべて順調でしたが、1週間後、Redisが流れていることがわかりました。 Redisは流れており、管理者はいません。午前中にコンソールを見ます。スワップの前に残っているメモリの量、スワップの前に残っているメモリの量を調べます。 多くのユーザーが金曜日に来ました。特に昼食後、十分な記憶がありませんでした。







結論はタランツールと同じです。 別のプロジェクト、間違いは同じです。 メモリを監視する必要があります。 キューの長さを監視する必要があります。 誰もが監視について多くのことを話します。私は繰り返しません。







Redisでは、ブロッキング読み取り操作とノンブロッキング読み取り操作の両方を実行することもできます。 監視のためだけにカウント操作が必要です。



どういうわけか、私は人気のあるビデオホスティングサイトからビデオをダウンロードするプロジェクトにリモートで取り組みました。







このプロジェクトの問題は何ですか? 実際、ビデオをアップロードすると変換されますが、ビデオが少し長ければ、Webクライアントは落ちます。 次のスキームを使用しました。







すべて順調です。 ファイルをダウンロードしました。 しかし、キューは一方向でのみ機能するため、Webスクリプトに通知する必要があります。ファイルは既にアップロードされています。







これはどのように行われますか? これには2つの方法があります。



















最初に、特定のタイムアウト後にステータスを確認します。 ステータスとは何ですか? これはキーバリューリポジトリです。同じRedisを使用することをお勧めします。 キーは、URLからMD5ハッシュを提供できます。 そして、変換後、キー値にステータスを書き込みます。 ステータスには、「完了」、「変換済み」、「見つかりません」などがあります。 1秒後、一定の時間の後、スクリプトはステータスを要求し、完了したかどうかを確認し、クライアントにすべてを表示します。 すべてが明確です。 これは、プーリングを使用した最初の方法です。



2つ目はWebソケットです。



















ファイルをアップロードします-これが2番目の方法です。 これはサブスクリプションです。 ここでは、Webソケットを使用しました。



これはどのように行われますか? ダウンロードを開始すると、すぐにRedisのチャンネルにサブスクライブします。 たとえば、memcahedなどを使用できた場合、Redisを使用しなかった場合は、Redisに関連付けられています。 チャンネル、チャンネル名を購読します。 大まかに言えば、URLからの同じMD5ハッシュです。



ファイルをダウンロードしたらすぐに、ステータスが「completed」またはステータスが「not found」のチャネルを取得してプッシュします。 そしてすぐに、すぐに私たちと一緒に、PushはステータスをWebスクリプトに与えます。 その後、ファイルが見つかったらアップロードします。







直接ではなく、ほぼそのようなスキームです。







これはどのように行われますか? データの特定のソースがあります-火山の温度、NASAから送信された望遠鏡を通して見える星の数、特定の株の取引の数...このデータを受け入れ、このデータを受け取ったバックグラウンドスクリプトはそれを特定のチャネルにプッシュします。 通常、JSノードが使用されるWebソケットを介したWebスクリプトは、特定のチャネルにサブスクライブし、そこでデータが受信されるとすぐに、このデータをWebソケットを介してクライアントスクリプトに送信し、そこで表示されます。







そのような解決策があります-MamecachedQ。 これはかなり古い解決策です。最初の1つと思います。 MamecachedとBerkeleyDbを使用して生成された、最も初期のキーバリューリポジトリの1つです。



この決定で注目すべきことは何ですか? Mamecachedプロトコルが使用されているという事実。







大きなマイナスは何ですか?ここでは、キューの長さを監視しません。 私が言ったこと-監視が必要ですが、この監視はここにありません。



キューについて言えば、Zerro MQについては言えません。







Zerro MQは優れた迅速なソリューションですが、キューブローカーではないため、理解する必要があります。 これは単なるAPI、つまり 1つのポイントを別のポイントに接続します。 または、多くのポイントを持つ1つのポイント。 ただし、ここにはキューがありません。ポイントの1つが消えると、一部のデータが失われます。 もちろん、同じZerro MQに同じブローカーを記述して実装することもできます...







Apache Kafka。 どういうわけか私はこのソリューションを使用しようとしました。 これはhadoopスタックのソリューションです。 原則として、優れた高性能ソリューションですが、大量のデータフローがあり、それを処理する必要がある場合に必要です。 そして、私はより簡単なソリューションを使用します。







非常に長い時間設定し、Zookepekなどで同期する必要があります。



プロトコル プロトコルとは何ですか?







あらゆる種類のソリューションを紹介しました。 ITコミュニティは「なぜ自転車を発明するのか、全体を標準化しよう」と考えて言った。 そして、プロトコルを思いついた。 最も初期のプロトコルの1つはSTOMPです。







彼の説明は、キューでできることをすべて網羅しています。



2番目のプロトコルMQTTは、Message Queue Telemetry Transportプロトコルです。







STOMPとは異なり、STOMPと同じすべての機能を原則的にカバーしますが、バイナリであるという事実により、STOMPはバイナリです。



以下は、プロトコルを操作するキューブローカーの最も著名な代表者です。







ActiveMQは、3つすべてのプロトコルを使用し、4つ(もう1つあります)も使用します。 RabbitMQは3つのプロトコルを使用します。 QpidはQとPを使用します。



AMQP-Advanced Message Queuing Protocolについて簡単に説明します。







彼のことを長い間話したら、彼の機能について1時間半も話すことができます。 簡単に。 ブローカーを理想的なメールサービスとして提示します。 Exchange-これは、メッセージが到着する送信者のメールボックスになります。







このExchangeにはタイププロパティがあります。







ここでは、PHPで宣言する方法を示しています。 ところで、このドライバーも開発しました。







送信者ボックスがあれば、受信者ボックスが必要です。 受信者のボックスには、呼び出しごとに1文字しか受け取れないという機能があります。 受信者のボックスにも名前とプロパティがあります。 次のようなものを宣言する必要があります。







送信者のメールボックスと受信者のメールボックスの間で、郵便配達員が走って私たちの手紙を運ぶルートを作成する必要があります。







このルートは、ルーティングキーによって決定されます。







接続を宣言するとき、ルーティングキーを指定する必要があります。 これは、接続を宣言する1つの方法です。



2番目のアプローチがあります。 Exchangeを宣言して、そこからキューにバインドすることができます。 その逆です-キューからExchangeへ、またはExchangeからキューへの接続を確立できますが、違いはありません。





メッセージがあります。 メッセージは、必ずroutingKey、つまり これが、郵便配達員がルートを実行するためのキーです。







私たちの郵便配達員には3つのタイプがあります:











  1. 最初のタイプは盲目の郵便配達員です。 彼らは鍵を読むことができず、彼らが私たちのために置いたルートに沿って走るだけです。 これらの郵便配達員は走っていて、これ

    最速の郵便配達員。

  2. 2番目のタイプの郵便配達員は少し読むことができますが、手紙を確認しますが、どのようなものかわかりません...彼らは手紙を確認しました。

    対応するパスを実行し、そのパスに沿って実行します。 つまり 私たちの受信者は、偶然の鍵に純粋になります。

  3. そして、3番目の種類の郵便配達員はトピックです。 マスクを設定できます。マスクはファイルシステムと同じで、郵便配達員はこのマスクで手紙を持ちます。



メッセージの送信方法のように見えます:







私たちの典型的な間違いは何ですか?







典型的な間違いは、接続の定義を忘れることが多いことです。 3番目のRabbitは多かれ少なかれ、Webインターフェースを備えており、Webインターフェースを介してすべてを見ることができます:言うこと、キュー、タイプ、交換の種類、持っているタイプ。



2番目の典型的な間違い。 キューまたは交換を宣言すると、デフォルトで自動削除されます-セッションが終了し、キューが削除されました。 したがって、毎回再アナウンスする必要があります。 原則として、これを行うことは望ましくありませんが、永続的なキューを作成し、耐久性を割り当てることをお勧めします。 耐久性は、永続キューがある場合、RabbitMQを再起動した後、このキューが一緒に存在するという兆候です。







RabbitMQはどうですか? 管理するのはあまり快適ではありませんが、Erlangを知っていれば拡張できます。 彼は記憶を非常に要求しています。 RabbitMQはErlang組み込みソリューションで動作しますが、大量のメモリを消費します。 他のリポジトリで動作するプラグインがいくつかありますが、私は正直にそれらで動作しませんでした。







このような雑誌「システム管理者」で、「サンドボックスのうさぎ」という記事を書きました-原則として、ここで言ったことと同じです。







また、この記事では、RabbitMQの使用方法、そこでキューのリダイレクトを実行できる方法、たとえば、キュ​​ーが読み取られない場合にこのデータが別のキューに書き込まれるようにする方法など、より興味深いパターンを描きました。別のスクリプトを読みます。 可能であれば、読んでください。



ロック







私は最初にそのようなプロジェクトについて話しました。 今日このプロジェクトを行った場合、マイクロサービスを使用します。







また、マイクロサービスでは、相互作用として同期が必要です。 同期には、Apache Zookeeperなどのツールを使用します。







Apache Zookeeperの哲学はznodeに基づいています。 Znodeは、ファイルシステムの要素と同様に、特定のパスを持っています。 また、ノードの作成、ノードの子の作成、子の受信、データの受信、データへの書き込みなどの操作があります。



Znodeには、シンプルとエフェメラルの2種類があります。







一時的-これらはznodeであり、ユーザーセッションが終了した場合、znodeは破棄され、自動削除されます。



シーケンスはzノードの自動インクリメントです。 これらはznodeであり、特定の名前と数値のプレフィックスが自動インクリメントされます。







設定例をオンザフライで使用して、すべての仕組みを説明します。 プロセスグループには、プロセスグループaとプロセスグループbの2つのグループがあります。 プロセス1はプロセス2に接続し、何らかの形で相互作用します。 プロセス2は、起動されると、Zookeeperに構成を書き込みます。







各プロセスは独自のznodeを作成します-最初のプロセス、2番目、3番目。



そして、プロセスの1つを停止するか、たとえば開始しました。 ここで、プロセスを停止する例について、以下を示しました。







プロセスが停止し、接続が切断され、znodeが削除され、イベントが送信されます。このznodeをリッスンしていたこと、1つのznodeが欠落していること。



















イベントが送信され、構成を再カウントします。 すべてが非常にうまく機能します。







このようなものはすべて同期します。 誰かがそこでバックアップと同期する方法の他の例があります。







この最後のスライドでは、キューサーバーのすべての機能を紹介します。 疑問符があるのは、議論の余地があるか、単にデータがありませんでした。 , , ? , , , . ? , . . ActiveMQ . Redis — ACL , . , . Redis? , - , , .







:



  1. . , RabbitMQ ,

    RabbitMQ Redis'.



  2. , . ? , . ,

    — .



  3. , .



連絡先



» akalend

» akalend@mail.ru



HighLoad++ Junior .



- HighLoad.Guide — , , , . 30 . !



— " - ", , HighLoad++ Junior . , , , — :)








All Articles