Новая функциональность Impress Application Server для Node.js

    Предоставляю вам краткий отчет об изменениях в Impress AS за последние несколько месяцев. Во-первых мы не пошли путем фреймворков, а ринулись в сферу серверов приложений, обеспечивая масштабирование в двух направлениях: (1) много приложений на одном сервере и (2) много серверов для одного приложения. Первое направление требовало развивать сендбоксинг (экранирования приложений друг от друга и от среды запуска), а второе направление предусматривало переход от кластера к многосерверному клауду. Тут нужно уточнить терминологию, в ноде кластером (cluster) принято называть способ многопоточного запуска приложений в режиме master/worker, поэтому, многосерверную инфраструктуру (с кластером на каждом сервере) мы будем называть клаудом (или прикладным облаком). Это облако ни как не связано с виртуализацией операционной системы, как это обычно ложно связывается в понимании широких масс. Облачные технологии совсем не обязаны быть организованы на базе технологий виртуализации, но другого простого способа масштабирования сейчас для массового потребления нет. Облачная виртуализация не гарантирует бесконечной виртуализации прикладных приложений, и всем разработчикам крупных высоконагруженных систем известно, что для сотен тысяч и десятков миллионов пользователей нужно создавать специальные прикладные решения, обеспечивающие прикладную виртуализацию, размазывающую задачи по серверам и объединяю их в одну, распределенно функционирующую, но централизовано управляемую систему. Именно эту задачу мы и взялись решать в Impress, надеюсь я отстоял терминологию и могу переходить к подробностям.

    Новшества и версии


    Сейчас есть две актуальные версии 0.0.71 и 0.1.2, которые можно установить соответственно:
    npm install impress@0.0.71     # старый стиль приложений с поддержкой развертывания на множество серверов
    npm install impress@0.1.2      # новый стиль приложений с сендбоксингом и поддержкой множества серверов
    

    Все новогодние праздники были цинично потрачены на реализацию сендбоксинга и глубокий рефакторинг Impress. Переписано до 80% ядра и версия 0.1.2 оттестирована на приложениях, написанных для 0.0.71 с их минимальной коррекцией. Функционально в этих версиях все одинаково, кроме сендбоксинга и особенностей написания приложений. Рефакторинг из старого стиля в новый очень прост и сводится к замене обращения к API сервера приложений. На днях я выпущу небольшую инструкцию по портированию, т.к. есть уже достаточное количество внешних разработчиков, использующих Impress, подложить им такой подарок на НГ не хотелось бы. С инструкцией же они, справятся с портированием за несколько минут, надеюсь.

    Итак, функциональные новшества, присутствующие в обоих версиях:
    • Реализация межпроцессового взаимодействия в виде трансляции сообщений между процессами через ZeroMQ (межсерверное взаимодействие) и IPC (в рамках одного сервера). Это необходимо, чтобы пользователи, приклеенные к своему процессу, могли взаимодействовать друг с другом.
    • Добавлена поддержка WebSocket (ее уже давно просили) и обновлена реализация Server-Sent Events. В демонстрационном приложении есть примеры их использования. Более того, эти протоколы могут быть доступны на том же порту (например 80), на котором приложения отдают свои страницы, просто по другому URL, что позволяет обойтись без CORS (кроссдоменных запросов, а другой порт, почему-то считается уже другим доменом).
    • Удалено много зависимостей, и модули используются только если они установлены и добавлены в конфигурацию. Это все благодаря библиотеке geoip-lite, которая решила вдруг оставлять 120Мб временных файлов и тяжелым драйверам БД, которые не все сразу нужны. Патч для geoip-lite я уже отправил автору, в npm его еще нет, но кому критично — ставьте через github, тут он уже доступен. UPD: как раз 2014-01-13 новая версия попала в npm.
    • Проведена оптимизация кеширования шаблонов, статики и обработчиков бизнес-логики, в том числе оптимизировано слежение за файловый системой и обновление кеша при изменении файлов на диске. А в новой версии, кеширование полностью переписано, чтобы обеспечить экранирование приложений в сандбоксах.

    Слайд для заманивания на просмотр презентации (кликабилен)


    Теперь, для пущей убедительности покажу крутой слайд, из презентации. Примеры кода в презентации актуальны только для версии 0.0.71, а булшитологические пафосные тексты актуальны и для 0.1.2, так что, просмотрите ее, чтобы получить общее представление о сервере приложений. Есть и англоязычная версия презентации. Я понимаю, что для глубокого ознакомления с Impress совершенно необходимы хорошие скринкасты, разнообразные примеры и статьи с объяснением этих примеров, они готовятся и будут доступны с ближайшее время. По поводу поддержки, интенсивности разработки и выхода версий, активности исправления обнаруженных багов и пожеланий по доработе, вы можете сложить впечатление на Гитхабе. Не стесняйтесь присылать вопросы и постать issues на русском или английском.

    Ссылки


    На Github: https://github.com/tshemsedinov/impress
    В npm: https://npmjs.org/package/impress
    Поделиться публикацией

    Комментарии 32

      0
      проверим вечерком, чего же там такого получилось :) давно еще заглядывал проект был сыроват, да и я про ноду только из далека слышал, сейчас и опыта чуток появилось и желание с http поработать.
        0
        Тимур, может ли приложение на Impress быть развернуто в облачном окружении? Например в Windows Azure, как описано тут?
          0
          Только в случае, если зада стоит не «развернуть Impress везде», а «развернуть все на Azure» ) Это будет последнее, что я захочу протестировать, там множество вещей, которые не понятно будут ли работать, например, как могут быть пересобраны бинарные библиотеки для BSON и ZeroMQ, как будет работать IPC и слежение за файловой системой, есть большие сомнения. Для Азура оптимально писать на .NET, а для Impress оптимально использовать физические машины.
          +1
          а обложка похожая leanpub.com/fp-oo
            0
            Это все пошло от шедевра импрессионизма, одной из вариаций картины Клода Моне «Дама с зонтиком», но т.к. у нас сервер приложений тоталитарный, то решено было заменить невнятную даму, внушительным изображением Энакина Скайуокер в преклонных годах, а с ним юный падаван, изучающий Node.js с темной стороны. Вообще, импрессионизм, как художественное направление, полностью соответствует тому «Аахх...», который вызывает нода у новообращенных. Не только Моне уловил это, иллюстрацию из Дега я уже постал однажды.
            0
            я правильно понял, что у вас два способа взаимодействия — ZeroMQ и IPC?
              0
              Между серверами ZeroMQ, а между master-ом и worker-ами в рамках одного сервера используется IPC.
                0
                надеюсь, IPC через ZeroMQ то? в этом был вопрос
                  0
                  IPC через child_process, ZeroMQ внутри сервера ни чем не ускоряет работу, только дополнительные вызовы. Кроме того, благодаря этому, на одном сервере Impress может работать без ZeroMQ.
                    0
                    что мешало тогда уже и межмашинную коммуникацию сделать через сокеты? Раз используете мощь ZMQ так зачем пренебрегать ее возможностями и поддерживать два апи
                      0
                      Между машинами ZMQ работает быстрее, а в пределах одного сервера IPC через child_process быстрре. Разница в использовании API — 10 строк кода, мы не за красоту концепций боремся, а за скорость.
                        0
                        ясно, ясно :) То есть, даже ZMQ не используется на полную, а просто замена BSD-сокеты. Ок.
                          0
                          Используется два паттерна pub/bub (однонаправленный широковещательный, для трансляции событий в воркеры) и req/rep (вдунаправленный, для отправки событий в контроллер кластера). А на полную это как, просветите может?
                            0
                            Ну вот я, собственно, не очень понимаю, как можно хоть бы эти два паттерна заменить на 10 строк своего кода и IPC.

                            кстати, какая версия zmq?

                            на полную — это значит использовать (если конечно надо, но мне моя практика буквально сегодняшняя, подсказывает, что зачастую облегчает очень) и другие паттерны (а их там много). Хотя, возможно это так с моей колокольни, применительно в этому проекту, может ничего остального не надо конечно
                              0
                              Задача такая, транслировать глобальные события на все инстансы Impress, чтобы на них можно было подписываться в любом инстансе и отправлять в любом инстансе. Одной пары паттернов для этого не достаточно, потому, что pub/sub подходит для распространения событий через подписку, но он однонаправленный и нужно держать открытым еще один сокет для отправки события из воркера в «контроллер облака». IPC же в рамках одного сервера все равно открыт и он сразу двунаправленный, т.е. кода с использованием ZMQ больше, чем с IPC. Но для взаимодействия в рамках одного сервера ZMQ просто оборачивает IPC, и только замедляет процесс. Поэтому IPC используется там, где он быстрее, а ZMQ там, где он быстрее. Версия ZMQ 3.2.2. Другие паттерны в данном случае не нужны, паттернов в «The Guide» описано много, а вот базовых типов сокетов очень мало (req, rep, xreq, xrep, pub, sub, push, pull, pair) и гораздо проще оперировать именно ими, собирая конкретные паттерны уже под задачу.
                                0
                                кстати, а есть паблик тесты на счет сильного замедления? очень интересно (я сознательно отбрасываю крайние случаи оптимизаций, где борьба идет за десятки наносекунд, а вот чтобы быть быстрее в сервере общего назначения — это должны быть десятки и десятки %).
                                  0
                                  Не совсем понял, тесты чего и сильного земедления чего? Мы разные варианты тестировали от случая к случаю, не для получения красивой статистики, а для принятия решений, и оформлять это в приличный вид не стали.
                                    0
                                    Вы утверждаете, что IPC через ZMQ существенно медленнее, чем через child_process. Вот мне и интересно, насколько. И интересно, реально ли увидеть эту разницу (не по факту что вот профайлер столько-то наносекунд показывает). Так как раз проектируем большую и ответственную систему с похожей архитектурой.
                                      0
                                      А, ну такой тест не сложно сделать, я думал сравнение разных паттернов для трансляции событий. Будет время — сделаем, чтобы не возникало сомнений. ZMQ вместо IPC может быть полезен только в плане унификации кода, у нас тоже были сторонники такого подхода, которые уже всю голову продолбили.
                                        0
                                        вы считаете, что унификация плохо? ;) да, она может стоить чего-то, и тут вопрос насколько сильно она дорога. потому и спросил про тесты скорости
                                          0
                                          Ну я еще причисляю к преимуществам, что в Impress есть режим, когда на одном сервере кластеризация происходит без ZMQ, а доставка событий в браузеры без WebSocket, на своей реализации SSE, чтобы можно было не тянуть много тяжелых и сложных в сборке зависимостей. Сейчас минимальный размер зависимостей 10Мб, а максимальный 74Мб. Библиотека ZMQ для Node.js требует установки Python, node-gyp, GCC, а для винды еще и Visual Studio, это не каждый выдержит. А так, конечно проще вынести это в настройки и тестировать будет удобнее.
              0
              Impress использует каждый инстанс nodejs для всех приложений? У v8 производительность резко падала раньше при размерах хипа > ~1.5гб. С этим проблем не будет у impress?
                0
                Это конфигурируемо, если в /config/cluster.js:strategy == «specialization», то для каждого приложения создается отдельный процесс, но эффективнее создавать сэндбоксы виртуальной машины внутри одного процесса, иначе, при большом количестве приложений будет уходить много процессорного времени. Каждый сендбокс полностью экранирован в плане выделения памяти (они имеют разный global), позволяет ли это уйти от ограничения на размер хипа — не знаю, пока не тестировали этого, хотя, нужно бы. В любом случае, есть экономия памяти в случае нескольких приложений в одном процессе, все подгруженные библиотеки лежат в памяти один раз для всех приложений, это не смотря на разделение памяти, т.е. если библиотека что-то поместит в глобальный контекст, то оно будет один раз лежать (например, кеш больших таблиц geoip), а вот если библиотека при вызове из сэндбокса приложения сделает замыкания, то они уже попадут в сэндбокс. То есть, от количества приложений память будет зависеть только если эти приложения будут ее активно тратить, размещать в ней свои структуры данных, кеш, состояние. Кроме того, в режиме «sticky», пользователи прилипают к своим процессам и дальше уже все их запросы идут только через один процесс, поэтому, состояние, относящееся к пользователю будет лежать только внутри его родного процесса. В общем, вопрос этот правильной, важный, чтобы его раскрыть, я могу отдельную статью на 10 страниц написать, но он еще требует глубокого изучения и дополнительных тестов.
                0
                Эм, я прямо впечатался презентацией, в первую очередь простотой конфигурации и масштабируемостью. Я как раз занялся вопросом поиска удобного менеджера приложений для node который бы подходил под эти требования:
                1. Приложения автоматически стартовали при рестарте сервера.
                2. Приложения автоматически стартовали после падения
                3. Была возможность перегрузить приложение вручную.
                4. Разные вкусности, типа отдельных логов на приложение и т.п.
                5. Просто добавлять новые приложения.
                И с первого взгляда Impress, это то что мне нужно, но меня смущает подход к модулям нетипичный для node.js. Смогу ли я удобно использовать пакеты из npm через require()? работает ли подход когда для каждого приложения создается свой package.json со своими зависимостями? Или мне стоит ограничится например supervisor для этой сферы использования?
                  0
                  Impress написан не для приложений Express/Connect и вообще не для приложений, созданных в стиле цепочек мидлвеаров. Приложения для Impress запускаются только в среде запуска Impress. Что касается require и внешних модулей, то сколько душе угодно. Модули Вы можете устанавливать через npm install из корня сервера приложений, чтобы они попадали в общий «node_modules», а потом в /applications/yourappname/config можете сделать файл yourModuleConfig.js в котором require и инициализация нужного модуля. Далее, во всех обработчиках этот модуль будет доступен. Например так можно написать файл инициализации и конфигурации модуля:
                  // file: /applications/example/config/passport.js
                  var p = require('passport'),
                  	pGoogle = require('passport-google-oauth');
                  	p.serializeUser(function(user, done) { done(null, user); });
                  	p.deserializeUser(function(user, done) { done(null, user); });
                  	module.exports = {
                  		lib: p,
                  		strategies: {
                  		    google: {
                  		        param: {
                  	    	        clientID: 'ID-HERE',
                  		            clientSecret: 'SECTET-HERE',
                  		            callbackURL: '/api/auth/google/callback'
                  		        },
                  		        strategy: pGoogle.OAuth2Strategy,
                  		        authenticate: function(req, token, refreshToken, profile, done) {...},
                  				successRedirect: '/', failureRedirect: '/'
                  			}
                  		}
                  	};
                  }
                  

                  Все файлы из /config загружаются автоматически, сначала те, о которых система знает, а потом и все остальные.
                  Обработчки, использующие модули, например, паспорт, будут выглядеть так:
                  // file: /applications/example/app/api/auth/google/signIn/get.js
                  module.exports = function(client, callback) {
                  	client.passport.init(function () {
                  		client.passport.strategies.google.authenticate(client.req, client.res, callback);
                  	}, callback);
                  };
                  // file: /applications/example/app/api/auth/google/callback/get.js
                  module.exports = function(client, callback) {
                  	client.passport.init(function () {
                  		client.passport.strategies.google.authenticateCallback(client.req, client.res, callback);
                  	}, callback);
                  };
                  

                  В данном случае, кстати, показано, как обернуть модуль паспорт, написанный для Express/Connect так, чтобы он думал, что все ок и он запускается в среде мидлверов в Express.
                    0
                    спасибо, стало яснее )
                      0
                      Пока механизм поддержки экспресовых модулей не универсальный, но во-первых, не вся нода это экспрес, а во-вторых, мы работаем над улучшением эмуляции экспреса и коннекта, насколько это позволит сделать архитектура. Ведь архитектура импреса — это отказ от цепочек вызовов мидлваров, что и есть одним из его преимуществ по скорости.
                    0
                    А то что impress требует права root для запуска сервера даже при установке через npm install impress в домашний каталог это нормально или у меня что-то пошло не так? Если да, то объясните пожалуйста с чем это связано и как лучше разворачивать impress на этапе отладки приложения. Песочница?
                      0
                      Права рута нужны только если в /config/servers.js есть хоть один port меньше 1024. Там после установки идет 80 порт. Можете установить через npm install impress, потом зайти и поправить конфиг.
                        0
                        Спасибо, сменил порт на 8080, полегчало)
                      0
                      А существует ли более последовательная и подробная документация чем в nmpjs.org? А то, несмотря на все статьи и пример, непонятно с какой стороны подступиться.

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

                    Самое читаемое