Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Bringing choas to order in your node.js app

1,856 views

Published on

Bringing Chaos to order in your node.js app - talk delivered at Node.js Dublin Meetup - talks about the CLS module.

Published in: Software, Internet
  • Nice !! Download 100 % Free Ebooks, PPts, Study Notes, Novels, etc @ https://www.ThesisScientist.com
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Hello! Get Your Professional Job-Winning Resume Here - Check our website! https://vk.cc/818RFv
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

Bringing choas to order in your node.js app

  1. 1. BRINGING CHAOS TO ORDER IN YOUR NODE.JS APP DAN JENKINS NIMBLE APE https://nimblea.pe
  2. 2. WHO AM I?
  3. 3. 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
  4. 4. 4 /danjenkins
  5. 5. 5 @dan_jenkins
  6. 6. 6 nimblea.pe | @nimbleapeltd
  7. 7. NODE.JS & I STARTED WITH NODE.JS WHEN IT WAS A BABY - 0.4 BUILT MANY, MANY MICROSERVICES AND REST APIS WITH NODE SPENTTHE PASTYEAR WORKING ON A PLATFORM CALLED RESPOKE - WEBRTC 8
  8. 8. FUNNY STORY… 9 H E L L O my name is Dan G O O G L E I / O
  9. 9. WARNING… SOME BAD PRACTICES ARE USED IN SOME OF THE EXAMPLES. I KNOW YOU SHOULDN’T DO THESE THINGS. BUT, ITS THE EASIEST WAYTO SHOW THE POINT IN THE SMALL CONFINES OF A SLIDE DECK. I PROMISE I KNOW WHAT I’M DOING… 10
  10. 10. NODE.JS APPS CAN GET IN A REAL MESS…
  11. 11. 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); }); }); }
  12. 12. 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"); });
  13. 13. 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"); });
  14. 14. EVERYONE HAS AT SOME POINT…
  15. 15. DON’T TRY AND DENY IT…
  16. 16. WHAT PROBLEM ARE WE TRYING TO SOLVE?
  17. 17. PASSING DATA AROUND TO OTHER FUNCTIONS FOR USE LATER
  18. 18. 19 req.id = ‘foo’; console.log(req.id);
  19. 19. FOR EXAMPLE, SERVICE BASED LOGGING
  20. 20. 21 ASSIGN A REQUEST ID HERE AND HAVE IT SENT ON TO ALL OTHER SERVICES WHICH MEANS ALLYOUR LOGS FROM ALLYOUR SERVICES ALLTIE TOGETHER
  21. 21. WHAT’S THE ANSWER?
  22. 22. 23 OBVIOUSLY…
  23. 23. CLS NODE-CONTINUATION-LOCAL-STORAGE USERLAND MODULE UTILISES ASYNC-LISTENER 24 /othiym23/node-continuation-local-storage / continuation-local-storage /package/async-listener
  24. 24. IT HAS NOTHING TO DO WITH LOCAL-STORAGE IN THE BROWSER
  25. 25. “Continuation-local storage works like thread-local storage in threaded programming, but is based on chains of Node-style callbacks instead of threads.” 26
  26. 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"); });
  27. 27. 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'); });
  28. 28. IT’S MORE IMPRESSIVE THAN IT LOOKS IN THIS SIMPLE EXAMPLE I PROMISE
  29. 29. THAT’S AWESOME! WHAT DO I NEED TO DO?
  30. 30. 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(); }); }
  31. 31. 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); }
  32. 32. SO IT JUST WORKS? I DON’T HAVE TO DO ANYTHING FUNKY?
  33. 33. KINDA…
  34. 34. BEWARE NATIVE INTERACTIONS
  35. 35. ANYTHING THAT TOUCHES NATIVE CODE; YOU’LL NEED TO SHIM
  36. 36. SHIM ALL THE THINGS
  37. 37. /package/shimmer /othiym23/shimmer
  38. 38. 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; }; });
  39. 39. OR USE AN EXISTING ONE
  40. 40. 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);
  41. 41. 42
  42. 42. YOU NEED TO BE WARY!
  43. 43. 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)); }; }); };
  44. 44. YOU HAVE TO KNOW WHAT TO SHIM!
  45. 45. AND BEWARE THE INTERNALS OF THE MODULE CHANGING
  46. 46. AN INTERNAL MODULE CHANGE !== A PUBLIC API CHANGE
  47. 47. WHICH MEANS YOU COULD BE HIT BY A MINOR VERSION BUMP
  48. 48. WHICH COULD RESULT IN A FAILING APPLICATION
  49. 49. IT ALSO HAS A PERFORMANCE COST THE MORE YOU HAVE,THE MORE YOU’LL IMPACTYOUR PERFORMANCE COST IS AGAINST A NAMESPACE, NOT DATA IN THE NAMESPACE KEEP THINGS TO A MINIMUM 50
  50. 50. SO WHY USE IT?
  51. 51. 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
  52. 52. THANKS! DAN JENKINS @dan_jenkins
  53. 53. 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

×