Примеры быстрой разработки API на масштабируемом сервера приложений Impress для Node.js

Dd3f18c87b851137000c7427d7bd5d32?s=47 fwdays
April 29, 2014

Примеры быстрой разработки API на масштабируемом сервера приложений Impress для Node.js

Тимур Шемсединов

Dd3f18c87b851137000c7427d7bd5d32?s=128

fwdays

April 29, 2014
Tweet

Transcript

  1. Примеры быстрой разработки API на масштабируемом сервере приложений Impress для

    Node.js Тимур Шемсеминов НИИ Системных Технологий, MetaSystems Inc., ITAdapter Inc. mailto:timur.shemsedinov@gmail.com https://github.com/tshemsedinov/impress http://habrahabr.ru/users/marcusaurelius/ https://www.npmjs.org/package/impress
  2. Сервер приложений Impress • Масштабирование, прозрачное для приложений • Изоляция

    приложений (память, конфигурация, БД) • Модификация кода без перезапуска • Быстрая отдача статики из памяти (gzip, js min.) • URL-rewriting регулярными выражениями, с внутренним перенаправлением или внешним HTTP-вызовам • Виртуальные хосты с поддержкой *.domain.com • Кэширование кода, статики и шаблонов в памяти • Возможность запускать на одном tcp порту несколько приложений и одно приложение на нескольких портах • Server-Sent Events и WebSocket на одном порту • Возможность иметь API без состояния (REST) и с состоянием (RPC), прилипание по IP и Cookie • Множество других вещей: встроенный механизм сессий, логирование, IPC и ZeroMQ, драйвера доступа к БД и т.д.
  3. Примеры быстрой разработки API Общие принципы: • Маршрутизация запросов на

    основе файловой системы • Каждый обработчик в отдельном файле • Не требуется готовить среду исполнения запроса в обработ- чиках, т.е. не нужно подгружать библиотеки, устанавливать соединение с БД, строить структуры памяти и т.д., запрос попадает сразу в подготовленную среду, и мы пишем только прикладной код • У каждого приложения есть свой изолированный глобальный контекст и все, что в него пишется, сохраняет свое состояние между запросами • Можно ответвлять долгие обработчики (workers) в отдельные процессы • Осуществляется глобальное (кросс-серверное) межпроцессовое взаимодействие при помощи трансляции событий
  4. 1. Простейший пример обработчика JSON /example/app/examples/simple/jsonPost.json/post.js module.exports = function(client, callback)

    { client.context.data = { a: 1 }; callback(); } --------------------------------------------------------------- HTTP POST /example/app/examples/simple/jsonPost.json { "a": 1 }
  5. 2. Простейший пример обработчика AJAX /examples/simple/ajaxTest.ajax/get.js module.exports = function(client, callback)

    { client.context.data = { parameterName: client.query.parameterName, }; callback(); } --------------------------------------------------------------- /examples/simple/ajaxTest.ajax/html.template AJAX Request with parameter returning back in template<br> parameterName: @parameterName@ --------------------------------------------------------------- HTTP GET /examples/simple/ajaxTest.ajax?parameterName=parameterValue AJAX Request with parameter returning back in template<br> parameterName: parameterValue
  6. 3. Пример вызова обработчика /js/init.js $.post('/examples/simple/jsonPost.json', { parameterName: "paramaterValue" },

    function(res) { console.log(res.valueLength); } ); --------------------------------------------------------------- HTTP POST /example/app/examples/simple/jsonPost.json { "status": 1, "parameterValue": "paramaterValue", "valueLength": 14, "requestCounter": 3 } --------------------------------------------------------------- Console: 14
  7. 4. Пример доступа к файловой системе /examples/simple/fsAccess.json/get.js module.exports = function(client,

    callback) { var filePath = client.hostDir+client.path+'/test.txt'; fs.readFile(filePath, 'utf8', function(error, data) { client.context.data = { fileContent: data, dataLength: data.length }; callback(); }); } --------------------------------------------------------------- HTTP GET /examples/simple/fsAccess.json { "fileContent": "?Example text file", "dataLength": 18 }
  8. 5. Пример HTTP-запроса из обработчика /examples/simple/httpRequest.json/get.js module.exports = function(client, callback)

    { var req = impress.http.request({ hostname: 'google.com', port: 80, path: '/', method: 'get' }, function(response) { var data = ''; response.on('data', function(chunk) {data=data+chunk;}); response.on('end', function() { client.context.data = data; callback(); }); } ); req.on('error', function(e) { client.context.data = "Can't get page"; callback(); }); req.end(); }
  9. 6. Пример доступа к MongoDB (чтение) /examples/mongodb/getData.json/get.js module.exports = function(client,

    callback) { dbAlias.testCollection.find({}).toArray( function(err, nodes) { client.context.data = nodes; callback(); } ); } --------------------------------------------------------------- HTTP GET mongodb/getData.json [ { "_id": "53547375894c3d3022000001" } ]
  10. 7. Примеры доступа к MongoDB /examples/mongodb/insertData.json/get.js module.exports = function(client, callback)

    { dbAlias.testCollection.insert(client.query, function(err) { client.context.data = !err; callback(); }); } --------------------------------------------------------------- /examples/mongodb/getCollections.json/get.js module.exports = function(client, callback) { dbImpress.connection.collections(function(err, collections) { var items = []; for (var i = 0; i < collections.length; i++) { items.push(collections[i].collectionName); } client.context.data = items; callback(); }); }
  11. 8. Пример с SQL-запросом (mysql) /examples/mysql/getCities.json/get.js module.exports = function(client, callback)

    { dbAlias.query( 'select * from City', function(err, rows, fields) { client.context.data = { rows:rows, fields:fields }; callback(); } ); }
  12. 9. Пример с разными типами ресурсов /examples/complex/getFsMongoRequest.json/get.js module.exports = function(client,

    callback) { impress.async.parallel({ file: function(callback) { var filePath = client.hostDir+client.path+'/test.txt'; fs.readFile(filePath, 'utf8', function(error, data) { callback(null, data); }); }, request: function(callback) { var req = impress.http.request({ hostname: 'google.com', port: 80, path: '/', method: 'get' }, function(response) { var data = ''; response.on('data', function(chunk) { data = data+chunk; }); response.on('end', function() { callback(null, data); }); } ); req.on('error', function(e) { callback(null, "Can't get page"); }); req.end(); }, ...
  13. ...продолжение примера /examples/complex/getFsMongoRequest.json/get.js ... mongo: function(callback) { dbAlias.testCollection.find({}).toArray(function(err, nodes) {

    callback(null, nodes); }); } }, function(err, results) { client.context.data = results; callback(); }); } --------------------------------------------------------------------------------- { "mongo": [ { "_id": "53547375894c3d3022000001" } ], "file": "?Example text file", "request": "<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n<TITLE>302 Moved</TITLE></HEAD><BODY>\n <H1>302 Moved</H1>\nThe document has moved\n <A HREF=\"http://www.google.com.ua/?gws_rd=cr&amp; ei=OWVWU5nHOqOc4wTbjYDgBw\">here</A>.\r\n</BODY></HTML>\r\n" }
  14. 10. Пример обработчика с состоянием /examples/memory/stateful.json/get.js module.exports = function(client, callback)

    { application.stateTest = application.stateTest || { counter: 0, addresses: [] }; application.stateTest.counter++; application.stateTest.addresses.push( client.req.connection.remoteAddress ); client.context.data = application.stateTest; callback(); }
  15. 11. Пример обработчика SSE /examples/events/connect.sse/get.js module.exports = function(client, callback) {

    client.sse.channel = 'TestEventStream'; callback(); } --------------------------------------------------------------- /js/init.js var sse = new EventSource("/examples/events/connect.sse"); sse.addEventListener("TestEventStream", function(e) { console.dir({ event: e.event, data: e.data }); });
  16. 12. Пример обработчика WebSocket /examples/events/connect.ws/get.js module.exports = function(client, callback) {

    var connection = client.res.websocket.accept(); connection.send('Hello world'); connection.on('message', function(message) { connection.send('I am here'); }); connection.on('close', function(reasonCode, description) { console.log('disconnected'); }); callback(); } --------------------------------------------------------------- /js/init.js ws = new WebSocket("ws://127.0.0.1:80/examples/events/connect.ws"); ws.onopen = function() {}; ws.onclose = function() {}; ws.onmessage = function(evt) { console.log("Message from server: "+evt.data); }
  17. Интроспекция API и файлов

  18. Шаблоны развертывания • Рекомендации по установке • Конфигурация сервера приложений

    /config/*.js • Стратегии запуска (single, multiple, specialization, sticky) • Параметры многопоточности: cluster.js • Порты сетевые интерфейсы и порты: servers.js • Параметры песочников, плагины и зоны видимости • Параметры контроллера прикладного облака: cloud.js • Параметры логирования: log.js • Конфигурация каждого приложения /applications/name/config/*.js • Подключения к базам данных: databases.js • Виртуалхосты: hosts.js • URL-реврайтинг и обтатный proxy: routes.js • Параметры сессий: sessions.js • Параметры отдачи статики и кеша: files.js • Любые собственные файлы конфигурации приложения
  19. Установка node.js и impress CentOS 6.5 (64bit) minimal curl http://.../impress/install.sh

    | sh --------------------------------------------------------------- #!/bin/bash yum -y update yum -y install wget yum -y groupinstall "Development Tools" cd /usr/src wget http://nodejs.org/dist/v0.10.26/node-v0.10.26.tar.gz tar zxf node-v0.10.26.tar.gz cd node-v0.10.26 ./configure make make install ln -s /usr/local/bin/node /bin ln -s /usr/local/bin/npm /bin mkdir /impress cd /impress npm install impress
  20. Управление сервисом (демоном) Если установить в каталог /impress Исполнится /impress/bin/install.sh

    • Устанавливается как сервис (демон) • Запускается при старте системы • Перезапускает свои потоки обработки при падениях /impress/bin/uninstall.sh • Останавливает сервер • Удаляет сервис из системы • Удаляет из автоматического старта После установи можно пользоваться service impress start service impress stop service impress restart service impress update service impress status
  21. Конфигурация сервера приложений /config/cloud.js module.exports = { name: "PrivateCloud", type:

    "standalone", controller: "127.0.0.1", pubSubPort: "3000", reqResPort: "3001", health: "2s" } --------------------------------------------------------------- /config/cluster.js module.exports = { check: "http://127.0.0.2/", name: "C1", cookie: "node", strategy: "multiple", // single, specialization, sticky workers: os.cpus().length, gcInterval: 0 }
  22. Конфигурация сетевых интерфейсов /config/servers.js module.exports = { www: { protocol:

    "http", address: "127.0.0.1", port: 80, applications: ["example", "host2"], nagle: true, slowTime: "1s" }, ssl: { protocol: "https", address: "127.0.0.1", port: 443, key: "example.key", cert: "example.cer" } }
  23. Конфигурация плагинов /config/plugins.js module.exports = [ "db", "db.schema", "db.mongodb", "db.memcached",

    "db.mysql", "db.mysql.schema", "impress.log", "impress.security", "impress.security.mongodb", "impress.mail", "impress.uglify", "impress.health", "impress.cloud", "impress.geoip", "impress.websocket", "impress.sse" ]
  24. Конфигурация логов и песочницы /config/log.js module.exports = { keepDays: 10,

    writeInterval: "3s", writeBuffer: 64*1024, fileTypes: [ "access", "error", "debug", "slow" ] } --------------------------------------------------------------- /config/sandbox.js module.exports = { modules: [ 'global', 'console', 'process', 'impress', 'db', 'domain', 'crypto', 'geoip', 'os', 'Buffer', 'stream', 'nodemailer', 'net', 'http', 'https', 'dgram', 'dns', 'url', 'path', 'fs', 'util', 'events', 'iconv', 'querystring', 'zlib', 'async' ]}
  25. Конфигурация хостов и URL-rewriting /applications/applicationName/config/hosts.js module.exports = [ "127.0.0.1", "mydomain.com",

    "*.domainname.com", ] --------------------------------------------------------------- /applications/applicationName/config/routes.js module.exports = [ { url: "/api/(one|two)/(.*)", rewrite: "/example/[1].json?par1=[2]" }, { url: "/api/(name1|name2|name3)/(.*)", rewrite: "/api/[1]/[2]", host: "example.com", port: 80, slowTime: "1s" } ]
  26. Конфигурация подключений к БД /applications/applicationName/config/databases.js module.exports = { mongoTest: {

    url: "mongodb://hostName:27017/databaseName", slowTime: "2s", collections: ["collection1", "collection2"], security: true, alias: "alias1" }, system: { url: "mysql://user:password@localhost/dbName", slowTime: 1000, alias: "aliasName" } }
  27. Конфигурация сессий и статики /applications/applicationName/config/sessions.js module.exports = { anonymous: true,

    cookie: "SID", characters: "ABCDEFGH...fghijkl...456789", length: 64, persist: true, database: "impress" } --------------------------------------------------------------- /applications/applicationName/config/files.js module.exports = { minify: false, static: [ "*/css/*", "*/images/*", "*/js/*", "*/favicon.ico", "*/favicon.png" ] }
  28. Примеры и демо-приложение

  29. Спасибо за внимание! Прошу задавать вопросы Тимур Шемсеминов НИИ Системных

    Технологий, MetaSystems Inc., ITAdapter Inc. mailto:timur.shemsedinov@gmail.com http://habrahabr.ru/users/marcusaurelius/ https://www.npmjs.org/package/impress https://github.com/tshemsedinov/impress