Наверх ▲

Node.js

Андрей Костенко Андрей Костенко Директор VisaMap Inc., Москва.

Андрей Костенко: Здравствуйте! Меня зовут Алексей Костенко.

Зачем нужно использовать node.js, каковы принципы его работы и чем он отличается от аналогов?

Начну с примера: нужно реализовать обычный счетчик посещений. Мы получаем http-запрос. Смотрим, есть ли пользователь с таким IP или сессией. Если нет, добавляем пользователя. В любом случае добавляем запись: «Вася зашел на index.html». 

Задачу можно решить несколькими способами. Первый вариант самый простой – получаем запрос, делаем SELECT-INSERT-INSERT.

Но если мы посмотрим на лог запросов, то, очевидно, следующий пользователь будет ждать, пока обработается предыдущий. 

Если мы посмотрим на обработку запроса, то зеленым цветом будут выделены части, где наш процессор занимается полезным делом. Он обрабатывает http-запрос. Дальше большую часть времени дожидается, когда будет выполнен запрос к базе данных и не занимается никаким полезным делом. 90% времени процессор валяет дурака. 

То же самое - с каждым запросом. Этот код в любом случае будет тормозить процессы, даже если его переписать на "Си" или оптимизировать.

Следующий вариант - тоже стандартный способ, который используется во многих приложениях. Это использование обработчиков. Процесс получает задачу, ведущий сервер - тоже. Обработчики, в свою очередь, в несколько потоков или процессов выполняют задачу. Так работает, например, Apache. 

 

Как это работает в идеальном случае?

Первый обработчик выполняет определенную часть задачи, например, SQL-запрос. Процессор не используется. В это время планировщик задачи отдает процессорное время второму процессу. Инициируется второй процесс. Время выполнения задач, конечно же, существенно ниже в идеальном случае. Но действительность вносит свои коррективы.

В Rambler возникали критические ситуации, когда появилась очень популярная новость. Нагрузка сразу вырастала вдвое. Допустим, система кое-как справляется. И тут сбрасывается кэш. 

Все запросы пользователей отправляются одномоментно. Это - клиническая ситуация.

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

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

Примерно то же самое происходит с Apache, другими сервисами, которые используют либо множество потоков, либо множество обработчиков. Процессор используется не рационально.

Плюсы работы с обработчиками

• Обработчик работает намного быстрее, чем один поток. На нем можно нормально писать. 

• Если вы написали сервер, который использует while/accept, "на коленке", согласно первому варианту, то его несложно превратить во второй вариант - практически без изменений кода. 

• Поддержка нескольких процессоров. Это касается большинства новых серверов. 

О недостатках 

• Если функционирует много обработчиков и используются системные вызовы fork, то мы наталкиваемся на другое ограничение. Мы уходим в минус в плане оперативной памяти. Например, запустить 10 тысяч процессов – это уже проблема. 

• В случае с потоками возникает другая проблема. Во многих популярных языках программирования они сделаны неудовлетворительно. Там, где они сделаны нормально, нужно разбираться с shared-переменными и так далее. Процессор также не будет использоваться по максимуму.

Перейдем к асинхронным вызовам, мультиплексированию.

Мы начинаем выполнять запрос и отдаем команду: «Начать select». Далее смотрим, что в это время можем сделать полезного. К нам пришел другой запрос - мы начинаем обрабатывать его. Даем аналогичную команду и т. д. Мы сами решаем, когда и для чего нужно использовать процессорное время. 

 

Мы рассмотрели плюсы и минусы асинхронных вызовов. 

Каковы положительные стороны?

• Используя асинхронный фреймворк, вы можете задействовать ресурсы процессора на 100 %. Работа даже на одном ядре будет выполняться намного быстрее, чем на двух-четырех ядрах (для некоторых задач) при использовании обработчиков.

• Вы сами выбираете, когда переключаться между задачами. Это обеспечивает прирост производительности.

Что можно сказать о недостатках?

• Осуществлять разработку и поддержку сложнее, чем в случае с обработчиками.

• Используется только одно ядро.

• В приложении нельзя использовать мультиплексирование. Можно, но не нужно. Или все модули, работа с сетью и диском должны быть неблокирующими.

Существует компромиссный вариант, который подразумевает наличие ведущего сервера и несколько асинхронных операторов.

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

Какой вариант лучше подходит для вашей задачи, я не знаю.

Но для себя я выработал мини-правило.

• Если задачу можно решить с помощью cron, я так и делаю.

• Если задачу можно решить самым простым способом, посредством while/accept и в ближайшем будущем не понадобится ее расширять, то я использую этот вариант.

• В третьем случае я использую fork, потому что он еще проще в разработке и поддержке. 

• Если все-таки нагрузка большая и есть сервер с большим количеством соединений, а вам нужно их обрабатывать, то используется асинхронное приложение.

 

Как работают сокеты?

Для них используется опция «O_NONBLOCK», которая действует после «read» или «write». Обычно, если мы читаем что-то из «read», то мы дожидаемся n-ного количества байтов и выходим. В этом случае «read» или «write», если произойдет блокирование, возвратят ошибку «EAGAIN». Это значит, что ничего прочитано и заблокировано не было.

Через некоторое время, если мы еще раз задействуем «read», то получим данные. Но если у нас один сокет, мы можем через некоторое время «дернуть» «read», чтобы узнать, пришли ли туда данные.

При работе с сетью используются следующие функции.

• «Select/poll», который работает везде. Однако он работает очень медленно. При увеличении количества дескрипторов, за которыми мы хотим следить, скорость его работы существенно снижается. Если у вас будет 10, 20 тысяч одновременных соединений, она упадет чуть ли не до нуля и станет бесполезной.

• Так как Линус Торвальдс еще не купил FreeBSD, то мы получаем два разных вызова, которые делают примерно одно и то же. «Epoll» - в Linux и «kqueue» - во FreeBSD. Их скорость зависит только от количества активных, поступивших дескрипторов. Их производительность существенно выше и позволяет обрабатывать довольно большое количество данных и одновременно следить за большим количеством дескрипторов.

Существуют следующие функции для работы с таймером.

• «Setitimer» – для своего кода.

• «Alarm» – для кода, которому вы хотите сделать неприятный сюрприз.

Для одной операционной системы - у нас «kqueue», для другой – «epoll».

Должна быть еще одна система, которая бы работала по принципу: «А сейчас мы посмотрим, чем бы заняться еще». Система называется циклом события (англ. event loop). У нее есть список задач, из которого она выбирает подходящие. Эти библиотеки, во-первых, являются абстракцией набора системных вызовов. Они работают на уровне функции обратного вызова. Они предоставляют один цикл ожидания события. 

Цикл должен быть один. Говорят, «libev» быстрее, чем «libevent», но тестирование пока не проводил.

Что такое node.js?

Это продукт, который комбинирует библиотеку «libevent» — она существенно быстрее конкурентов. Он использует движок JavaScript V8 от Google, который создает многие современные интерпретируемые языки программирования по производительности, а также язык программирования JavaScript, предназначенный для неблокирующей работы. 

Вы пишете домашнюю страничку на index.html, добавляете туда JavaScript, неблокирующие приложения и создаете асинхронное приложение. В данной ситуации использовать JavaScript особенно удобно.  Даже, насколько я помню, в версии Firefox 3.3.0 весь JavaScript выполнялся в одном потоке, в одном процессе. Если какой-то из процессов перегружал Firefox, то «зависало» все. 

Итак, вы установили node.js. Вы работаете с HTTP, работа с файловой системой - неблокирующая. В наличии - SSL, DNS и таймеры. С JavaScript работа идет так же, как с AJAX - на странице.

Чего еще нет, но может дать node.js? 

• NPM – это Node Package Manager, с помощью которого можно поставить практически любой модуль. У node.js есть отладчик, с которым не все так хорошо. 

• Есть модуль «Socket.IO», который предоставляет абстракцию. Вы можете написать один код, который будет работать и на сервере с сетью, и на клиенте в браузере. Он точно так же будет работать с сокетами, используя один из возможных протоколов. Используется либо flash, либо «костыли» с AJAX.

• CoffeeScript – это специальный язык программирования, который компилируется в JavaScript. Разработка становится чуть удобнее, потому что те костыли, которые вбиваются JavaScript, не вошли в стандарт, но их часто приходится использовать. 

Например, я часто пишу «var self = this», чтобы создать замыкание. Там это делать не нужно. Необъявленная перемена без «var» все равно локальная, так же, как в Python. Если не писать «var», то глобальная переменная не объявляется. 

• Async-интерфейс применим к 99 % приложений. По собственному опыту скажу, что асинхронный интерфейс может быть использован для всего, что можно. PostgreSQL, MySQL, Beanstalk и так далее. Есть интерфейс ко всему, и он будет неблокирующим – вы сразу можете его использовать.

 

С какими трудностями сталкивается программист, когда пытается работать с node.js?

С ROException, обычным приложением на Perl или Pyton – без проблем. «Бросили» исключение, «выловили» его на более высоком уровне, обработали ошибку или спрятали подальше от менеджеров. 

Здесь так не получится. Вы «выбросили» исключение, а дальше обычно приложение выходит по стеку вызовов наверх. У вас его нет. Т.к. приложение асинхронное, оно просто не знает, что делать дальше. Поэтому выбрасывание исключения необходимо только в том случае, если вы хотите завершить приложение в аварийном порядке. Либо используйте ту часть, где не используются обратные вызовы. 

С отладчиком – то же самое. Используя Firebug Debugger, вы не можете войти в onClick. Есть свои особенности работы с обратными вызовами. Войти внутрь функции, которую требует обратный вызов, не получится. То же самое — с профайлерами.

При разработке кода возникает еще одна проблема. Хочется написать что-то побольше, а потом брать оттуда логические блоки, функции и так далее. 

С node.js — другая проблема. Он предоставляет очень хороший интерфейс для создания "спагетти", использования замыкания из верхнего уровня и так далее. Если написать код с множеством обратных вызовов и анонимных функций, разложить его обратно получается иногда очень сложно.

История успеха – это ВКонтакте XMPP.  Когда-то они даже обещали открыть его исходники, но пока не решились. Также node.js используют 37Signals, Wikia, Palm/HP WebOS. Использовали его и в Rambler.

 Тесты производительности

Давайте перейдем к тестам производительности. Я считаю, что они имеют небольшое значение. Аналогичная история возникает при использовании сортировки пузырьком или, например, QuickSort. На медленном компьютере QuickSort будет работать медленнее, чем сортировка пузырьком - на очень быстром.

Но в любом случае при увеличении количества данных медленный компьютер с QuickSort лучше быстрого, потому что там используется более быстрый алгоритм. 

 

Опыты с HTTP и языком программирования показали, что при увеличении количества соединений скорость практически не будет падать. Важна именно технология, а не скорость работы. Node.js — впереди всех.

Комментарии

Нет ни одного комментария

Только пользователи могут оставлять комментарии

Возможно, вам будет интересно:

Ольга Павлова

Ольга Павлова

Владелец 80% компании «Собака Павлова»

Я абсолютно уверена в том, что большинство из вас вещь, которая называется словом "usability" еще не щупали руками. Или, может быть, щупали как-то случайно… В общем, не успели поиграть. Постараюсь рассказать о том, как в это можно поиграть. И где в нее можно играть.

Алексей Полковников

Алексей Полковников

Президент ассоциации управления проектами СОВНЕТ (представитель России в IPMA), генеральный директор и управляющий партнер ГК «Проектная ПРАКТИКА»

Рассказ, в первую очередь, о существующей сертификации в области профессионального проектного менеджмента.

Сергей Скворцов

Сергей Скворцов

Исполнительный директор Openstat, FreeBSD committer, Perl adept и прочая, прочая...

Есть такие слова, которые многие не любят (например, «синергия», «миссия»). Мне кажется, что «бизнес-модель» – одно из них. На самом деле, в этом докладе речь пойдет о том, что это вполне хорошее, правильное понятие, которое нужно понимать и использовать.