Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Примеры быстрой разработки API на масштабируемо...

fwdays
April 29, 2014

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

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

fwdays

April 29, 2014
Tweet

More Decks by fwdays

Other Decks in Programming

Transcript

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

    Node.js Тимур Шемсеминов НИИ Системных Технологий, MetaSystems Inc., ITAdapter Inc. mailto:[email protected] 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. Шаблоны развертывания • Рекомендации по установке • Конфигурация сервера приложений

    /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 • Любые собственные файлы конфигурации приложения
  18. Установка 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
  19. Управление сервисом (демоном) Если установить в каталог /impress Исполнится /impress/bin/install.sh

    • Устанавливается как сервис (демон) • Запускается при старте системы • Перезапускает свои потоки обработки при падениях /impress/bin/uninstall.sh • Останавливает сервер • Удаляет сервис из системы • Удаляет из автоматического старта После установи можно пользоваться service impress start service impress stop service impress restart service impress update service impress status
  20. Конфигурация сервера приложений /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 }
  21. Конфигурация сетевых интерфейсов /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" } }
  22. Конфигурация плагинов /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" ]
  23. Конфигурация логов и песочницы /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' ]}
  24. Конфигурация хостов и 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" } ]
  25. Конфигурация подключений к БД /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" } }
  26. Конфигурация сессий и статики /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" ] }
  27. Спасибо за внимание! Прошу задавать вопросы Тимур Шемсеминов НИИ Системных

    Технологий, MetaSystems Inc., ITAdapter Inc. mailto:[email protected] http://habrahabr.ru/users/marcusaurelius/ https://www.npmjs.org/package/impress https://github.com/tshemsedinov/impress