То, что вы, вероятно, видите, является либо асинхронной частью реализации внутри res.end()
для фактической отправки большого объема данных, либо вы видите, что все данные отправляются очень быстро и последовательно, но клиенты не могут обрабатывать его достаточно быстро, чтобы фактически показывать его последовательно, и поскольку каждый клиент находится в своем собственном отдельном процессе, они «появляются», чтобы показать, что он прибывает одновременно, только потому, что они слишком медленно реагируют, чтобы показать действительную последовательность поступления.
Нужно использовать сетевой сниффер, чтобы увидеть, какой из них на самом деле происходит, или запустить несколько разных тестов, или поместить некоторую запись в реализацию res.end()
, или нажать на некоторую запись в стеке TCP клиента, чтобы определить фактический порядок. прибытия пакета среди различных запросов.
Если у вас один сервер и один обработчик запросов, выполняющий синхронный ввод-вывод, вы не сможете одновременно обрабатывать несколько запросов. Если вы считаете, что это происходит, то вам придется документировать, как именно вы это измерили или пришли к выводу (поэтому мы можем помочь вам разобраться в вашем недопонимании), потому что это не то, как работает node.js при использовании блокирующих, синхронных операций ввода-вывода, таких как как fs.readFileSync()
.
node.js запускает ваш JS как однопоточный, и когда вы используете блокировку синхронного ввода-вывода, он блокирует этот единственный поток Javascript. Вот почему вы никогда не должны использовать синхронный ввод-вывод на сервере, за исключением, возможно, кода запуска, который запускается только один раз при запуске.
Очевидно, что fs.readFileSync('./big.file')
является синхронным, поэтому ваш второй запрос не будет запущен до тех пор, пока не будет выполнен первый fs.readFileSync()
. И снова и снова вызывать его для одного и того же файла будет очень быстро (кэширование диска ОС).
Но, res.end(data)
неблокирующий, асинхронный. res
- это поток, и вы даете потоку некоторые данные для обработки. Он будет отправлять столько данных, сколько может, через сокет, но если он получает управление потоком по протоколу TCP, он приостанавливается до тех пор, пока в сокете не останется больше места для отправки. То, как много это произойдет, зависит от разных вещей на вашем компьютере, его конфигурации и сетевого подключения к клиенту.
Итак, что может происходить, так это последовательность событий:
Первый запрос приходит и делает fs.readFileSync()
и звонит res.end(data)
. Это начинает отправку данных клиенту, но возвращается, прежде чем это будет сделано из-за управления потоком TCP. Это отправляет node.js обратно в цикл обработки событий.
Второй запрос приходит и делает fs.readFileSync()
и звонит res.end(data)
. Это начинает отправку данных клиенту, но возвращается, прежде чем это будет сделано из-за управления потоком TCP. Это отправляет node.js обратно в цикл обработки событий.
В этот момент цикл обработки событий может начать обработку третьего или четвертого запросов или может обслуживать еще несколько событий (изнутри реализации res.end()
или writeStream из первого запроса, чтобы продолжать отправлять больше данных. Если он обслуживает эти события, он может создать видимость (с точки зрения клиента) истинного параллелизма различных запросов).
Кроме того, клиент может вызывать его последовательность. Каждый клиент читает разные буферизованные сокеты, и если они все находятся в разных терминалах, то они многозадачны. Таким образом, если в каждом клиентском сокете имеется больше данных, чем он может прочитать и отобразить немедленно (что, вероятно, имеет место), то каждый клиент будет читать некоторые данные, отображать некоторые, читать еще некоторые, отображать еще немного и т. Д ... Если задержка между отправкой ответа каждого клиента на ваш сервер меньше, чем задержка чтения и отображения на клиенте, тогда клиенты (каждый из которых находится в своем собственном отдельном процессе) могут работать одновременно.
Когда вы используете асинхронный ввод-вывод, такой как fs.readFile()
, тогда правильно написанный Javascript-код node.js может иметь много запросов «в полете» одновременно.На самом деле они не работают одновременно в одно и то же время, но можно запустить, выполнить некоторую работу, запустить асинхронную операцию, а затем дать возможность запустить другой запрос.При правильно написанном асинхронном вводе / выводе из внешнего мира может возникать параллельная обработка, даже если это больше похоже на совместное использование одного потока, когда обработчик запросов ожидает завершения асинхронного запроса ввода / вывода.Но код сервера, который вы показываете, не является этим кооперативным асинхронным вводом-выводом.