バックプレッシャーを適用して、アプリが WebSocket メッセージで圧迫されたり、WebSocket サーバーにメッセージがフラッディングされたりすることを防ぎます。
背景
WebSocket API
WebSocket API WebSocket プロトコルへの JavaScript インターフェースを提供します。 双方向の対話型のコミュニケーションセッションを ブラウザとサーバーの間で通信が行われます。 この API を使用すると、サーバーにメッセージを送信して、イベント ドリブンのレスポンスを受信できます。 サーバーをポーリングして応答をポーリングすることなく、
Streams API
Streams API ネットワーク経由で受信したデータチャンクのストリームに、JavaScript からプログラムでアクセスできるようにする 必要に応じて処理できます ストリームに関する重要なコンセプトとして、 バックプレッシャー。 これは、単一のストリームまたはパイプ チェーンが 読み取りや書き込みの速度を調節します。 ストリーム自体またはパイプライン チェーンの後続ステップのストリームがまだビジー状態である場合 より多くのチャンクを受け入れる準備ができていない場合 必要に応じてチェーンを介してシグナルを逆方向に送信し、配信を遅らせます。
現在の WebSocket API の問題
受信したメッセージにバックプレッシャーを適用することは不可能
現在の WebSocket API では、メッセージへの応答は
WebSocket.onmessage
サーバーからメッセージを受信したときに呼び出される EventHandler
。
負荷の高いデータ クランチ オペレーションを実行する必要があるアプリケーションがあるとします。
新しいメッセージを受け取るたびに通知されるようになります。
おそらく次のコードのようなフローをセットアップします。
process()
呼び出しの結果を await
しているため、問題ないはずです。
// A heavy data crunching operation.
const process = async (data) => {
return new Promise((resolve) => {
window.setTimeout(() => {
console.log('WebSocket message processed:', data);
return resolve('done');
}, 1000);
});
};
webSocket.onmessage = async (event) => {
const data = event.data;
// Await the result of the processing step in the message handler.
await process(data);
};
間違い!現在の WebSocket API の問題は、バックプレッシャーを適用する方法がないことです。
メッセージが process()
メソッドで処理できるよりも早く到着した場合、
レンダリング プロセスでは、それらのメッセージをバッファリングしてメモリをいっぱいにし、
CPU 使用率が 100% または両方によって応答しなくなる可能性があります。
送信されたメッセージへのバックプレッシャーの適用は非人間工学的
送信されたメッセージにバックプレッシャーを適用することは可能ですが、
WebSocket.bufferedAmount
これは非効率的で人間工学的にも不十分です。
この読み取り専用プロパティは、キューに追加されたデータのバイト数を返します。
呼び出しを使用する
WebSocket.send()
まだネットワークに送信されていません。
この値は、キュー内のすべてのデータが送信されると 0 にリセットされます。
ただし、WebSocket.send()
を呼び出し続けると、
その数は増え続けます
WebSocketStream API とは
WebSocketStream API は、存在しないか、人間工学的でないバックプレッシャーの問題に対処する WebSocket と API の使用を可能にしています つまり、バックプレッシャーは追加費用なしで「無料で」適用されます。
WebSocketStream API に推奨されるユースケース
この API を使用できるサイトの例は次のとおりです。
- インタラクティビティを維持する必要がある、高帯域幅の WebSocket アプリケーション、 特に動画や画面共有に関する課題です
- 同様に、ブラウザで大量のデータを生成する動画キャプチャなどのアプリケーション サーバーにアップロードする必要のあるデータです。 バックプレッシャーにより、クライアントはデータをメモリに蓄積する代わりに、データの生成を停止できます。
現在のステータス
ステップ | ステータス |
---|---|
1. 説明を作成 | 完了 |
2. 仕様の初期ドラフトを作成する | 作成中 |
3. フィードバックの収集と設計の反復処理 | 作成中 |
4. オリジン トライアル | 完了 |
5. リリース | 開始していません |
WebSocketStream API の使用方法
入門例
WebSocketStream API は Promise ベースであるため、自然に処理できます。
使用できます。
まず、新しい WebSocketStream
を作成し、WebSocket サーバーの URL を渡します。
次に、接続が opened
になるまで待ちます。
その結果
ReadableStream
および/または
WritableStream
。
この関数を呼び出すことで、
ReadableStream.getReader()
メソッドでは、最終的に
ReadableStreamDefaultReader
,
次に read()
を押します。
ストリームが完了するまで(つまり、次の形式のオブジェクトが返されるまで)
{value: undefined, done: true}
。
したがって、
WritableStream.getWriter()
メソッドでは、最終的に
WritableStreamDefaultWriter
,
次に write()
を押します。
エクスポートできます。
const wss = new WebSocketStream(WSS_URL);
const {readable, writable} = await wss.opened;
const reader = readable.getReader();
const writer = writable.getWriter();
while (true) {
const {value, done} = await reader.read();
if (done) {
break;
}
const result = await process(value);
await writer.write(result);
}
バックプレッシャー
約束のバックプレッシャー機能はどうなりますか?
先ほども説明したように、この特典は「無料で」入手できます。追加の手順は必要ありません。
process()
に余分な時間がかかる場合、パイプラインの準備ができてから次のメッセージが消費されます。
同様に WritableStreamDefaultWriter.write()
ステップ
安全に続行できる場合にのみ続行されます。
高度な例
WebSocketStream の 2 番目の引数は、将来の拡張を可能にするオプション バッグです。
現在選択できるオプションは protocols
のみです。
これは UDM イベントと同じように
WebSocket コンストラクタの 2 番目の引数:
const chatWSS = new WebSocketStream(CHAT_URL, {protocols: ['chat', 'chatv2']});
const {protocol} = await chatWSS.opened;
選択したprotocol
と潜在的なextensions
は辞書の一部です
WebSocketStream.opened
Promise 経由で使用できます。
ライブ接続に関するすべての情報は、この Promise によって提供されます。
なぜなら、接続に失敗した場合は関係ないからです。
const {readable, writable, protocol, extensions} = await chatWSS.opened;
閉じた WebSocketStream 接続に関する情報
取得された情報は、
WebSocket.onclose
、
WebSocket.onerror
件のイベント
WebSocket が、WebSocketStream.closed
Promise を介して使用できるようになりました。
正常に終了しなかった場合、Promise は拒否されます。
それ以外の場合は、サーバーから送信されたコードと理由に解決されます。
発生する可能性のあるすべてのステータス コードとその意味については、
CloseEvent
ステータス コードのリスト。
const {code, reason} = await chatWSS.closed;
WebSocketStream 接続の終了
WebSocketStream は
AbortController
。
そのため、AbortSignal
を渡します。
WebSocketStream
コンストラクタに渡します。
const controller = new AbortController();
const wss = new WebSocketStream(URL, {signal: controller.signal});
setTimeout(() => controller.abort(), 1000);
代わりに WebSocketStream.close()
メソッドを使用することもできます。
その主な目的は
コード
サーバーに送信された理由を
確認できます
wss.close({code: 4000, reason: 'Game over'});
段階的な機能強化と相互運用性
現在、Chrome は WebSocketStream API を実装する唯一のブラウザです。
従来の WebSocket API との相互運用性を確保するため、
受信したメッセージにバックプレッシャーを適用することはできません。
送信されたメッセージにバックプレッシャーを適用することは可能ですが、
WebSocket.bufferedAmount
これは非効率的で人間工学的にも不十分です。
機能検出
WebSocketStream API がサポートされているかどうかを確認するには、次のコマンドを使用します。
if ('WebSocketStream' in window) {
// `WebSocketStream` is supported!
}
デモ
対応ブラウザでは、埋め込み iframe で WebSocketStream API の動作を確認できます。 または Glitch で直接アップロードできます。
フィードバック
Chrome チームでは、WebSocketStream API の使用経験についてお聞かせください。
API 設計について教えてください
API で想定どおりに機能していないものはありますか? あるいは、アイデアを実装するために不足しているメソッドやプロパティがないでしょうか。 セキュリティ モデルについて質問や意見がある場合は、 対応する GitHub リポジトリで仕様の問題を報告します。 既存の問題に意見を追加したりできます
実装に関する問題を報告する
Chrome の実装にバグは見つかりましたか?
または、実装が仕様と異なっていますか?
new.crbug.com でバグを報告します。
できるだけ詳しい情報、再現手順、
[コンポーネント] ボックスに「Blink>Network>WebSockets
」と入力します。
Glitch は、すばやく簡単に再現可能なケースを共有するのに最適です。
API のサポートを表示する
WebSocketStream API を使用する予定はありますか? 皆様の公開サポートは、Chrome チームが機能の優先順位付けを行う際に役立てられます 他のブラウザ ベンダーをサポートすることがいかに重要であるかを示しています。
ハッシュタグを使用して @ChromiumDev にツイートしてください
#WebSocketStream
どこで、どのように使用されているかをお知らせください。
関連情報
- 公開解説
- WebSocketStream API のデモ |WebSocketStream API デモソース
- バグのトラッキング
- ChromeStatus.com のエントリ
- Blink コンポーネント:
Blink>Network>WebSockets
謝辞
WebSocketStream API は Adam Rice によって実装され、 平野豊 ヒーロー画像作成者: Daan Mooij スプラッシュを解除。