新しい1C-Bitrixプッシュサーバーについて





少し前に、 Bitrix24サービス用の新しいプッシュサーバーを開発する必要がありました。 Nginxのモジュールに基づいて実装された以前のバージョンには、多くの問題を引き起こす多くの機能がありました。 その結果、プッシュサーバーを作成する時が来たことがわかりました。 ここで、これがどのように起こったかについてお話したいと思います。



プッシュサーバー(別名プルサーバー、別名インスタントメッセージングサーバー)は、ブラウザーを介してポータルにアクセスするユーザー、またはデスクトップアプリケーションやモバイルアプリケーションを使用して接続するユーザー間でメッセージをすばやく交換できるように設計されています。 ブラウザとアプリケーションの両方が、プッシュサーバーへの永続的な接続を確立して維持します。 これは通常、WebSocketを使用して行われます。このテクノロジーがブラウザーでサポートされていない場合は、ロングポーリングが使用されます。これは絶えず長い調査です。 これは、サーバーからの応答を40秒待機するAjax要求です。 応答が受信されるか、タイムアウトが発生すると、要求が繰り返されます。 現在、ほとんどのお客様はWebSocketを使用しています。







ポーリング -ロングポーリング技術を使用した化合物の数

Websockets -WebSocketsテクノロジーを使用した接続の数。

チャネル - チャネルの数。



私たちに合わなかったもの



以前のサーバーは次のように機能しました。システムはnginxモジュールでユーザーにメッセージを公開し、メッセージを保存して受信者に送信する役割をすでに担っていました。 目的のユーザーがオンラインの場合、すぐにメッセージを受け取りました。 ユーザーが不在だった場合、プッシュサーバーは、蓄積されたメッセージを転送するために彼の出現を待っていました。 ただし、nginxモジュールは頻繁にクラッシュし、未送信のメッセージはすべて失われました。 それは問題の半分になりますが、各モジュールのドロップ後、PHPバックエンドの負荷が大幅に増加しました。



これは、プッシュサーバーのアーキテクチャが原因でした。 クライアントが接続を確立すると、一意のチャネル識別子が割り当てられます。 つまり、ユーザーは特定のチャネルを聴き、ポータルはそのチャネルにメッセージを書き込みます。 さらに、そのような権利を持っているのはポータルだけです。これはセキュリティのために行われたものであり、外部からこのチャネルに書き込むことはできません。



チャネルを作成するには、ポータルがメッセージを送信してチャネルを初期化する必要があります。 そして、nginxモジュールがクラッシュすると、すべてのチャネルが無効化され、すべてのポータルが同時にプッシュサーバーに新しいチャネルを作成し始めました。 それは一種のDDoSであることが判明しました。ポータルが機能するバックエンドのPHPは、応答を停止しました。 これは深刻な問題でした。



プッシュサーバーに蓄積されたメッセージをゼロにすることは大きな問題ではありませんでした。メッセージはポータルで複製され、ユーザーはページが更新されたときに受信したためです。 ただし、DDoSは、ポータルが利用できない可能性があるため、はるかに深刻な問題でした。



新しいプッシュサーバー



新しいサーバーを最初から作成することにしました。 開発環境として、Node.jsを使用することにしました。 それ以前は、このプラットフォームでは動作しませんでしたが、その助けを借りて、多くの接続を保持するサービスを作成できるという事実に買収されました。 さらに、JavaScriptを使用する多くの開発者がいるため、新しいシステムをサポートする人がいました。



重要な条件は、以前のサーバーで使用するプロトコルとの互換性を維持することでした。 これにより、JavaScriptで実装されたブラウザに実装されたクライアント部分を上書きできなくなりました。 また、プッシュサーバーにメッセージを送信するバックエンドのPHP部分をそのままにすることもできました。



まず、パフォーマンスをテストするために小さなプロトタイプが作成されました。 その機能は制限されており、すべてのメッセージをNode.jsのメモリに直接保存しました。 プッシュサーバーは同時に何万もの接続を保持する必要があるため、開発ではクラスタリングサポートを実装しました。 現在、ロシアのセグメントには、着信接続を提供するNode.jsアプリケーションの6つのプロセスと、メッセージの発行を担当する2つのプロセスで構成されるプッシュサーバーがあります。



プロトタイプや完成品で行われたように、メッセージをメモリに保存することは不可能でした。 ポータルに複数のユーザーがいて、リクエストが異なるプロセスで処理されているとします。 これらのユーザーが一般的なチャットに参加している場合、そのユーザーからのすべてのメッセージはすべての参加者に同時に送信されます。 また、各プロセスのメモリ領域は分離されているため、プッシュサーバーのメモリにメッセージを保存することはできません。 もちろん、共有メモリのようなこともできますが、このアプローチは実装するのにあまり信頼性がなく、便利ではないため、すべてのメッセージをRedisに保存することにしました。 これは、memcacheのようなNoSQLキー値リポジトリですが、より高度です。 Key-Valueだけでなく、Key-Dictionary、Key-List、つまり、より複雑なデータ構造も保存できます。 そのため、Redisを使用して、すべてのメッセージ、チャネル統計、オンラインステータスを保存します。



Redisには便利な機能もあります- チャンネルをサブスクライブする 。 つまり、Node.jsアプリケーションの8つのプロセスはすべて、Redisと永続的に接続しています。 また、メッセージが書き込まれた場合、メッセージを追加するためにサブスクライブされているすべてのプロセスに送信されます。 一般的なスキームは次のとおりです。ユーザーはポータルにメッセージを書き込み、PHPのバックエンドに移動し、そこで保存されてからNode.jsアプリケーションに送信されます。 メッセージ自体には、すべてのデータ属性、つまり作成者、宛先などが含まれます。 投稿を担当する2つのプロセスのうちの1つは、このリクエストを取得して処理し、Redisに公開します。 彼はメッセージを書き、他の6つのプロセスに新しいメッセージが到着し、サブスクライバーに送信できることを通知します。



WebSocketを使用するために、 wsというオープンソースのNode.jsモジュールを使用しました。



遭遇した困難



開発段階で特別な困難はありませんでした。 テスト段階では、高負荷のエミュレートに焦点を当て、テストは良い結果を示しました。 新しいシステムを展開するとき、最初に古いサーバーを離れることによって自分自身を保護しました。 すべてのメッセージが複製されたため、新しいサーバーがクラッシュした場合に古いサーバーに切り替えることができます。



判明したように、私たちは何も保証しませんでした。 展開後、場合によっては、負荷が大幅に増加するとシステムが低下することが判明しました。 一方では、テストにより、非常に大きな負荷を保持するサーバーの能力が確認されました。 ただし、これは「実際の」負荷ではありません。ユーザーは異なるIP、チャネル、ブラウザからログインし、WebSocketのサポートが異なります。



なぜ転倒が起こったのかはわかりませんでした。 この問題はTCP接続を確立する段階で発生したため、処理を有名で多くの接続を保持できるnginxサーバーに転送することにしました。 同時に、Node.jsプロセスはバックエンドサーバーとして機能し始めました。 新しいスキームでは、いくつかのリンクを削除しました:





その結果、サーバー操作スキームは次のようになり始めました。







改善点



サービスの実装後、何が改善できるかが明らかになりました。 たとえば、プロトコルを最適化し、一部の操作でリソースを節約し始めました。



特に、さまざまな種類のメッセージの保存期間を制限していました。 以前は、すべてのメッセージが1日など、長期間保存されていました。 しかし、すべてのメッセージがサーバー上に長く留まる価値があるわけではありません。 たとえば、チャットで誰かが書き始めたサービスメッセージは、関連性が失われるため、2分以上は表示されません。



ただし、個人メッセージは、送信後数時間たっても宛先に配信するのに十分な時間保存する必要があります。 たとえば、ユーザーはラップトップを閉じて仕事を辞め、朝に開いてすぐに蓄積されたメッセージを受信しました。 または、携帯電話のアプリケーションを最小化し、夕方に展開して、ページをリロードせずに締めくくるコメントを見ました。 しかし、誰かがオンラインになったという報告や、誰かが書き始めたという報告は、数分より長くは続かないはずです。 そこで、メモリに保存されているメッセージの数を節約しました。



また、プッシュサーバーでの作業のセキュリティを改善しました。 ユーザーが参加すると、一意のチャンネル識別子を取得します-これは32文字のランダムな文字列です。 しかし、それを傍受すれば、他の人のメッセージを聞くことができます。 そのため、この特定のチャネル識別子に固有の特別な署名を追加しました。 チャネル自体は、識別子と同様に定期的に変更されます。



チャンネル自体はサーバーに24時間保存されますが、チャンネルへの記録には12時間しかかかりません。 残りの保存時間は、ユーザーが以前に送信したメッセージを受信できるようにするために必要です。 実際、夕方にラップトップが1つのチャネル識別子でスリープ状態になった場合、午前中にラップトップは目覚め、サーバーの方を向きます。 メッセージを送信した後、サーバーは古いチャネルを閉じ、このユーザー用に新しいチャネルを作成します。



* * *



すべての改善と最適化の後、高負荷に耐えることができる新しい安定した動作するプッシュサーバーができました。 ただし、その改良はまだ終わっていません。実装と最適化を計画しているものがいくつかあります。 しかし、これは未来の物語です。



All Articles