Почему node.js быстр, когда он однопоточный?
Несмотря на то, что node.js является однопоточным, он быстрее? Я не запускал никаких тестов для поиска статистики, но, копаясь в форумах node.js, я обнаружил, что все говорят, что это быстрее и легче. Но как бы он ни был легок, как однопоточный сервер может быть быстрее многопоточного?
2 ответа
Во-первых, почему программа быстрее, когда многопоточная?
Отчасти это связано с тем, что многопоточная программа может работать на нескольких ядрах, но главная причина, безусловно, заключается в том, что когда поток ожидает какой-либо операции ввода-вывода (что очень часто, особенно на сервере), другие потоки все еще может прогрессировать
Теперь, как насчет узла?
Узел не однопоточный. Сценарий пользователя в JS выполняется в одном потоке, но все операции ввода-вывода изначально обрабатываются libuv и ОС, которые являются многопоточными.
На практике это означает, что несколько запросов обрабатываются параллельно. Вот очень (очень) упрощенный пример возможной последовательности действий:
user script | node + OS "threads" (libuv)
-------------------------------------------------------------
receive and analyze request 1 |
ask node for file 1 | fetching file 1
receive and analyze request 2 | fetching file 1
ask node for file 2 | fetching file 1, fetching file 2
prepare response header 1 | fetching file 2
tell node to send file 1 | send file 1, fetching file 2
prepare response header 2 | send file 1
tell node to send file 2 | send file 1, send file 2
Вся архитектура узла (и io.js) позволяет легко иметь высокий уровень параллелизма. Пользовательский поток вызывается циклом событий только для очень коротких задач, которые останавливаются при следующей операции ввода-вывода (ну, в действительности, не только ввода-вывода, но чаще всего), когда ваш код дает узлу обратный вызов, который будет вызван после завершения операции.
Конечно, это работает только тогда, когда вы используете асинхронные функции Node. Каждый раз, когда вы используете функцию, оканчивающуюся на "Синхронизация", например writeFileSync, вы побеждаете параллелизм.
Node.js не является однопоточным: см. https://nodejs.org/about/:
любые соединения могут быть обработаны одновременно
Фактически, он не использует системные потоки, а вместо этого использует движок V8 вместе с библиотекой libuv для многопоточности посредством асинхронных обратных вызовов.
Также вы можете использовать дополнительный дочерний процесс через child_process.fork
Наконец, это никоим образом не влияет на скорость реакции или общую скорость двигателя. Многопоточность здесь для масштабируемости.
Поскольку nodejs не будет ждать ответа, вместо этого он следует программированию, управляемому событиями, с обратными вызовами, т.е. после отправки запроса он будет помещен в очередь событий, и каждый запрос обрабатывается одним потоком, но этот поток просто отправит запрос и перейти к следующему запросу и так далее, не дожидаясь ответа. После обработки запроса будет выполнена соответствующая функция обратного вызова запроса.