물이 흘러 나오고 있는 소방 호스.

WebSocketStream: WebSocket API와 스트림 통합

역 압력(backpressure)을 적용하여 앱이 WebSocket 메시지의 홍수에 빠지거나 WebSocket 서버에 메시지가 넘쳐나는 것을 방지합니다.

Published on Updated on

Translated to: English, Português, 中文, Pусский, 日本語

배경

WebSocket API

WebSocket APIWebSocket 프로토콜에 JavaScript 인터페이스를 제공하며 이를 통해 사용자의 브라우저와 서버 간에 양방향 대화형 통신 세션을 열 수 있습니다. 이 API를 사용하면 서버에 메시지를 보낼 수 있으며, 응답을 위해 서버를 폴링하지 않아도 이벤트 기반 응답을 받을 수 있습니다.

Streams API

Streams API를 사용하면 JavaScript가 네트워크를 통해 수신한 데이터 청크 스트림에 프로그래밍 방식으로 액세스하고 원하는 대로 처리할 수 있습니다. 스트림의 맥락에서 중요한 개념은 역 압력입니다. 이것은 단일 스트림 또는 파이프 체인이 읽기 또는 쓰기 속도를 조절하는 프로세스입니다. 스트림 자체 또는 파이프 체인의 나중 스트림이 여전히 사용 중이고 아직 더 많은 청크를 수락할 준비가 되지 않은 경우 적절하게 전달을 느리게 하기 위해 체인을 통해 뒤로 신호를 보냅니다.

현재 WebSocket API의 문제

수신한 메시지에 역 압력을 적용하는 것은 불가능

현재 WebSocket API에서는 서버에서 메시지를 수신할 때 호출되는 EventHandlerWebSocket.onmessage에서 메시지에 대한 반응이 발생합니다.

새 메시지가 수신될 때마다 많은 양의 데이터 처리 작업을 수행해야 하는 애플리케이션이 있다고 가정해 보겠습니다. 여러분은 아마 아래 코드와 비슷한 흐름을 설정했을 것이고, 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() 메서드가 처리할 수 있는 것보다 빠르게 도착하면 렌더링 프로세스는 해당 메시지를 버퍼링하여 메모리를 채우거나, 100% CPU 사용량으로 인해 응답하지 않게 되거나, 두 가지 모두를 수행합니다.

보낸 메시지에 역 압력을 적용하는 것은 인체 공학적이지 않습니다

보낸 메시지에 역 압력을 적용할 수 있지만, 비효율적이며 인체 공학적이지 않은 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는 프라미스 기반이므로 요즘의 JavaScript 세계에서 자연스럽게 처리할 수 있습니다. 새 WebSocketStream을 구성하고 WebSocket 서버의 URL을 전달하여 시작합니다. 다음으로 connection이 설정될 때까지 기다리면 ReadableStream 및/또는 WritableStream의 결과를 얻습니다.

ReadableStream.getReader() 메서드를 호출하면 스트림이 완료될 때까지 read() 데이터를, 그리고 {value: undefined, done: true} 형식의 개체를 반환할 때까지 최종적으로 ReadableStreamDefaultReader를 얻을 수 있습니다.

WritableStream.getWriter() 메서드를 호출 WritableStreamDefaultWriter 얻을 수 있으며, 이를 통해 데이터를 write()

  const wss = new WebSocketStream(WSS_URL);
const {readable, writable} = await wss.connection;
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에 대한 두 번째 인수는 향후 확장을 허용하는 옵션 가방(option bag)입니다. 현재 유일한 옵션은 WebSocket 생성자에 대한 두 번째 인수와 동일하게 작동하는 protocols입니다.

const chatWSS = new WebSocketStream(CHAT_URL, {protocols: ['chat', 'chatv2']});
const {protocol} = await chatWSS.connection;

선택한 protocol과 잠재적인 extensionsWebSocketStream.connection 프라미스를 통해 사용할 수 있는 사전의 일부입니다. 라이브 연결에 대한 모든 정보는 연결이 실패하더라도 관련이 없기 때문에 이 프라미스에 의해 제공됩니다.

const {readable, writable, protocol, extensions} = await chatWSS.connection;

닫힌 WebSocketStream 연결에 대한 정보

WebSocket API의 WebSocket.oncloseWebSocket.onerror 이벤트에서 사용할 수 있었던 정보를 이제 WebSocketStream.closed 프라미스를 통해 사용할 수 있습니다. 이 프라미스는 불완전하게 닫힌 이벤트에서는 거부하며 그렇지 않은 경우에는 서버에서 보낸 코드와 이유를 확인합니다.

가능한 모든 상태 코드와 그 의미는 CloseEvent 상태 코드 목록에 설명되어 있습니다.

const {code, reason} = await chatWSS.closed;

WebSocketStream 연결 닫기

WebSocketStream은 AbortController로 닫을 수 있습니다. 따라서, AbortSignalWebSocketStream 생성자에 전달하도록 합니다.

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 팀이 기능의 우선 순위를 정하고 브라우저 공급업체에 이 API의 지원이 얼마나 중요한지 보여주기 위해서는 여러분의 공개 지원이 힘이 됩니다.

@ChromiumDev으로 해시태그 #WebSocketStream를 포함한 트윗을 보내서 어디에서 어떻게 활용하고 있는지 알려주세요.

유용한 링크

감사의 말

WebSocketStream API는 Adam RiceYutaka Hirano에 의해 구현되었습니다. UnsplashDaan Mooij의 영웅 이미지.

Updated on Improve article

We serve cookies on this site to analyze traffic, remember your preferences, and optimize your experience.