Pull to refresh

Прототип тоталитарного фреймворка для node.js

Reading time6 min
Views13K
на переработкуЕсли Вы программируете на node.js, но устали писать роутинг запросов в коде, у Вас нет предубеждения против использования глобальных переменных в служебных целях и Вы согласны, что излишняя свобода губительна для масс, то тоталитарный кружок выходного дня приготовил для Вас прототип альтернативной платформы для разработки веб приложений. Предупреждаю, что тоталитарный стиль предполагает не встраивание фреймворка в приложение через require, а наоборот, встраивание своего приложения в структуру фреймворка, где фрагменты кода приложения будут на каждом шагу иметь дело с дополнительными ограничениями и навязанными структурами кода и данных. Про то, что «less-than-expert» смогут разрабатывать высокопроизводительные системы, как утверждают разработчики ноды — ну тут Вы сами понимаете, что это будут за системы, особливо асинхронные, с потерянными коллбеками и утечками памяти на каждом шагу. В плане защиты от дурака, сей прототип чудес не доставляет. И конечно же, ожидаю от Вас много конструктивной критики, потому, что прототип сырой, хоть и собрал в себе множество концептуальных наработок нашей команды за последнее десятилетие. Даже название Impress появилось всего два дня назад и, да — это самый сложный вопрос.

Возможности и особенности


  • Маршрутизация URL на базе файловой системы — создал каталог — вот уже и часть URL, каталог незамедлительно наследует все обработчики от родительского каталога, а все, что будет помещено в сам каталог, переопределит логику исполнения или внешний вид страниц. Т.к. в основном мы ориентируемся на одностраничные приложения с динамическим взаимодействием с сервером, то все это, по большей части, нужно не для генерации страниц, а для быстрого написания серверного API на базе JSON. Последовательность обработки запросов, сначала ищет и исполняет файл request.js в запрошенном каталоге или по цепочке родительских каталогов пока не найдет или не упрется в корень приложения, потом ищет и исполняет файл с именем, соответствующим имени HTTP метоада (get.js, post.js и т.д.), потом рендерит шаблоны.
  • Кеширование серверного JavaScript и шаблонов в оперативной памяти — все обработчики и шаблоны при первом обращении забираются в память, где снабжаются индексами для быстрого поиска при повторном использовании.
  • Возможность изменения кода приложений без перезагрузки основного приложения — за всеми каталогами, из которых считывались файлы, устанавливается слежение, и при изменении кода и шаблонов на диске, мы обновляем кеш, а при удалении файлов — очищаем кеш.
  • Обслуживание сразу множества портов, сетевых интерфейсов, хостов и протоколов — т.к. у нас statefull приложения, т.е. мы храним состояние в оперативной памяти (сессии пользователей и состояние других объектов предметной области), то очень полезно, чтобы все серверные компоненты, обслуживающие одного пользователя на разных портах (например HTTP и SSE), имели общую разделяемую память.
  • Несколько стратегий запуска:
    • Один процесс (все обслуживается в одном потоке обработки).
    • Специализированные процессы (для каждого порта свой worker + главный процесс master).
    • Многопоточная не специализированная обработка (однотипные workerы, не специализированные процессы, master принимает запросы со всех портов и распределяет их рандомно по workerам).
    • Многопоточная обработка с привязкой клиентов по IP адресам к определенным workerам (sticky by IP) для того, чтобы последующие запросы клиента потом попадали именно в тот worker, в котором уже развернута его сессия и другие структуры состояния, с ним связанные.
    • Специализация процессов с изоляцией по приложениям (не включена в релиз, тестируется) — если запущено несколько приложений в одной системе, и одно может мешать другому, то лучше разделить их в разные workerы.
    • Отпочковывание отдельных процессов для обработки определенных URL (не включена в релиз, разрабатывается) — это специально для обработчиков, которые предполагают долгую блокирующую логику, для этих случаев, схема с общим реактором не подходит, а мы отдаем пользователю «запрос обрабатывается», отпочковываем обработчик, а в конце обработки он вернет результаты по IPC в родительский worker, который уже, например, по SSE вернет их клиенту.
  • Встроенный простой шаблонизатор с поддержкой include, наследованием и переопределением фрагментов шаблонов в подкаталогах. Шаблонизатор имеет доступ только к данным, сформированным обработчиками бизнес-логики и помещенными в res.context. Если Вы спросите, почему я не прикрутил сюда готовый, например, EJS, то посмотрите их код, в EJS, например, куча синхронных операций, у других — другие дефекты, а ничего сложного то нам и не нужно для AJAX-приложений. На клиенте берите любой шаблонизатор — не регламентируется.
  • Поддержка cookies и механизм сессий — в том числе состояния в памяти или хранимого состояния в MongoDB, если worker падает, то при следующем запросе состояние подымется из базы, а сохраняется оно только при внесении изменений. Неопознанные ключи сессий отвергаются и удаляются.
  • Отдача статического контента и стриминг — тут еще много нужно дорабатывать, скоро появится кеширование статики в памяти и управление этим кешем в конфиге.
  • Встроенный маршрутизатор запросов (reverse-proxy) с поддержкой url-rewriting — можем отдельные URLы, по регулярному выражению, перенаправлять на другой хост и порт, обеспечивая возврат ответа пользователю от него.
  • Гибкая конфигурация в формате JSON с возможностью изменять без полной перезагрузки основного приложения (которая сейчас все еще иногда рушится).
  • Простое логирование запросов — его или нужно развить или прикрутить готовое решение.
  • Встроенные средства доступа к БД — в качестве БД пока только Mongo, но самостоятельно доступаться к другим базам не возбраняется.

Планы на ближайшее время


  • Добавить поддержку оффлайн приложений (HTML5 Offline Applications Cache) с манифестами и т.д.
  • Реализовать загрузку файлов через POST запросы.
  • Кешировать статику в оперативной памяти с гибким управлением этим кешем из конфигурации.
  • Прикрутить опциональный geoIP лукап.
  • Сделать настраиваемые шаблоны страниц ошибок (404 и т.д.).
  • Ввести ограничения доступа к каталогам через файлики access.js, в них помещаемые.
  • Поддержка опционального вывода результатов API обработчиков не только в JSON, но и в XML (при моей индивидуальной непереносимости XML, прошу учесть сей героический шаг), а также, прием запросов в API в формате XML (держите меня семеро).
  • Простенькая CMS с хранением контента в MongoDB.
  • Плагин для отправки email из приложений.
  • Поддержка SSE (Server-Sent Events) для трансляции событий с сервера на клиенты по инициативе сервера (сейчас в разработке).
  • Демонизация и «graceful shutdown» (сейчас в разработке).

Установка и настройка


1. Ставим из npm (https://npmjs.org/package/impress)
$ npm install impress

2. Копируем шаблон проекта — содержимое каталога /node_modules/impress/examples/copyContentToProjectFolder переносим в каталог проекта (server.js, setup.js, config.js и каталог sites).

3. Насраиваем файл config.js пройдемся по разделам конфига:
databases — базы, которые будут автоматически открыты при старте и перечисленные коллекции будут доступны помещены таким образом: db.dbName.collectionName.find(...). Пример конфигурации:
dbName: {
	url: "mongodb://localhost:27017/dbName",
	collections: ["collname1", "collname2"]
}

session — имя длина и набор символов для генерации сессионного cookie, имя базы для постоянного хранения сессий.
cluster — настройка стратегии инстанциирования (тип многопоточнисти).
servers — именованные сервера (интерфейс/порт), для каждого поле hosts — массив именованных хостов, которые нужно отдавать с этого сервера, static — массив масок файлов для отдачи статики, например ["/css/*", "/images/*", "/js/*", "/favicon.ico", "/index.html"].
hosts — именованные хосты (виртуальные хосты), можно использовать маски для именования, например "*.myhost.com".
routes — именованные маршруты переадресации запросов.

4. Для инициализации структур данных в MongoDB, запустите node setup.js и жмем «y».

5. В файле server.js можем написать еще дополнительный свой код на инициализацию:
require('impress');
impress.init(function() {
	// Сюда свой код
});

6. И стартуем сервер
$ node server.js

Смотрим примеры


1. Шаблоны: при запущенном сервере открываем http://localhost
и шаблон смотрим тут: /sites/localhost/html.template
Смотрим на приложение, нажимаем «Create account», «Sign In»

2. Пример переопределения шаблона «left.template» с наследованием логики, открываем http://localhost/override
и шаблон смотрим тут: /sites/localhost/override/left.template
базовый шаблон тут: /sites/localhost/html.template
обработчик серверной логики тут: /sites/localhost/request.js
3. Пример API метода с JSON ответом смотрим: http://localhost/api/examples/methodName.json
и код тут /sites/localhost/api/examples/methodName.json/get.js
4. Пример запуска анонимной сессии: http://localhost/api/auth/anonymousSession.json
и код соответственно: /sites/localhost/api/auth/anonymousSession.json/get.js
5. Пример POST запроса: усилием мысли делаем POST на localhost/api/auth/regvalidation.json с параметром «Email»
и код: /sites/localhost/api/auth/regvalidation.json/post.js
6. Пример доступа в базу MongoDB:: http://localhost/api/examples/getUsers.json
и код тут: /sites/localhost/api/examples/getUsers.json/get.js
или вот он прямо:
module.exports = function(req, res, callback) {
	res.context.data = [];
	db.impress.users.find({}).toArray(function(err, nodes) {
		res.context.data = nodes;
		callback();
	});
}

Ссылки


На Github: https://github.com/tshemsedinov/impress
В npm: https://npmjs.org/package/impress

UPD: Очень грубые тестирования на хостинге Hetzner EX4 (Intel Core i7-2600 Quad-Core, 16 GB DDR3 RAM) дали 22`572 запроса в секунду с шаблоном из 5 файлов и простеньким запросом в базу.
Tags:
Hubs:
+5
Comments21

Articles

Change theme settings