ป้องกันไม่ให้แอปจมอยู่กับข้อความ WebSocket หรือทำให้เซิร์ฟเวอร์ WebSocket ทำงานหนักเกินไปด้วยข้อความที่ใช้แรงกดดัน
ข้อมูลเบื้องต้น
WebSocket API มีอินเทอร์เฟซ JavaScript ให้กับโปรโตคอล WebSocket ซึ่งทำให้สามารถเปิดเซสชันการสื่อสารแบบอินเทอร์แอกทีฟแบบ 2 ทางระหว่างเบราว์เซอร์ของผู้ใช้กับเซิร์ฟเวอร์ได้ API นี้ช่วยให้คุณส่งข้อความไปยังเซิร์ฟเวอร์และรับการตอบกลับที่ทำงานตามเหตุการณ์ได้โดยไม่ต้องตรวจสอบเซิร์ฟเวอร์เพื่อรับการตอบกลับ
Streams API
Streams API ช่วยให้คุณเข้าถึงสตรีมข้อมูลที่ได้รับผ่านเครือข่ายแบบเป็นโปรแกรม และประมวลผลข้อมูลได้ตามต้องการ แนวคิดสำคัญในบริบทของสตรีมคือแรงย้อนกลับ นี่เป็นกระบวนการที่สตรีมเดียวหรือห่วงโซ่ไปป์กำหนดความเร็วในการอ่านหรือเขียน เมื่อตัวสตรีมเองหรือสตรีมในภายหลังในห่วงโซ่ไปป์ยังไม่ว่างและยังไม่พร้อมที่จะรับส่วนอื่นเพิ่ม สตรีมจะส่งสัญญาณย้อนกลับผ่านเชนเพื่อชะลอการแสดงโฆษณาตามความเหมาะสม
ปัญหาเกี่ยวกับ 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% หรือทั้ง 2 อย่าง
การใช้แรงกดดันย้อนกลับกับข้อความที่ส่งไม่เป็นไปตามหลักการยศาสตร์
คุณอาจใช้แรงดันย้อนกลับกับข้อความที่ส่งได้ แต่จะต้องมีการหยั่งสัญญาณพร็อพเพอร์ตี้ WebSocket.bufferedAmount
ซึ่งไม่มีประสิทธิภาพและไม่เป็นไปตามหลักสรีรศาสตร์
พร็อพเพอร์ตี้แบบอ่านอย่างเดียวนี้จะแสดงจำนวนไบต์ของข้อมูลที่อยู่ในคิวโดยใช้การเรียกใช้ WebSocket.send()
แต่ยังไม่ได้ส่งไปยังเครือข่าย
ค่านี้จะรีเซ็ตเป็น 0 เมื่อมีการส่งข้อมูลที่อยู่ในคิวทั้งหมดแล้ว
แต่หากคุณเรียกใช้ WebSocket.send()
ต่อไป ค่าก็จะไต่ระดับต่อไป
WebSocketStream API คืออะไร
WebSocketStream API รับมือกับปัญหาความดันย้อนกลับที่ไม่มีอยู่หรือไม่ทำงานโดยการรวมสตรีมเข้ากับ WebSocket API ซึ่งหมายความว่าคุณสามารถใช้ Backpressure ได้ "ฟรี" โดยไม่มีค่าใช้จ่ายเพิ่มเติม
กรณีการใช้งานที่แนะนำสำหรับ WebSocketStream API
ตัวอย่างเว็บไซต์ที่ใช้ API นี้ได้ ได้แก่
- แอปพลิเคชัน WebSocket ที่มีแบนด์วิดท์สูงซึ่งจำเป็นต้องคงการโต้ตอบไว้ โดยเฉพาะวิดีโอและการแชร์หน้าจอ
- ในทํานองเดียวกัน โปรแกรมจับภาพวิดีโอและแอปพลิเคชันอื่นๆ ที่สร้างข้อมูลจํานวนมากในเบราว์เซอร์ซึ่งจําเป็นต้องอัปโหลดไปยังเซิร์ฟเวอร์ การใช้แรงดันย้อนกลับจะทำให้ไคลเอ็นต์หยุดสร้างข้อมูลแทนการสะสมข้อมูลในหน่วยความจำ
สถานะปัจจุบัน
ขั้นตอน | สถานะ |
---|---|
1. สร้างคําอธิบาย | เสร็จสมบูรณ์ |
2. สร้างฉบับร่างแรกของข้อกําหนด | กำลังดำเนินการ |
3. รวบรวมความคิดเห็นและทำซ้ำเกี่ยวกับการออกแบบ | กำลังดำเนินการ |
4. ช่วงทดลองใช้จากต้นทาง | เสร็จสมบูรณ์ |
5. เปิดตัว | ยังไม่เริ่ม |
วิธีใช้ WebSocketStream API
WebSocketStream API ทำงานตามสัญญา ซึ่งทำให้การใช้งาน API นี้รู้สึกเป็นธรรมชาติใน 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()
จะดำเนินการต่อก็ต่อเมื่อทำได้อย่างปลอดภัยเท่านั้น
ตัวอย่างขั้นสูง
อาร์กิวเมนต์ที่สองของ 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.opened
ข้อมูลเกี่ยวกับการเชื่อมต่อ WebSocketStream แบบปิด
ข้อมูลที่พร้อมใช้งานจากเหตุการณ์ WebSocket.onclose
และ WebSocket.onerror
ใน WebSocket API พร้อมให้ใช้งานแล้วผ่านสัญญา WebSocketStream.closed
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!
}
สาธิต
ในเบราว์เซอร์ที่รองรับ คุณดูการทำงานของ 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 Demo | แหล่งที่มาของ WebSocketStream API Demo
- ข้อบกพร่องการติดตาม
- รายการ ChromeStatus.com
- คอมโพเนนต์ Blink:
Blink>Network>WebSockets
ขอขอบคุณ
WebSocketStream API ดำเนินการโดย Adam Rice และ Yutaka Hirano