図書館
Webソケット用のサーバーとクライアントの作成は非常に困難です。幸いなことに、Webソケット用のサーバーを提供する代替のラチェットライブラリはほとんどありません。 内部では、 ReactPHPとGuzzleのいくつかの部分を使用します (Symfonyコンポーネントにも依存しますが、この場合は完全に冗長であることが判明しました)。 RatchetのPawlも使用します。これはWebソケットのクライアントです。
建築
CleverStyle CMSは複数のサーバーで動作するため、ここでこの機能をサポートすることにしました。 このために、サーバープールが作成されます。 これは、MySQLのMEMORYネームプレートであり、WSSのように接続するためのパブリックアドレスである1つの列があります。 。 起動された各サーバーはそれ自体をプールに追加し、単独ではない場合、最初のサーバーにマスターとして接続します(1つである場合、それ自体がマスターになります)。 マスターが応答しない場合、プールから削除され、次のマスターがマスターになり、全員がそれに接続します。 これにより、転倒に対する抵抗力と完全な分散化が保証されます。
サーバーの1つがクライアントからメッセージを受信すると、そのメッセージをマスターに送信し、マスターはそれを他の全員に送信します。 簡単にするために、サーバーもWebソケットを使用して相互に通信します。 特定のIDを持つユーザーにメッセージを送信する必要がある場合、特にマスターにも送信されるため、ユーザーが接続しているサーバーとタブの数はまったく関係ありません。
メッセージの送受信
メッセージ形式は単純に選択されました:
{ "action" : "some/action", "details" : [] }
アクションは単なる文字列であり、一部にスカラーが含まれている場合があります。その後、1つの要素を持つ配列に変換されます。
この形式は、クライアントからサーバーへ、およびサーバーからクライアントへの送信に一般的です。 唯一の違いは、クライアントのアクションが接尾辞:errorで終了できることです。便宜上、クライアントで2つのコールバック-successとerrorを指定できます。
クライアントでは、 cs.WebSocketsオブジェクトの一連のメソッドによってメッセージが送受信されます(サーバーへの接続を自動的に確立および維持し、現在のセッションを使用して自身を認証します)。
- on(アクション、コールバック、エラー)
- オフ(アクション、コールバック、エラー)
- 一度(アクション、コールバック、エラー)
- 送信(アクション、詳細)
とても単純なので、おそらくどこにも簡単ではないでしょう。 渡される部分は配列であるため、各要素は個別の引数として渡されます。
サーバー上ではすべてが少し複雑です。 メッセージは、 \ cs \ modules \ WebSockets :: send_to_clients($ action、$ details、$ send_to、$ target)メソッドを使用して送信されます(サーバーの下だけでなく、通常のページでも送信できます。この場合、接続は1つで確立されます)サーバーから送信され、メッセージは内部形式で送信され、その後クライアントに届きます)。
追加の引数を使用すると、メッセージを配信する必要があるユーザーまたはグループ、または複数の特定のユーザーを指定できます。
受信するには、標準のイベントシステムを使用し、 WebSockets / {action}イベントにサブスクライブする必要があります( WebSockets / register_actionイベントが発生するとサブスクリプションが作成されます)。 {action}はクライアントから受信したもので、現在のユーザー(送信者)もイベントに送信されます、彼のセッションと言語。
Helloサービスの例:
<?php use cs\Event, cs\modules\WebSockets\Server; // Register actions Event::instance()->on('WebSockets/register_action', function () { // If `hello` action from user Event::instance()->on('WebSockets/hello', function ($data) { $Server = Server::instance(); // Send `hello` action back to the same user with the same content if ($data['details']) { $Server->send_to_clients( 'hello', $data['details'], Server::SEND_TO_SPECIFIC_USERS, $data['user'] ); } else { $Server->send_to_clients( 'hello:error', $Server->compose_error( 400, 'No hello message:(' ), Server::SEND_TO_SPECIFIC_USERS, $data['user'] ); } }); }); ?>
// Since everything is asynchronous - lets add handler first cs.WebSockets.once('hello', function (message) { alert(message); }); // Now send request to server cs.WebSockets.send('hello', 'Hello, world!');
すべてが非常に簡単です。
サーバー起動
ここでは、もう少し複雑です。 コマンドラインからだけでなく、Webインターフェース(管理パネルにもボタンがある)からの起動をサポートすることが決定されました。 さらに、PHPでコンソールコマンドを実行できる場合、Webバージョンは引き続きサーバーのコンソールバージョンを内部で起動します。 サーバーは、 0.0.0.0または127.0.0.1の設定で指定されたポートでリッスンします(その後、Nginx、またはその場所にあるものはすべて、すべての接続をwssに送信します://web.site/WebSockets 、つまり、ユーザーはそれを使用しています) 80または443ポート。他のポートは、パブリックWi-Fiなどの多くの状況でブロックできます。
Webソケットサーバーには、単純なスーパーバイザーオプションがあります。これは、サーバーに問題がないかどうかを確認するための追加の複製プロセスです。 コンソールバージョンでは、プロセスがクラッシュするとすぐにサーバーを再起動します.Webバージョンでは、テスト接続を使用してサーバーが稼働しているかどうかを10秒間隔でチェックし、そうでない場合は再起動します。
エンジンは、カーネルが起動し、ページに対応する特定のモジュールが実行され、結論が出るように設計されています。 したがって、Webソケットサーバーは通常のモジュールですが、出力に到達せず、イベントループが開始して接続をリッスンします。
いくつかの落とし穴
- イベントループは1つだけです。 この点で、クライアントとサーバーの両方(マスター/メンバーサーバーと通信するときに両方がメッセージを送受信できるため)は、同じイベントループを使用します。
- サーバーが起動する前、イベントループが開始した後、すべての線形コードは停止するまでブロックされます(最初のアイテムと交差しますが、それでも)
- メモリを保護する必要があります。 オブジェクトにキャッシュがある場合は、それらを無効にする必要があります。そうしないと、数日後にプロセスが大量のメモリを消費する可能性があるため、Siegeなどを使用してサーバーをロードすることで状況を確認できます
- 時間を節約する必要があります。 ほとんどの場合、組み込み機能の非ブロッキング代替オプションをすべてに使用しないため、ブロッキング操作が実行されるまで次のクライアントはサービスされないため、サーバーの下で何かの作業を最小限に抑えます。
- 長期実行のためにコードを準備します(私の場合、スクリプトの起動時に定数が使用された場所がありましたが、これは長時間のプロセスの場合に関連しなくなりました)。
それだけです
Webソケットと同様の非同期的なものはPHPに「ネイティブ」ではないため、頻繁に言及される場合、Node.jsについても言及します。また、PHPと組み合わせる方法も言及します。後者も同じパラダイムで簡単に機能し、死ぬことはありません各リクエスト(特定のスキルがあれば、Reactを使用して純粋なPHPでHTTPサーバーを作成できます )。
試してみる
プレイしたい場合、Dockerコンテナがあなたを待っています:
docker run --rm -p 8888:8888 -v /some_dir:/web nazarpc/cleverstyle-cms
次に、ブラウザでlocalhost:8888に移動し、Webソケットモジュールをオンにして、サーバーを起動します。 / some_dirフォルダーで、モジュールを追加し、実験し、試してください。すべてがすぐにデモに入ります。 停止後は、 / some_dirを削除するだけです ( --rmスイッチは、コンテナ自体を自動的に削除します)。
ソースコード