rapid api development examples for impress application server / node.js (jsfwdays 2014)
DESCRIPTION
Application code and the server configuration examples with file-system access, RAM state, database access and parallel asynchronous processing of different resource types by stateful and stateless API requests.TRANSCRIPT
Rapid API development examples for Impress Application Server (Node.js)
Timur ShemsedinovResearch Institute of System Technologies (UA, Kiev), MetaSystems Inc.
mailto:[email protected] https://github.com/tshemsedinov/impresshttp://habrahabr.ru/users/marcusaurelius/ https://www.npmjs.org/package/impress
Impress Application Server is:
• Scaling transparent to applications• Application isolation (memory configuration database)• Deployment or update code without restart• Highload serving static files from memory (gzip, js minification, etc.)• URL-rewriting using RegExp, with internal redirection or sending
external HTTP-requests (reverse proxying)• Virtual hosts with multi-domain support (e.g.: *.domain.com)• Executable code and static caching in RAM• TCP port multiplexing and demultiplexing between applications• Server-Sent Events and WebSocket support• Stateless API (REST) and stateful API (RPC) support with IP-sticky
and Cookie-sticky to associated processes and global state sync.• Many other features: sessions, rotating logging, IPC and ZeroMQ,
database access drivers, etc.
Rapid API development examples
General principles:• Request routing based on file system• Each handler in separate file, handler inherit/override mechanism• Handlers need not prepare request execution environment, such as
import/load libraries, establish db connections, build memory structures, etc., request will come to ready application environment and handler will contain just applied code
• Each application have own isolated context and in-memory state• Long and batch workers in separate threads (forking by Impress
Application Server into parallel processes)• Global cross-server interprocess communication based on system
IPC and ZeroMQ to translate events, synchronize state, use reactive approach and actor-pattern
1. Simple JSON handler
/example/app/examples/simple/jsonPost.json/post.jsmodule.exports = function(client, callback) { client.context.data = { a: 1 }; callback();}---------------------------------------------------------------HTTP POST /example/app/examples/simple/jsonPost.json{ "a": 1}
2. Simple AJAX handler with template
/examples/simple/ajaxTest.ajax/get.jsmodule.exports = function(client, callback) { client.context.data = { parameterName: client.query.parameterName, }; callback();}
---------------------------------------------------------------/examples/simple/ajaxTest.ajax/html.templateAJAX Request with parameter returning back in template<br>parameterName: @parameterName@
---------------------------------------------------------------HTTP GET/examples/simple/ajaxTest.ajax?parameterName=parameterValueAJAX Request with parameter returning back in template<br>parameterName: parameterValue
3. Client-side example
/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
4. File system access example
/examples/simple/fsAccess.json/get.jsmodule.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}
5. HTTP-request from API handle example
/examples/simple/httpRequest.json/get.jsmodule.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();}
6. MongoDB (read) access example
/examples/mongodb/getData.json/get.jsmodule.exports = function(client, callback) { dbAlias.testCollection.find({}).toArray( function(err, nodes) { client.context.data = nodes; callback(); } );}
---------------------------------------------------------------HTTP GET mongodb/getData.json[ { "_id": "53547375894c3d3022000001" }]
7. MongoDB write and metadata examples
/examples/mongodb/insertData.json/get.jsmodule.exports = function(client, callback) { dbAlias.testCollection.insert(client.query, function(err) { client.context.data = !err; callback(); });}---------------------------------------------------------------/examples/mongodb/getCollections.json/get.jsmodule.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(); });}
8. SQL-query (MySql) access example
/examples/mysql/getCities.json/get.jsmodule.exports = function(client, callback) { dbAlias.query( 'select * from City', function(err, rows, fields) { client.context.data = { rows:rows, fields:fields }; callback(); } );}
9. Async parallel resource access example
/examples/complex/getFsMongoRequest.json/get.jsmodule.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(); },...
...previous example end
/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& ei=OWVWU5nHOqOc4wTbjYDgBw\">here</A>.\r\n</BODY></HTML>\r\n"}
10. Stateful API handler example
/examples/memory/stateful.json/get.jsmodule.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();}
11. SSE handle example
/examples/events/connect.sse/get.jsmodule.exports = function(client, callback) { client.sse.channel = 'TestEventStream'; callback();}
---------------------------------------------------------------/js/init.jsvar sse = new EventSource("/examples/events/connect.sse");
sse.addEventListener("TestEventStream", function(e) { console.dir({ event: e.event, data: e.data });});
12. WebSocket handler example
/examples/events/connect.ws/get.jsmodule.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.jsws = 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);}
API and FS introspection screens
Deployment patterns
• Installation script with deployment recomendations• Applications Server Configuration /config/*.js
• Start strategies (single, multiple, specialization, sticky)• Multithreading parameters: cluster.js• Network interfaces and port configuration: servers.js• Sandboxes configuration, plugins and access• Impress Applied Controller configuration: cloud.js• Logging configuration: log.js
• Application configuration /applications/name/config/*.js• Database access parameters: databases.js• Virtualhosts: hosts.js• URL-rewriting and reverse proxy configuration: routes.js• Session parameters: sessions.js• Static files and caching parameters: files.js• Application specific configuration files
Server installation (node.js + Impress)
CentOS 6.5 (64bit) minimalcurl http://.../impress/install.sh | sh---------------------------------------------------------------#!/bin/bashyum -y updateyum -y install wgetyum -y groupinstall "Development Tools"cd /usr/srcwget http://nodejs.org/dist/v0.10.26/node-v0.10.26.tar.gztar zxf node-v0.10.26.tar.gzcd node-v0.10.26./configuremakemake installln -s /usr/local/bin/node /binln -s /usr/local/bin/npm /binmkdir /impresscd /impressnpm install impress
Application Server as Linux Daemon
If installing Impress into absolute path /impress
Run /impress/bin/install.sh for:• Setup and configure as Linux daemon• Run at system startup• Auto-restart daemon workers on fails
Run /impress/bin/uninstall.sh for:• Stop Application Server• Remove from system startup• Remove Daemon from system
After install as a service (daemon) you can use:service impress startservice impress stopservice impress restartservice impress updateservice impress status
Application Server Configuration
/config/cloud.jsmodule.exports = { name: "PrivateCloud", type: "standalone", controller: "127.0.0.1", pubSubPort: "3000", reqResPort: "3001", health: "2s"}---------------------------------------------------------------/config/cluster.jsmodule.exports = { check: "http://127.0.0.2/", name: "C1", cookie: "node", strategy: "multiple", // single, specialization, sticky workers: os.cpus().length, gcInterval: 0}
Network interfaces & ports config
/config/servers.jsmodule.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" }}
Plugins configuration
/config/plugins.jsmodule.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"]
Logging and sandbox configuration
/config/log.jsmodule.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']}
Virtualhosts and URL-rewriting config
/applications/applicationName/config/hosts.jsmodule.exports = [ "127.0.0.1", "mydomain.com", "*.domainname.com",]---------------------------------------------------------------/applications/applicationName/config/routes.jsmodule.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" }]
Database access configuration
/applications/applicationName/config/databases.jsmodule.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" }}
Session and static files serving config
/applications/applicationName/config/sessions.jsmodule.exports = { anonymous: true, cookie: "SID", characters: "ABCDEFGH...fghijkl...456789", length: 64, persist: true, database: "impress"}---------------------------------------------------------------/applications/applicationName/config/files.jsmodule.exports = { minify: false, static: [ "*/css/*", "*/images/*", "*/js/*", "*/favicon.ico", "*/favicon.png" ]}
Примеры и демо-приложение
Thanks for attention!Questions please
Timur ShemsedinovResearch Institute of System Technologies (UA, Kiev), MetaSystems Inc.
mailto:[email protected]://habrahabr.ru/users/marcusaurelius/https://www.npmjs.org/package/impresshttps://github.com/tshemsedinov/impress