Я використовую Node.js на роботі, і вважаю це дуже потужним. Змушений вибрати одне слово для опису Node.js, я б сказав «цікаво» (що не є суто позитивним прикметником). Спільнота живе і зростає. JavaScript, незважаючи на свої дивацтва, може бути чудовою мовою для кодування. І ви щодня переосмислюєте власне розуміння "найкращої практики" та моделей добре структурованого коду. Зараз у Node.js впадає величезна енергія ідей, і робота над нею викриває все це мислення - велику розумову важку атлетику.
Node.js у виробництві, безумовно, можливий, але далеко не "під ключ" розгортання, здавалося б, обіцяне документацією. З Node.js v0.6.x "кластер" був інтегрований у платформу, забезпечуючи один із важливих будівельних блоків, але мій сценарій "production.js" все ще ~ 150 рядків логіки для обробки таких речей, як створення журналу каталог, переробка загиблих працівників тощо. Для "серйозної" виробничої послуги вам також потрібно бути готовим до того, щоб заглушити вхідні з'єднання та зробити все, що Apache робить для PHP . Якщо чесно, у Ruby on Rails є саме ця проблема. Він вирішується за допомогою двох взаємодоповнюючих механізмів: 1) Нанесення Ruby на рейки / вузол.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 модуль, пояс утиліти структурованих даних.
Плюси мінуси:
- Про: Для серверного хлопця написання JavaScript на бекенді було «ліком для шлюзу» для вивчення сучасних моделей інтерфейсу. Я більше не боюся писати клієнтський код.
- Про: Схильний заохочувати належну перевірку помилок (помилка повертається практично всіма зворотними викликами, натягуючи програміста на обробку; також, async.js та інші бібліотеки обробляють парадигму "провал, якщо будь-який з цих підзадач не працює", набагато краще, ніж типовий синхронний код )
- Про: Деякі цікаві і зазвичай важкі завдання стають тривіальними - як отримання статусу завдань у польоті, спілкування між працівниками або обмін станом кешу
- Pro: Величезна спільнота і багато чудових бібліотек на основі надійного менеджера пакунків (npm)
- Con: У JavaScript немає стандартної бібліотеки. Ви настільки звикаєте до імпорту функціональних можливостей, що вам здається дивним, коли ви використовуєте JSON.parse або якийсь інший метод вбудовування, що не потребує додавання npm-модуля. Це означає, що існує все п'ять версій всього. Навіть модулі, що входять до ядра Node.js, мають ще п’ять варіантів, якщо ви не задоволені реалізацією за замовчуванням. Це призводить до швидкої еволюції, але і певного рівня плутанини.
На відміну від простої моделі "один процес на запит" ( LAMP ):
- Pro: масштабування до тисяч активних з'єднань. Дуже швидко і дуже ефективно. Для веб-флоту це може означати зменшення кількості необхідних коробок у 10 разів порівняно з PHP або Ruby
- Про: Написання паралельних шаблонів легко. Уявіть, що вам потрібно отримати три (або N) краплі з Memcached . Зробіть це в PHP ... Ви щойно написали код, отримує першу крапку, потім другу, потім третю? Нічого собі, це повільно. Існує спеціальний модуль PECL, щоб виправити цю конкретну проблему для Memcached, але що робити, якщо ви хочете отримати деякі дані Memcached паралельно із запитом у вашій базі даних? У Node.js, оскільки парадигма є асинхронною, дуже природно мати веб-запит робити кілька речей паралельно.
- Con: Асинхронний код принципово складніший за синхронний код, і крива попереднього навчання може бути важкою для розробників без чіткого розуміння того, що насправді означає одночасне виконання. Тим не менш, це набагато менш складно, ніж писати будь-який багатопотоковий код із блокуванням.
- Con: Якщо обчислювальний запит працює, наприклад, 100 мс, він зупинить обробку інших запитів, які обробляються в тому ж процесі Node.js ... AKA, кооператив-багатозадачність . Це можна пом'якшити за допомогою шаблону веб-робітників (відкручування підпроцесу для вирішення дорогого завдання). Крім того, ви можете використовувати велику кількість працівників Node.js і лише дозволяти кожному одночасно обробляти один запит (як і раніше досить ефективно, оскільки немає переробки процесів).
- Con: Запуск виробничої системи набагато складніше, ніж модель CGI, наприклад Apache + PHP, Perl , Ruby тощо. Невиправлені винятки призведуть до збиття всього процесу, що вимагає логіки для перезавантаження непрацюючих працівників (див. Кластер ). Модулі з навантаженим кодовим кодом помилки можуть важко зірвати процес. Щоразу, коли робітник помирає, будь-які запити, з якими він звертався, відпадають, тому один API-помилка може легко погіршити послугу для інших інтерфейсів API.
На відміну від написання "справжнього" сервісу на Java / C # / C (C? Дійсно?)
- Про: Робити асинхронність у Node.js простіше, ніж робити безпеку потоків у будь-якому іншому місці, і, мабуть, забезпечує більшу користь. Node.js - це найменш болісна асинхронна парадигма, з якою я коли-небудь працював. З гарними бібліотеками це лише трохи важче, ніж писати синхронний код.
- Pro: Немає багатопотокових / блокувальних помилок. Правда, ви вкладаєте наперед в написанні більш багатослівного коду, який виражає належний асинхронний робочий процес без блокуючих операцій. І вам потрібно написати декілька тестів і змусити роботу працювати (мова сценаріїв, а назви змінних жирових пальців перехоплюються лише під час одиничного тестування). Але, як тільки ви змусите його працювати, площа поверхні для heisenbugs - дивні проблеми, які проявляються лише один раз на мільйон пробігів - площа поверхні просто набагато менша. Податки, що пишуть код Node.js, сильно завантажені у фазу кодування. Тоді ви, як правило, стабільний код.
- Pro: JavaScript набагато легший для вираження функціональності. Це важко довести це словами, але JSON , динамічне введення тексту, лямбда-нотація, успадкування прототипів, легкі модулі, що б там не було ... це просто прагне брати менше коду, щоб висловити ті самі ідеї.
- Con: Можливо, вам дуже, дуже подобаються послуги кодування на Java?
Для отримання іншої точки зору на JavaScript та Node.js перегляньте повідомлення від Java до Node.js , повідомлення в блозі про враження та досвід досвід розробника Java.
Модулі
При розгляді вузла, мати на увазі , що ваш вибір бібліотек JavaScript буде DEFINE ваш досвід. Більшість людей використовують щонайменше двох, помічник асинхронного шаблону (Step, Futures, Async) та цукровий модуль JavaScript ( Underscore.js ).
Помічник / JavaScript Цукор:
- Underscore.js - використовуйте це. Просто зроби це. Це робить ваш код приємним і читабельним з такими речами, як _.isString () та _.isArray (). Я не дуже впевнений, як можна в іншому випадку написати безпечний код. Крім того, для покращеного командного рядка-фу, перегляньте мій власний Underscore-CLI .
Модулі асинхронного малюнка:
- Крок - дуже елегантний спосіб виразити поєднання послідовних і паралельних дій. Моя особиста рекомендація. Дивіться мій пост про те, як виглядає крок код.
- Ф'ючерси - набагато гнучкіший (це справді гарна річ?) Спосіб виразити замовлення через вимоги. Може виражати такі речі, як "початок a, b, c паралельно. Коли A і B закінчать, почніть AB. Коли A і C закінчать, запускайте AC". Така гнучкість вимагає більшої обережності, щоб уникнути помилок у вашому робочому процесі (як, наприклад, ніколи не викликати зворотний дзвінок або викликати його кілька разів). Дивіться публікацію Райноса про використання ф'ючерсів (це посада, яка змусила мене "отримати" ф'ючерси).
- Async - більш традиційна бібліотека з одним методом для кожного шаблону. Я почав з цього перед моїм релігійним перетворенням на крок та подальше усвідомлення того, що всі зразки в Async можна виразити на етапі за допомогою ще однієї читабельної парадигми.
- TameJS - Написав OKCupid, це докомпілятор, який додає нове первісне мовне "очікування" для елегантного написання послідовних та паралельних робочих процесів. Візерунок виглядає дивовижно, але він вимагає попередньої компіляції. Я все ще вирішую це питання.
- StreamlineJS - конкурент TameJS. Я схиляюся до Таме, але ти можеш скласти свою думку.
Або прочитати все про асинхронні бібліотеки, дивіться у цій панелі-інтерв'ю з авторами.
Веб-рамки:
- Висловіть Great Ruby on Rails-esk для організації веб-сайтів. Він використовує JADE як механізм шаблонів XML / HTML, що робить побудову HTML набагато менш болючим, майже елегантним.
- jQuery Хоча технічно не є модулем вузла, jQuery швидко стає фактичним стандартом для користувацького інтерфейсу на стороні клієнта. jQuery надає CSS-подібним селекторам "запит" для наборів елементів DOM, над якими потім можна керувати (встановити обробники, властивості, стилі тощо). Одночасно, рамки CSS Bootstrap для Twitter , Backbone.js для MVC- шаблону та Browrify.js, щоб зшити всі ваші JavaScript файли в один файл. Всі ці модулі стають фактичними стандартами, тому вам слід принаймні перевірити їх, якщо ви ще не чули про них.
Тестування:
- JSHint - Потрібно використовувати; Я спочатку не користувався цим, що зараз здається незрозумілим. JSLint повертає купу основних перевірок, які ви отримуєте зі складеною мовою, як Java. Невідповідні дужки, незадекларовані змінні, типи багатьох форм і розмірів. Ви також можете ввімкнути різні форми того, що я називаю "анальний режим", де ви перевіряєте стиль пробілу та щось таке, що гаразд, якщо це ваша чашка чаю - але реальна цінність отримується з отримання миттєвого відгуку про точний номер рядка, де ви забули закриття ")" ... без того, щоб запускати свій код і натискати рядок, що порушує. «JSHint» є більш налаштованим варіантом Douglas Крокфорд «s JSLint .
- Конкуренція Mocha - Vows, яку я починаю віддавати перевагу. Обидві рамки обробляють основи досить добре, але складні візерунки, як правило, легше виразити в мокко.
- Обітниці обітниць дійсно досить елегантні. І він виводить чудовий звіт (--spec), який показує, які тестові справи пройшли / провалили. Витратьте його на 30 хвилин, і ви зможете з мінімальними зусиллями створити основні тести для своїх модулів.
- Zombie - Безголове тестування HTML та JavaScript з використанням JSDom як віртуального "браузера". Дуже потужний матеріал. Поєднайте його з Replay, щоб отримати блискавичні детерміновані тести коду браузера.
- Коментар про те, як "думати про" тестування:
- Тестування не є обов'язковим. З такою динамічною мовою, як JavaScript, статичних перевірок дуже мало. Наприклад, передача двох параметрів методу, який очікує, що 4 не зламається, поки код не буде виконаний. Досить низький бар для створення помилок у JavaScript. Основні тести мають важливе значення для створення розриву у верифікації зі складеними мовами.
- Забудьте про перевірку, просто змусьте ваш код виконати. Для кожного методу мій перший випадок перевірки - це "нічого не ламається", і саме так випалює найчастіше. Доведіть, що ваш код працює, не викидаючи 80% помилок, і зробить так багато, щоб поліпшити впевненість у коді, що ви опинитеся назад і додасте пропущені випадки перевірки, які ви пропустили.
- Почніть з малого і зламайте інерційний бар’єр. Всі ми ліниві і притиснуті до часу, і тестування легко розглядати як "зайву роботу". Тож почніть з малого. Напишіть тестовий випадок 0 - завантажте свій модуль та повідомте про успіх. Якщо ви змусите себе робити саме це, то інерційний бар'єр для тестування порушений. Це <30 хв., Щоб зробити це вперше, включаючи читання документації. Тепер напишіть тестовий випадок 1 - зателефонуйте до одного із своїх методів та переконайтесь, що "нічого не ламається", тобто що ви не отримаєте помилку назад. Тест 1 повинен зайняти у вас менше однієї хвилини. З інертністю пізніше стає поступово розширювати покриття тесту.
- Тепер розробляйте свої тести разом із кодом. Не залякуйте те, як виглядатиме "правильний" тест на кінці до кінця з макетними серверами та ін. Код починається простим і розвивається для обробки нових справ; тести теж. Коли ви додаєте нові випадки та складність у свій код, додайте тестові випадки для здійснення нового коду. Коли ви знайдете помилки, додайте перевірки та / або нові випадки, щоб покрити несправний код. Коли ви налагоджуєтесь і втрачаєте довіру до фрагмента коду, поверніться та додайте тести, щоб довести, що він робить те, що ви думаєте, що це таке. Захоплюйте рядки з прикладних даних (з інших служб, з яких ви телефонуєте, з веб-сайтів, які ви скребите, будь-які інші) та подайте їх у свій код розбору. Кілька випадків тут, поліпшена перевірка там, і ви отримаєте надійний код.
Також ознайомтесь з офіційним списком рекомендованих модулів Node.js. Однак, вузол модулів GitHub у Вікі набагато повніший і хороший ресурс.
Щоб зрозуміти Node, корисно врахувати декілька ключових варіантів дизайну:
Node.js - НА БІЛЬШОГО ПОДІЇ та АСИНХРОННОГО / НЕ БЛОКУВАННЯ. Події, як-от вхідне 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, офіційний модуль кластера, пов'язаний тут, замінює версію 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 тут залишився; інші мови орієнтуються на неї як на ІЛ, і талант світового класу змагається за створення найсучасніших двигунів JavaScript. Через роль JavaScript у веб-переглядачі витрачається величезна кількість інженерних зусиль для того, щоб JavaScript швидко виблискував. V8є останнім і найбільшим механізмом javascript, принаймні на цей місяць. Це здуває інші мови сценаріїв і в ефективності, і в стабільності (дивлячись на тебе, Рубі). І це стане тільки краще, коли величезні команди, що працюють над проблемою в Microsoft, Google та Mozilla, змагаються за створення найкращого механізму JavaScript (Це вже не "інтерпретатор JavaScript", як усі сучасні двигуни роблять тонни JITкомпіляція під капотом з інтерпретацією лише як резервна копія для Execute-Once коду). Так, ми всі хочемо, щоб ми могли виправити декілька варіантів вибору мови 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 це не так вже й складно, і ви отримуєте багато переваг на додаток до ефективності та масштабованості для тисяч одночасних з'єднань. ..