リアルタイムシステムとしてのWebサービス

12月上旬、サンクトペテルブルクで、Mail.Ru Groupとのパートナーシップにより、ACM ICPCプログラミング世界選手権の準決勝が開催されました。 チャンピオンシップの一環として、参加者と会い、Webサービスをリアルタイムシステムにする方法について話しました。 そして今、Habréに関する私のレポートを共有したいと思います。



リアルタイムシステムといえば、私たちは人々の生活が情報システムの反応の速度に依存する原子力発電所、飛行機、または同様のものを表します。 リアルタイムシステムでガベージコレクションのためにチームが10秒間スローダウンする場合、結果は嘆かわしい以上のものになる可能性があります。 反応は瞬時に、保証された時間である必要があります。



もちろん、Webサービスが実行されているとき、人の生活はメールで手紙を開いた速さに依存しませんが、Webサービスの要件はほぼ同じです。 15年前、ユーザーがリンクをクリックしたとき、彼は10秒の反応を期待していました。 当時の低速イン​​ターネットでは、これは正常でした。 現代のインターネットとは、広いチャネル、高速のコンピューターを意味します。 ユーザーにとっては、すべてが迅速に機能し、サービスにも同じことが期待されます。



ユーザーがどこかをクリックすると、クリックに対する反応を即座に受け取ることを期待します。 インスタントとは何ですか? 人にとっては、実際の人間の目は約10ミリ秒の間を区別しますが、快適な遅延は200ミリ秒のオーダーの応答時間と見なされます 。 Webサービスは200ミリ秒以内にユーザーのアクションに応答する必要があります-少ないほど良いです。



したがって、実際には、最新のWebサービスはリアルタイムシステムである必要があります。 この要件を満たす方法については、Mail.Ru Mailの例で説明します。



リクエストの処理時間はいくらですか



Webサービスへのリクエストの処理時間は、次のもので構成されます。



1最初はDNSクエリです。 もちろん、クライアントがしばらくの間DNS解決の結果をキャッシュするため、DNSクエリは常に発生するわけではありません。 原則として、長期間キャッシュできますが、DNSレコードにはTTLなどがあります。 小さい場合、クライアントはTTL減衰中に別のDNSクエリを作成する必要があります。 今がその時です。



2次に、ブラウザからサーバーへの接続があります。 接続は複数のネットワークラウンドトリップであり、HTTPS接続の場合はさらに多くのラウンドトリップがあります。 また、時間です。



3接続後、要求はネットワーク経由で送信されます。 ブラウザはリクエストを送信し、サーバーで処理されます。



4次に、ブラウザはサーバーからの応答を受信し、HTMLを美しいビューにレンダリングします。その後、ユーザーに表示します。



これらのアクションはすべて厳密に順番に実行されることを理解することが重要です。 いずれかの段階での遅延は応答時間の増加につながりますが、別の段階を最適化した場合、それを短縮することはできません。 たとえば、サーバーでのリクエストの処理をどのように最適化しても、接続が遅い場合、リクエストの合計処理時間は接続時間より速くなりません。



広大さを受け入れることは不可能なので、この記事ではサーバーでの処理を検討し、Mail.Ru Mailでどのように高速を実現するかを説明します。



サーバーで何が起こるか



サーバーでの処理も、いくつかの連続したアクションで構成されます。



Webサーバーは次のことを行う必要があります。



1接続を受け入れます。

2解析リクエスト。

3リクエストをハンドラに渡します。

4リクエストのすべてのビジネスロジックを実行します。

5ハンドラーから応答を取得します。

6クライアントに答えを与えます。



繰り返しますが、これらのアクションはすべて厳密にシーケンシャルであり、上記のその他すべてのアクションも要約しています。



Mail.Ru Mailでメッセージを読み取る特定の例を見てみましょう。リクエスト処理の内部で何が起こっているのか、どのようなビジネスロジックがあるのか​​を見てみましょう。















readmsg処理の内部で何が起こりますか? Webサーバーのビジネスロジックプロセッサは、最初にセッションを確認し、メッセージヘッダーを取得するストレージサーバーを見つける必要があります。 次に、メッセージヘッダーのデータを要求します。これは別のサーバーへの旅行です。 その後、彼は文字のテキストを要求し、その中にXSSが存在するかどうかを解析し、そこから自然に切り取ります。 次に、フィッシングの画像をチェックし、最後に、スパム対策のために内部統計システムにデータを送信します。



したがって、要求処理は多数の順次アクションです。 すべてがサンドイッチのように配置されています。1つのサーバーが別のサーバーをアドレス指定し、1つのサーバーが次のアドレスを指定します。

ここには、最も基本的なアクションのみがリストされています。 現在、それらはすべて順次実行されています。 私たちはこれらのプロセスの並列化に取り組んでいますが、今のところ、持っているものを最適化しています。



したがって、これらすべてのアクションは可能な限り迅速に行う必要があります。 次に、これを達成する方法について、ポイントごとに説明します。



セッション検証



セッションはインメモリデータベースに保存する必要があります。 この目的でMySQL、Postgres、またはOracleを使用することはお勧めしません。 なんで? 実行時に予測できないことがSQLデータベースで発生する可能性があるため(たとえば、インデックスがキャッシュからプッシュされる)、ディスクに移動し、これを実行するのを止めることはほとんどできません(理論的にはもちろん、できますが、常に必要です)データベースはすべてをディスクに保存するため、これを念頭に置いてください)。 ロードされたデータベースでのディスクアクセスは、時間内に予測不能です。 たぶん、いや、そしてスティック。



さらに、汎用SQLデータベースは多くの異なる不必要なロジックを実行します。それは、それが愚かであるためではなく、あまりにも一般的であり、特定のタスクに合わせられていないためです。 これは、セッション検証の一部として何を意味しますか? ある種のデータベースにアクセスしてSELECTを実行し始め、データベースも一貫して多数のアクションを実行し、応答時間全体を増加させ続けていること。



SQLに保存することはできず、NoSQLストレージのみ、メモリのみ、ハードコアのみを保存できます。 Mail.Ruでは、独自のTarantoolデータベースをインメモリストレージとして使用します。 メモリからのみ読み取り、メモリおよびディスクに書き込みます。 APPENDのみが使用され、ランダムシークがないため、記録も非常に高速です。 したがって、ほとんどの場合、Tarantoolからの答えは1ミリ秒の何分の1かです。



メールストレージ



次に、メールストアへのクエリを検討します。 ここではディスクから逃げることはできませんが、最適化の余地はまだあります。



まず、ストレージプロトコル自体に最小限のネットワークラウンドトリップが必要です。 理想的には、1つ、最大2つです。 内部ネットワークも完全ではないという理由だけで、各ラウンドトリップが続く可能性があるため、どこかで速度が低下する可能性があります。 往復が多いほど、この事故に遭遇する可能性が高くなります。 また、たとえば、リクエスト全体を構成する30個のうち少なくとも1回のラウンドトリップでこのランダム性をキャッチすると、リクエスト全体が遅くなります。 したがって、往復回数が少ないほど、この確率は低くなります。



さらに、ストレージ自体は、メモリ内で最もホットなデータをキャッシュし、ディスクへの数ヒットに対して文字通りキャッシュされないものを提供する必要があります。 これらがランダムヒットである場合、常に非常に少ないはずです。 長いファイルに対して多くの検索を行うべきではありません。異なる場所からの多くの読み取りがディスクに負担をかけ、頭を動かします。これにより、必然的にリクエストの処理時間が増加します。



文字の解析



動的メモリを使用せずに、1つのパスで解析を行う必要があります。 そして、最も重要なのは、この基本原則を変更せずに、解析に新しい機能を追加することです。 なぜそう はい。パスが2つあり、2つ、3つ、3つ、10つあり、ミリ秒後にミリ秒単位でクエリ時間を増やし始めるためです。



なぜ動的メモリがないのですか? 動的メモリのランタイムは予測不能だからです。 メモリが断片化されている場合-長い間、幸運であり、断片化がない場合、すぐに。 このインジケーターに依存することはできませんが、リアルタイムシステムがあるため、mallocを使用しないでください。たとえば、スラブアロケーターなどのアロケーター、またはある種の静的バッファーを使用する必要があります。



フィッシング



画像のフィッシングの確認は、フィッシング対策データベースが完全にメモリ内になければならないという意味で(MemcachedやTarantoolなど)、セッションの呼び出しと非常に似ており、メッセージ内のすべての画像に対して1つの要求を行う必要があります。 レターに100枚の写真が含まれている場合、memcacheに100リクエストする必要はありません。 1つの接続のフレームワーク内でも、100のリクエストは100の往復です。 1つのリクエストを行い、そこからすべてを一括取得します。 各ラウンドトリップは、内部ネットワークの場合でも、遅延に10分の1ミリ秒を加えたものです。 そして、この遅延は、第一に増加しており、第二に、例えば100ミリ秒でどこかに移動する可能性があります。 偶然。 そしてそれだけです。 繰り返しますが、リクエストが多いほど、発生する可能性が高くなります。



統計



統計も最小限のプロトコルを使用して送信する必要があります。 理想的には、UDP経由ですが、TCPであっても、タイムアウトを設定して、数ミリ秒以下の制御された時間で動作するようにする必要があります。 さらに、統計ではサードパーティのライブラリを使用しないでください。 サードパーティのライブラリを使用する場合、それらを完全に確認する必要があります。



良い例があります。隣の部署の人があなたのところに来ると想像してください。 彼は、オンラインで機能するコードで、たとえばスパム対策部門に統計を送信するためのロジックを追加するように求め、ライブラリを提供します。 あなたは彼のコードを素朴に自分自身に挿入し、それを通して統計を送り、それを本番に置くと、すべてがあなたのために遅くなる。 なんで? また、このライブラリは、たとえば、何らかの種類のキューをメモリに格納しているため、キューがオーバーフローし、屋根が吹き飛ばされます。 コードにタイムアウトがあるように見えます。 読み取りタイムアウトだけであり、接続タイムアウトではないことがわかりました。



したがって、サードパーティのライブラリではタイムアウトと呼ばれるパラメーターを信じないでください。 誰も信じないで、あなたの目だけを信じてください。 この機能に責任がある場合は、使用するすべてのコードを自分で確認し、その仕組みを完全に理解する必要があります。 自分で何かを書いたり、速度を落とすことのできない有名なものを使用したりする方が、安くて速い場合もあります。



結論



結論として-オンラインで動作するWebシステムのこれらの部分の開発者向けのいくつかの結論。



常にミリ秒を覚えておいてください。 それらは合計されるだけで、残念ながら減算されることはありません。 何らかの遅延がある場合は常に追加されます。



使用する場合は、サードパーティのコードのパフォーマンスを確認してください。



すべてをタイムアウトで囲みます。



さて、データベース、いくつかのサードパーティのリポジトリなどを参照しても、内部に魔法があり、すべてが即座に行われるとは考えていません。 実際、同じコード内では最適ではない可能性があります。



Webサービスの作業の最適化についてまだ質問がある場合、または有用な経験を共有したい場合は、コメントでお話しできることを嬉しく思います。



All Articles