이 질문을해야한다면 대부분의 웹 응용 프로그램 / 서비스가하는 일에 익숙하지 않을 것입니다. 모든 소프트웨어가 다음을 수행한다고 생각할 것입니다.
user do an action
│
v
application start processing action
└──> loop ...
└──> busy processing
end loop
└──> send result to user
그러나 이것은 웹 애플리케이션 또는 실제로 데이터베이스를 백엔드로 사용하는 애플리케이션이 작동하는 방식이 아닙니다. 웹 앱은 다음을 수행합니다.
user do an action
│
v
application start processing action
└──> make database request
└──> do nothing until request completes
request complete
└──> send result to user
이 시나리오에서 소프트웨어는 데이터베이스가 돌아 오기를 기다리는 0 % CPU 시간을 사용하여 대부분의 실행 시간을 소비합니다.
멀티 스레드 네트워크 앱 :
멀티 스레드 네트워크 앱은 다음과 같이 위의 작업을 처리합니다.
request ──> spawn thread
└──> wait for database request
└──> answer request
request ──> spawn thread
└──> wait for database request
└──> answer request
request ──> spawn thread
└──> wait for database request
└──> answer request
따라서 스레드는 데이터베이스가 데이터를 반환하기를 기다리는 0 % CPU를 사용하여 대부분의 시간을 보냅니다. 그렇게하는 동안 스레드마다 필요한 별도의 프로그램 스택을 포함하는 스레드에 필요한 메모리를 할당해야했습니다. 또한 전체 프로세스를 시작하는 것만 큼 비싸지 않은 스레드를 시작해야합니다. 싼.
단일 스레드 이벤트 루프
대부분의 시간을 0 % CPU를 사용하므로 CPU를 사용하지 않을 때 일부 코드를 실행하지 않는 이유는 무엇입니까? 이런 식으로 각 요청은 여전히 멀티 스레드 응용 프로그램과 동일한 양의 CPU 시간을 갖지만 스레드를 시작할 필요는 없습니다. 그래서 우리는 이것을합니다 :
request ──> make database request
request ──> make database request
request ──> make database request
database request complete ──> send response
database request complete ──> send response
database request complete ──> send response
실제로 두 접근 방식은 처리를 지배하는 데이터베이스 응답 시간이기 때문에 거의 동일한 대기 시간으로 데이터를 반환합니다.
여기서 가장 큰 장점은 새로운 스레드를 생성 할 필요가 없어서 많은 속도를 낼 수있는 malloc을 많이 할 필요가 없다는 것입니다.
마술, 보이지 않는 스레딩
겉보기에 신비한 것은 위의 두 가지 접근 방식이 모두 "병렬"로 워크로드를 관리하는 방법입니다. 답은 데이터베이스가 스레드된다는 것입니다. 따라서 단일 스레드 앱은 실제로 다른 프로세스의 다중 스레드 동작 인 데이터베이스를 활용합니다.
단일 스레드 접근 방식이 실패하는 경우
데이터를 반환하기 전에 많은 CPU 계산을 수행해야하는 경우 단일 스레드 앱이 크게 실패합니다. 이제는 데이터베이스 결과를 처리하는 for 루프를 의미하지 않습니다. 그것은 여전히 대부분 O (n)입니다. 내 말은 푸리에 변환 (예 : mp3 인코딩), 광선 추적 (3D 렌더링) 등을 수행하는 것입니다.
단일 스레드 응용 프로그램의 또 다른 함정은 단일 CPU 코어 만 사용한다는 것입니다. 따라서 쿼드 코어 서버가 있다면 (현재는 드문 일이 아닙니다) 다른 3 개의 코어를 사용하지 않는 것입니다.
멀티 스레드 접근이 실패하는 경우
스레드 당 많은 RAM을 할당해야하는 경우 멀티 스레드 앱이 크게 실패합니다. 첫째, RAM 사용량 자체는 단일 스레드 앱만큼 많은 요청을 처리 할 수 없음을 의미합니다. 더구나, malloc은 느리다. 많은 웹 객체에 공통 인 많은 객체를 할당하면 단일 스레드 앱보다 속도가 느려질 수 있습니다. 이것은 node.js가 일반적으로이기는 곳입니다.
다중 스레드를 악화시키는 사용 사례 중 하나는 스레드에서 다른 스크립팅 언어를 실행해야하는 경우입니다. 먼저 일반적으로 해당 언어의 전체 런타임을 malloc해야합니다. 그런 다음 스크립트에서 사용하는 변수를 malloc해야합니다.
따라서 C 또는 go 또는 java로 네트워크 응용 프로그램을 작성하는 경우 스레딩 오버 헤드가 일반적으로 그리 나쁘지 않습니다. PHP 또는 Ruby를 제공하기 위해 C 웹 서버를 작성하는 경우 Javascript 또는 Ruby 또는 Python으로 더 빠른 서버를 작성하는 것이 매우 쉽습니다.
하이브리드 접근법
일부 웹 서버는 하이브리드 방식을 사용합니다. 예를 들어 Nginx와 Apache2는 네트워크 처리 코드를 이벤트 루프의 스레드 풀로 구현합니다. 각 스레드는 이벤트 루프를 동시에 실행하여 단일 스레드 요청을 처리하지만 여러 스레드간에 요청이로드 밸런싱됩니다.
일부 단일 스레드 아키텍처도 하이브리드 방식을 사용합니다. 단일 프로세스에서 여러 스레드를 시작하는 대신 쿼드 코어 시스템에서 4 개의 node.js 서버와 같은 여러 애플리케이션을 시작할 수 있습니다. 그런 다음로드 밸런서를 사용하여 프로세스간에 작업 부하를 분산시킵니다.
사실상 두 가지 접근 방식은 기술적으로 동일한 미러 이미지입니다.