JavaScript의 파일 탐색 알고리즘을 설명하는 요지를 보고 있습니다
으아아아저는 알고리즘의 모든 틈새에 async/await를 밀어넣으려는 (저자가 아닌 언어의) 충동에 놀랐습니다. 저는 Node.js의 아키텍처에 대해 잘 모르지만, 고도의 비동기 기능 뒤에 어떤 의도가 있다고 가정합니다. 이것은 선언적 절차적 C/C++ 스레드/프로세스 모델에 주로 노출된 나에게 매우 혼란스럽고 새로운 것입니다. 내가 알고 싶은 것은 사람들이 의견을 가질 수 있는 "모델이 무엇인지" 또는 "더 나은지"가 아니라 "비동시성을 유도하는 아이디어는 무엇입니까?"입니다. 이는 일반적인 작업 기반에 대한 응답성에 대한 브라우저의 요구 사항입니다. 효율성 문제?
내 질문은 "왜 그렇게 비동기성이 많은가?"입니다. 나는 의견을 찾는 것이 아니라 Node.js 또는 JavaScript의 아키텍처를 설명하기 위해 역사와 진화를 이해하는 사람을 찾고 있습니다.
핵심 포인트: https://gist.github.com/lovasoa/8691344
P粉3289113082024-04-02 15:52:50
글쎄, 높은 수준에서는 해당 문장의 첫 번째 부분과 해당 문장의 두 번째 부분이 충돌합니다. 서버가 비효율적이라면 동시에 도착하는 많은 클라이언트 요청에 적절하게 응답할 수 없습니다. 따라서 가능한 한 클라이언트 요청에 응답할 수 있도록 서버를 보다 효율적으로 만들고 싶습니다.
이제 몇 단계로 돌아가서 표시된 코드에서 정확히 무슨 일이 일어나고 있는지 이해해야 합니다. 첫째, 언어는 Javascript이지만 이 코드의 핵심 논리와 async
、await
및 생성기를 사용하는 방법은 Javascript 언어 때문만은 아닙니다. 이는 Javascript가 실행되는 특정 환경(이 경우 nodejs) 때문입니다.
이 환경은 이벤트 루프를 사용하고 단일 Javascript 스레드를 실행합니다. 다른 운영 체제 스레드는 다양한 시스템 작업과 일부 라이브러리 구현에 사용되지만 nodejs가 Javascript를 실행할 때 한 번에 하나의 Javascript만 실행합니다(단일 스레드).
동시에 서버를 설계할 때 서버가 들어오는 많은 요청에 응답할 수 있기를 원합니다. 하나의 요청을 처리하고 다음 요청을 시작하기 전에 첫 번째 요청이 완료될 때까지 다른 모든 요청을 기다리게 하는 것을 원하지 않습니다. 그러나 nodejs 이벤트 루프 모델은 다중 스레드를 사용하지 않으므로 동시에 여러 요청 핸들러를 직접 실행하지 않습니다.
nodejs가 배포한 솔루션은 다양한 서버 요청 처리기의 주요 활동과 요청 처리에 가장 많은 시간을 소비하는 것이 I/O(예: 네트워크, 파일 I/O 또는 데이터베이스) /O라는 사실에서 비롯됩니다. 이는 자체(Javascript가 아닌) 구현이 있는 하위 수준 작업입니다.
따라서 모든 I/O 작업에 대해 비동기 모델을 배포합니다. 잘 작성된 서버는 비동기 I/O 작업을 시작할 수 있으며, 처리되는 동안(Nodejs 인터프리터 자체에서 실행되는 코드가 아님) 인터프리터와 NodeJS 이벤트 루프는 자유롭게 다른 작업을 수행하고 다른 요청을 처리할 수 있습니다. 얼마 후 비동기 작업이 완료되면 이벤트가 이벤트 루프에 삽입되고, 인터프리터가 수행 중이던 작업이 완료되면 비동기 작업의 결과를 처리하고 작업을 계속할 수 있습니다.
이 방법으로 Javascript는 단일 스레드에서만 실행되지만 들어오는 많은 요청을 동시에 "처리"할 수 있습니다.
예, 이는 이전 C/C++ 스레딩 모델과 완전히 다른 모델입니다. Nodejs에서 효율적이고 효과적인 서버 코드를 작성하기 위한 다양한 모델을 배우거나 배우지 않거나 둘 중 하나입니다. 이전 모델을 고수하려면 스레드(Java, C++ 등)에서 요청 핸들러를 실행하는 다른 환경을 선택하고 올바르게 작성된(물론 관련 디자인 및 테스트 오버헤드와 함께) 잘 수행하는 것을 목표로 하십시오. 모든 멀티스레드 동시성을 철저히 테스트했습니다.
nodejs 모델의 가장 큰 장점 중 하나는 멀티스레드 실행 모델에서 발생하는 많은 동시성 문제에 덜 민감하다는 것입니다. Nodejs 모델에는 때때로 해결 방법이 필요한 몇 가지 단점도 있습니다. 예를 들어 Javascript로 작성된 요청 핸들러에 CPU 집약적인 코드가 있는 경우 여전히 문제가 발생하므로 CPU 집약적인 코드를 기본 이벤트 루프에서 다른 이벤트 루프로 이동하는 방법을 찾아야 합니다. thread 또는 프로세스(작업 대기열일 수도 있음) 그러나 I/O는 모두 비동기식이므로 문제를 일으키지 않고 기본 스레드에 남아 있을 수 있습니다.
별도의 동시 스레드에 있어야 하는 코드가 적을수록 발생할 가능성이 있는 동시성 버그가 줄어들고 코드를 완전히 테스트하기가 더 쉽습니다.
글쎄, 뭔가를 반복하려고 하기 때문에 반복을 원합니다. 이벤트 루프를 차단하고 싶지 않거나 이것이 작업을 완료하는 데 필요한 유일한 작업 유형인 경우(예: 데이터베이스에서 조회 수행) 이벤트 루프에서 비동기 작업을 사용하려고 합니다.
비동기 작업을 사용하는 것은 무언가를 최적화하는 것이 아닙니다. 이는 좋은 서버 코드를 작성하고 이벤트 루프를 차단하지 않는 핵심 설계에 관한 것입니다. 그리고 실제로 nodejs에서 가능한 인터페이스(예: 데이터베이스 인터페이스 또는 네트워크 인터페이스)는 비동기 인터페이스만 제공합니다.
이 질문을 하신 방식에 따르면 핵심 Nodejs 아키텍처를 더 잘 이해하고 이벤트 루프 작동 방식과 비동기 I/O 작업 작동 방식에 대해 자세히 읽어보시면 도움이 될 것입니다.
먼저 비동기 API(네트워크나 데이터베이스 등)를 사용한다면 선택의 여지가 없습니다. 이 API를 사용하기 위해 비동기 코드를 디자인하게 됩니다. 비동기식 또는 동기식 API 사용 중에서 선택할 수 있는 경우(Node.js의 파일 시스템 액세스와 마찬가지로) 모든 API 호출에서 이벤트 루프를 차단할지 여부를 선택할 수 있습니다. 이벤트 루프를 차단하면 서버의 확장성과 응답성이 심각하게 손상됩니다.
이 특정 코드 예제는 실제로 async
、await
、生成器和 yield
와 동일한 구현에서 비동기 언어 기능의 주방 싱크를 사용하려고 시도합니다. 나는 일반적으로 이것을하지 않습니다. 이 구현의 요점은 다음과 같이 매우 간단하게 사용할 수 있는 인터페이스를 생성할 수 있다는 것입니다.
그리고 walk()
的内部是异步的。此实现向 API 用户隐藏了几乎所有异步实现的复杂性,这使得 API 更易于编码。通过将单个 await
올바른 위치에 배치하면 API 사용자는 거의 동기식으로 코딩할 수 있습니다. 이러한 Javascript 언어 기능(약속, 비동기, 대기, 생성기 등)의 목적은 비동기 작업을 보다 쉽게 코딩할 수 있도록 하는 것입니다.
이벤트 루프 모델의 장점
프로그래밍이 쉽습니다. 모든 Javascript가 동일한 스레드에서 실행되고 모든 공유 데이터 액세스가 동일한 스레드에서 이루어지기 때문에 일반적으로 스레드에서 공유 데이터에 액세스할 때 동시성 문제를 처리할 필요가 없습니다. 공유 데이터에 액세스하기 위해 뮤텍스가 필요하지 않습니다. 이러한 뮤텍스로 인해 교착 상태가 발생할 위험이 없습니다.
오류가 적습니다. 스레드에서 공개 데이터에 액세스하는 것은 버그 없는 코드를 작성하는 것이 훨씬 더 어렵습니다. 완벽하게 작성되지 않으면 코드가 경쟁 조건을 겪거나 동시성 보호가 부족할 수 있습니다. 더욱이 이러한 경쟁 조건은 테스트하기 어려운 경우가 많으며 서버에 과부하가 걸릴 때까지 명확하지 않을 수 있으며, 그런 경우에도 재현하기가 쉽지 않습니다.
더 높은 확장성(경우에 따라). 주로 I/O 바인딩된 코드의 경우 협력 이벤트 루프 모델을 사용하면 확장성이 향상될 수 있습니다. 이는 처리 중인 각 요청으로 인해 별도의 운영 체제 스레드 및 추가 오버헤드가 발생하지 않기 때문입니다. 대신, 일반적으로 다음 콜백이나 약속을 기다리는 것과 관련된 클로저에 소량의 애플리케이션 상태만 있습니다.
이벤트 루프 프로그래밍에 관한 기사
멋진 아이들이 이벤트 루프를 사용하는 이유 - 이것은 Java 프로그래밍에서 이벤트 루프를 사용하는 것에 관한 것이지만 논의는 모든 환경에 적용됩니다