WebSocketStream: WebSocket API로 스트림 통합

백프레셔를 적용하여 앱이 WebSocket 메시지에 몰아치거나 WebSocket 서버가 메시지로 플러딩되는 것을 방지합니다.

배경

WebSocket API

WebSocket API WebSocket 프로토콜에 JavaScript 인터페이스를 제공합니다. 이를 통해 양방향 커뮤니케이션 세션을 시작할 수 있습니다. 서버 간 트래픽을 전송할 수 있습니다. 이 API를 사용하면 서버에 메시지를 보내고 이벤트 기반 응답을 받을 수 있습니다. 응답을 위해 서버를 폴링하지 않고

Streams API

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

현재 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() 메서드가 처리할 수 있는 것보다 더 빨리 도착하면 렌더링 프로세스가 이러한 메시지를 버퍼링하여 메모리를 채우거나, 100% CPU 사용량 또는 두 가지 모두에 따라 응답하지 않을 수 있습니다.

보낸 메시지에 백프레셔를 적용하는 것이 인체공학적이지 않음

보낸 메시지에 백프레셔를 적용할 수는 있지만 WebSocket.bufferedAmount 드림 비효율적이고 인체공학적입니다. 이 읽기 전용 속성은 큐에 추가된 데이터의 바이트 수를 반환합니다. 다음을 사용하여 WebSocket.send()님, 아직 네트워크로 전송되지 않았습니다. 이 값은 대기 중인 모든 데이터가 전송되면 0으로 재설정됩니다. 하지만 WebSocket.send()를 계속 호출하면 계속 상승할 것입니다.

WebSocketStream API란 무엇인가요?

WebSocketStream API는 존재하지 않거나 인체공학적 백프레셔 문제를 처리함 이를 통해 웹 소켓 API를 사용할 수 있습니다. 즉, 추가 비용 없이 백프레셔를 '무료'로 적용할 수 있습니다.

WebSocketStream API 추천 사용 사례

이 API를 사용할 수 있는 사이트의 예는 다음과 같습니다.

  • 상호 작용을 유지해야 하는 고대역폭 WebSocket 애플리케이션, 특히 동영상 및 화면 공유입니다.
  • 마찬가지로 브라우저에서 많은 데이터를 생성하는 동영상 캡처 및 기타 애플리케이션 서버에 업로드해야 합니다. 백프레셔를 사용하면 클라이언트는 메모리에 데이터를 누적하는 대신 데이터 생성을 중지할 수 있습니다.

현재 상태

단계 상태
1. 설명 만들기 완전함
2. 사양의 초기 초안 만들기 진행 중
3. 의견 수집 및 디자인 반복 진행 중
4. 오리진 트라이얼 완전함
5. 출시 시작되지 않음

WebSocketStream API 사용 방법

소개 예시

WebSocketStream API는 프라미스 기반이므로 다루는 것이 자연스럽게 느껴집니다. 구현될 것입니다. 먼저 새 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에 대한 두 번째 인수는 향후 확장을 허용하는 옵션 백입니다. 현재 유일한 옵션은 protocols입니다. 이 함수는 포드의 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 연결에 관한 정보

Google Ad Manager에서 WebSocket.onclose 및 일정 WebSocket.onerror개 이제 WebSocketStream.closed 프로미스를 통해 WebSocket API를 사용할 수 있습니다. 부정한 종료가 발생하면 프로미스가 거부됩니다. 그렇지 않으면 서버에서 보낸 코드와 이유로 확인됩니다.

가능한 모든 상태 코드와 그 의미는 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 어디서 어떻게 사용하는지 Google에 알려주세요.

유용한 링크

감사의 말씀

WebSocketStream API는 Adam Rice히라노 유타카 Daan Mooij가 히어로 이미지 스플래시 해제.