Выключатели

Основы слесарных слесарно сборочных работ. Привет студент

Основы слесарных слесарно сборочных работ. Привет студент

Посторонись, пресловутый PHP! Долой Java! Старичок Perl, тебе так вообще давно пора на пенсию. И как же вы уже достали, попсовые Ruby и Python! Все эти давно знакомые технологии уже не торкают. Зато сегодня мы посмотрим на чрезвычайно прогрессивный подход, когда для написания серверного кода используется… JavaScript.

Есть идея для стартапа. Теперь вопрос: на чем его писать? Конечно, есть всеми любимый РНР - что может быть легче для веб-сайта! Но скажи честно, как-то не тянет, правда? Ведь чтобы сделать что-то стоящее, голого РНР не хватит. Сразу придется прикручивать еще и AJAX, чтобы данные незаметно подгружались без обновления всей страницы целиком, да и это не решит всех проблем. О том, что PHP не так хорош, ты задумаешься в тот самый момент, когда к тебе разом ломанется много народа. А все потому что РНР (как и подавляющее большинство других языков, на которых строят сайты) даже в нашем, черт подери, двадцать первом веке, работают по классической схеме «запрос-ответ». Запрос страницы заставляет веб-сервер поднять указанный скрипт, выполнить его (линейно, строка за строкой весь твой код), а результат возвратить браузеру клиента. После этого скрипт «умирает», а следующий же запрос запустит всю эту адскую машинку заново. А если таких запросов одновременно тысяча? Старый добрый подход называется CGI (интерфейс взаимодействия веб-сервера и интерпретатора языка, на котором написана страница). Хитрые надстройки вроде FastCGI расширяют протокол, позволяя избежать выгрузки скрипта после первого запроса. Таким образом, когда второй пользователь запросит ту же страницу, для него будет уже все готово, останется только выполнить скрипт с новыми параметрами. Но все эти ухищрения - все равно не то.

Что такое хорошо, а что такое плохо

Многие разработчики всегда считали JavaScript просто «примочкой» к браузеру, эдаким недоязыком, который годится разве что для управления формами и манипулирования DOM-деревом веб-страницы. Некоторые до сих пор думают, что «java» в названии что-то да значит! 🙂 Действительно, язык очень простой. Впрочем, настоящие программисты давно научились творить с его помощью чудеса, предоставив нам потрясающе удобные онлайн-сервисы, которыми мы ежедневно пользуемся. Многие из таких профи пошли дальше и, трезво посмотрев на сам язык и его возможности, особенно по части работы с событиями, решили: а что если на JavaScript написать сервер? Ты получаешь возможность написать на одном и том же языке все части сайта: что серверную часть, что саму клиентскую страничку. Кроме того, JS отлично, просто идеально подходит для разных веб-штучек. Он очень простой и одновременно гибкий, что позволяет писать код в разных парадигмах: от обычного процедурного до ООП в смеси с функциональным стилем. А главное - это тотальная асинхронность. Это значит, что твой код будет выполняться не последовательно, как в случае с PHP/Perl-скриптами, а именно в тот момент, когда для него будут готовы все данные. Ведь для веба не надо большой вычислительной мощности - большую часть времени сервер ожидает событий вроде получения данных формы, выборки из базы данных или, что еще хуже, ответа на запрос к другому серверу. Обычный РНР-скрипт в такое время простаивает, а значит, простаивает весь поток, не позволяя серверу задействовать его для других пользователей. В такой ситуации не спасает даже Nginx. В случае с JavaScript ты просто указываешь, какую функцию необходимо выполнить, когда произойдет определенное событие, и все. В это время другой код может спокойно выполняться. Такая функция называется callback, или обработчик событий. Хотя писать действительно сложный код в таком стиле немного неудобно, особенно если твоя функция зависит от нескольких событий сразу, но и для этого уже придумали свои фреймворки, зачастую гораздо более мощные и элегантные, чем все эти РНР/Ruby/Python.

А к чему она вообще, эта асинхронность?

Для примера ограничений последовательного выполнения кода рассмотрим два типовых примера кода на PHP и JavaScript, выполняющих одно и то же. Начнем с любимого PHP:

$result = $db->fetchOne("SELECT user_name FROM user_accounts WHERE id = 1");
echo "Мое имя: " . $result . ";";

В первой строке мы посылаем простой SQL-запрос к БД на выборку имени пользователя, у которого id = 1.Обрати внимание: в этом месте скрипт останавливается, и следующая строка не будет выполнена до того самого момента, пока запрос не будет обработан базой, а результат не возвратится в переменную $result. Да, в нашем примере это тысячные доли секунды, но в реальности и запросы гораздо сложнее, и базы по размеру зачастую составляют гигабайты, и запросов таких может одновременно быть пара тысяч. А теперь попробуем написать код на JS, используя асинхронный стиль:

db.query("SELECT user_name FROM user_accounts WHERE id = 1", function(err, res)
{
if (!err) sys.log("Мое имя: " + res);
});
sys.log("Продолжаем выполнение");

Тут опять же создается запрос к базе данных, однако кроме самого SQL-выражения в запросе передается еще и функция-обработчик (callback). Эта функция будет вызвана именно тогда, когда придет ответ от базы данных, а до этого момента выполнение скрипта ни в коем случае не будет стопориться. Для примера в следующей строке мы просто выводим строку в консоль, чтобы показать, что выполнение сценария продолжается сразу после формирования запроса, не ожидая его завершения. Собственно, в основе любого варианта серверного JavaScript заложена концепция событий и callback’ов, то есть обработчиков событий. Ты можешь описывать собственные события. Тогда ход выполнения приложения будет зависеть от событий, которые возникают в результате активности пользователя на странице («форма заполнена» или «новое сообщение» и т.д.) или генерируются внутри самого сервера (как, например, в случае с обращением к базе данных). Те действия, которые необходимо выполнять в случае наступления событий, описываются внутри функций обработчиков событий.

Движок, вот в чем вопрос

Короче говоря, использовать JavaScript не только для клиентской части, но и на серверной стороне - это хорошо и даже приятно. Другой вопрос - каким образом это возможно осуществить? Сегодня есть четыре основных движка, которые используются на серверах.

Rhino - движок от компании Mozilla, написанный на Java и поддерживающий последнюю 1.7 версию стандарта JS, который к тому же дополняет язык собственными расширениями и объектами. Основным преимуществом движка является работа поверх стандартной JVM, а значит, его можно использовать в любой среде, где работает Java. Другими словами, можно применять современные веб-серверы типа jetty, но при этом писать на любимом JS. Кстати, Rhino применяют на облачном хостинге от Google! А вот с производительностью сложнее. Она зависит, с одной стороны, от движка и применяемых там технологий, вроде JIT-компиляции, и от работы самой Java-машины. Кстати, многие тестеры, которые говорят, что Rhino очень медленный, забывают, что движок имеет два режима работы: интерпретации, когда скрипт каждый раз преобразуется в Java байт-код (аналогично PHP), и компиляции, когда такое преобразование происходит только раз, а потом многократно исполняется. Первый режим выгоден, когда ты отлаживаешь код, который меняется каждую минуту, второй больше подходит для рабочей версии проекта, работающей под нагрузкой.

SpiderMonkey - еще один движок от Mozilla, на этот раз на C. Кстати, это вообще первый в мире движок JS, написанный еще в Netscape - сегодня он открыт и используется в таких популярных продуктах как Firefox, Adobe Acrobat и даже в одном из эмуляторов серверов онлайн-игры Ultima Online. Далее разработчики сильно модифицировали его, добавив компиляцию JS напрямую в ассемблерный код, и переименовали в TraceMonkey - именно этот движок используется в ветке 3.6 Firefox’а. В основном SpiderMonkey используют в ПО, которое написано на С/С++ и нуждается в скриптовом языке. Из известных продуктов: Comet-сервер APE, noSQL БД CouchDB, серверная платформа Jaxer и модуль к Apache mod_js.

Futhark - это движок от Opera, который, кроме браузера, используется в их инновационном сервисе Unite (типа встроенный сервер в каждом браузере), а также на их серверах, обслуживающих мобильный браузер Opera Mini. Жаль, что движок закрыт, и его пока нигде за пределами самой Opera не применяют.

V8 - движок от Google, который используется в Chrome и является основой будущей Chrome OS. Сегодня это самый крутой, быстрый и мощный движок, в котором JS-код напрямую преобразуется в ассемблер целевого процессора, что позволяет обойти по скорости все остальные движки. Кроме этого гугловцы используют множество ухищрений для оптимизации, хранят в памяти скомпилированный код, оптимизируют его на лету (например, удаляют блоки кода, которые по решению компилятора вообще не могут быть задействованы, и т.п.). На базе этого движка построена самая популярная и быстроразвивающаяся серверная платформа - Node.JS .

Node.JS

Вероятно, именно после выхода Chrome разработчики смекнули, что такой быстрый движок можно успешно использовать и на сервере. Первым опытом стал проект V8cgi, который просто позволял писать серверные сценарии, работающие с любым веб-сервером по стандартному протоколу CGI. Дальнейшие эксперименты привели к рождению проекта Node.JS - полностью самостоятельной платформы, включающей, кроме движка, встроенный сервер (HTTP и TCP/UDP/Unix-soket) и базовый набор библиотек, а также предоставляющей полностью асинхронную работу с файлами и сетевыми устройствами.

Проект развивается настолько быстро и активно, что уже сейчас готов к промышленному использованию. Это, в частности, доказывает опыт парней из Plurk (азиатский аналог твиттера), которые полностью перенесли свой comet-сервер, изначально написанный на Java и солидном JBoss Netty, на Node.JS и, по отзывам, сократили потребление памяти буквально на гигабайты. А масштабы у них еще те - более сотни тысяч одновременных соединений.

Запустить HTTP-сервер, способный обрабатывать асинхронно тысячи подключений - это несколько строк кода:

var sys = require("sys"), http = require("http");
http.createServer(function (req, res)
{
res.writeHead(200, {"Content-Type": "text/plain"});
res.end("Hello Worldn");
}).listen(80, "127.0.0.1");
sys.puts("Server running at http://127.0.0.1:80/");

Чтобы запустить сервер, скопируй код в файл example.js и укажи его при запуске демона node:

% node example.js
Server running at http://127.0.0.1:80/

Маленький тест провести очень просто. Можно взять программу Apache Bench - очень простую тулзу для проведения нагрузочного тестирования, и запустить ее: running «ab -n 1000 -c 100 ‘http://127.0.0.1:80/’». Таким образом, бенчмарк будет «обстреливать» сервер тысячами запросов, используя 100 одновременных подключений. На моем ноутбуке сервер выдержал больше 3000 запросов в секунду. Это очень много!

Сам сервер написан на C++ и совсем немножко на ассемблере, однако большая часть библиотек из дистрибутива разработана на JavaScript. В состав базового набора сервера входят только основные функции, остальное оставлено на плечах разработчиков, которые уже написали сотни разных библиотек и фреймворков. Впрочем, молодость проекта дает о себе знать: многих привычных для других решений модулей еще нет, а у многих библиотек текущая версия - 0.0.1, что не придает уверенности в их стабильности. Некоторые тривиальные задачи могут вообще не иметь готового к закачке решения, но бывает и наоборот - количество реализаций, зачастую радикально разных по архитектуре, исчисляется десятками (доступ к базе MySQL, например). Хотя большинство библиотек написано на чистом JavaScript, есть и такие, что требуют компиляции модуля к серверу, что обещает гораздо большую скорость - они просто расширяют стандартный API сервера.

Готовые наработки для серверного JavaScript

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

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

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

JSGI (JavaScript gate interface) - разработан специальный протокол взаимодействия связи веб-демона и серверных сценариев на JavaScript. Увы, спецификацию пока полностью поддерживает только проект Rhino в окружении сервера jetty.

Особенности Node.JS

Основной особенностью Node, кроме полной асинхронности, является его однопоточная модель. Другими словами, все операции выполняются в одном и том же потоке ОС, даже если у твоего сервера тысяча одновременных пользователей. Правда, доступно создание дочерних процессов и низкоуровневое управление исполнением скриптов (загрузка, компиляция, работа с ассемблерным кодом, исполнение). Для реализации многопоточности и задействования всех ядер современных процессоров рекомендуется просто загружать несколько копий приложения, но можно взять на вооружение WebWorker из стандарта HTML5 и распределить работу приложения по нескольким дочерним процессам. Не думай, что раз нет многопоточности - это тормоз и отстой. Вспомни, что веб-приложение делает полезную работу очень быстро, а большую часть времени просто ожидает чего-то (данных от базы, от memcached’а или новомодной NoSQL-базы), либо просто держит в памяти открытые соединения для Comet’а, поэтому в одном потоке можно обработать и десяток тысяч, не прибегая к кластеризации.

Второй особенностью архитектуры Node является событийность. Почти каждая операция имеет коллбэки, генерирует событие, а пользователю доступен объект EventEmiter, через который можно буквально одной строкой генерировать свои события (это несложно, ведь событие - это просто строка с названием, а также список параметров, которые передаются в обработчик).

Сам по себе Node построен вокруг EventLoop - глобального цикла обработки событий, который на каждом тике проверяет, готовы ли данные для какого-либо из определенных пользователем коллбэков. Если данные есть, начинается выполнение кода. Если не осталось больше кода - ожидаем следующего вызова. Цикл выполняется вне JS, а в самом движке, написанном на C, вследствие чего это происходит очень и очень быстро (порядка сотен тысяч раз в секунду). Что-то типа бесконечного цикла. В дополнение к этому в сервер встроен очень эффективный сборщик мусора (GC), поэтому даже тысячи подключений не вызывают переполнения памяти и падения сервера. Node.JS обладает встроенной родной системой работы с событиями.

Простейший Steaming-сервер

Попробуем написать простой, но в тоже время полезный пример, который в реальном времени будет выдавать пользователю полезную информацию без перезагрузки страницы. Вот идея для нашего приложения - брать новые данные, которые добавляются в текстовый лог, и выводить их в реальном времени на веб-странице:

var sys = require("sys"), net = require("net"), spawn = require("child_process").spawn, http = require("http");
sys.puts("nMy process PID: " + process.pid + "n");
var tail = spawn("tail", ["-f", "/var/log/nginx/access.log"]);
// указываем названия логфайла
sys.puts("Start tailing");
tail.stdout.addListener("data", function (data)
{ sys.puts(data);
//дублируем себе на консоль
});
http.createServer(function(req,res)
{
res.sendHeader(200,{"Content-Type": "text/plain"});
tail.stdout.addListener("data", function (data) { res.write(data); });
}).listen(80);

С помощью функции spawn() мы создаем дочерний процесс утилиты tail, которая, собственно, и занимается тем, что считывает новые данные, которые появляются в логфайле. Процесс запускается только раз во время старта сервера. Собственно, наша задача - отлавливать моменты, когда команда tail будет выводить новые данные из логфайла и транслировать вывод на веб-страницу каждому подключившемуся клиенту. Для этого мы будем следить за возникновением события data (появлением новых данных) для переменной, к которой запущен процесс утилиты tail, и выводить их на страницу с помощью команды write(). Таким образом, соединение будет оставаться открытым для каждого HTTP-запроса. Вот и все. Следить за активностью процесса не так просто для обычного веб-приложения, но ничего не стоит для неблокируемой архитектуры Node.JS и логики выполнения, основанной на событиях. Нам остается только запустить скрипт: «node tail.js error.log» и открыть в браузере http://localhost:80. На странице будут отображаться все сообщения, которые появляются в логфайле error.log.

Вот и сказочке конец

Сейчас, выбирая на чем бы таком написать очередное web 2.0 приложение, где не только красивый клиентский код, но и что-то надо делать на сервере, тебя парит одна грустная мысль о том, что все уже изобрели и написали до тебя. Те же языки, что и десять лет назад, те же библиотеки, протоколы и сервера. РНР уже ого сколько лет, Perl так вообще седой, Python у всех на слуху, а Ruby успел надоесть. Писать для веба стало рутиной - посмотри, как твои друзья сидят и думают, что же сделать с 25-мегабайтным монстром Zend-framework. А тебе хочется что-то нового, быть на острие прогресса, создавать то, на чем потом будут писать все, а сейчас знают только увлеченные хакеры и ищущие себя дзен-программеры? Посмотри на JavaScript в сервере, он просто создан для этого. Очень простой, мощный, еще не погрязший в тонне библиотек и фреймворков. Задачи, которые на РНР не решить вообще, на базе Node.JS решаются буквально десятком строк. И, возможно, именно такое программирование, наконец, принесет утраченное чувство наслаждения от разработки!

WWW

  • Материалы по NodeJS: groups.google.com/group/nodejs
  • Русскоязычный сайт и форум: forum.nodejs.ru
  • Информация о серверном JS: en.wikipedia.org/wiki/Server-side_JavaScript
  • Хороший мануал для начинающих по Node.JS: www.slideshare.net/the_undefined/nodejs-a-quick-tour
  • Презентациявведение по Node.JS: nodejs.org/jsconf.pdf

INFO

Большинство, если не все, библиотеки и проекты на Node.JS сосредоточены на Github, поэтому если нет какого-то модуля, нужного тебе, ищи его там.

На сервере (IIS) по следующим причинам:

    Передача навыков - мы хотели бы использовать JavaScript и jQuery на сервере и не использовать, например, VB Script./классический asp. .Net framework/Java и т. Д. Исключается из-за этого.

    Улучшены параметры поиска/доступности. Мы хотели бы иметь возможность использовать jQuery в качестве системы шаблонов, но это небезопасно для поисковых систем и пользователей с отключенным - если мы не можем выборочно запускать этот код на сервере.

В IIS и Windows Server есть значительные инвестиции, поэтому изменение не является вариантом.

Я знаю, что вы можете запускать jScript в IIS с помощью хоста Windows Script, но я не уверен в масштабируемости и процессе, связанном с этим. Я также не уверен, что это будет иметь доступ к DOM.

Вот диаграмма, которая, надеюсь, объясняет ситуацию. Мне было интересно, если кто-нибудь сделал что-нибудь подобное?

EDIT: я не ищу критика в веб-архитектуре, я просто хочу знать, есть ли какие-либо возможности для манипулирования DOM страницы, прежде чем она будет отправлена ​​клиенту, используя javascript. Jaxer - один из таких продуктов (без IIS). Спасибо.

5

7 ответы

Идея повторного использования клиентского JS на сервере может казаться соблазнительной, но я не уверен, что сам jQuery будет готов к запуску в серверной среде.

Вам нужно будет определить глобальный контекст для jQuery каким-то образом, инициализируя окно, документ, имя пользователя, местоположение и т. Д. . Я не уверен, что это выполнимо.

Кроме того, как отметил Cheeso, Active Server Pages - очень устаревшая технология, в начале века она была заменена на ASP.Net Microsoft. Раньше я использовал систему устаревания с использованием ASP 3.0 более года, и это было больно. Самое замечательное времяпрепровождение - отладка: вы вряд ли найдете что-нибудь для этой цели сегодня и должны будете ослабить красивые ошибки, как в журнале IIS:

ошибка "800a9c68"
Определенная пользователем или объектная ошибка

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

JScript работает в IIS с помощью ASP. Активные серверные страницы.
Он был впервые доступен в 1996 году.

В конце концов ASP.NET был представлен как преемник. Но ASP по-прежнему поддерживается.

Однако для DOM-страницы нет DOM.

Возможно, вам придется немного пересмотреть свою архитектуру.

Я думаю, что единственные жизнеспособные решения, которые вы, вероятно, найдете в любом месте рядом с готовыми к работе, включают в себя размещение IIS перед Java. Есть две браузерные среды, которые я знаю о закодированных для Java:

Если вы хотите что-то чистое-IIS/MS, я думаю, что ваше наблюдение за хостом WindowsScript и/или чем-то вроде полузаброшенного JScript.NET, вероятно, примерно так же близко, как и вы, а также порт (который вы, вероятно, придется начинать) с чем-то вроде Env-js или HTMLUnit.

Кроме того, я не знаю, видели ли вы список решений на стороне сервера в Википедии:

Наконец... вы, вероятно, могли бы написать пригодную для использования jQuery-подобную библиотеку на любом языке, на котором уже есть какая-то библиотека DOM и первоклассные функции (или, если это невозможно, eval). См. Например, pQuery для Perl (http://metacpan.org/pod/pQuery). Это даст вам преимущества стиля jQuery для манипулирования документами. Передача навыков велика, и у JavaScript есть замечательное слияние очень приятных функций, но, с другой стороны, разработчики, которые заботятся о том, чтобы изучать несколько языков, также великолепны, а - не единственный приятный язык.

Что именно вы подразумеваете под

от RisingStack . Переведено с разрешения правообладателей.

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

Модуль http для вашего Node.js-сервера

Когда вы начинаете создавать HTTP-приложения в Node.js, встроенные модули http/https - это то, с чем вы будете взаимодействовать.

Давайте создадим ваш первый HTTP-сервер на Node.js! Нам будет нужно подключить модуль http и привязать наш сервер к порту 3000 .

// содежимое index.js
const http = require("http")
const port = 3000 const requestHandler = (request, response) => {
console.log(request.url)
response.end("Hello Node.js Server!")
} const server = http.createServer(requestHandler) server.listen(port, (err) => {
if (err) {

})

Затем запускаем этот скрипт:

$ node index.js

Что нужно здесь отметить:

  • requestHandler: эта функция будет вызываться каждый раз, когда на сервер придёт запрос. Если вы откроете в своём браузере адрес localhost:3000 , два сообщения появятся в консоли: одно для / и одно для favicon.ico .
  • if (err) : обработка ошибок: если порт уже занят или есть какие-то другие причины, по которым сервер не может быть запущен, мы получим уведомление об этом.

Модуль http крайне низкоуровневый: создание сложного веб-приложения с использованием вышеприведенного фрагмента кода очень трудоемко. Именно по этой причине мы обычно выбираем фреймворки для работы над нашими проектами. Есть множество фреймворков, вот самые популярные:

  • express
  • hapi
  • koa
  • restify

В этой и следующих главах мы будем использовать Express, так как именно для него вы можете найти множество модулей в NPM.

Express

Быстрый, гибкий, минималистичный веб-фреймворк для Node.js -  http://expressjs.com/

Добавление Express в ваш проект - это просто установка через NPM:

$ npm install express --save

После того, как вы установили Express, давайте посмотрим, как создать приложение аналогичное тому, что мы написали ранее:


const app = express()
const port = 3000 app.get(‘/’, (request, response) => {
response.send("Hello from Express!")
}) app.listen(port, (err) => {
if (err) {
return console.log("something bad happened", err)
} console.log(`server is listening on ${port}`)
})

Самое большое различие, которое вы можете здесь заметить, заключается в том, что Express по умолчанию даёт вам роутер. Вам не нужно вручную разбирать URL, чтобы решить, что делать, вместо этого вы определяете маршрутизацию приложения с помощью app.get , app.post , app.put и так далее, а они уже транслируются в соответствующие HTTP-запросы.

Одна из самых мощных концепций, которую реализует Express - это паттерн Middleware.

Middleware - промежуточный обработчик

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

На диаграмме вы можете увидеть, как запрос идёт через условное Express-приложение. Он проходит через три промежуточных обработчика. Каждый обработчик может изменить этот запрос, а затем, основываясь на вашей бизнес-логике, третий middleware отправит ответ, либо запрос попадёт в обработчик соответствующего роута.

На практике вы можете сделать это следующим образом:

Const express = require("express")
const app = express() app.use((request, response, next) => {
console.log(request.headers)
next()
}) app.use((request, response, next) => {
request.chance = Math.random()
next()
}) app.get("/", (request, response) => {
response.json({
chance: request.chance
})
}) app.listen(3000)

Что следует здесь отметить:

  • app.use: это то, как вы можете описать middleware. Этот метод принимает функцию с тремя параметрами, первый из которых является запросом, второй - ответом, а третий - коллбеком next . Вызов next сигнализирует Express о том, что он может переходить к следующему промежуточному обработчику.
  • Первый промежуточный обработчик только логирует заголовки и мгновенно вызывает следующий.
  • Второй добавляет дополнительное свойство к запросу - это одна из самых мощных функций шаблона middleware. Ваши промежуточные обработчики могут добавлять дополнительные данные к объекту запроса, который могут считывать/изменять middleware, расположенные ниже.

Обработка ошибок

Как и во всех фреймворках, правильная обработка ошибок имеет решающее значение. В Express вы должны создать специальный промежуточный обработчик - middleware с четырьмя входными параметрами:

Const express = require(‘express’)
const app = express() app.get(‘/’, (request, response) => {
throw new Error(‘oops’)
}) app.use((err, request, response, next) => {
// логирование ошибки, пока просто console.log
console.log(err)
response.status(500).send(‘Something broke!’)
}) app.listen(3000)

Что следует здесь отметить:

  • Обработчик ошибок должен быть последней функцией, добавленной с помощью app.use .
  • Обработчик ошибок принимает коллбек next . Он может использоваться для объединения нескольких обработчиков ошибок.

Рендеринг HTML

Ранее мы рассмотрели, как отправлять JSON-ответы. Пришло время узнать, как отрендерить HTML простым способом. Для этого мы собираемся использовать пакет handlebars с обёрткой express-handlebars .

Сначала создадим следующую структуру каталогов:

├── index.js
└── views
├── home.hbs
└── layouts
└── main.hbs

После этого заполните index.js следующим кодом:

// index.js
const path = require("path")
const express = require("express")
const exphbs = require("express-handlebars")
const app = express() app.engine(".hbs", exphbs({
defaultLayout: "main",
extname: ".hbs",
layoutsDir: path.join(__dirname, "views/layouts")
}))
app.set("view engine", ".hbs")
app.set("views", path.join(__dirname, "views"))
app.listen(3000)

Приведенный выше код инициализирует движок handlebars и устанавливает каталог шаблонов в views/layouts . Это каталог, в котором будут храниться ваши шаблоны.

После того, как вы сделали эту настройку, вы можете поместить свой начальный html в main.hbs . Чтобы всё было проще, давайте сразу перейдём к этому:



Express handlebars


{{{body}}}

Вы можете заметить метку {{{body}}}  - здесь будет размещен ваш контент. Давайте создадим home.hbs !

Hello {{name}}

Последнее, что мы должны сделать, чтобы заставить всё это работать, - добавить обработчик маршрута в наше приложение Express:

App.get("/", (request, response) => {
response.render("home", {
name: "John"
})
})

Метод render принимает два параметра:

  • Первый - это имя шаблона.
  • Второй - данные, необходимые для рендеринга.

Как только вы сделаете запрос по этому адресу, вы получите что-то вроде этого:



Express handlebars


Hello John



Это всего лишь верхушка айсберга. Чтобы узнать, как добавить больше шаблонов (и даже частичных), обратитесь к официальной документации express-handlebars .

Отладка Express

В некоторых случаях вам может потребоваться выяснить, что происходит с Express, когда приложение работает. Для этого вы можете передать следующую переменную окружения в Express: DEBUG=express* .

Вы должны запустить свой Node.js HTTP-сервер, используя:

$ DEBUG=express* node index.js

Резюме

Вот как вы можете настроить свой первый HTTP-сервер на Node.js с нуля. Я рекомендую Express для начала, а затем поэкспериментируйте.

Андрей Сумин : Добрый день, меня зовут Андрей Сумин, я работаю в компании Mail.Ru. Есть немного лишнего времени, поэтому я подготовил небольшой бонус - как раз на оставшиеся двадцать минут. Я покажу вам возможности нашего шаблонизатора, который мы сделали, чтобы достичь тех цифр, которые нам нужны.

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

Ниже вы видите то, во что json.name превращается на сервере.

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

Safe=true. Ниже вы видите компилированный код. По "safe=true" сразу видно, что, допустим, у нас исчез "try catch".

Конструкции, которые нужны любому шаблонизатору - это "if" и, соответственно, "choose". Но обычно используется форма "if else", очень нужная разработчику. Но если вы хотите валидный XML, то, к сожалению, единственный способ - это "choose".

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

Естественно, используются циклы. Куда же без циклов? Цикл по массиву, цикл по хэшу.

Еще наш шаблонизатор по умолчанию "тримит" все, что находится между тэгами.

Боюсь, что здесь могут присутствовать те, кому эти вещи не знакомы. Но в html пробел - это значащий символ. Поэтому желательно указывать, что вы хотите в этом месте иметь пробел. Да, есть такие специфические вещи.

Fest:script нужен, чтобы мы прямо посреди шаблонизатора могли исполнить какой-нибудь JavaScript-код.

Сейчас я попробую сделать небольшие демонстрации, чтобы вы видели, к чему это приводит. Вот, у меня есть вчерашняя заготовка. Это обычный HTTP-сервер на Node.js.

(Докладчик показывает демонстрацию.)

Он запускает некоторый сервер. Отсюда мы берем шаблон. Шаблон я, естественно, покажу. У меня тут некоторые демонстрации закомментированы. Берем шаблон, он должен превратиться в JavaScript-функцию "template". Вот здесь мы тысячу раз этот шаблон применяем. Так как основная тема моего доклада - это все-таки цифры, то нам интересно измерить его производительность в каких-то стрессовых ситуациях. Ниже, соответственно, мы выводим результаты для этого шаблона.

Давайте посмотрим повнимательнее. Первый шаблон - это цикл в десять тысяч итераций.

Давайте здесь поставим лучше html, так будет интересней. Он выводит span, внутри которого мы выводим значение "hello" из хэша входного в шаблон и занимаемся конкатенацией с текущей датой. Конкатенация строк - достаточно "дорогая" операция. Это все пройдет десять тысяч раз. Плюс я хочу вам еще показать, что и сам шаблон выполнится тысячу раз.

Давайте попробуем запустить...

Сейчас будут цифры на основе Node.js. V8 используется Node.js. Мы видим десять тысяч вот таких конкатенаций. Не волнуйтесь, я вывожу в браузер только последние, иначе у меня были бы проблемы с памятью в браузере. 10 тысяч конкатенаций выполнены тысячу раз. Они нам дали пять секунд. Это означает, что на один раз пришлось 10 тысяч конкатенаций, и это заняло у нас 5 миллисекунд на Node.js на сервере.

Можем посмотреть, сколько времени это займет в браузере (если мы этот же шаблон отправим прямо в браузер).

Цифра чуть-чуть отличается. Можем, на самом деле, еще посмотреть другой браузер. Нас же интересует JavaScript-шаблонизатор. В общем, вы уже видите, что получится. Это Opera. Значит, он должен иметь одни и те же вещи на клиенте и на сервере. Там почти минута - 40 секунд, по-моему. В любом случае, даже если здесь будет очень большая цифра, ее нужно делить на тысячу.

Вот, пожалуйста. 39 секунд. Видимо, остальное время он потратил на рендеринг в самом браузере. Даже когда у нас в разных браузерах получаются цифры, различающиеся в несколько раз (может быть, даже на порядок) - вы все равно учитываете, что все это надо делить на тысячу. Это означает, что на один проход ушло 39 миллисекунд.

Чтобы шаблонизировать, мы этот шаблонизатор используем на клиенте для проектов, которые используются, - это могут быть мобильные версии проектов, для старых версий Android, и у нас никаких проблем с производительностью нет. В IE я, к сожалению, сейчас не могу показать, потому что у меня все-таки Mac. Но есть некоторые интересные нюансы.

У нас шаблонизатор по умолчанию со включенным "escaped". Об этом будет речь в основной части доклада. Если мы знаем, что наши данные точно не введены пользователем, то "escaped" можно выключить.

Давайте посмотрим, что произойдет с цифрами. Это сейчас на сервере. Это V8 на сервере через Node.js.

Так, видимо, я где-то ошибся. Вот здесь "save true". Это не должно влиять. А, ну, да, конечно - сервер-то надо перезапустить. Я все-таки привык с клиентом работать, где нажимаешь Ctrl+S+F5.

Вот, собственно, разница.

Очень много времени требует "html escape". Это я взял, чтобы продемонстрировать порядок проблем, с которыми приходится сталкиваться.

Что касается "save", на предыдущем слайде каждое JS-выражение, которое делает верстальщик, по умолчанию оборачивается в "try catch".

Это обходится, на самом деле, не очень дорого, по крайней мере, в V8.

Разницы особо никакой. Хотя, на самом деле, в "try catch" было обернуто, как вы понимаете, тысяча на десять тысяч. В общем, операций очень много - 10 миллионов. В современных браузерах это выполняется очень быстро, и в современных движках тоже.

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

Теперь второй интересный пример, без которого мы не смогли жить. Он не новый. Мы его позаимствовали в каком-то виде из XSLT. Тоже об этом буду говорить. Сейчас - демонстрация. Все-таки, возможно, кто-то придет к началу доклада.

У нас есть отдельный шаблон, который выводит span. Внутри span у нас есть вызов (вот, я его выделил) - fest:get. Означает, что в этом месте вывести блок с именем "word". А ниже идет определение того, что это содержимое того блока, соответственно, и равно слово "word".

Вот. Мы выводим содержимое этого блока. По шаблону видно, что просто включается в этот файл, в котором сказано вывести span с этим блоком. Больше ничего не происходит, потому что "if" у нас возвращает "ложь" (по крайней мере, условия в "if" возвращают "ложь").

Понятно, что поменялось? Это для примера, что в браузере, собственно, эти же шаблоны точно также отрабатывают. Вот это то самое наследование, которое есть во всех современных шаблонизаторах (и не очень современных). Очень близко, по-моему, к "Django" - там практически то же самое.

Я хочу сказать, что вот этот XML-синтаксис - это "syntactic sugar" для верстальщиков. У нас некоторые люди, которые знакомы с XSL, когда видели этот синтаксис, начинали писать на нем как на родном. Но это приводило к проблемам. Вот, у нас есть "fest value", которое я вот здесь вывожу. Вот оно, допустим - "fest value".

Они приходили и спрашивали, есть ли у него атрибут "формат", чтобы можно было сразу в "fest value" это как-то сформатировать. На что я им отвечал: "Вот в этом конкретно пункте забудьте про XSL. У вас есть внутри JavaScript". В том числе, про этот же самый синтаксис. Он сделан исключительно для того, чтобы рядовые задачи приводили к очень быстрым решениям по скорости. Поэтому никаких особых "наворотов" там нет. Если "навороты" есть, это должно быть осознанное решение конкретного разработчика. Оно должно быть выражено в JavaScript.

Вот поинтереснее задача. Допустим, у нас есть JavaScript-библиотека… Вот эта функция умеет склонять слова. На входе она получает число и массив слов, которые надо склонять. Мы в этой строчке ее подключаем. Значит, на сервере в этот момент этот JavaScript выполнится, создаст эту функцию.

Создаем массив слов. Вот небольшой массив, который выведет результат от нуля до девяти, как эта функция себя будет вести с такими входными параметрами.

Вот функция слова просклоняла.

Само по себе это не очень интересно, если бы не следующий пример.

Теперь она - шаблон, который генерирует html для браузера. Внимание: здесь уже не "fest include", а "fest insert". В этом месте вставляем этот JavaScript. Браузер его получит именно как JavaScript-программу.

Вот получаем ровно тот же пример. Вот она наша функция пришла на клиент, и мы можем ее использовать на клиенте.

Самый большой бонус, ради которого мы бились - чтобы одни и те же библиотеки, шаблоны, программы на JavaScript могли использоваться на клиенте и на сервере. Раньше это было невозможно как раз потому, что было медленно и непонятно. Вдруг кто помнит первые попытки использовать JavaScript на сервере - это был, по-моему, какой-то IDE. Jagser, по-моему, он назывался.

Реплика из зала : Это Aptana?

Андрей Сумин : Да, Aptana. Там был, конечно, полный провал. Я его честно попробовал, честно старался, но сдался. С появлением отдельных движков от разных браузеров, с появлением конкуренции, когда они соревнуются друг с другом по производительности, мы получили возможность использовать JavaScript на сервере.

У вас есть вопросы по моим примерам?

Реплика из зала : Можно посмотреть скомпилированный код?

Андрей Сумин : Да. Скомпилированный код можем посмотреть. Вот, скомпилированный код шаблона. Тут некоторые служебные функции идут сначала, конечно же. Как я говорил, нужен escape.

Вот, допустим, наш цикл. Вот я его выделил. Давайте попробую приблизить. Вот тот самый hello+Date. Как я и обещал, у нас все по умолчанию в "try catch". Так что если вдруг вам попался верстальщик, который не очень соображает в JavaScript, он, по крайней мере, вам ничего не сломает. Escape html - по-честному, все без обмана.

Реплика из зала : А у вас в команде есть кто-то, кто разбирается в JavaScript?

Андрей Сумин : Да. У меня в команде есть больше, наверное, чем три или четыре (может, даже чем пять людей), которые умеют "готовить" java script. Они понимают, что это такое, и как его "готовить". Гораздо большая опасность - это такая архитектурная "травма" JavaScript - так называемое слово "var". Если вы его не объявили, то у вас будут проблемы. На сервере это фактически утечка памяти, потому что по умолчанию переменная попадет в список глобальных и там и останется, по крайней мере, до перезагрузки контекста.

Но у нас есть Straight mode, который не позволяет этого сделать. Текущий шаблонизатор по умолчанию работает со Straight mode. Я пытался вызвать утечки памяти - у меня не получилось.

Начнем рассказ про выполнение JavaScript на сервере.

Зачем это нужно?

Естественно, первый вопрос, который возникает, когда мы говорим про JavaScript на сервере - это вопрос "зачем?". Я очень много программировал на JavaScript , я его очень люблю. Но этого недостаточно. Надо все-таки заниматься делом.

В больших компаниях (я работал в нескольких больших компаниях) есть такая особенность. У нас есть очень много сильных backend-разработчиков. Они умеют писать код, много знают про серверы. Поэтому пускай они пишут шаблонизатор для верстальщиков (если не требуется взять какой-то особенный). Из-за этого возникает куча проблем. У этих разработчиков свои проблемы, свое начальство, им надо что-то делать. Когда к ним приходили с просьбами типа "дайте нам что-нибудь доделать, нам чего-то не хватает", мне это все жутко не нравилось.

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

Конечно, как я говорил, шаблонизация на клиенте. Обо всем этом мы задумались, начиная с проекта "Почта". Проект "Почта" не может обходиться без шаблонизации на стороне сервера, потому что нужен быстрый старт. Конкуренты не спят. Он не может обходиться без шаблонизации на стороне клиента, потому что применяется Ajax, тут тоже все должно быть быстро. Пользователь должен быть удовлетворен, потому что очень легко перейти к конкурентам.

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

Чего же мы хотели?

Как я уже говорил, помимо JavaScript, я еще очень много писал на XSL. Это тоже очень хороший шаблонизатор, возможно, самый мощный. Но тоже не без "родовых травм". Хотя какие-то его возможности нужны.

Плюс, если у нас есть XSL, один из его недостатков - это то, что в него не встроен какой-то алгоритмически полный язык. Когда мы хотим пересортировать там массив - это еще решаемая задача. Но когда мы хотим там пройтись "регуляркой", в XSL это настоящий ад.

Поэтому мы захотели лучшего от этих двух вещей?

Вот пример, который я вам показывал, он очень похож на "Django". Мы объявляем некоторый блок. Вот здесь у нас есть "title". Его содержимое сразу же определяется - что это "Mail.ru". Если мы подключаем этот шаблон как есть на странице, то у нас выводится "title" с заголовком "Mail.ru". Все проекты, которые есть на Mail.ru, могут его подключить и иметь единый заголовок.

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

Сам JavaScript.

Нам очень хочется, чтобы с входными данными можно было работать алгоритмически полным языком. Вот есть такая конструкция, как "fest script". В ней можно что-то поделать именно на JavaScript, а ниже через "fest value", собственно, это вынести.

Вот этот шаблон выведет в Mail.ru.

Сам шаблонизатор.

На самом деле, у нас было очень много споров по поводу XML-синтаксиса. XML-синтаксис избыточен - с этим глупо спорить. Однако наша основная задача состояла в том, чтобы научиться "готовить" JavaScript на сервере. Поэтому не хотелось параллельно решать еще и задачи "давайте мы придумаем в нем синтаксис", "давайте мы в нем придумаем эскейпинг", "давайте мы в нем напишем всякие плагины к IDE". Не хотелось этого решать. Поэтому мы взяли XML, получились с короткой поддержкой IDE.

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

Плюс там еще во всех IDE есть подсветка, автоматическая табуляция. Не хотелось заморачиваться.

Еще в XML есть по умолчанию такая хорошая вещь, как пространства имен. Это расширяемость.

У нас есть шаблонизатор, он не очень-то много умеет. У вас есть реальные проекты. Вдруг вам стала нужна мультиязычность. "Зашивать" ее в fest-шаблон по умолчанию - это как-то странно. Он "разбухнет" и перестанет поддерживаться. А если вы объявляете, допустим, пространство имен своего проекта, то вы можете "перехватить" в компиляторе это событие и обработать это по-своему. Допустим, у вас могут быть специфичные пространства имен fest и Mail.ru.

За многие годы у нас появилась куча инструментов работы с XML. Когда я занимался компиляцией из fest-шаблона в XML, у меня был богатый выбор того, чем можно воспользоваться. В результате я воспользовался SAX-парсером, но не факт, что на этом остановлюсь.

Преобразование XML to XML

Опять же, я упоминал, что у нас выходные строки не очень простые. XML - это достаточно "развесистый" язык. Поэтому заниматься "эскейпингом" не очень хотелось. Когда у вас управляющие конструкции такие же, как в XSL, у вас практически все проблемы "эскейпинга" исчезают. Плюс еще есть дополнительные вещи, как CDATA, которые позволяют "эскейпить" тоже.

Реализация

Когда мы определились, как должен выглядеть шаблон (примерно поняли, что нам нужно для тестового стенда, чтобы он начал работать), мы приступили к реализации самого компилятора XML в JavaScript.

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

Но конкретно на этой задаче произошло обратное. Мы взяли шаблон, поняли, какой хотим результат, и начали компилировать. Я делал свою версию, а Костя делал свою версию. Мы раз в неделю собирались и, грубо говоря, замеряли цифры. Правда, его цифры были все время чуть-чуть быстрее.

Структура против функции

Мы выбрали два принципиально разных подхода. Я решил компилировать XML в структуру. А Костя компилировал сразу в функцию. Мне это поначалу показалось не очень безопасным.

Чтобы вы понимали, компиляция в структуру - это примерно такой массив.

Хеши означают действие, а строки можно либо вывести сразу к клиенту, либо что-то с ними сделать.

Для пояснения. Первый хеш - "action":"template" означает, что начинается шаблон. Со второй строкой ничего делать не надо, ее можно прямо так вывести к клиенту. Третья строчка означает, что четвертую строку надо пропустить через val , и результат val уже вывести к клиенту.

Или, например (наверное, так будет понятнее), рассмотрим вариант с "if".

Первые, вторые строчки - то же самое, что на предыдущем слайде. Третья строка означает, что следующее выражение надо выполнить. Если оно является истинным, то, соответственно, нужно вывести "true". Если оно является ложным, то надо вывести "false".

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

С реализацией в функцию все понятно. Просто по каким-то условиям начинает конкатенироваться изначальная переменная, которая выставлена в пустую строку.

Результат мы хотели получить вот такой.

Это один из наших главных проектов. Естественно, думая о шаблонизаторе, надо было думать об этом проекте. Список писем с достаточным количеством папок.

Скажу честно: первая реализация, которую я сделал, решала эту задачу за 200 миллисекунд.

Мы с Костей "бодались", по-моему, в течение месяца или полутора месяцев. Приходили после выходных, и один говорил: "А у меня 180!" Второй отвечал: "А у меня 150!" И так далее, и тому подобное. На самом деле, в какой-то момент я сдался, потому что понял, что уже не догоню. Мы начали делать реализацию с функцией, она победила. Когда мы все-таки "вылизали" все до конца, у нас на эту задачу уходило 3 миллисекунды.

Список писем рисовался за 3 миллисекунды. Трансформация примерно такая, это максимально близко к самым простым конструкциям JavaScript. "For" - в "for", "if" - в "if". Choose - это "if {} else".

Нам пришлось немного помучиться с fest:set, поскольку я понимал, что нам нельзя дальше жить без этого переопределения. Оно делается тоже не очень сложно. У нас по мере первого выполнения функции шаблона сначала создается как раз объект "set". По мере того, как ему попадаются XML-блоки "set", переписывается функция, которая, собственно, переопределяет содержимое этого блока.

Поэтому, если "set" много, то при отрабатывании шаблона, который вернет XML, выполнятся только последние из них, а не все. Это тоже дало небольшой прирост.

Очень интересный эффект дало следующее. С первой реализацией мы не очень сильно заморачивались. Когда было 200 миллисекунд - мы, в общем, этого не видели. Но компилировали мы сначала в такую структуру. У нас есть исходный HTML, который компилировался просто в конкатенацию строчка за строчкой.

Когда мы подбирались к трем миллисекундам, мы нехитрой "регуляркой" в скомпилированном шаблоне заменили эти "плюс-равно" на одну строку - мы получили, на самом деле, приличную цифру - плюс 30 % производительности.

Еще раз поговорим о безопасности.

Безопасность

Под безопасностью я понимаю не только XSS: нам обязательно нужно, чтобы шаблонизатор минимизировал его "из коробки". Причем я надеюсь, что он их исключает "из коробки". Но "try catch" должен быть, если вдруг нерадивый верстальщик возьмет свойства без определения (англ. undefined).

Поэтому по умолчанию все в try catch. Тем более, по нашим тестам это "бесплатно".

Это escape. Там есть и escape JavaScript, escape HTML. Пользовательские данные, чтобы не было доступа.

Опять же, как мы видели, это самая "дорогая" операция в нашем шаблонизаторе.

Конечно, используем "strict mode", чтобы не было утечек памяти.

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

Как я уже говорил, компиляция по умолчанию - это как раз "try catch" плюс "escape".


Интеграция

Мы получили цифры, которые хотели. Или, по крайней мере, получили какие-то приличные цифры, с которыми не стыдно прийти.

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

Это "Си" - у нас HTTP-сервер на "Си", и на нем много логики написано. Perl: большая часть почты написана на Perl. Какие-то проекты пишутся на Python. Node.js у нас на продакшне нет, но мы его тоже используем в разработке, естественно, с ним надо жить.

Казалось бы, когда мы добавляем к этому многообразию еще и V8, то мы сделаем только хуже. Но когда на части проектов мы добавили V8 ко всем этим технологиям… На самом деле, для это несложно. Нужно, чтобы V8 (там есть Google API) опрокинула вот эти три функции для логирования.

V8 - это просто библиотека, сама по себе она ничего не умеет. Это библиотека, которая принимает что-то на вход, что-то дает на выход. Естественно, когда у вас внутри что-то происходит, вы хотите об этом сообщать внешнему миру. Этот самый fest_log - уже на усмотрение технологии. Он либо в консоль, либо в браузер, либо письмом, либо СМС-кой системному администратору может сообщать, что внутри V8 что-то не так. А V8 эту функцию внутри получает и может как-то взаимодействовать с ней и через нее с внешним миром.

Fest_file, естественно, шаблонизация. Она бьется на файлы, "include", "insert" и так далее, и тому подобное. Поэтому когда V8 собирает и компилирует файлы, ей нужно обрабатывать "include" и "insert". Поэтому внешняя среда должна предоставить V8 возможность прочитать файл.

Dirname - это такая мелочь. Когда вы хотите прочитать файл, вам нужно на что-то опереться. Это директория текущего исполняемого JavaScript-файла. Вот этих трех конструкций достаточно, чтобы V8 заработала.

Произошло следующее. У нас на сервере back-end целый зоопарк технологий. Но есть проект, который на сервере использует fest. Есть проект, который на клиенте использует fest. Есть проект, который на клиенте и на сервере использует fest. Везде единый синтаксис, и верстальщики мигрируют между проектами, никаких проблем с этим нет. Конечно, мы "из коробки" получили интеграцию с браузером.

Fest компилируется в примитивные JavaScript-конструкции, которые (я уверен) принимаются даже Explorer 5. Просто негде было попробовать, извините.

Работа с реальными пользователями

Итак, у нас есть реализация, и нам все-таки хочется добраться до пользователей, узнать, как все заработает у них. Я с этой реализацией пришел к Игорю и сказал, что у нас тестово-нагрузочные прогоны укладываются в три миллисекунды. Он ответил: "Пляши, как хочешь - а там на все четыре".

Вы должны понимать, что продакшн - это, помимо трансформации, еще какие-то данные, которые надо получить, что-то с ними делать. А 4 миллисекунды - это на все.

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

Наш HTTP-сервер написан на "Си", мы его называем Light. Когда он получает данные от сервера back-end, то он хранит их в плоском хэше. На самом деле, это примитивный вариант. Он должен легко читаться. На всякий случай уточню, что это список писем. Первая строчка означает, что его длина равна пяти, вторая строчка означает, что у первого письма заголовок "letter", а третья строка означает, что оно не прочтенное.

Естественно, когда мы говорим о JavaScript, то хотим видеть внутри него вот такой V8.

Каким-то образом нам нужно это засунуть внутрь V8. Мы пробовали очень много вариантов. Пересмотрели всякие Binding, PerlOuI и свои решения - по-моему, около двух недель мучились. Вот вариант, который мы точно попробовали - это решение проблемы через V8 API.

Я пробовал, но, к сожалению, не получилось привести код на "Си". Его реально очень много. Я попробую объяснить на словах.

Через V8 API можно было сделать так: если вы в JavaScript пишете JSON, в этот момент V8 обращается к вашему коду на "Си". Код на "Си" в этот момент может вернуть то, что он считает нужным. Он может написать JSON.name. Это будет в два подхода - первый раз он обратится за JSON, а второй - за name. Это очень долго.

Второй вариант - это JSON.parse. Когда вы поднимаете контекcт V8, то на "Си" вы собираете строчку, которая один в один похожа на JSON. Отправляете ее в контекст, и прямо в контексте накладываете на это V8 JSON.parse "из коробки". Соответственно, уже внутри контекста у вас есть хеш, который можно отправлять в шаблон. Это тоже оказалось медленно.

Самый быстрый вариант - это как раз третий вариант. Я попробую все-таки кусок кода на "Си" от него показать.

Вот у вас есть вторая строчка. Это как раз шаблон. Та JavaScript функция, в которую компилируется fest. Она на вход ждет JSON. Вы собираете такую строчку, вызов этой функции, и первый и единственный ее аргумент - это тот JSON, который надо использовать. Даете команду выполнить это.

Цифры не очень были. При ограничении в четыре миллисекунды, две из которых - это трансформация. Мы их замеряли отдельно. Четыре секунды занимала подготовка данных.

Вы сейчас смотрите: казалось бы, четыре миллисекунды на подготовку данных - ну и что? А это 67 % от трансформации.

67 % времени у нас занимает подготовка данных для генерации этого html. У нас была задача уложиться в те же мощности, что сейчас есть. 6 миллисекунд вместо 4, это близко. Но все-таки было обидно. На самом деле, в этот момент мы почти забросили эту идею.

Но я все-таки нашел в себе силы взять тот хеш, который вы помните. Я взял его в текстовом виде, пришел домой. Честно говоря, сейчас не помню код. Но я из текстового вида его как-то превратил в JSON Node.js.

Я пришел к Игорю с этими цифрами и ожидал услышать от него: "Ты, конечно, молодец. Node.js тоже молодец. В Node.js мы писать не будем. Давай все-таки трезво оценивать мысли". Но услышал абсолютно обратное. Если на Node.js можно, почему мы не можем его использовать? В этот момент я понял, что мы решим эту задачу.

Что мы сделали? Давайте вернемся к тем данным, которые у нас есть в HTTP-сервере.

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

Мы выбросили сразу любые конвертации, которые у нас занимали 4 миллисекунды. Осталось время на проброс. Но оказалось, что это время - 1 миллисекунда. При ограничении в 4 миллисекунды… Напоминаю, что это список писем. Мы получили те миллисекунды, к которым стремились. Допустили к этому реальных пользователей. Не то чтобы они это видели, но ситуация стандартная.

Мы взяли один из серверов и выключили его из балансировки. Подняли там всю инфраструктуру, по которой отрисовывается список писем. "Поспритили" часть реальных запросов настоящих пользователей с настоящими данными и настоящим списком писем. Просто послали данные на этот сервер (продублировали). У нас тут результаты 30-часовых нагрузочных тестов.

Да, кстати, эти результаты показало одно ядро, не сервер, хочу отметить. Сервер обработал 10 миллионов хитов за 30 часов. Среднее время трансформации составило 1,6 миллисекунды. Не то чтобы сервер оказался быстрее. Просто у реальных пользователей разные настройки… Мы-то все тестировали на 25-ти письмах, а реальных пользователей просто местами меньше. Цифра ниже как раз показывает, что полпроцента, допустим, вообще-то укладывались в десять миллисекунд. Но, видимо, просто у них стоит в настройках 200 писем.

Думаю, что цифры прямо пояснять не надо. Тут видно, что у большинства как раз меньше двух миллисекунд. 90 % запросов меньше двух миллисекунд.

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

Когда мы с Игорем с цифрами, которые мы получили, пришли к Ермакову (тогда он был заместителем технического директора), я сказал: "Нагрузочные тесты хорошие, цифры хорошие, задача реальная. Хотим запускать это на пользователей". Как в любой большой компании, у нас есть пара проектов, которые лежат. Вроде как и надо их делать, а вроде и не надо. Я говорю: "Давай мы сделаем какой-нибудь из таких проектов на V8 на fest. Если что-то пойдет не так, то ничего страшного. Забудем про этот проект точно так же, как и забывали последний год. Но если все будет хорого, мы получим работающую штуку, от которой можно отталкиваться".

Игорь посмотрел на меня, посмотрел на цифры и говорит: "Цифры хорошие. Но ты правда хочешь V8 на продакшне?" Я говорю: "Хочу". Он ответил: "Тогда начинай с главной страницы. Иначе эти "сопли" так и будут размазываться дальше". Это была одна страница. Верстка заняла три дня. Мы опять выделили один из серверов front-end и запустили туда половину обычной нагрузки. Получили потребление ресурсов процессора в три раза больше, чем соседний front-end, который сейчас работает на пользователей.

Выглядело это вообще как полнейший провал. На самом деле, мы очень крупно ошиблись (я потом покажу, где). Но это нам дало еще оптимизации. Все было очень грустно, потому что мы настолько были погружены в эту задачу, что у меня весь код шаблонизатора был в голове. Я мог его перебирать в мыслях, не обращаясь к IDE. Я не понимал, где мы могли так проиграть по времени, потому что во всех предыдущих тестах все было хорошо.

Мы проиграли в шесть раз по сравнению с текущим сервером.

Не первый раз, что называется. Стали смотреть. Главная страница занимает 165 килобайт. Из них V8 генерирует 65 килобайт. Остается 100 килобайт. У нас есть технология RB внутри. Чтобы вы понимали, главная страница - это все-таки витрина остальных проектов. Поэтому RB - это средство доставки данных от этих проектов на главную страницу. RB в HTTP-сервер отдает уже, в общем-то, в виде html. Его шаблонизировать не надо. Если надо, то этим RB занимается внутри себя. Поэтому происходит следующее.

У нас есть технология RB. Она "общается" с HTTP-сервером, HTTP-сервер отдает результат V8. V8 конкатенирует это со своими данными, отдает обратно. Еще одно примечание для тех, кто ничего не читал про V8. Он внутри у себя все держит в utf-16. Это означает, что мы… А у нас шла, естественно, от utf-8. Utf-8 utf-16 обратно в utf8.

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

У нас есть некоторая строка. С этой строкой конкатенируется результат работы RB, и дальше он конкатенируется со следующей строкой V8.

Возникает вопрос: если с RB мы ничего не делаем, зачем ее вообще отдавать V8 и тратить на это ресурсы? Поэтому мы сделали небольшой хак. Из "Си" пробросили еще две функции. Это функция push.К счастью, главная страница у нас сплошная. Поэтому там не надо мучиться с "set" и "get".

Не заостряйте на этом внимание. Если кто-то будет досконально разбираться, вы поймете, о чем я говорю. Там шаблонизация сплошная, вернее, линейная. Как только шаблон отработал "кусок", мы точно знаем, что его можно отдавать пользователям.

У нас следующее происходило: библиотека V8 генерировала по своей логике часть строки и отдавала ее сразу в HTTP-сервер. Он его может сразу отдавать клиенту. Дальше, по логике, нужно выдать кусок из RB. Мы этот кусок даже не получаем. Мы HTTP-серверу сразу говорим: "Бери этот кусок и отдавай сразу клиенту - нам от него ничего не нужно". Получили еще 30 %. Но, на самом деле, оставался проигрыш в четыре раза.

Я заподозрил что-то неладное. Взял вот эту нагрузку, количество хитов, которое мы отправляем на тестовый сервер. Умножил на количество серверов, которое у нас сейчас "поднято", и получил вот это количество хитов в сутки.

440 000 000

А по всем TNS-счетчикам у нас вот столько хитов в сутки.

110 000 000

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

Уже на работающем сервере мы некоторое время имели возможность переключаться между шаблонизаторами. Верхний график - это память. V8 памяти нужно, конечно, чуть-чуть больше. Провал по памяти - это как раз переключение на старый шаблонизатор. Нижний график - это как раз "камень". По нему изменений нет. Мы запустили это все на пользователей.

Сейчас вся главная страница Mail.ru отдается через V8.

V8 на данный момент генерирует 65 килобайт данных. Может быть, сегодня уже чуть-чуть больше - я все-таки две недели готовил доклад. Время, которое библиотеке нужно для генерации этих данных - одна миллисекунда. Плюс 40 мегабайт на контекст с учетом того, что у нас количество ядер на сервере доходит до 8, не знаю, или до 16-ти. Это, в принципе, не очень напрягает, проблем с этим нет.

Это почти история успеха. Здесь присутствует Игорь Сысоев. Все, кто когда-нибудь думал про V8 на сервере, конечно, должны были прочитать его статью о проблемах V8.

Если я не прав, надеюсь, он меня поправит. Насколько я помню, там речь идет вот о чем. В V8, во-первых, контекст поднимается не мгновенно. Если говорить про реальные проекты, то у нас контекст поднимается с поднятием HTTP-сервера и только один раз. Это занимает около 2 миллисекунд, и, в общем, с этим можно жить.

Вторая проблема. Если вдруг случилось так, что библиотека V8 хотела выделить себе память и не смогла, она в этот момент "падает". В момент, когда мы занимались этим шаблонизатором c JavaScript на сервере, мне удалось выйти на разработчика V8 - Вячеслава Егорова. Он, кстати, часто выступает, это известная личность. Можно легко найти информацию про него. Он эти догадки почти подтвердил. Он утверждает (мы не проверяли, честно), что в текущей реализации V8, если ему не удалось аллоцировать память, выбрасывает исключение (англ. exeption), которое можно перехватить.

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

Что касается Nginx, скорее всего, такое поведение критично. У нас в Mail.ru, по заверениям одного из коллег, в том же самом RB работа в условиях нехватки памяти считается штатной ситуацией. А там, где нехватка памяти считается штатной ситуацией, скорее всего, будут проблемы. Но если мы говорим про обычный проект, то если у вас вдруг начнет заканчиваться память на машине, вы будете думать не про V8, а совсем про другие вещи.

Еще один неприятный момент.

Это статья про V8. Trunk V8, оказывается, очень активно разрабатывается. Самому Вячеславу не удалось воспроизвести эту ситуацию. Но у нас она воспроизводится "на ура". Я надеюсь, что мы поможем ему ее решить.

В какой-то момент мы запустили V8, и у нас обнаружалась утечка памяти. Мы очень долго искали утечку у себя, а потом переключились с trunk на версию 3.6.8, и проблема исчезла. Trunk проблемный. Стабильная версия Node.js - это, на данный момент, 06.14, и она тоже живет на 3.6. Если вы работаете с trunk, помните: вы работаете с чем-то, что не очень стабильно себя ведет. Все-таки переключайтесь на стабильные версии, за которые разработчики отвечают.

Как раз ссылка на API V8, которая очень понадобится, если вдруг этим займетесь. Ссылка на наш шаблонизатор. Я не могу сказать, что он OpenSource по одной простой причине. На самом деле, OpenSource предполагает некоторую ответственность за то, что ты делаешь. Мы ее, скорее всего, ее на себя возьмем, но конкретно сейчас мы к этому не готовы. Поэтому я просто говорю, что мы просто в открытую разрабатываемся.

Все, что я говорил про V8 trunk, может случиться и у нас. На данный момент мы ничего не стабилизируем. Это продукт, который мы разрабатываем для себя, но в открытом виде. С оглядкой на то, что мы его хотим выложить в OpenSource. Если будут какие-то просьбы типа "pull request" (они уже были, кстати, от некоторых людей), то такую заинтересованность мы, конечно, приветствуем.

У меня все. Я готов ответить на ваши вопросы.

Вопросы и ответы

Реплика из зала : Спасибо за доклад. У меня такой вопрос. Я не очень понял предысторию, почему нужно было использовать V8. У вас был сайт…

Андрей Сумин : Чуть больше, чем один сайт.

Реплика из зала : У вас были скрипты. Эти скрипты на серверах back-end стали тормозить. Так было или не так?

Андрей Сумин : Нет, совсем. Под "сайтом" вы понимаете проекты "Mail.ru Почта"?

Реплика из зала : Да.

Андрей Сумин : Основных проблем несколько. Первое - у нас двойная шаблонизация. Шаблоны, которые применяются на сервере, нельзя использовать на клиенте. Это проблема. Список писем мы обязаны отрендерить и на сервере, и на клиенте. Поэтому нам нужны были шаблоны, которые работают везде. Нужно, чтобы один и тот же шаблон выдавал один и тот же результат и на клиенте, и на сервере. Это первая и самая главная предпосылка для перехода на V8.

Вторая предпосылка. У меня в команде много людей, которые знают JavaScript. Но нет ни одного человека, который в достаточной для Mail.ru квалификации знает, допустим, "Cи", Python, или что-либо подобное. У меня есть люди, которые способны писать быстрый, хороший JavaScript, без утечек памяти и так далее, и тому подобное. Но нет людей, которые способны это писать на другом языке. Не потому, что они, в принципе, не способны, а потому, что они этим не занимаются каждый день. Плюс к тому, что нужны единые шаблоны на клиенте и на сервере, еще нужны специалисты, которые могут это сделать. Это две основные причины.

Реплика из зала : Нет, вот, смотрите. Если мне нужно список отрендерить на сервере, я там рендерю этот список, и каждому элементу там присваиваю какой-нибудь идентификатор. Или группе элементов присваиваю класс. Пишу на JavaScript скрипт, который что-то там делает. Может, замены какие-то, не знаю… Просто не совсем понятна мотивация использования. Почему двойная шаблонизация получается?

Андрей Сумин : У нас на "Почте" есть список писем. Он составляется из каких-то данных, его надо отрисовать. Дальше мы приходим на клиент, и список писем у нас обновляется раз в какое-то время (потому что человеку могут прийти новые письма). Соответственно, его надо повторно рендерить. Он приходит, естественно, на клиент в виде данных. "Гонять" html затратно по времени. Он приходит в виде какого-то JSON.

Плюс у нас есть разного рода оптимизации. Пришел JSON, его надо превратить в html. Это, опять же, шаблонизация. Плюс списки писем. Для каждого письма достаточно информации, чтобы при клике на письмо пользователю уже можно было что-то отобразить - допустим, заголовок письма. У нас есть тема, автор, мы можем это отобразить, дожидаясь контента письма. Это еще одна шаблонизация.

На клиенте у нас очень много шаблонизаций. Всякие ухищрения типа "я добавлю идентификатор и напишу простенький код на JavaScript" реально не работают. А когда человек находится на странице с письмом, он там может нажать F5, и в этот момент мы должны отрендерить это письмо на сервере, потому что так быстрее, как ни странно.

Письмо - это такой непростой объект. Там очень много всякой логики шаблонизации. "От кого", "Кому", "Прочтенное", "Непрочтенное", "Важное", "Не важное". Есть вложения, нет вложений. Много всего.

Реплика из зала : Ясно. Спасибо.

Реплика из зала : У меня два вопроса. Первый: почему вы выбрали V8. Смотрели ли вы в сторону JavaScript от Mozilla (SpiderMonkey, TraceMonkey)?

Андрей Сумин : Честно говоря, мы в его сторону посмотрели. Лично я выбрал V8, скорее, по политическим соображениям. Большая компания делает продукт для себя. Причина была такая. Когда мы стали результирующие шаблоны запускать на клиенте, в частности, шаблон со списком писем в Chrome "экспандился" за 6 миллисекунд, а в Mozilla - за 3 миллисекунды. Я задумался: может быть, неправильно был сделан выбор?

Мы в результате все-таки достигли 1 миллисекунды (для сравнения: у нас сервер в Gzip тратит сильно больше времени, чем V8). Я решил пока остановиться на V8. Может быть, V8 еще обгонит SpiderMonkey. Хотя на данный момент на нашем шаблонизаторе SpiderMonkey быстрее.

Причины пока политические. С учетом того, что по скорости нам некуда дальше гнаться. Другие проблемы сейчас надо решать.

Реплика из зала : Второй вопрос. Как сервер выглядит с точки зрения архитектуры? Это один процесс, который обрабатывает кучу соединений, или как?

Андрей Сумин : Игорь ответит - он писал это. Лучше ему право ответа отдать.

Игорь Сысоев : Да, это один процесс, который обрабатывает кучу соединений.

Реплика из зала : Понятно. Epoll Linux у вас?

Игорь Сысоев : Да. Один процесс.

Реплика из зала : Все соединения обрабатываются в одном контексте?

Игорь Сысоев : Да, один контекст.

Реплика из зала : Понятно. Спасибо.

Реплика из зала : У меня вопрос, во-первых, про переопределение шаблонов. Как решить такую задачу? Скажем, у нас есть какой-нибудь блок, мы хотим на какой-нибудь странице вывести этот блок в точности и добавить к нему еще что-то. Добавить какую-то дополнительную информацию. Переопределиться с вызовом базового блока. Ты сказал, что у вас есть JavaScript. Как это будет выражаться? Придется написать ассемблерную вставку в этот XML-синтаксис на JavaScript?

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

Реплика из зала : Нет, я-то, конечно, понимаю, что такие семантические удобства "бесплатно" относительно скорости не даются. У меня вопрос в другом. Какие у вас тогда библиотеки блоков, что вам не нужно переопределяться с вызовом базового метода?

Андрей Сумин : Сейчас я не очень понял вопрос.

Реплика из зала : У вас есть какие-то библиотеки, которые потом используются и в которых есть переопределение? Или просто все написано на одном проекте в один слой, и никаких переопределений нет, потому и никакой богатой семантики не нужно.

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

Реплика из зала : У меня еще один вопрос. Ты рассказывал, как здорово, что есть классные JavaScript-специалисты. Для них все это понятно и удобно. Зачем тогда XML-синтаксис, почему бы и синтаксис не уложить в JavaScript-синтаксис? Тогда специалист будет использовать "родной" JavaScript-редактор, например. Банально код ему будет проще читать, поскольку это один и тот же код. Мы вроде бы унифицировали исполнение этих шаблонов с точки зрения движка. Но с точки зрения синтаксиса мы, наоборот, создали вторую сущность.

Андрей Сумин : Сущность создали. Но тут несколько причин. Это следствие нескольких решений. Во-первых, я упоминал, что задача, которую нам точно надо было решить, - это научиться "готовить" JavaScript на сервере. Для того, чтобы ее решить, не хотелось отвлекаться на придумывание синтаксисов, решение проблем с "эскапированием" и так далее.

Реплика из зала : Но в JavaScript придуман синтаксис, и решены проблемы с "эскапированием". Точно так же, как с XML.

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

Реплика из зала : Почему? Почему я придумываю язык, а не пишу на JavaScript?

Андрей Сумин : Потому что конкатенировать строчки на JavaScript - это очень неблагодарное занятие.

Реплика из зала : Ладно. Не понимаю.

Андрей Сумин : Второе. Может быть, когда JavaScript на сервере станет обычным делом на многих проектах (может быть, не для всего проекта), по крайней мере, когда к этому будут относиться как к обычной практике, скорее всего, появится другой шаблонизатор, не fest. Очень может быть. Почему нет?

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

Второе. 99 % наших выходных документов - это XML. Когда у тебя управляющая конструкция XML и на выходе XML, проекту сильно проще, потому что XML понимают редакторы. Они же это валидируют, плюс есть куча инструментов по работе с XML. На текущий момент это финальное решение. Что будет через год, я не могу сказать.

Реплика из зала : Окей, спасибо. Я заканчиваю. Да, правильно ты говоришь, на мой взгляд. Самая главная проблема - это привнести JavaScript на сервер, а дальше это можно уже оптимизировать. Это, грубо говоря, принципиальный шаг. А дальше идут уже непринципиальные мелкие улучшения, которые можно сделать в будущем. Спасибо за то, что ты тоже такой опыт провел.

Реплика из зала : У меня вопрос в продолжение одного из предыдущих - про один процесс. Это действительно один процесс, к которому идут все запросы? Или это несколько процессов на разных портах, куда через upstream, допустим, Nginx передает различные запросы от разных пользователей?

Игорь Сысоев : Нет. Там нет Nginx. Там - да, один процесс, куда все идет. На самом деле, там на одной машине несколько форков. Все они на ядрах "висят". Туда как-то балансируются клиенты.

Реплика из зала : Понятно. Спасибо.

Реплика из зала : Еще такой вопрос. Несколько лет назад я тоже пытался заниматься JavaScript на сервере. С отладкой был вообще сущий ад. Какие инструменты отладки используете, чтобы все это профилировать?

Андрей Сумин : У нас сейчас есть очень большой бонус - это Node.js. Все, что мы запускаем, "из коробки" работает в Node.js. Там достаточно хороший инструмент отладки. Плюс, если мы говорим именно про шаблонизатор, мы всегда можем свой шаблон отправить на клиент. Там еще больше инструментов отладки.

Третье: как я уже говорил, могут быть какие-то специфические накладки со средой. Можно сделать так, чтобы среда просто "пробрасывала" функцию. У нас функция лог "пробрасывает". На самом деле она "log", "var" и "error" "пробрасывает", и V8 может "общаться" с внешним миром. Там уже среда куда-то выведет эти данные. Плюс еще в самом V8 API есть очень много средств работы с V8 через API V8. По крайней мере, когда он "падает" - он через API говорит, что он "падает".

Реплика из зала : Спасибо большое.

Реплика из зала : Спасибо за внимание.

Андрей Сумин : Спасибо всем.