⚠️ 광과민증이 있으신 분들은 생략하셔도 좋을 것 같아요.
아래의 정적 이미지를 참조하세요. 표시등이 정말 빠르게 깜박이기 시작합니다!
제목을 기억하세요... 여기서는 스트림에 대해 이야기하고 있습니다.
프로토콜, 패킷, 순서 지정, ack 및 nack에 대해 이야기할 수 있지만 여기서는 스트림에 대해 이야기하고 있습니다. 아마 짐작하셨겠지만(저는 당신을 믿습니다 =D) 스트림은 바이너리이거나 문자열입니다.
예, 문자열은 전송되기 전에 압축됩니다. 하지만 프런트엔드 및 백엔드 개발에서 일반적으로 관심을 두는 부분은 문자열과 바이너리입니다.
다음 예에서는 JS 스트림을 사용합니다.
Node에는 고유한 레거시 구현이 있지만 앞이나 뒤의 동일한 코드인 스트림을 처리할 수 있는 방법이 있습니다.
다른 언어에도 스트림을 처리하는 방식이 있지만 보시다시피... 스트림을 처리하는 실제 코드 부분은 그다지 복잡하지 않았습니다(복잡한 일이 발생하지 않는다는 뜻은 아닙니다).
여러 소스의 데이터를 소비해야 하는 프런트엔드가 있습니다.
IP/포트를 통해 각 소스에 개별적으로 액세스할 수 있지만 사용 및 제어가 용이하도록 API 게이트웨이 뒤에 배치합니다.
링크에서 저장소를 확인하고 직접 실행하는 방법을 배워서 직접 사용해 보세요.
https://github.com/Noriller/chunk-busters
따라 볼 수 있는 비디오 버전:
https://youtu.be/QucaOfFI0fM
소스가 있고 가져오고 기다리고 렌더링합니다. 헹구고 반복하세요.
await fetch1(); handleResult(1); await fetch2(); handleResult(2); ... await fetch9(); handleResult(9);
아무도 그렇게 하지 않을 거라고 생각하실 수도 있겠지만…
이 예에서는 뭔가 잘못되었다는 것이 분명하지만, 여기에 빠지는 것은 그리 어렵지 않습니다.
분명한 점은 느립니다. 각 요청을 실행하고 기다려야 하며, 느리면… 기다려야 합니다.
각 요청을 개별적으로 기다리고 싶지는 않으시므로 모두 실행한 다음 완료될 때까지 기다립니다.
await Promise.all([ fetch1(), fetch2(), ... fetch9(), ]); handleAllResults(results);
이런 걸 했을 테니까 좋은 거겠죠?
즉, 하나의 요청이 느린 경우를 제외하고는 다른 모든 요청이 이미 완료되었더라도 해당 요청이 완료될 때까지 기다려야 한다는 의미입니다.
느린 요청이 있을 수 있으므로 모두 실행하고 기다리지만 요청이 오면 가능한 경우 이미 결과에 대해 작업을 수행하므로 마지막 요청이 도착하면 다른 요청도 이미 완료됩니다.
await fetch1(); handleResult(1); await fetch2(); handleResult(2); ... await fetch9(); handleResult(9);
이것이 최고의 솔루션이겠죠?
음… 뭔가 이상한데요?
v1을 기억하시나요? 네… 다음과 같아야 합니다.
http/1에서 정확히 동일한 엔드포인트로 가질 수 있는 연결 수에는 제한이 있으며, 그뿐만 아니라 브라우저에 따라 다르며 브라우저마다 제한이 다를 수 있습니다.
그냥 http/2만 사용하면 된다고 생각할 수도 있지만… 이것이 좋은 해결책이더라도 여전히 프런트엔드에서 여러 엔드포인트를 처리해야 합니다.
이 문제에 대한 좋은 해결책이 있을까요?
v0을 다시 살펴보지만 스트림을 사용합니다…
당신은 똑똑하기 때문에 경고가 조금 망가졌기 때문에 이것을 기대했을 것입니다…
어쨌든... 가져오는 동안 렌더링합니다.
await Promise.all([ fetch1(), fetch2(), ... fetch9(), ]); handleAllResults(results);
v0이 이 문제를 처리하는 최악의 방법이라 할지라도 스트림을 사용하면 크게 개선됩니다. 총 대기 시간이 같아도 무엇이든 보여 주어 사용자를 속일 수 있습니다.
v5 - v1, 다시 말하지만 스트림이 있습니다!
네...더 이상은 못 참겠어요...그래서...
v6 -
프런트엔드에서 너무 많은 것을 관리해야 했습니다. 이를 백엔드로 오프로드할 수 있다면 모든 소스를 처리할 하나의 엔드포인트를 가질 수 있습니다.
이는 프런트엔드 및 http/1 문제의 복잡성을 해결합니다.
await Promise.all([ fetch1().then(handleResult), fetch2().then(handleResult), ... fetch9().then(handleResult), ]);
// usually we do this: await fetch(...).then((res) => { // this json call accumulate all the response // that later is returned for you to use return res.json() })
이에 사용된 코드는 기본적으로 앞면과 뒷면 모두 동일합니다.
await fetchAll(); handleAllResults(results);
버퍼에 들어오는 문자열을 추가하고, 구문 분석하고, 사용 가능한 청크가 있는지 확인하고, 사용하고는 잊어버립니다. 즉, 적은 양의 RAM으로도 한 번에 한 덩어리씩 TB의 데이터를 수신/소비할 수 있습니다.
네가 무슨 생각을 하는지 알겠다...그리고 멍청하고...광기이기도 하다...
무오오오오오오오 웹소켓이 필요해요!
아니요, 우리 집에는 웹소켓이 있어요!
집에서의 웹소켓: 다음은?
당신은 똑똑합니다. 소스가 여전히 데이터를 생성하고 있다면… 일부 변수를 업데이트할 수 있을 것이라고 생각했을 것입니다…
이렇게 하면 하나의 연결을 사용하여 더 많은 데이터를 얻거나 생성되는 내용을 변경할 수 있습니다.
예… 그렇게 하실 수 있을 것 같은데요… 그리고 저는 여러분의 주장에 따라 예를 들었습니다. =디
그래도… 멍청한 생각인데, 실제 제작 환경에서 어디에서 쓸 수 있을지 모르겠네요. MPA와 Ajax 사이의 상호작용은 충분했지만 동일한 서버에 대한 연결이 충분하지 않았던 어색한 JS 단계로 시간을 거슬러 올라가면(일부 브라우저는 2개로 제한됩니다!) 아마도 그럴 수도 있겠죠?
그 외에는 모르겠습니다. 있다면... 알려주세요.
위의 예에서 중앙 보드, 특히 '진행 테두리'에 주목하세요. 계속 업데이트되는 것을 볼 수 있습니다. 네트워크 탭을 열면 GET 연결이 끝나기 전에 절대 닫히지 않는 것을 볼 수 있습니다. 또한 아직 살아있는 연결이 수행하는 작업을 변경하는 여러 다른 요청도 볼 수 있습니다. 이 모든 것이 바닐라 http/1을 사용합니다.
이 예는 제가 만들 수 있는 가장 기본적인 예입니다. 파싱이 더 쉽기 때문에 JSON 대신 간단한 문자열을 사용하기도 합니다.
JSON을 사용하려면 문자열을 축적해야 합니다(이유로 인해 백엔드 응답을 JSON.stringify해야 합니다).
그런 다음 어디를 구분할지 확인한 다음 해당 값을 구문 분석하거나 진행하면서 구문 분석하세요.
첫 번째로 NDJSON을 생각해 보세요. JSON 배열 대신 새 줄로 객체를 분리한 다음 중단할 위치를 "더 쉽게" 찾은 다음 각각 JSON.parse를 수행하고 객체를 사용할 수 있습니다.
후자의 경우 다음과 같이 구문 분석합니다. 배열에 있다는 것을 알고 이제는 객체입니다. 첫 번째 키입니다. 이제 키 값이고, 다음 키, 건너뛰고, 다음 키… 등등… 수동으로 만드는 것은 사소한 일이 아니지만, wait에서 점프한 후 렌더링으로 렌더링하는 것과 같습니다. 이 모든 것은… 제외하고… 더 작은 규모입니다.
사람들은 예제를 호스트하는 것을 좋아합니다. 이 예제는 직접 실행해야 합니다… 예제를 다른 곳에서 호스트하지 않는 이유가 이제 명확해지기를 바라지만, 또 다른 한 가지는 여기서는 어떤 오류도 예상하지 않는다는 것입니다. 무엇보다 네트워크 오류를 추가하세요...글쎄...
오류는 처리해야 하지만 이로 인해 복잡성이 더욱 가중됩니다.
어쩌면… 상황에 따라…
라고 말할 수도 있습니다.스트리밍이 답인 곳도 있지만 대부분의 경우… json을 기다리는 것으로 충분합니다(물론 더 쉬울 수도 있음).
하지만 스트림에 대해 배우면 프런트엔드든 백엔드든 일부 문제를 해결할 수 있는 방법이 열립니다.
프런트엔드에서는 언제든지 이를 사용하여 사용자를 "속일" 수 있습니다. 모든 곳에 스피너를 표시하는 대신, 시간이 걸리더라도 나타나는 대로 표시하고 추가로 표시할 수 있습니다. 사용자가 상호작용하는 것을 차단하지 않는 한... 단순히 스피너를 표시하는 것보다 "느린" 것을 무엇보다 실제로 더 빠르게 느끼는 만들 수도 있습니다. .
백엔드에서는 전면, 데이터베이스 또는 그 사이의 모든 데이터를 있는 그대로 구문 분석할 수 있으므로 RAM을 절약할 수 있습니다. 필요에 따라 데이터를 처리하고 OOM(메모리 부족) 오류를 발생시키는 전체 페이로드를 기다리지 않고 데이터를 보냅니다. GB 또는 TB 단위의 데이터… 물론이죠. 왜 안 되겠습니까?
React가 느린가요? 이 전체 예제 프론트엔드는 React를 사용하여 수행되었으며 모든 깜박이는 "조명"과 관련된 "주요" 작업 외에도 많은 다른 작업이 진행되고 있습니다.
예... 충분히 빠르게 진행하면 예제가 따라잡을 수 없어 정지되기 시작합니다. 하지만 분당 수천 개의 렌더링이 진행되기 때문에... 대부분의 애플리케이션에는 충분하다고 생각합니다.
그리고 언제든지 성능을 향상시킬 수 있습니다. "진행률 경계"의 경우 렌더링에서 일부를 저장해야 하는 경우 연기된 값을 사용하여 더 부드럽게 만들었습니다... "조명"에 대해 이 작업과 기타 성능 향상을 수행할 수 있었습니다. 하지만 제목에 "조명"이 깜박이는 것을 멈추게 할 뿐이고(이렇게 하면 좋은 데모가 될 수 없음) 제목에 있는 "전기" 밑줄도 그만큼 재미있지 않을 것입니다. 입니다.
이 예에서는 모든 "개선 사항"이 이상적이지는 않지만 일반 애플리케이션의 경우... 많은 양을 처리할 수 있습니다. 그리고 더 필요한 것이 있으면 이 경우 다른 솔루션을 사용하십시오.
무기고에 스트림을 추가하세요... 만병통치약은 아닐 수도 있지만 언젠가는 분명 도움이 될 것입니다.
그리고 그걸로 뭔가를 하려고 하는데 도움이 필요하다면… 제게 전화해 보세요. =피
위 내용은 청크 버스터즈: 강을 건너지 마세요!의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!