Ngăn ứng dụng của bạn bị ngập trong các thông báo WebSocket hoặc làm ngập một máy chủ WebSocket bằng các thông báo bằng cách áp dụng áp lực ngược.
Thông tin khái quát
API WebSocket cung cấp một giao diện JavaScript cho giao thức WebSocket, giúp bạn có thể mở một phiên giao tiếp tương tác hai chiều giữa trình duyệt của người dùng và một máy chủ. Với API này, bạn có thể gửi thông báo đến một máy chủ và nhận các phản hồi dựa trên sự kiện mà không cần thăm dò máy chủ để nhận phản hồi.
Streams API
Streams API cho phép JavaScript truy cập theo phương thức lập trình vào các luồng dữ liệu nhận được qua mạng và xử lý chúng theo ý muốn. Một khái niệm quan trọng trong bối cảnh luồng dữ liệu là áp suất ngược. Đây là quy trình mà một luồng duy nhất hoặc một chuỗi ống dẫn điều chỉnh tốc độ đọc hoặc ghi. Khi bản thân luồng hoặc một luồng sau đó trong chuỗi đường ống vẫn đang bận và chưa sẵn sàng chấp nhận thêm các khối, luồng đó sẽ gửi tín hiệu ngược lại thông qua chuỗi để làm chậm quá trình phân phối khi thích hợp.
Vấn đề với WebSocket API hiện tại
Không thể áp dụng áp lực ngược cho các thông báo đã nhận
Với WebSocket API hiện tại, việc phản hồi một thông báo diễn ra trong WebSocket.onmessage
, một EventHandler
được gọi khi một thông báo được nhận từ máy chủ.
Giả sử bạn có một ứng dụng cần thực hiện các thao tác xử lý dữ liệu lớn bất cứ khi nào nhận được một tin nhắn mới.
Có lẽ bạn sẽ thiết lập quy trình tương tự như mã bên dưới và vì bạn await
kết quả của lệnh gọi process()
, nên bạn sẽ ổn, phải không?
// 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);
};
Sai rồi! Vấn đề với WebSocket API hiện tại là không có cách nào để áp dụng áp lực ngược.
Khi các thông báo đến nhanh hơn tốc độ xử lý của phương thức process()
, quy trình kết xuất sẽ lấp đầy bộ nhớ bằng cách lưu vào bộ nhớ đệm các thông báo đó, không phản hồi do mức sử dụng CPU là 100% hoặc cả hai.
Việc áp dụng áp lực ngược cho các thông báo đã gửi là không phù hợp
Bạn có thể áp dụng áp lực ngược cho các thông báo đã gửi, nhưng điều này liên quan đến việc thăm dò ý kiến về thuộc tính WebSocket.bufferedAmount
. Việc này không hiệu quả và không thuận tiện.
Thuộc tính chỉ đọc này trả về số byte dữ liệu đã được đưa vào hàng đợi bằng các lệnh gọi đến WebSocket.send()
, nhưng chưa được truyền đến mạng.
Giá trị này sẽ đặt lại về 0 sau khi tất cả dữ liệu trong hàng đợi đã được gửi, nhưng nếu bạn tiếp tục gọi WebSocket.send()
, giá trị này sẽ tiếp tục tăng.
WebSocketStream API là gì?
API WebSocketStream giải quyết vấn đề về áp suất ngược không tồn tại hoặc không thuận tiện bằng cách tích hợp các luồng với API WebSocket. Điều này có nghĩa là bạn có thể áp dụng áp lực ngược "miễn phí" mà không mất thêm chi phí.
Các trường hợp sử dụng được đề xuất cho WebSocketStream API
Ví dụ về các trang web có thể sử dụng API này:
- Các ứng dụng WebSocket có băng thông cao cần duy trì tính tương tác, đặc biệt là video và tính năng chia sẻ màn hình.
- Tương tự, tính năng quay video và các ứng dụng khác tạo ra nhiều dữ liệu trong trình duyệt cần được tải lên máy chủ. Với áp suất ngược, ứng dụng có thể ngừng tạo dữ liệu thay vì tích luỹ dữ liệu trong bộ nhớ.
Trạng thái hiện tại
Bước | Trạng thái |
---|---|
1. Tạo video giải thích | Hoàn tất |
2. Tạo bản nháp ban đầu của quy cách | Đang tiến hành |
3. Thu thập ý kiến phản hồi và lặp lại quy trình thiết kế | Đang tiến hành |
4. Bản dùng thử theo nguyên gốc | Hoàn tất |
5. Khởi chạy | Chưa bắt đầu |
Cách sử dụng WebSocketStream API
WebSocketStream API dựa trên lời hứa, giúp bạn cảm thấy thoải mái khi xử lý API này trong thế giới JavaScript hiện đại.
Bạn bắt đầu bằng cách tạo một WebSocketStream
mới và truyền cho nó URL của máy chủ WebSocket.
Tiếp theo, bạn đợi kết nối opened
, dẫn đến ReadableStream
và/hoặc WritableStream
.
Bằng cách gọi phương thức ReadableStream.getReader()
, cuối cùng bạn sẽ nhận được một ReadableStreamDefaultReader
, sau đó bạn có thể read()
dữ liệu từ đó cho đến khi luồng hoàn tất, tức là cho đến khi luồng trả về một đối tượng có dạng {value: undefined, done: true}
.
Theo đó, bằng cách gọi phương thức WritableStream.getWriter()
, cuối cùng bạn sẽ nhận được một WritableStreamDefaultWriter
, sau đó bạn có thể write()
dữ liệu đến.
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);
}
Áp suất ngược
Vậy còn tính năng áp suất ngược đã hứa thì sao?
Bạn sẽ nhận được "miễn phí" mà không cần thực hiện thêm bước nào.
Nếu process()
mất thêm thời gian, thì thông báo tiếp theo sẽ chỉ được sử dụng khi quy trình sẵn sàng.
Tương tự, bước WritableStreamDefaultWriter.write()
chỉ tiếp tục nếu an toàn.
Ví dụ nâng cao
Đối số thứ hai cho WebSocketStream là một nhóm tuỳ chọn để cho phép mở rộng trong tương lai.
Lựa chọn duy nhất là protocols
, hoạt động giống như đối số thứ hai cho hàm khởi tạo WebSocket:
const chatWSS = new WebSocketStream(CHAT_URL, {protocols: ['chat', 'chatv2']});
const {protocol} = await chatWSS.opened;
protocol
đã chọn cũng như extensions
tiềm ẩn nằm trong từ điển có sẵn thông qua lời hứa WebSocketStream.opened
.
Mọi thông tin về kết nối trực tiếp đều được cung cấp theo lời hứa này, vì thông tin đó không liên quan nếu kết nối không thành công.
const {readable, writable, protocol, extensions} = await chatWSS.opened;
Thông tin về kết nối WebSocketStream đã đóng
Thông tin có trong các sự kiện WebSocket.onclose
và WebSocket.onerror
trong WebSocket API hiện có trong lời hứa WebSocketStream.closed
.
Lệnh hứa từ chối trong trường hợp đóng không đúng cách, nếu không, lệnh này sẽ phân giải thành mã và lý do do máy chủ gửi.
Tất cả mã trạng thái có thể có và ý nghĩa của chúng được giải thích trong danh sách mã trạng thái CloseEvent
.
const {code, reason} = await chatWSS.closed;
Đóng kết nối WebSocketStream
Bạn có thể đóng WebSocketStream bằng AbortController
.
Do đó, hãy truyền một AbortSignal
đến hàm khởi tạo WebSocketStream
. AbortController.abort()
chỉ hoạt động trước khi bắt tay, chứ không hoạt động sau đó.
const controller = new AbortController();
const wss = new WebSocketStream(URL, {signal: controller.signal});
setTimeout(() => controller.abort(), 1000);
Ngoài ra, bạn cũng có thể sử dụng phương thức WebSocketStream.close()
, nhưng mục đích chính của phương thức này là cho phép chỉ định mã và lý do được gửi đến máy chủ.
wss.close({closeCode: 4000, reason: 'Game over'});
Cải tiến tăng dần và khả năng tương tác
Chrome hiện là trình duyệt duy nhất triển khai API WebSocketStream.
Để có khả năng tương tác với WebSocket API cổ điển, bạn không thể áp dụng áp lực ngược cho các thông báo đã nhận.
Bạn có thể áp dụng áp lực ngược cho các thông báo đã gửi, nhưng điều này liên quan đến việc thăm dò ý kiến về thuộc tính WebSocket.bufferedAmount
. Việc này không hiệu quả và không thuận tiện.
Phát hiện đối tượng
Để kiểm tra xem WebSocketStream API có được hỗ trợ hay không, hãy sử dụng:
if ('WebSocketStream' in window) {
// `WebSocketStream` is supported!
}
Bản minh hoạ
Trên các trình duyệt được hỗ trợ, bạn có thể thấy API WebSocketStream đang hoạt động trong iframe được nhúng hoặc trực tiếp trên Glitch.
Phản hồi
Nhóm Chrome muốn biết trải nghiệm của bạn khi sử dụng WebSocketStream API.
Hãy cho chúng tôi biết về thiết kế API
Có vấn đề gì về API khiến bạn không hài lòng không? Hoặc có phương thức hoặc thuộc tính nào bị thiếu mà bạn cần triển khai ý tưởng của mình không? Bạn có câu hỏi hoặc bình luận về mô hình bảo mật? Báo cáo vấn đề về quy cách trên kho lưu trữ GitHub tương ứng hoặc thêm ý kiến của bạn vào một vấn đề hiện có.
Báo cáo vấn đề về việc triển khai
Bạn có phát hiện thấy lỗi trong quá trình triển khai của Chrome không?
Hoặc việc triển khai có khác với quy cách không?
Báo cáo lỗi tại new.crbug.com. Nhớ cung cấp càng nhiều thông tin chi tiết càng tốt, hướng dẫn đơn giản để tái hiện và nhập Blink>Network>WebSockets
vào hộp Thành phần.
Thể hiện sự ủng hộ đối với API
Bạn có dự định sử dụng WebSocketStream API không? Sự ủng hộ công khai của bạn giúp nhóm Chrome ưu tiên các tính năng và cho các nhà cung cấp trình duyệt khác thấy tầm quan trọng của việc hỗ trợ các tính năng này.
Gửi một tweet đến @ChromiumDev bằng thẻ bắt đầu bằng #WebSocketStream
và cho chúng tôi biết bạn đang sử dụng tính năng này ở đâu và như thế nào.
Đường liên kết hữu ích
- Thông tin giải thích công khai
- WebSocketStream API Demo | Nguồn WebSocketStream API Demo
- Lỗi theo dõi
- Mục nhập trên ChromeStatus.com
- Thành phần Blink:
Blink>Network>WebSockets
Lời cảm ơn
API WebSocketStream được Adam Rice và Yutaka Hirano triển khai.