Я использую Node.js на работе и считаю его очень мощным. Вынужденный выбрать одно слово для описания Node.js, я бы сказал «интересный» (что не является чисто положительным прилагательным). Сообщество является ярким и растущим. JavaScript, несмотря на его странности, может быть отличным языком для написания кода. И вы будете ежедневно переосмысливать свое собственное понимание «наилучшей практики» и шаблонов хорошо структурированного кода. В Node.js прямо сейчас течет огромная энергия идей, и работа в ней подвергает вас всему этому мышлению - великому умственному поднятию тяжестей.
Node.js в производстве определенно возможен, но далеко от «под ключ» развертывания, казалось бы, обещанного документацией. С Node.js v0.6.x «кластер» был интегрирован в платформу, предоставляя один из основных строительных блоков, но мой сценарий «production.js» все еще ~ 150 строк логики для обработки таких вещей, как создание журнала каталог, утилизация мертвых рабочих и т. д. Для «серьезной» производственной службы вам также необходимо быть готовым регулировать входящие соединения и делать все то, что Apache делает для PHP . Чтобы быть справедливым, Рубин на Rails имеет эту точную проблему. Это решается с помощью двух дополнительных механизмов: 1) Установка Ruby на Rails / Node.Apache / Lighttd ). Веб-сервер может эффективно обслуживать статический контент, осуществлять доступ к ведению журналов, перезаписывать URL-адреса, прерывать SSL , применять правила доступа и управлять несколькими вспомогательными службами. Для запросов, которые попадают в реальную службу узла, веб-сервер передает запрос через. 2) Использование фреймворка, такого как Unicorn, который будет управлять рабочими процессами, периодически их перерабатывать и т. Д. Мне еще предстоит найти обслуживающую фреймворк Node.js, который кажется полностью запеченным; он может существовать, но я еще не нашел его и до сих пор использую ~ 150 строк в моем ручном свернутом файле «production.js».
При чтении таких фреймворков, как Express, создается впечатление, что стандартная практика состоит в том, чтобы просто обслуживать все через один сервис Node.js "на все руки" ... "app.use (express.static (__ dirname + '/ public'))" , Для менее загруженных сервисов и разработки это, вероятно, хорошо. Но как только вы попытаетесь увеличить нагрузку на свой сервис и запустить его в режиме 24/7, вы быстро обнаружите мотивы, побуждающие большие сайты получать хорошо запеченный и надежный C-код, такой как Nginx, выходящий на их сайт и обрабатывающий все запросов статического содержимого (... пока вы не настроите CDN , например Amazon CloudFront )). Для несколько юмористического и откровенно негативного взгляда на это, посмотрите на этого парня .
Node.js также находит все больше и больше необслуживаемых применений. Даже если вы используете что-то другое для обслуживания веб-контента, вы все равно можете использовать Node.js в качестве инструмента сборки, используя модули npm для организации вашего кода, Browserify для сшивания его в единый ресурс и uglify-js для минимизации его для развертывания. , Для работы с Интернетом JavaScript является идеальным соответствием импеданса и часто делает его наиболее простым способом атаки. Например, если вы хотите разбираться с кучей полезных нагрузок ответов JSON , вы должны использовать мой модуль подчеркивания-CLI , служебный пояс структурированных данных.
За и против:
- Pro: Для серверного человека написание JavaScript на бэкэнде было «наркотиком шлюза» для изучения современных шаблонов пользовательского интерфейса. Я больше не боюсь писать клиентский код.
- Pro: имеет тенденцию поощрять правильную проверку ошибок (ошибка возвращается практически всеми обратными вызовами, из-за чего программист вынужден ее обрабатывать; также async.js и другие библиотеки обрабатывают парадигму «сбой, если любая из этих подзадач» намного лучше, чем типичный синхронный код) )
- Pro: Некоторые интересные и обычно сложные задачи становятся тривиальными - например, получение статуса для выполняемых задач, общение между работниками или совместное использование состояния кэша.
- Pro: Огромное сообщество и множество отличных библиотек, основанных на надежном менеджере пакетов (npm)
- Против: JavaScript не имеет стандартной библиотеки. Вы настолько привыкли импортировать функциональность, что это кажется странным, когда вы используете JSON.parse или какой-то другой встроенный метод, который не требует добавления модуля npm. Это означает, что существует пять версий всего. Даже модули, включенные в «ядро» Node.js, имеют еще пять вариантов, если вы недовольны реализацией по умолчанию. Это приводит к быстрой эволюции, но также и некоторой путанице.
По сравнению с простой моделью «один процесс на запрос» ( LAMP ):
- Pro: Масштабируется до тысяч активных соединений. Очень быстро и очень эффективно. Для веб-флота это может означать сокращение требуемого количества блоков в 10 раз по сравнению с PHP или Ruby.
- Pro: Написание параллельных шаблонов легко. Представьте, что вам нужно получить три (или N) больших двоичных объекта из Memcached . Сделайте это в PHP ... вы только что написали код, извлекающий первый BLOB-объект, затем второй, а затем третий? Вау, это медленно. Существует специальный модуль PECL, чтобы исправить эту конкретную проблему для Memcached, но что, если вы хотите извлечь некоторые данные Memcached параллельно с запросом вашей базы данных? В Node.js, потому что парадигма асинхронная, веб-запрос делает несколько вещей параллельно, это очень естественно.
- Против: Асинхронный код принципиально сложнее, чем синхронный код, и предварительная кривая обучения может оказаться сложной для разработчиков без четкого понимания того, что на самом деле означает параллельное выполнение. Тем не менее, это гораздо проще, чем писать многопоточный код с блокировкой.
- Con: Если запрос с интенсивными вычислениями выполняется, например, в течение 100 мс, он остановит обработку других запросов, которые обрабатываются в том же процессе Node.js ... AKA, кооперативная многозадачность . Это может быть смягчено с помощью шаблона Web Workers (выделение подпроцесса для решения дорогостоящей задачи). В качестве альтернативы, вы можете использовать большое количество рабочих Node.js и позволить каждому из них обрабатывать один запрос одновременно (все еще довольно эффективно, потому что нет повторного использования процесса).
- Против: Запуск производственной системы НАМНОГО сложнее, чем модель CGI, такая как Apache + PHP, Perl , Ruby и т. Д. Необработанные исключения приведут к останову всего процесса, что потребует логики для перезапуска сбойных рабочих (см. Кластер ). Модули с ошибочным нативным кодом могут привести к аварийному завершению процесса. Всякий раз, когда работник умирает, любые запросы, которые он обрабатывал, отбрасываются, поэтому один ошибочный API может легко ухудшить службу для других связанных API.
По сравнению с написанием «реального» сервиса в Java / C # / C (C? Действительно?)
- Pro: Выполнение асинхронного в Node.js проще, чем обеспечение безопасности потоков где-либо еще и, возможно, обеспечивает большую выгоду. Node.js - безусловно наименее болезненная асинхронная парадигма, в которой я когда-либо работал. С хорошими библиотеками это лишь немного сложнее, чем писать синхронный код.
- Pro: Нет ошибок многопоточности / блокировки. Правда, вы заранее инвестируете в написание более подробного кода, который выражает правильный асинхронный рабочий процесс без операций блокировки. И вам нужно написать несколько тестов и заставить их работать (это язык сценариев, а имена переменных с толстыми аппликатурами перехватываются только во время модульных тестов). НО, как только вы заставите его работать, площадь поверхности для heisenbugs - странные проблемы, которые проявляются только один раз за миллион прогонов - эта площадь поверхности просто намного ниже. Налоги на написание кода Node.js сильно загружены в фазе кодирования. Тогда вы, как правило, получаете стабильный код.
- Pro: JavaScript намного легче для выражения функциональности. Трудно доказать это словами, но JSON , динамическая типизация, лямбда-нотация, наследование прототипов, легковесные модули, что угодно ... просто требуется меньше кода, чтобы выразить те же идеи.
- Con: Может быть, вам действительно очень нравятся сервисы кодирования в Java?
Еще один взгляд на JavaScript и Node.js можно найти в блоге « От Java до Node.js» , где рассказывается о впечатлениях и опыте Java-разработчика по изучению Node.js.
Модули
При рассмотрении узла, имейте в виду, что ваш выбор библиотек JavaScript определит ваш опыт. Большинство людей используют как минимум два, асинхронный помощник по шаблонам (Step, Futures, Async) и модуль сахара JavaScript ( Underscore.js ).
Помощник / JavaScript Сахар:
- Underscore.js - используйте это. Просто сделай это. Это делает ваш код красивым и читаемым с такими вещами, как _.isString () и _.isArray (). Я не совсем уверен, как вы могли бы написать безопасный код в противном случае. Кроме того, для расширенной командной строки fu, проверьте мой собственный Underscore-CLI .
Модули асинхронной структуры:
- Шаг - очень элегантный способ выразить комбинации последовательных и параллельных действий. Мои личные рекомендации. Смотрите мой пост о том, как выглядит код Step.
- Фьючерсы - гораздо более гибкий (действительно ли это хорошая вещь) способ выразить порядок через требования. Может выражать такие вещи, как «начать a, b, c параллельно. Когда A и B закончат, начните AB. Когда A и C закончат, запустите AC». Такая гибкость требует большей осторожности, чтобы избежать ошибок в вашем рабочем процессе (например, никогда не вызывать обратный вызов или вызывать его несколько раз). См. Сообщение Рейноса об использовании фьючерсов (этот пост заставил меня "получить" фьючерсы).
- Async - более традиционная библиотека с одним методом для каждого шаблона. Я начал с этого до моего религиозного перехода к шагу и последующего осознания того, что все шаблоны в асинхронности могут быть выражены в шаге с помощью одной более читаемой парадигмы.
- TameJS - написанный OKCupid, это прекомпилятор, который добавляет новый примитив языка «await» для элегантного написания последовательных и параллельных рабочих процессов. Шаблон выглядит потрясающе, но требует предварительной компиляции. Я все еще решаюсь об этом.
- StreamlineJS - конкурент TameJS. Я склоняюсь к Тейму, но ты можешь решить сам.
Или чтобы прочитать все об асинхронных библиотеках, посмотрите это интервью с авторами.
Веб-фреймворк:
- Express Great Ruby on Rails-esk Framework для организации веб-сайтов. Он использует JADE в качестве движка шаблонов XML / HTML, что делает построение HTML гораздо менее болезненным, даже почти элегантным.
- jQuery Хотя технически не является модулем узла, jQuery быстро становится стандартом де-факто для пользовательского интерфейса на стороне клиента. jQuery предоставляет CSS-подобные селекторы для «запроса» на наборы элементов DOM, с которыми затем можно работать (задавать обработчики, свойства, стили и т. д.). В том же духе, CSS-фреймворк Bootstrap в Twitter , Backbone.js для шаблона MVC и Browserify.js для сшивания всех ваших файлов JavaScript в один файл. Все эти модули становятся стандартами де-факто, поэтому вы должны хотя бы проверить их, если не слышали о них.
Тестирование:
- JSHint - должен использовать; Сначала я этим не пользовался, что сейчас кажется непостижимым. JSLint добавляет кучу базовых проверок, которые вы получаете с помощью скомпилированного языка, такого как Java. Несоответствующие скобки, необъявленные переменные, шрифты разных форм и размеров. Вы также можете включить различные формы того, что я называю «анальным режимом», когда вы проверяете стиль пробелов и тому подобное, что нормально, если это ваша чашка чая - но реальная ценность заключается в получении мгновенной обратной связи о точном номере строки, где Вы забыли закрывающее ")" ... без необходимости запуска кода и попадания в оскорбительную строку. «JSHint» является более настраиваемым вариантом Douglas Крокфорд «s JSLint .
- Мокко конкурент к обетам, которые я начинаю отдавать предпочтение. Обе структуры достаточно хорошо справляются с основами, но сложные шаблоны легче выразить в Mocha.
- Vows Vows действительно довольно элегантно. И он выводит прекрасный отчет (--spec), показывающий, какие тестовые примеры пройдены / не пройдены. Потратьте 30 минут на его изучение, и вы сможете создавать базовые тесты для своих модулей с минимальными усилиями.
- Zombie - Безголовое тестирование HTML и JavaScript с использованием JSDom в качестве виртуального «браузера». Очень мощный материал. Объедините его с Replay, чтобы получить молниеносные детерминированные тесты кода в браузере.
- Комментарий о том, как «думать о» тестировании:
- Тестирование не является обязательным. В динамическом языке, таком как JavaScript, статических проверок очень мало. Например, передача двух параметров методу, который ожидает 4, не сломается, пока код не будет выполнен. Довольно низкая планка для создания ошибок в JavaScript. Базовые тесты необходимы для восполнения пробела в проверке скомпилированных языков
- Забудьте проверку, просто сделайте ваш код выполненным. Для каждого метода мой первый случай проверки - «ничего не ломается», и именно этот случай срабатывает чаще всего. Доказательство того, что ваш код выполняется без сбоев, улавливает 80% ошибок и сделает так много, чтобы повысить уверенность в коде, и вы вернетесь назад и добавите нюансы проверки, которые вы пропустили.
- Начните с малого и разбейте инерционный барьер. Мы все ленивы и тратят время, и тестирование легко воспринимать как «дополнительную работу». Так что начните с малого. Напишите контрольный пример 0 - загрузите ваш модуль и сообщите об успехе. Если вы заставляете себя делать именно это, то инерционный барьер для тестирования преодолевается. Это <30 минут, чтобы сделать это в первый раз, включая чтение документации. Теперь напишите тестовый пример 1 - вызовите один из ваших методов и убедитесь, что «ничего не ломается», то есть вы не получили ошибку назад. Тестовый пример 1 займет у вас менее одной минуты. С исчезновением инерции становится легко постепенно увеличивать охват тестами.
- Теперь развивайте свои тесты с помощью своего кода. Не пугайтесь того, как будет выглядеть «правильный» сквозной тест с фиктивными серверами и всем этим. Код начинается с простого и развивается для обработки новых случаев; тесты тоже должны. Когда вы добавляете новые случаи и новую сложность в свой код, добавляйте контрольные примеры для реализации нового кода. По мере обнаружения ошибок добавляйте проверки и / или новые случаи, чтобы покрыть некорректный код. Когда вы отлаживаете и теряете доверие к коду, вернитесь и добавьте тесты, чтобы доказать, что он делает то, что вы думаете. Захватите строки данных примера (из других сервисов, которые вы вызываете, веб-сайты, которые вы просматриваете, и т. Д.) И передайте их в свой код анализа. Здесь есть несколько случаев, улучшенная проверка, и вы получите очень надежный код.
Также ознакомьтесь с официальным списком рекомендуемых модулей Node.js. Тем не менее, GitHub's Node Modules Wiki является гораздо более полным и хорошим ресурсом.
Чтобы понять Node, полезно рассмотреть несколько ключевых вариантов дизайна:
Node.js является EVENT ОСНОВАНИЯ и ASYNCHRONOUS / неблокируемому, События, такие как входящее HTTP-соединение, запускают функцию JavaScript, которая выполняет небольшую работу и запускает другие асинхронные задачи, такие как подключение к базе данных или получение контента с другого сервера. После запуска этих задач функция события завершается, и Node.js возвращается в спящий режим. Как только происходит что-то еще, например, когда устанавливается соединение с базой данных или внешний сервер отвечает содержимым, функции обратного вызова запускаются, и выполняется больше кода JavaScript, что может привести к запуску еще более асинхронных задач (например, запроса к базе данных). Таким образом, Node.js успешно чередует действия для нескольких параллельных рабочих процессов, выполняя любые операции, разблокированные в любой момент времени. Вот почему Node.js отлично справляется с управлением тысячами одновременных подключений.
Почему бы просто не использовать один процесс / поток на соединение, как все остальные?В Node.js новое соединение - это всего лишь очень маленькое выделение кучи. Для раскрутки нового процесса на некоторых платформах требуется значительно больше памяти, мегабайт. Но реальная стоимость - это накладные расходы, связанные с переключением контекста. Когда у вас есть 10 ^ 6 потоков ядра, ядро должно проделать большую работу, чтобы выяснить, кто должен выполнять следующее. Большая часть работы была посвящена созданию планировщика O (1) для Linux, но, в конце концов, гораздо эффективнее иметь один управляемый событиями процесс, чем 10 ^ 6 процессов, конкурирующих за время процессора. Кроме того, в условиях перегрузки многопроцессорная модель ведет себя очень плохо, из-за чего не хватает критически важных служб администрирования и управления, особенно SSHD (это означает, что вы даже не можете войти в окно, чтобы выяснить, насколько он на самом деле ввернут).
Node.js ЕДИНСТВЕННАЯ РЕЗЬБА и БЕСПЛАТНО . Node.js, как очень обдуманный выбор дизайна, имеет только один поток на процесс. Из-за этого для нескольких потоков принципиально невозможно получить доступ к данным одновременно. Таким образом, замки не нужны. Нити тяжелые. Действительно очень сложно. Если вы в это не верите, значит, вы не сделали достаточно многопоточного программирования. Правильно установить блокировку сложно, и это приводит к ошибкам, которые действительно трудно отследить. Устранение блокировок и многопоточности делает один из самых отвратительных классов ошибок просто исчезающим. Это может быть единственным большим преимуществом узла.
Но как мне воспользоваться преимуществами моего 16-ядерного блока?
Два пути:
- Для больших сложных вычислительных задач, таких как кодирование изображений, Node.js может запускать дочерние процессы или отправлять сообщения дополнительным рабочим процессам. В этом проекте у вас был бы один поток, управляющий потоком событий, а N процессов, выполняющих сложные вычислительные задачи и пережевывающих остальные 15 процессоров.
- Для масштабирования пропускной способности на веб-сервисе вы должны запустить несколько серверов Node.js на одном компьютере, по одному на ядро, используя кластер (В случае Node.js v0.6.x, официальный модуль «cluster», связанный здесь, заменяет версию learnboost, которая имеет другой API). Эти локальные серверы Node.js могут затем конкурировать в сокете, чтобы принимать новые соединения, балансируя нагрузку между ними. Как только соединение принято, оно становится тесно связанным с одним из этих общих процессов. В теории это звучит плохо, но на практике это работает довольно хорошо и позволяет избежать головной боли при написании поточно-ориентированного кода. Кроме того, это означает, что Node.js получает отличную привязку к кэшу процессора, более эффективно используя пропускную способность памяти.
Node.js позволяет вам делать действительно мощные вещи, не потревожив. Предположим, у вас есть программа Node.js, которая выполняет различные задачи, прослушивает команды на TCP- порту, кодирует некоторые изображения, что угодно. С помощью пяти строк кода вы можете добавить портал HTTP на основе веб-управления, который показывает текущее состояние активных задач. Это легко сделать:
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end(myJavascriptObject.getSomeStatusInfo());
}).listen(1337, "127.0.0.1");
Теперь вы можете нажать на URL и проверить состояние вашего запущенного процесса. Добавьте несколько кнопок, и у вас появится «портал управления». Если у вас запущен скрипт Perl / Python / Ruby, просто «добавить портал управления» не совсем просто.
Но разве JavaScript не медленный / плохой / злой / порождение дьявола? У JavaScript есть некоторые странные странности, но с «хорошими частями» есть очень мощный язык, и в любом случае, JavaScript - это язык клиента (браузера). JavaScript здесь, чтобы остаться; другие языки нацелены на него как на IL, и талант мирового класса конкурирует за создание самых продвинутых движков JavaScript. Из-за роли JavaScript в браузере огромные усилия инженеров направлены на то, чтобы сделать JavaScript быстрым. V8является последним и лучшим движком javascript, по крайней мере, на этот месяц. Он поражает другие языки сценариев как эффективностью, так и стабильностью (глядя на вас, Руби). И это будет только лучше с огромными командами, работающими над проблемой в Microsoft, Google и Mozilla, конкурирующими за создание лучшего движка JavaScript (Это больше не «интерпретатор» JavaScript, поскольку все современные движки делают тонны JITкомпиляция под капотом с интерпретацией только как запасной вариант для кода, выполняемого один раз). Да, мы все хотели бы исправить некоторые странные варианты языка JavaScript, но это на самом деле не так уж и плохо. А язык настолько чертовски гибок, что вы действительно не кодируете JavaScript, вы кодируете Step или jQuery - больше, чем любой другой язык, в JavaScript библиотеки определяют опыт. Чтобы создавать веб-приложения, вам все равно нужно знать JavaScript, поэтому кодирование с ним на сервере имеет своего рода синергию с набором навыков. Это заставило меня не бояться писать код клиента.
Кроме того, если вы ДЕЙСТВИТЕЛЬНО ненавидите JavaScript, вы можете использовать синтаксический сахар, такой как CoffeeScript . Или что-нибудь еще, что создает код JavaScript, например, Google Web Toolkit (GWT).
Говоря о JavaScript, что такое «закрытие»? - Довольно причудливый способ сказать, что вы сохраняете переменные лексической области в цепочках вызовов. ;) Нравится:
var myData = "foo";
database.connect( 'user:pass', function myCallback( result ) {
database.query("SELECT * from Foo where id = " + myData);
} );
// Note that doSomethingElse() executes _BEFORE_ "database.query" which is inside a callback
doSomethingElse();
Видите, как вы можете просто использовать «myData», не делая ничего неловкого, например, спрятать его в объект? И в отличие от Java, переменная "myData" не обязательно должна быть доступна только для чтения. Эта мощная языковая функция делает асинхронное программирование гораздо менее многословным и менее болезненным.
Написание асинхронного кода всегда будет сложнее, чем написание простого однопоточного скрипта, но с Node.js это не намного сложнее, и вы получаете много преимуществ в дополнение к эффективности и масштабируемости для тысяч одновременных соединений. ..