How NOT to write in Node.js

12,733 views

Published on

Node.js facts and myths, revealed architecture and scaling eventapproach.

Published in: Technology, Business
0 Comments
10 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
12,733
On SlideShare
0
From Embeds
0
Number of Embeds
7,574
Actions
Shares
0
Downloads
35
Comments
0
Likes
10
Embeds 0
No embeds

No notes for slide

How NOT to write in Node.js

  1. 1. How NOT to write in Node.js Piotr Pelczar me@athlan.pl PHPers, 6 Feb 2014
  2. 2. About me Piotr Pelczar me@athlan.pl
  3. 3. About me getfokus.com useselly.com
  4. 4. How software lives in hardware? • Operating systems are process based • Each process has assigned processor, registers, memory http://www.cs.uic.edu/~jbell/CourseNotes/OperatingSystems/4_Threads.html
  5. 5. How software lives in hardware? • Process paralelism using threads (thread pools) • Switching processor over processes/threads causes context switching http://www.cs.uic.edu/~jbell/CourseNotes/OperatingSystems/4_Threads.html
  6. 6. How software lives in hardware? 1. Avoid context switching = wasting time
  7. 7. How software lives in hardware? In trivial, sequential approach • Each operation is executed sequentially: O(t) > O(t+1) • if O(t) stucks, O(t+1) waits… http://cs.brown.edu/courses/cs196-5/f12/handouts/async.pdf
  8. 8. How software lives in hardware? This is cool, software flow is predictible But not in high throughput I/O
  9. 9. http://blog.mixu.net/2011/02/01/understanding-the-node-js-event-loop/
  10. 10. How software lives in hardware? High throughput I/O doesn’t mean: • Memory operations • Fast single-thread computing
  11. 11. How software lives in hardware? High throughput I/O means: • HTTP requests • Database connections • Queue system dispatching • HDD operations
  12. 12. Single-threaded, event loop model Problem: Imagine a man, who has a task: 1. Walk around 2. When bucket is full of water, just pour another bucket 3. Go to next bucket http://www.nightmare.com/medusa/async_sockets.html
  13. 13. Single-threaded, event loop model What is nonblocking I/O? Imagine a man, who has a task: 1. Walk around 2. When bucket is full of water, just pour another bucket 3. Go to next bucket http://www.nightmare.com/medusa/async_sockets.html
  14. 14. Single-threaded, event loop model Problem: Imagine a man, who has a task: 1. Walk around 2. When bucket is full of water, just pour another bucket, if not… continue 3. Go to next bucket http://www.nightmare.com/medusa/async_sockets.html
  15. 15. Single-threaded, event loop model How it is realised in low-level operating system? • select() • /dev/pool descriptors • kqueue • pool • epool
  16. 16. Single-threaded, event loop model How it is realised in low-level operating system? #include <sys/types.h> #include <sys/socket.h> int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timeval *timeout);
  17. 17. Single-threaded, event loop model 2. Avoid I/O blocking
  18. 18. Node.js architecture http://nodejs.org/logos/
  19. 19. Node.js architecture JavaScript C/C++ Node std library node bindings (socket, http, …) Google V8 Thread Pool (libeio) Event loop (libev)
  20. 20. Node.js architecture • Single-threaded no context switching • Event loop no waits • Javascript (Google V8) • ECMA-262 support
  21. 21. Node.js architecture http://www.tocadoelfo.com.br/2012/04/por-que-estou-aprendendo-nodejs.html
  22. 22. ECMA-262 support var arr = [] for(var i = 0, j = arr.length; i < j; ++i) { var row = arr[i] // ufff… } var arr = [] arr.forEach(function(row) { })
  23. 23. „Parallelism” is a myth
  24. 24. Node.js architecture • Everything runs in parallel except your code • When currently code is running, (not waiting for I/O descriptors) whole event loop is blocked
  25. 25. „Parallelism” • Let’s compute Fibonacci function fib(n) { return (n < 2) ? 1 : (fib(n-2)+fib(n-1)); } This will simply block main (single) thread.
  26. 26. „Parallelism” • How about process.nextTick() ?
  27. 27. „Parallelism” • … but next iteration over function is delayed into next event loop iteration • That means each descriptor is checked before computation.
  28. 28. „Parallelism” process.nextTick() to speed up computations is a myth Node.js is not good solution to make single-process computations
  29. 29. „Parallelism” You can still fork another process • threads_a_gogo pool = require('threads_a_gogo').createPool(5) pool.any.eval('myFunction( ... )') • Fork process var fork = require('child_process').fork; var child = fork(__filename, [ 'arg1' ]);
  30. 30. Callback hell is a myth we can deal with it
  31. 31. Callback hell • Language construction – callback function • When operation is ready, call function passed as an argument someOperation(arg1, arg2, function() { console.log('cool!'); })
  32. 32. Callback hell • When many operations are ready…
  33. 33. var amqp = require('amqp'); var connection = amqp.createConnection(); connection.on('ready', function() { connection.exchange("ex1", function(exchange) { connection.queue('queue1', function(q) { q.bind(exchange, 'r1'); q.subscribe(function(json, headers, info, m) { console.log("msg: " + JSON.stringify(json)); }); }); }); }); This is callback hell
  34. 34. Callback hell Deal with callback hell in many ways: • Define callbacks as local variables • Use async module • Use PROMISE design pattern
  35. 35. Callback hell Define callbacks as local variables, like this: var whenSthElseDone = function() { // done... } var whenSthDone = function() { operation(whenSthElseDone) } operation(whenSthDone)
  36. 36. Callback hell Use async module: async.series([ function(callback) { ], function() { operation1(callback) }, }) function(callback) { operationBloking() return callback() } // all done...
  37. 37. Callback hell PROMISE design pattern: var Promise = require('promise'); var promise = new Promise(function (resolve, reject) { operation1(function (err, res) { if (err) reject(err); else resolve(res); }); });
  38. 38. Callback hell PROMISE design pattern: promise.then(function(res) { // done... })
  39. 39. Events (event bus) Triggering event does’t block whole event loop is a myth
  40. 40. Events var eventbus = require('events').EventEmitter myFunction() { operation1(function() { eventbus.emit('myFunctionIsDone', { result: 1234 }) } }
  41. 41. Events var eventbus = require('events').EventEmitter eventbus.on('myFunctionIsDone', function(data) { console.log('cool! ') }) Trigger waits until all listeners are done. So do not make long-running operations in event subscribers.
  42. 42. Scaling EventEmitter Triggering events in event bus via EventEmitter does not scale!
  43. 43. Scaling EventEmitter • Triggering events in event bus via EventEmitter does not scale! • Events are visible locally in main thread
  44. 44. Scaling EventEmitter We need to use external queue system to touch distributed nodes (3rd party for our application written in Node.js)
  45. 45. Scaling EventEmitter RabbitMQ is cool because: • based on AMQP protocol (so integrates 3rd party software – an abstraction) • Queue async system • Publish-subscribe async model • Request-Response model
  46. 46. Scaling EventEmitter Emit: 1. Trigger event in your application via EventEmitter 2. Catch it locally 3. Re-trigger via RabbitMQ
  47. 47. Scaling EventEmitter Receive: 1. Subscribe on RabbitMQ exchange 2. Trigger local event in application
  48. 48. Scaling EventEmitter This is only to separate responsibilities and good design of application
  49. 49. Not only Node.js Node.js is not a religion! It is only an implementation of async programming model
  50. 50. Not only Node.js Node.js is not a religion. Last time - hype. It only utilizes event-loop model, known from a long time… • GUI frameworks • Tornado – Python • Linkedin parseq – Java • Async and Await – C# • Cramp – Ruby • reactphp – PHP, ehhh… WAT?!
  51. 51. Event loop, single-threaded model is not solution for all your problems… Especially for long-running computings in single thread. It’s good for high throughput I/O.
  52. 52. Thank you. Q&A? Piotr Pelczar me@athlan.pl

×