Node.js: Asynchronous I/O for Fun and Profit

1,652
-1

Published on

http://www.infoq.com/presentations/Nodejs-Asynchronous-IO-for-Fun-and-Profit

Published in: Technology, News & Politics
0 Comments
4 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
1,652
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
30
Comments
0
Likes
4
Embeds 0
No embeds

No notes for slide

Node.js: Asynchronous I/O for Fun and Profit

  1. 1. Node.js: Asynchronous I/O for Fun and Pro t Stefan Tilkov @ QCon London 2011Friday, March 11, 2011
  2. 2. Stefan Tilkov @stilkov stefan.tilkov@innoq.com http://www.innoq.comFriday, March 11, 2011
  3. 3. Concurrent Request ProcessingFriday, March 11, 2011
  4. 4. Friday, March 11, 2011
  5. 5. Friday, March 11, 2011
  6. 6. Friday, March 11, 2011
  7. 7. Friday, March 11, 2011
  8. 8. Friday, March 11, 2011
  9. 9. Friday, March 11, 2011
  10. 10. read requestFriday, March 11, 2011
  11. 11. read request parse requestFriday, March 11, 2011
  12. 12. read request parse request processFriday, March 11, 2011
  13. 13. read request parse request process send backend requestFriday, March 11, 2011
  14. 14. read request parse request process send backend request read backend answerFriday, March 11, 2011
  15. 15. read request parse request process send backend request read backend answer processFriday, March 11, 2011
  16. 16. read request parse request process send backend request read backend answer process format responseFriday, March 11, 2011
  17. 17. read request parse request process send backend request read backend answer process format response send responseFriday, March 11, 2011
  18. 18. read request parse request process send backend request read backend answer process format response send responseFriday, March 11, 2011
  19. 19. Blocking I/O ProblemsFriday, March 11, 2011
  20. 20. Blocking I/O Problems Thread starvationFriday, March 11, 2011
  21. 21. Blocking I/O Problems Thread starvation Memory utilizationFriday, March 11, 2011
  22. 22. Blocking I/O Problems Thread starvation Memory utilization External dependenciesFriday, March 11, 2011
  23. 23. Blocking I/O Problems Thread starvation Memory utilization External dependencies Cascading problemsFriday, March 11, 2011
  24. 24. Blocking I/O Problems Thread starvation Memory utilization External dependencies Cascading problems Non-streaming approachFriday, March 11, 2011
  25. 25. User Space KernelFriday, March 11, 2011
  26. 26. Event Loop while (true) ready_channels = select(io_channels) for (channel in ready_channels) performIO(channel)Friday, March 11, 2011
  27. 27. Async I/O CharacteristicsFriday, March 11, 2011
  28. 28. Async I/O Characteristics Program always runningFriday, March 11, 2011
  29. 29. Async I/O Characteristics Program always running I/O-bound calls never blockFriday, March 11, 2011
  30. 30. Async I/O Characteristics Program always running I/O-bound calls never block Kernel handles I/OFriday, March 11, 2011
  31. 31. Async I/O Characteristics Program always running I/O-bound calls never block Kernel handles I/O Noti cation via eventsFriday, March 11, 2011
  32. 32. Async I/O Characteristics Program always running I/O-bound calls never block Kernel handles I/O Noti cation via events Used for timers, le I/O, net I/O, ...Friday, March 11, 2011
  33. 33. requests/second http://blog.webfaction.com/a-little-holiday-presentFriday, March 11, 2011
  34. 34. memory http://blog.webfaction.com/a-little-holiday-presentFriday, March 11, 2011
  35. 35. /dev/poll select() aio_*() poll() kqueue() epoll()Friday, March 11, 2011
  36. 36. java.nio .NET I/O Completion PortsFriday, March 11, 2011
  37. 37. Async I/O PerceptionFriday, March 11, 2011
  38. 38. Async I/O Perception Not widely knownFriday, March 11, 2011
  39. 39. Async I/O Perception Not widely known Low levelFriday, March 11, 2011
  40. 40. Async I/O Perception Not widely known Low level Hard to useFriday, March 11, 2011
  41. 41. Async I/O Perception Not widely known Low level Hard to use Exception rather than ruleFriday, March 11, 2011
  42. 42. JavaScriptFriday, March 11, 2011
  43. 43. JavaScript PerceptionFriday, March 11, 2011
  44. 44. JavaScript Perception “Toy language”Friday, March 11, 2011
  45. 45. JavaScript Perception “Toy language” IncompatibleFriday, March 11, 2011
  46. 46. JavaScript Perception “Toy language” Incompatible Inherent design problemsFriday, March 11, 2011
  47. 47. JavaScript Perception “Toy language” Incompatible Inherent design problems Low PerformanceFriday, March 11, 2011
  48. 48. http://commons.wikimedia.org/wiki/File:Audi_S5_V8_FSI_engine.jpgFriday, March 11, 2011
  49. 49. Friday, March 11, 2011
  50. 50. http://commons.wikimedia.org/wiki/File:Ateles_paniscus_-Brazil-8.jpgFriday, March 11, 2011
  51. 51. Friday, March 11, 2011
  52. 52. The JavaScript Arms RaceFriday, March 11, 2011
  53. 53. Friday, March 11, 2011
  54. 54. Friday, March 11, 2011
  55. 55. http://oreilly.com/catalog/9780596517748Friday, March 11, 2011
  56. 56. Friday, March 11, 2011
  57. 57. JavaScript TodayFriday, March 11, 2011
  58. 58. JavaScript Today Popular & widely usedFriday, March 11, 2011
  59. 59. JavaScript Today Popular & widely used O en mandatoryFriday, March 11, 2011
  60. 60. JavaScript Today Popular & widely used O en mandatory FastFriday, March 11, 2011
  61. 61. JavaScript Today Popular & widely used O en mandatory Fast CompatibleFriday, March 11, 2011
  62. 62. JavaScript Today Popular & widely used O en mandatory Fast Compatible Best practicesFriday, March 11, 2011
  63. 63. Node.jsFriday, March 11, 2011
  64. 64. Node.js Architecture v8 libev libeio http_parser c_aresFriday, March 11, 2011
  65. 65. Node.js Architecture Network/Platform layer (C) v8 libev libeio http_parser c_aresFriday, March 11, 2011
  66. 66. Node.js Architecture API (JavaScript) Network/Platform layer (C) v8 libev libeio http_parser c_aresFriday, March 11, 2011
  67. 67. High-performance network runtime, using JavaScript as a high-level DSLFriday, March 11, 2011
  68. 68. var net = require(net); var server = net.createServer(function (socket) { socket.write("Echo serverrn"); socket.pipe(socket); }) server.listen(8124, "127.0.0.1"); Code samples: http://github.com/stilkov/node-samples echo.jsFriday, March 11, 2011
  69. 69. var net = require(net); var server = net.createServer(function (socket) { socket.write("Echo serverrn"); socket.setEncoding(ascii); socket.on(data, function(data) { socket.write(data.toUpperCase()); }); }); server.listen(8124, "127.0.0.1"); echo-upcase.jsFriday, March 11, 2011
  70. 70. var sys = require("sys"), http = require("http"), url = require("url"), path = require("path"), fs = require("fs"); var dir = process.argv[2] || ./public; var port = parseFloat(process.argv[3]) || 8080; sys.log(Serving files from + dir + , port is + port); http.createServer(function(request, response) { var uri = url.parse(request.url).pathname; var filename = path.join(process.cwd(), dir, uri); path.exists(filename, function(exists) { if(exists) { fs.readFile(filename, function(err, data) { response.writeHead(200); response.end(data); }); } else { sys.log(File not found: + filename); response.writeHead(404); response.end(); } }); }).listen(port); le-server.jsFriday, March 11, 2011
  71. 71. Concurrency Level: 100 Time taken for tests: 6.000 seconds Complete requests: 10000 Failed requests: 0 Write errors: 0 Keep-Alive requests: 0 Total transferred: 710781 bytes HTML transferred: 150165 bytes Requests per second: 1666.72 [#/sec] (mean) Time per request: 59.998 [ms] (mean) Time per request: 0.600 [ms] (mean, across all concurrent requests) Transfer rate: 115.69 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 8 8.3 5 57 Processing: 1 51 44.4 40 307 Waiting: 0 43 43.5 30 302 Total: 1 59 44.8 50 316 Percentage of the requests served within a certain time (ms) 50% 50 66% 58 75% 68 80% 73 90% 112 95% 174 98% 206 99% 224 100% 316 (longest request)Friday, March 11, 2011
  72. 72. http.createServer(function(request, response) { var uri = url.parse(request.url).pathname; var filename = path.join(process.cwd(), dir, uri); sys.log(Serving file + filename); path.exists(filename, function(exists) { if(exists) { fs.readFile(filename, function(err, data) { var hash = crypto.createHash(md5); hash.update(data); response.writeHead(200, { Content-Type: text/plain, Content-MD5: hash.digest(base64) } ); response.end(data); }); } else { response.writeHead(404); response.end(); } }); }).listen(port); le-server-md5.jsFriday, March 11, 2011
  73. 73. HTTP ChunkingFriday, March 11, 2011
  74. 74. HTTP/1.0 connect Client ServerFriday, March 11, 2011
  75. 75. HTTP/1.0 connect send request Client ServerFriday, March 11, 2011
  76. 76. HTTP/1.0 connect send request send response Client ServerFriday, March 11, 2011
  77. 77. HTTP/1.0 connect send request send response close connection Client ServerFriday, March 11, 2011
  78. 78. HTTP/1.0 connect send request send response close connection Client connect ServerFriday, March 11, 2011
  79. 79. HTTP/1.0 connect send request send response close connection Client connect Server send requestFriday, March 11, 2011
  80. 80. HTTP/1.0 connect send request send response close connection Client connect Server send request send response dataFriday, March 11, 2011
  81. 81. HTTP/1.0 connect send request send response close connection Client connect Server send request send response data ...Friday, March 11, 2011
  82. 82. HTTP/1.0 connect send request send response close connection Client connect Server send request send response data ... send response dataFriday, March 11, 2011
  83. 83. HTTP/1.0 connect send request send response close connection Client connect Server send request send response data ... send response data close connectionFriday, March 11, 2011
  84. 84. HTTP/1.1: Content-length Client ServerFriday, March 11, 2011
  85. 85. HTTP/1.1: Content-length connect Client ServerFriday, March 11, 2011
  86. 86. HTTP/1.1: Content-length connect Connection: keep-alive Client ServerFriday, March 11, 2011
  87. 87. HTTP/1.1: Content-length connect Connection: keep-alive send request Client ServerFriday, March 11, 2011
  88. 88. HTTP/1.1: Content-length connect Connection: keep-alive send request send response Client ServerFriday, March 11, 2011
  89. 89. HTTP/1.1: Content-length connect Connection: keep-alive send request send response Content-length: xxx Client ServerFriday, March 11, 2011
  90. 90. HTTP/1.1: Content-length connect Connection: keep-alive send request send response Content-length: xxx Client ... ServerFriday, March 11, 2011
  91. 91. HTTP/1.1: Content-length connect Connection: keep-alive send request send response Content-length: xxx Client ... Server send requestFriday, March 11, 2011
  92. 92. HTTP/1.1: Content-length connect Connection: keep-alive send request send response Content-length: xxx Client ... Server send request send responseFriday, March 11, 2011
  93. 93. HTTP/1.1: Content-length connect Connection: keep-alive send request send response Content-length: xxx Client ... Server send request send response Content-length: yyyFriday, March 11, 2011
  94. 94. HTTP/1.1: Content-length connect Connection: keep-alive send request send response Content-length: xxx Client ... Server send request send response Content-length: yyy close connectionFriday, March 11, 2011
  95. 95. HTTP/1.1: Transfer-encoding: chunked Client ServerFriday, March 11, 2011
  96. 96. HTTP/1.1: Transfer-encoding: chunked connect Connection: keep-alive Client ServerFriday, March 11, 2011
  97. 97. HTTP/1.1: Transfer-encoding: chunked connect Connection: keep-alive send request Client ServerFriday, March 11, 2011
  98. 98. HTTP/1.1: Transfer-encoding: chunked connect Connection: keep-alive send request send response data Client ServerFriday, March 11, 2011
  99. 99. HTTP/1.1: Transfer-encoding: chunked connect Connection: keep-alive send request send response data Transfer-encoding: chunked Client ServerFriday, March 11, 2011
  100. 100. HTTP/1.1: Transfer-encoding: chunked connect Connection: keep-alive send request send response data Transfer-encoding: chunked Client xxx↵[data] ServerFriday, March 11, 2011
  101. 101. HTTP/1.1: Transfer-encoding: chunked connect Connection: keep-alive send request send response data Transfer-encoding: chunked Client xxx↵[data] Server send response data xxx↵[data]Friday, March 11, 2011
  102. 102. HTTP/1.1: Transfer-encoding: chunked connect Connection: keep-alive send request send response data Transfer-encoding: chunked Client xxx↵[data] Server send response data xxx↵[data] send response data 0↵Friday, March 11, 2011
  103. 103. HTTP/1.1: Transfer-encoding: chunked connect Connection: keep-alive send request send response data Transfer-encoding: chunked Client xxx↵[data] Server send response data xxx↵[data] send response data 0↵ close connectionFriday, March 11, 2011
  104. 104. http.createServer(function(request, response) { var uri = url.parse(request.url).pathname; var filename = path.join(process.cwd(), dir, uri); path.exists(filename, function(exists) { if(exists) { f = fs.createReadStream(filename); f.on(open, function() { response.writeHead(200); }); f.on(data, function(chunk) { response.write(chunk); }); f.on(error, function(err) { // ... }); f.on(end, function() { response.end(); }); } else { response.writeHead(404); - response.end(); } }); }).listen(port); stream- le-server.jsFriday, March 11, 2011
  105. 105. var hashFile = function(filename, cb) { path.exists(filename, function(exists) { if(exists) { r = fs.createReadStream(filename); var hash = crypto.createHash(md5); r.on(data, function(data) { hash.update(data); }); r.on(end, function() { cb(hash.digest(base64)); }); } else { throw File + filename + does not exist or can not be read; } }); } var filename = path.join(process.argv[2]); hashFile(filename, function(hash) { console.log(filename + : + hash); }); hash- le-stream.js (see stream- le-server-md5.js)Friday, March 11, 2011
  106. 106. var options = function(request) { // ... } http.createServer(function(request, response) { sys.log("--> " + request.url); var remoteRequest = http.request(options(request), function (remoteResponse) { response.writeHead(remoteResponse.statusCode, remoteResponse.headers); remoteResponse.on(data, function (chunk) { response.write(chunk); }); remoteResponse.on(end, function () { sys.log("<-- " + response.statusCode + " " + request.url); response.end(); }); }); request.on(data, function (chunk) { remoteRequest.write(chunk); }); request.on(end, function () { remoteRequest.end(); }); }).listen(port); proxy.jsFriday, March 11, 2011
  107. 107. http.createServer(function(request, response) { sys.log("--> " + request.url); var remoteRequest = http.request(options(request), function (remoteResponse) { response.writeHead(remoteResponse.statusCode, remoteResponse.headers); remoteResponse.on(end, function () { sys.log("<-- " + response.statusCode + " " + request.url); }); util.pump(remoteResponse, response); }); util.pump(request, remoteRequest); }).listen(port); proxy-pump.jsFriday, March 11, 2011
  108. 108. Asynchronous Programming ChallengesFriday, March 11, 2011
  109. 109. or: Why Programming with Callbacks SucksFriday, March 11, 2011
  110. 110. Friday, March 11, 2011
  111. 111. var bold = function(text) { return text.bold(); }; var capitalize = function(text) { return text.toUpperCase(); }; console.log("Synchronous:"); var result1 = capitalize("Hello, synchronous world."); var result2 = bold(result1); console.log("Sync result is " + result2); async1.jsFriday, March 11, 2011
  112. 112. var boldAsync = function(text, callback) { setTimeout(function (text) { callback(text.bold()); }, 100, text); }; var capitalizeAsync = function(text, callback) { setTimeout(function (text) { callback(text.toUpperCase()); }, 100, text); }; async1.jsFriday, March 11, 2011
  113. 113. var boldAsync = function(text, callback) { setTimeout(function (text) { callback(text.bold()); }, 100, text); }; var capitalizeAsync = function(text, callback) { setTimeout(function (text) { callback(text.toUpperCase()); }, 100, text); }; console.log("Asynchronous:"); capitalizeAsync("Hello, asynchronous world.", function(result1) { boldAsync(result1, function(result2) { console.log("Async result is " + result2); }); }); async1.jsFriday, March 11, 2011
  114. 114. try { console.log("Synchronous:"); var result1 = capitalize(null); var result2 = bold(result1); console.log("Sync result is " + result2); } catch (exception) { console.log("Sync exception caught: " + exception); } async2.jsFriday, March 11, 2011
  115. 115. try { console.log("Asynchronous:"); capitalizeAsync(text, function(result1) { boldAsync(result1, function(result2) { console.log("Async result is " + result2); }); }); } catch (exception) { console.log("Async exception caught: " + exception); } async2.jsFriday, March 11, 2011
  116. 116. // bad, dont do this try { console.log("Asynchronous:"); capitalizeAsync(text, function(result1) { boldAsync(result1, function(result2) { console.log("Async result is " + result2); }); }); } catch (exception) { console.log("Async exception caught: " + exception); } async2.jsFriday, March 11, 2011
  117. 117. var boldAsync = function(text, callback) { setTimeout(function (text) { try { callback(null, text.bold()); } catch (exception) { callback(exception); } }, 100, text); }; var capitalizeAsync = function(text, callback) { setTimeout(function (text) { try { callback(null, text.toUpperCase()); } catch (exception) { callback(exception); } }, 100, text); }; async3.jsFriday, March 11, 2011
  118. 118. capitalizeAsync(text, function(err, result1) { if (!err) { boldAsync(result1, function(err, result2) { if (!err) { console.log("Async result is " + result2); } else { console.log("Handling async error: " + err); } }); } else { console.log("Handling async error: " + err); } }); async3.jsFriday, March 11, 2011
  119. 119. var handleError = function(err, fn) { if (err) { console.log("Handling async error: " + err); } else { fn(); } } capitalizeAsync(text, function(err, result1) { handleError(err, function () { boldAsync(result1, function(err, result2) { handleError(err, function () { console.log("Async result is " + result2); }); }); }); }); async3.jsFriday, March 11, 2011
  120. 120. var step = require("step"); step( function () { capitalizeAsync(text, this); }, function (err, result) { if (err) throw err; boldAsync(result, this); }, function(err, result) { if (err) { console.log("Handling async error: " + err); } else { console.log("Async result is " + result); } } ); async3.jsFriday, March 11, 2011
  121. 121. var words = [one, two, three, four, five]; var upcasedWords = []; words.forEach(function (word) { capitalize(word, function(err, word) { upcasedWords.push(word); }); }); console.log(Done, upcased words: < + upcasedWords.join( ) + >); parallel1.jsFriday, March 11, 2011
  122. 122. var words = [one, two, three, four, five]; var upcasedWords = []; // bad, dont do this words.forEach(function (word) { capitalize(word, function(err, word) { upcasedWords.push(word); }); }); console.log(Done, upcased words: < + upcasedWords.join( ) + >); parallel1.jsFriday, March 11, 2011
  123. 123. var count = words.length; words.forEach(function (word) { capitalize(word, function(err, word) { upcasedWords.push(word); if (--count === 0) { console.log(Done, upcased words: < + upcasedWords.join( ) + >); } }); }); parallel1.jsFriday, March 11, 2011
  124. 124. var words = [one, two, three, four, five]; step( function () { var i, length; for (i = 0, length = words.length; i < length; i++) { capitalize(words[i], this.parallel()); } }, function (err) { if (err) throw err; var upcasedWords = Array.prototype.slice.call(arguments); upcasedWords.shift(); console.log(Done, upcased words: < + upcasedWords.join( ) + >); } ); parallel2.jsFriday, March 11, 2011
  125. 125. Tools & EcosystemFriday, March 11, 2011
  126. 126. Friday, March 11, 2011
  127. 127. npm node package manager Connect Asynchronous, low-level HTTP handler framework inspired by Rack/WSGI Express Sinatra-inspired Web framework on top of Connect multi-node Spawns child processes sharing listeners node-inspector Visual debugger for Node.js >700 more modules see https://github.com/joyent/node/ wiki/modulesFriday, March 11, 2011
  128. 128. var multi = require("multi-node"); var server = http.createServer(function(request, response) { var uri = url.parse(request.url).pathname; var filename = path.join(process.cwd(), dir, uri); path.exists(filename, function(exists) { if(exists) { fs.readFile(filename, function(err, data) { if (err) { sys.log(Error serving file + filename + + err); sys.log(request: + uri); } response.writeHead(200, { X-Node-Id: process.pid }); response.end(data); }); } else { response.writeHead(404); response.end(); } }); }); var nodes = multi.listen({ port: port, nodes: 10 }, server); sys.log("Server " + process.pid + " running at http://localhost:" + port); multi- le-server.jsFriday, March 11, 2011
  129. 129. SummaryFriday, March 11, 2011
  130. 130. Node.js popularizes the “right way” of network programmingFriday, March 11, 2011
  131. 131. JavaScript doesn’t suck as much as you thinkFriday, March 11, 2011
  132. 132. There’s a smart and active communityFriday, March 11, 2011
  133. 133. Node.js is fun to use!Friday, March 11, 2011
  134. 134. Thank you! Q&AFriday, March 11, 2011
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×