ป้องกันไม่ให้แอปจมอยู่กับข้อความของ WebSocket หรือทำให้ข้อความในเซิร์ฟเวอร์ WebSocket ท่วมท้นด้วยการใช้การกด Backpress
ที่มา
WebSocket API
WebSocket API มีอินเทอร์เฟซ JavaScript สำหรับโปรโตคอล WebSocket ซึ่งทำให้สามารถเปิดเซสชันการสื่อสารแบบอินเทอร์แอกทีฟแบบ 2 ทางระหว่างเบราว์เซอร์ของผู้ใช้กับเซิร์ฟเวอร์ได้ เมื่อใช้ API นี้ คุณจะส่งข้อความไปยังเซิร์ฟเวอร์และรับการตอบกลับที่ขับเคลื่อนด้วยเหตุการณ์ได้โดยไม่ต้องสำรวจเซิร์ฟเวอร์เพื่อดูการตอบกลับ
Streams API
Streams API ช่วยให้ JavaScript เข้าถึงสตรีมข้อมูลของกลุ่มข้อมูลที่ได้รับผ่านเครือข่ายแบบเป็นโปรแกรมและประมวลผลได้ตามต้องการ แนวคิดสำคัญในบริบทของสตรีมคือกดดันย้อนกลับ นี่เป็นกระบวนการที่สตรีมเดียวหรือเชนไปป์จะควบคุมความเร็วในการอ่านหรือเขียน เมื่อตัวสตรีมเองหรือสตรีมในเชนไปป์ยังไม่ว่างและยังไม่พร้อมรับท่อนอื่นๆ สตรีมจะส่งสัญญาณย้อนกลับผ่านเชนเพื่อให้การนำส่งช้าตามความเหมาะสม
ปัญหาเกี่ยวกับ WebSocket API ปัจจุบัน
ไม่สามารถกดดันเครื่องหมายกดลงในข้อความที่ได้รับ
เมื่อใช้ WebSocket API ปัจจุบัน การแสดงความรู้สึกต่อข้อความจะเกิดขึ้นใน WebSocket.onmessage
ซึ่งเป็น EventHandler
ที่เรียกใช้เมื่อได้รับข้อความจากเซิร์ฟเวอร์
สมมติว่าคุณมีแอปพลิเคชันที่ต้องดำเนินการปรับเปลี่ยนข้อมูลจำนวนมากเมื่อได้รับข้อความใหม่
คุณอาจตั้งค่าขั้นตอนคล้ายๆ กับโค้ดด้านล่างนี้ และเนื่องจากคุณawait
ผลของการเรียก process()
คุณก็จะทำได้ดีใช่ไหม
// 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 ปัจจุบันคือไม่มีวิธีใช้ Backpressure
เมื่อข้อความมาถึงเร็วกว่าที่วิธีที่ process()
จัดการได้ กระบวนการแสดงผลจะเต็มหน่วยความจำด้วยการบัฟเฟอร์ข้อความเหล่านั้น หรืออาจไม่ตอบสนองเนื่องจากการใช้งาน CPU 100% หรือทั้งสองอย่าง
การกดแรงดันต่ำกับข้อความที่ส่งไม่เป็นไปตามสรีรศาสตร์
ระบบใช้แรงกดดันต่ำกับข้อความที่ส่งได้ แต่ก็ต้องทำการสำรวจพร็อพเพอร์ตี้ WebSocket.bufferedAmount
ซึ่งไร้ประสิทธิภาพและไม่เป็นไปตามสรีรศาสตร์
พร็อพเพอร์ตี้แบบอ่านอย่างเดียวนี้จะแสดงจำนวนไบต์ของข้อมูลที่อยู่ในคิวโดยใช้การเรียกใช้ไปยัง WebSocket.send()
แต่ยังไม่ได้ส่งไปยังเครือข่าย
ค่านี้จะรีเซ็ตเป็น 0 เมื่อส่งข้อมูลในคิวทั้งหมดแล้ว แต่หากคุณเรียกใช้ WebSocket.send()
ต่อไป ค่าก็จะเพิ่มขึ้นเรื่อยๆ
WebSocketStream API คืออะไร
WebSocketStream API จัดการกับปัญหาแรงดันย้อนกลับที่ไม่มีจริงหรือไม่ทำงานโดยการผสานรวมสตรีมกับ WebSocket API ซึ่งหมายความว่าคุณสามารถใช้แรงกดดันต่ำได้ "ฟรี" โดยไม่มีค่าใช้จ่ายเพิ่มเติม
กรณีการใช้งานที่แนะนำสำหรับ WebSocketStream API
ตัวอย่างของเว็บไซต์ที่ใช้ API นี้ได้มีดังนี้
- แอปพลิเคชัน WebSocket แบนด์วิดท์สูงที่ต้องคงการโต้ตอบไว้ โดยเฉพาะวิดีโอและการแชร์หน้าจอ
- ในทำนองเดียวกัน การจับภาพวิดีโอและแอปพลิเคชันอื่นๆ ที่สร้างข้อมูลจำนวนมากในเบราว์เซอร์ซึ่งจำเป็นต้องอัปโหลดไปยังเซิร์ฟเวอร์ เมื่อใช้ Backpressure ไคลเอ็นต์จะหยุดสร้างข้อมูลแทนที่จะสะสมข้อมูลในหน่วยความจำ
สถานะปัจจุบัน
วิธีใช้ WebSocketStream API
ตัวอย่างข้อมูลเบื้องต้น
WebSocketStream API เป็นไปตามสัญญา ซึ่งทำให้การจัดการกับ JavaScript นั้นดูเป็นธรรมชาติในโลก JavaScript ยุคใหม่
เริ่มต้นด้วยการสร้าง WebSocketStream
ใหม่และส่ง URL ของเซิร์ฟเวอร์ WebSocket ให้กับไฟล์ดังกล่าว
ต่อไป ให้คุณรอให้การเชื่อมต่อเป็น 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);
}
แรงดันสูง
แล้วฟีเจอร์ Backpressure คืออะไร
ตามที่ฉันเขียนด้านบน คุณจะได้รับสิทธิ์ "ฟรี" โดยไม่มีขั้นตอนเพิ่มเติมใดๆ
หาก process()
ใช้เวลาเกิน ระบบจะใช้ข้อความถัดไปเมื่อไปป์ไลน์พร้อมใช้งานเท่านั้น
ในทำนองเดียวกัน ขั้นตอน WritableStreamDefaultWriter.write()
จะดําเนินการต่อเมื่อปลอดภัยเท่านั้น
ตัวอย่างขั้นสูง
อาร์กิวเมนต์ที่ 2 ของ WebSocketStream คือขอบเขตตัวเลือกสำหรับการขยายในอนาคต
ปัจจุบันตัวเลือกเดียวที่มีคือ protocols
ซึ่งทำงานเหมือนกับอาร์กิวเมนต์ที่ 2 ของตัวสร้าง WebSocket
const chatWSS = new WebSocketStream(CHAT_URL, {protocols: ['chat', 'chatv2']});
const {protocol} = await chatWSS.opened;
protocol
ที่เลือกและ extensions
ที่เป็นไปได้เป็นส่วนหนึ่งของพจนานุกรมที่มีให้บริการผ่าน WebSocketStream.opened
สัญญา
ข้อมูลทั้งหมดเกี่ยวกับการเชื่อมต่อแบบสดได้มาจากสัญญานี้ เนื่องจากข้อมูลดังกล่าวไม่เกี่ยวข้องหากการเชื่อมต่อล้มเหลว
const {readable, writable, protocol, extensions} = await chatWSS.opened;
ข้อมูลเกี่ยวกับการเชื่อมต่อ WebSocketStream แบบปิด
ข้อมูลที่มีอยู่จากเหตุการณ์ WebSocket.onclose
และ WebSocket.onerror
ใน WebSocket API พร้อมให้ใช้งานแล้วผ่าน WebSocketStream.closed
สัญญา
สัญญาจะปฏิเสธในกรณีที่การปิดไม่เรียบร้อย
มิฉะนั้น ระบบจะแก้ไขโค้ดและเหตุผลที่เซิร์ฟเวอร์ส่ง
รหัสสถานะทั้งหมดที่เป็นไปได้และความหมายของรหัสสถานะนั้นอธิบายไว้ในรายการรหัสสถานะ 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!
}
เดโม
ในเบราว์เซอร์ที่รองรับ คุณจะดู WebSocketStream API ที่ใช้งานได้ใน iframe ที่ฝังไว้หรือใน 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>Network>WebSockets
กิตติกรรมประกาศ
Adam Rice และ Yutaka Hirano ติดตั้งใช้งาน WebSocketStream API รูปภาพหลักของ Daan Mooij ใน Unsplash