Bringing choas to order in your node.js app
-
Upload
dan-jenkins -
Category
Software
-
view
450 -
download
0
Transcript of Bringing choas to order in your node.js app
BRINGING CHAOS TO ORDER IN YOUR NODE.JS APP
DAN JENKINS
NIMBLE APE
https://nimblea.pe
WHO AM I?DAN JENKINS
NODE.JS DEVELOPER / ARCHITECT
GOOGLE DEVELOPER EXPERT IN WEBRTC
LOVE LEGO & TECHNIC
GENERAL GEEK
FOUNDER OF NIMBLE APE LTD
❤ OPEN SOURCE
3
NODE.JS & I
STARTED WITH NODE.JS WHEN IT WAS A BABY - 0.4
BUILT MANY, MANY MICROSERVICES AND REST APIS WITH NODE
SPENT THE PAST YEAR WORKING ON A PLATFORM CALLED RESPOKE - WEBRTC
8
WARNING…
SOME BAD PRACTICES ARE USED IN SOME OF THE EXAMPLES.
I KNOW YOU SHOULDN’T DO THESE THINGS.
BUT, ITS THE EASIEST WAY TO SHOW THE POINT IN THE SMALL CONFINES OF A SLIDE DECK.
I PROMISE I KNOW WHAT I’M DOING…
10
WHO’S WRITTEN CODE LIKE THIS ?
12
createAdministrator: function (req, res) {
Administrators.create({some: data}, function cb(error, admin) {
if (error) { console.error('Error creating Admin'); res.send(500) return; }
res.admin = admin;
doAnotherCall(res, function cb2(err2){ res.send(200, res.admin); });
}); }
OR LIKE THIS
13
var http = require('http'); var uuid = require('uuid');
function respond(res) { console.log('Got request with ID', res.id); res.end('Response'); }
var server = http.createServer(function handleRequest(req, res){ res.id = uuid(); respond(res) });
server.listen(8080, function(){ console.log("Server listening on: http://localhost:8080"); });
OR LIKE THIS
14
var http = require('http'); var uuid = require('uuid');
function doSomething(res, id) { console.log('Do something with request with ID', id); respond(res, id); }
function respond(res, id) { console.log('Responding to request with ID', id); res.end('Response'); }
var server = http.createServer(function handleRequest(req, res){ var id = uuid(); doSomething(res, id); });
server.listen(8080, function(){ console.log("Server listening on: http://localhost:8080"); });
21
ASSIGN A REQUEST ID HERE
AND HAVE IT SENT ON TO ALL OTHER SERVICES
WHICH MEANS ALL YOUR LOGS FROM ALL YOUR SERVICES ALL TIE TOGETHER
CLS
NODE-CONTINUATION-LOCAL-STORAGE
USERLAND MODULE
UTILISES ASYNC-LISTENER
24
/othiym23/node-continuation-local-storage
/ continuation-local-storage
/package/async-listener
“Continuation-local storage works like thread-local storage in
threaded programming, but is based on chains of Node-style callbacks instead of threads.”
26
IT MEANS YOU CAN STOP DOING THIS…
27
var http = require('http'); var uuid = require('uuid');
function respond(res) { console.log('Got request with ID', res.id); res.end('Response'); }
var server = http.createServer(function handleRequest(req, res){ res.id = uuid(); respond(res); });
server.listen(8080, function(){ console.log("Server listening on: http://localhost:8080"); });
AND START DOING THIS…
28
var http = require('http'); var uuid = require('uuid'); var createNamespace = require('continuation-local-storage').createNamespace; var namespace = createNamespace('request-life');
function respond(res) { console.log('Got request with ID', namespace.get('requestId')); res.end('Response'); }
var server = http.createServer(function handleRequest(req, res){ namespace.set('requestId', uuid()); respond(res); });
server.listen(8080, function(){ console.log('Server listening on: http://localhost:8080'); });
1
31
var createNamespace = require('continuation-local-storage').createNamespace; var session = createNamespace('my session');
var db = require('./lib/db.js');
function start(options, next) { db.fetchUserById(options.id, function (error, user) { if (error) return next(error);
session.set('user', user);
next(); }); }
2
32
var getNamespace = require('continuation-local-storage').getNamespace; var session = getNamespace('my session');
var render = require('./lib/render.js')
function finish(response) { var user = session.get('user'); render({user: user}).pipe(response); }
39
var http = require('http'); var shimmer = require('shimmer');
shimmer.wrap(http, 'request', function (original) { return function () { console.log("Starting request!"); var returned = original.apply(this, arguments) console.log("Done setting up request -- OH YEAH!"); return returned; }; });
41
require('cls-mysql')(ns); require('cls-redis')(ns); require('cls-q')(ns); require('cls-bluebird')(ns); require('cls-es6-promise')(ns); require('cls-bcrypt')(ns);
44
// Copyright (c) 2015. David M. Lee, II 'use strict';
var shimmer = require('shimmer');
// require mysql first; otherwise you can get some bizarre // "object is not a function" errors if cls-mysql is loaded first. require('mysql'); var Protocol = require('mysql/lib/protocol/Protocol'); var Pool = require('mysql/lib/Pool');
module.exports = function(ns) { shimmer.wrap(Protocol.prototype, '_enqueue', function(enqueue) { return function(sequence) { sequence._callback = ns.bind(sequence._callback); return enqueue.call(this, sequence); }; });
shimmer.wrap(Pool.prototype, 'getConnection', function(getConnection) { return function(cb) { return getConnection.call(this, ns.bind(cb)); }; }); };
IT ALSO HAS A PERFORMANCE COST
THE MORE YOU HAVE, THE MORE YOU’LL IMPACT YOUR PERFORMANCE
COST IS AGAINST A NAMESPACE, NOT DATA IN THE NAMESPACE
KEEP THINGS TO A MINIMUM
50
IF YOU’RE DOING IT RIGHT…YOU SHOULD BE USING SHRINKWRAP ANYWAY
(OR ANOTHER TECHNIQUE TO LOCK DEPENDENCIES)
SO BREAKAGES SHOULD BE LIMITED TO YOUR DEVELOPMENT ENVIRONMENT
WHICH MEANS MOAR BONUS FOR LITTLE TO NO RISK
VERY LOW PERFORMANCE COST
52
MOST EXAMPLES WERE TAKEN FROM
https://github.com/othiym23/node-continuation-local-storage
https://github.com/othiym23/shimmer
https://github.com/building5/cls-mysql
54