суббота, 13 июля 2013 г.

Как работает NodeJs в сравнении с другими серверами

Самый распространенный подход в создании серверов состоит в следующем. Параллельно запускается множество процессов, цель которых обслуживать запросы клиентов. Если приходит запрос от клиента, то на его обработку выделяется один запущенный заранее процесс, пока в нем происходит вычисления, ожидания ответа с базы, ждет только этот клиент, который обслуживается данным запросом. Все остальные пришедшие запросы, попадают в свободные процессы и так нет проблем с обработкой запросов, пока есть свободные клиенты и успевают освобождаться занятые под новопришедшие.

Количество возможных процессов под сервер ограничено, потому что процессы операционной системы достаточно тяжелые штуки, это проблему попытались решить потоками, но они не намного легче процессов. Дальше всего пошел Erlang, который создает свои собственные процессы и потоки, очень легковесные, на данный момент это самый стойкий сервер.
NodeJs пошел своим путем, он выполняется в одном процессе и в одном потоке, но в асинхронном режиме. За счет колбеков, он выполняет скрипт, нужно обратиться к базе, он к ней обратился, подписал колбек на момент получения ответа и пошел дальше, обрабатывать другие запросы, когда приходит ответ от базы он это видит в своем цикле и уже выбирает не обработать новый запрос, а ответить на старый. В такой схеме работы ноде нет равных, сверхскоросной IO его стихия. Но вот вычислительные операции это слабость ноды, если он нарывается на такую, то ждать будет все вокруг, потому что нод однопроцессный, все у него последовательно. Эту проблему можно решать и рассмотрим это чуток позже, а пока разберемся в схеме работы ноды.

Нод - это сишная библиотека libUV. Ее первая задача это выполнять цикл ноды, каждая его итерация возможна до тех пор, пока существует хотя бы один колбек подписанный на какое-то событие или таймер.
Такой цикл начинается из того, что в виртуальную машину Google V8 передается javascript, который нужно выполнить из колбека, который подписан на что-то, или просто у нас запущен скрипт и это у нас первое его выполнение. Произошло выполнение, если у нас там вызвались какие-то IO-операции(асинхронные) или запуски таймеров, то libUV выполняет их, ведь все это реализовано в нем, он пользуясь операционной системой делает это IO операции, и засыпает после выполнения первого цикла, потом на входе в него проверяется есть ли какие-то обработчики или таймеры, проверяет не произошло ли то что они ждут, и самый верхний в стеке отправляют снова в Google V8, с пришедшими данными, которых он ждал.
Так все повторяется пока у нас не пропадут все обработчики и таймеры. Кроме того мы можем любой таймер, сокет, чтение из файла и другую асинхронную штуку, обьявить unref(вызвав такой метод на соответсвующем обьектом), и тогда такой обьект не будет проверятся этим событийным циклом, тоесть если все первостепенные обработчки будут отписаны, и таймеры выполнены, то эти обьекты ожидаться не будут, libUV просто завершит процесс.

Теперь как же поступать с случаями, когда нам нужно считать. Для решения этой проблемы у ноды есть три способа:
1) с помощью модуля cluster запустить несколько процессов ноды организовать общение между ними и таким образом не беспокоиться когда один из них подвиснет на вычисление, другие смогут обрабатывать запросы.
2) Использовать для вычислительных целей сервер на другой платформе, который будет работать паралельно с нодой, и подписываться на его завершение исчисления, опять таки колбеком -- никаких зависаний, все занято тем, что получается у него лучше всего.
3) Разбить сложное вычисление на части с помощью функции setImidiate.


Комментариев нет:

Отправить комментарий