[
  {
    "path": "README.md",
    "content": "# NodeJS-Nucleus-Plus-Internals\nNodeJS源码分析-由浅入深了解架构运行原理\n\nNode版本基于v8.9.3 \n\n从运行入口开始深入源码分析，由浅入深，共同学习。\n\n\n [1-0 NodeJS源码分析-1 Hello World](https://github.com/fzxa/NodeJS-Nucleus-Plus-Internals/blob/master/chapter1/chapter1-0.md)\n \n [1-1 NodeJS源码解析 - HTTP Server模块](https://github.com/fzxa/NodeJS-Nucleus-Plus-Internals/blob/master/chapter1/chapter1-1.md)\n \n [1-2 NodeJS源码分析 - Stream模块](https://github.com/fzxa/NodeJS-Nucleus-Plus-Internals/blob/master/chapter1/chapter1-2.md)\n \n持续更新..\n \n### NodeJS系统架构图：\n![image](node-system.png)\n - Javascript V8 Engine: Nodejs javascript运行引擎\n - Libuv 是专门为Node.js开发的一个封装库，提供跨平台的异步I/O能力.\n - C-ares：提供了异步处理 DNS 相关的能力。\n - http_parser、OpenSSL、zlib 等：提供包括 http 解析、SSL、数据压缩等其他的能力。\n \n### NodeJS流程图\n \n ![image](https://github.com/fzxa/NodeJS-Nucleus-Plus-Internals/blob/master/chapter1/images/node-loop.png)\n \n\n"
  },
  {
    "path": "chapter1/chapter1-0.md",
    "content": "### NodeJS源码分析-1 Hello World\n\n#### 简要\nNode已经如今发展很快，已经相对稳定和成熟，在某些时候有必要知道其内部运行原理以及运行处理过程。\n种一棵树最好的时间是十年前 其次是现在。希望能坚持下去。\n\n### Nodejs当前最新版本 8.9.4\n[NodeJS官方网站下载源码](https://nodejs.org/en/download/)\n![image](images/chapter1-0.png)\n\nNode.js主要分为四大部分，Node Standard Library，Node Bindings，V8，Libuv\n\n大体流程是这样的：\n\n1. 初始化 V8 、LibUV , OpenSSL\n\n2. 创建 Environment 环境\n\n3. 设置 Process 进程对象\n\n4. 执行 node.js 文件\n\n\n\n解压包后代码结构如下：\n```\n├── AUTHORS\n├── BSDmakefile   # bsd平台makefile文件\n├── BUILDING.md\n├── CHANGELOG.md\n├── CODE_OF_CONDUCT.md\n├── COLLABORATOR_GUIDE.md\n├── CONTRIBUTING.md\n├── CPP_STYLE_GUIDE.md\n├── GOVERNANCE.md\n├── LICENSE\n├── Makefile     # Linux平台makefile文件\n├── README.md\n├── android-configure\n├── benchmark\n├── common.gypi\n├── configure\n├── deps          # Node底层核心依赖； 最核心的两块V8 Engine和libuv事件驱动的异步I/O模型库\n├── doc           \n├── lib           # Node后端核心库\n├── node.gyp      # Node编译任务配置文件 \n├── node.gypi\n├── src           # C++内建模块\n├── test          # 测试代码\n├── tools         # 编译时用到的工具\n└── vcbuild.bat   # Windows跨平台makefile文件\n```\n\n### Hello World 底层运行过程\n[官方Hello World代码](https://nodejs.org/en/about/)\n```js\n#app.js\nconst http = require('http');\n\nconst hostname = '127.0.0.1';\nconst port = 3000;\n\nconst server = http.createServer((req, res) => {\n  res.statusCode = 200;\n  res.setHeader('Content-Type', 'text/plain');\n  res.end('Hello World\\n');\n});\n\nserver.listen(port, hostname, () => {\n  console.log(`Server running at http://${hostname}:${port}/`);\n});\n```\n\n一个简单的HelloWorld涉及到多个模块：\n- global \n- module\n- http\n- event\n- net \n\n### 1.0 从main执行到js\n入口 src/node_main.cc 106行 通过 src/node.cc 调用 node::Start(argc, argv);\nnode_main.cc\n```c\nnamespace node {\n  extern bool linux_at_secure;\n}  // namespace node\n\nint main(int argc, char *argv[]) {\n#if defined(__linux__)\n  char** envp = environ;\n  while (*envp++ != nullptr) {}\n  Elf_auxv_t* auxv = reinterpret_cast<Elf_auxv_t*>(envp);\n  for (; auxv->a_type != AT_NULL; auxv++) {\n    if (auxv->a_type == AT_SECURE) {\n      node::linux_at_secure = auxv->a_un.a_val;\n      break;\n    }   \n  }\n#endif\n  // Disable stdio buffering, it interacts poorly with printf()\n  // calls elsewhere in the program (e.g., any logging from V8.)\n  setvbuf(stdout, nullptr, _IONBF, 0); \n  setvbuf(stderr, nullptr, _IONBF, 0); \n  // main作为入口调用node::Start\n  return node::Start(argc, argv);\n}\n#endif\n```\n\n### 1.1 node::Start 加载js\n调用顺序：\n\nStart() -> LoadEnviroment() -> ExecuteString()\n\n最终在LoadEnvrioment()里面加载node.js文件，调用ExecuteString() 解析执行node.js文件，返回值是一个f_value\n\n并且在ExecuteString()调用V8的 Script::Compile() 和 Script::Run()两个接口去解析执行js代码。\n\n\n\nnode.cc\n```c\n# Nodejs启动入口， \ninline int Start(Isolate* isolate, IsolateData* isolate_data,\n                 int argc, const char* const* argv,\n                 int exec_argc, const char* const* exec_argv) {\n  HandleScope handle_scope(isolate);\n  Local<Context> context = Context::New(isolate);\n  Context::Scope context_scope(context);\n  Environment env(isolate_data, context);\n  CHECK_EQ(0, uv_key_create(&thread_local_env));\n  uv_key_set(&thread_local_env, &env);\n  env.Start(argc, argv, exec_argc, exec_argv, v8_is_profiling);\n\n  const char* path = argc > 1 ? argv[1] : nullptr;\n  StartInspector(&env, path, debug_options);\n\n  if (debug_options.inspector_enabled() && !v8_platform.InspectorStarted(&env))\n    return 12;  // Signal internal error.\n\n  env.set_abort_on_uncaught_exception(abort_on_uncaught_exception);\n\n  if (force_async_hooks_checks) {\n    env.async_hooks()->force_checks();\n  }\n\n  {\n    Environment::AsyncCallbackScope callback_scope(&env);\n    env.async_hooks()->push_async_ids(1, 0);\n    \n    //加载nodejs文件后调用ExecuteString()\n    LoadEnvironment(&env); \n    env.async_hooks()->pop_async_id(1);\n  }\n\n  env.set_trace_sync_io(trace_sync_io);\n  //事件循环池\n  {\n    SealHandleScope seal(isolate);\n    bool more;\n    PERFORMANCE_MARK(&env, LOOP_START);\n    do {\n      uv_run(env.event_loop(), UV_RUN_DEFAULT);\n\n      v8_platform.DrainVMTasks();\n\n      more = uv_loop_alive(env.event_loop());\n      if (more)\n        continue;\n\n      EmitBeforeExit(&env);\n\n      // Emit `beforeExit` if the loop became alive either after emitting\n      // event, or after running some callbacks.\n      more = uv_loop_alive(env.event_loop());\n    } while (more == true);\n    PERFORMANCE_MARK(&env, LOOP_EXIT);\n  }\n\n  env.set_trace_sync_io(false);\n\n  const int exit_code = EmitExit(&env);\n  RunAtExit(&env);\n  uv_key_delete(&thread_local_env);\n\n  v8_platform.DrainVMTasks();\n  WaitForInspectorDisconnect(&env);\n#if defined(LEAK_SANITIZER)\n  __lsan_do_leak_check();\n#endif\n\n  return exit_code;\n}\n\n```\n\n### 核心运行流程\n整体运行流程图\n![image](images/node-loop.png)\n\n1. 核心数据结构 default_loop_struct 结构体为struct uv_loop_s\n当加载js文件时，如果代码有io操作，调用lib模块->底层C++模块->LibUV(deps uv)->拿到系统返回的一个fd（文件描述符），和 js代码传进来的回调函数callback，封装成一个io观察者（一个uv__io_s类型的对象），保存到default_loop_struct.\n\n2. 进入事件池， default_loop_struct保存对应io观察着，V8 Engine处理js代码, main函数调用libuv进入uv_run(), node进入事件循环 ,判断是否有存活的观察者\n- 如果也没有io, Node进程退出\n- 如果有io观察者， 执行uv_run()进入epoll_wait()线程挂起，io观察者检测是否有数据返回callback, 没有数据则会一直在epoll_wait()等待执行 server.listen(3000)会挂起一直等待。\n\n###  Module对象\n根据CommonJS规范，每一个文件就是一个模块，在每个模块中，都会有一个module对象，这个对象就指向当前的模块。\nmodule对象具有以下属性：\n- id：当前模块的bi\n- exports：表示当前模块暴露给外部的值\n- parent： 是一个对象，表示调用当前模块的模块\n- children：是一个对象，表示当前模块调用的模块\n- filename：模块的绝对路径\n- paths：从当前文件目录开始查找node_modules目录；然后依次进入父目录，查找父目录下的node_modules目录；依次迭代，直到根目录下的node_modules目录\n- loaded：一个布尔值，表示当前模块是否已经被完全加载\n\n示例：\n```js\nmodule.exports = { \n    name: 'fzxa',\n    getAge: function(age){\n            console.log(age)\n    }   \n}\nconsole.log(module)\n```\n执行node module.js 返回如下\n```js\nModule {\n  id: '.',\n  exports: { name: 'fzxa', getAge: [Function: getAge] },\n  parent: null,\n  filename: '/Users/fzxa/Documents/study/module.js',\n  loaded: false,\n  children: [],\n  paths: \n   [ '/Users/fzxa/Documents/study/node_modules',\n     '/Users/fzxa/Documents/node_modules',\n     '/Users/fzxa/node_modules',\n     '/Users/node_modules',\n     '/node_modules' ] }\n```\nmodule对象具有一个exports属性，该属性就是用来对外暴露变量、方法或整个模块的。当其他的文件require进来该模块的时候，实际上就是读取了该模块module对象的exports属性\n\n### exports对象\nexports和module.exports都是引用类型的变量，而且这两个对象指向同一块内存地址\n```\nexports = module.exports = {};\n```\n例子：\n```js\nvar module = {\n    exports: {}\n}\n\nvar exports = module.exports\n\nfunction change(exports) {\n    //为形参添加属性，是会同步到外部的module.exports对象的\n    exports.name = \"fzxa\"\n    //在这里修改了exports的引用，并不会影响到module.exports\n    exports = {\n        age: 24\n    }\n    console.log(exports) //{ age: 24 }\n}\n\nchange(exports)\nconsole.log(module.exports) //{exports: {name: \"fzxa\"}}\n```\n直接给exports赋值，会改变当前模块内部的形参exports对象的引用，也就是说当前的exports已经跟外部的module.exports对象没有任何关系了，所以这个改变是不会影响到module.exports的\n\nmodule.exports就是为了解决上述exports直接赋值，会导致抛出不成功的问题而产生的。有了它，我们就可以这样来抛出一个模块了.\n\n### require方法\nNode中引入模块的机制步骤\n1. 路径分析\n2. 文件定位\n3. 编译执行\nNode对引入过的模块也会进行缓存。不同的地方是，node缓存的是编译执行之后的对象而不是静态文件\n\nModule._load的源码：\n```js\nModule._load = function(request, parent, isMain) {\n\n  //  计算绝对路径\n  var filename = Module._resolveFilename(request, parent);\n\n  //  第一步：如果有缓存，取出缓存\n  var cachedModule = Module._cache[filename];\n  if (cachedModule) {\n    return cachedModule.exports;\n\n  // 第二步：是否为内置模块\n  if (NativeModule.exists(filename)) {\n    return NativeModule.require(filename);\n  }\n\n  // 第三步：生成模块实例，存入缓存\n  var module = new Module(filename, parent);\n  Module._cache[filename] = module;\n\n  // 第四步：加载模块\n  try {\n    module.load(filename);\n    hadException = false;\n  } finally {\n    if (hadException) {\n      delete Module._cache[filename];\n    }\n  }\n\n  // 第五步：输出模块的exports属性\n  return module.exports;\n};\n```\n在Module._load方法的内部调用了Module._findPath这个方法，这个方法是用来返回模块的绝对路径的，源码如下：\n```js\nModule._findPath = function(request, paths) {\n\n  // 列出所有可能的后缀名：.js，.json, .node\n  var exts = Object.keys(Module._extensions);\n\n  // 如果是绝对路径，就不再搜索\n  if (request.charAt(0) === '/') {\n    paths = [''];\n  }\n\n  // 是否有后缀的目录斜杠\n  var trailingSlash = (request.slice(-1) === '/');\n\n  // 第一步：如果当前路径已在缓存中，就直接返回缓存\n  var cacheKey = JSON.stringify({request: request, paths: paths});\n  if (Module._pathCache[cacheKey]) {\n    return Module._pathCache[cacheKey];\n  }\n\n  // 第二步：依次遍历所有路径\n  for (var i = 0, PL = paths.length; i < PL; i++) {\n    var basePath = path.resolve(paths[i], request);\n    var filename;\n\n    if (!trailingSlash) {\n      // 第三步：是否存在该模块文件\n      filename = tryFile(basePath);\n\n      if (!filename && !trailingSlash) {\n        // 第四步：该模块文件加上后缀名，是否存在\n        filename = tryExtensions(basePath, exts);\n      }\n    }\n\n    // 第五步：目录中是否存在 package.json \n    if (!filename) {\n      filename = tryPackage(basePath, exts);\n    }\n\n    if (!filename) {\n      // 第六步：是否存在目录名 + index + 后缀名 \n      filename = tryExtensions(path.resolve(basePath, 'index'), exts);\n    }\n\n    // 第七步：将找到的文件路径存入返回缓存，然后返回\n    if (filename) {\n      Module._pathCache[cacheKey] = filename;\n      return filename;\n    }\n }\n\n  // 第八步：没有找到文件，返回false \n  return false;\n};\n```\n当我们第一次引入一个模块的时候，require的缓存机制会将我们引入的模块加入到内存中，以提升二次加载的性能。但是，如果我们修改了被引入模块的代码之后，当再次引入该模块的时候，就会发现那并不是我们最新的代码，这是一个麻烦的事情。如何解决呢\nrequire有如下方法：\nrequire(): 加载外部模块\nrequire.resolve()：将模块名解析到一个绝对路径\nrequire.main：指向主模块\nrequire.cache：指向所有缓存的模块\nrequire.extensions：根据文件的后缀名，调用不同的执行函数\n\n\n```\n//删除指定模块的缓存\ndelete require.cache[require.resolve('/*被缓存的模块名称*/')]\n\n// 删除所有模块的缓存\nObject.keys(require.cache).forEach(function(key) {\n     delete require.cache[key];\n})\n```\n\n\n### HTTP_Server \n首先需要创建一个 http.Server 类的实例，然后监听它的 request 事件\n\nrequestListener 回调函数作为观察者，监听了 request 事件， 默认超时时间为2分\n\nlib/_http_server.js\n```js\nfunction Server(requestListener) {\n  if (!(this instanceof Server)) return new Server(requestListener);\n  net.Server.call(this, { allowHalfOpen: true }); \n\n  if (requestListener) {\n    this.on('request', requestListener);\n  }\n\n  // Similar option to this. Too lazy to write my own docs.\n  // http://www.squid-cache.org/Doc/config/half_closed_clients/\n  // http://wiki.squid-cache.org/SquidFaq/InnerWorkings#What_is_a_half-closed_filedescriptor.3F\n  this.httpAllowHalfOpen = false;\n\n  this.on('connection', connectionListener);\n\n  this.timeout = 2 * 60 * 1000;\n  this.keepAliveTimeout = 5000;\n  this._pendingResponseData = 0;\n  this.maxHeadersCount = null;\n}\n```\n\n观察者 connectionListener 处理 connection 事件。\n这时，则需要一个 HTTP parser 来解析通过 TCP 传输过来的数据：\n\nlib/_http_server.js\n```js\nfunction connectionListener(socket) {\n  debug('SERVER new http connection');\n\n  httpSocketSetup(socket);\n\n  // Ensure that the server property of the socket is correctly set.\n  // See https://github.com/nodejs/node/issues/13435\n  if (socket.server === null)\n    socket.server = this;\n\n  // If the user has added a listener to the server,\n  // request, or response, then it's their responsibility.\n  // otherwise, destroy on timeout by default\n  if (this.timeout)\n    socket.setTimeout(this.timeout);\n  socket.on('timeout', socketOnTimeout);\n\n  var parser = parsers.alloc();\n  parser.reinitialize(HTTPParser.REQUEST);\n  parser.socket = socket;\n  socket.parser = parser;\n  parser.incoming = null;\n\n  // Propagate headers limit from server instance to parser\n  if (typeof this.maxHeadersCount === 'number') {\n    parser.maxHeaderPairs = this.maxHeadersCount << 1;\n  } else {\n    // Set default value because parser may be reused from FreeList\n    parser.maxHeaderPairs = 2000;\n  }\n\n  var state = { \n    onData: null,\n    onEnd: null,\n    onClose: null,\n    onDrain: null,\n    outgoing: [], \n    incoming: [], \n    // `outgoingData` is an approximate amount of bytes queued through all\n    // inactive responses. If more data than the high watermark is queued - we\n    // need to pause TCP socket/HTTP parser, and wait until the data will be\n    // sent to the client.\n    outgoingData: 0,\n    keepAliveTimeoutSet: false\n  };  \n  state.onData = socketOnData.bind(undefined, this, socket, parser, state);\n  state.onEnd = socketOnEnd.bind(undefined, this, socket, parser, state);\n  state.onClose = socketOnClose.bind(undefined, socket, state);\n  state.onDrain = socketOnDrain.bind(undefined, socket, state);\n  socket.on('data', state.onData);\n  socket.on('error', socketOnError);\n  socket.on('end', state.onEnd);\n  socket.on('close', state.onClose);\n  socket.on('drain', state.onDrain);\n  parser.onIncoming = parserOnIncoming.bind(undefined, this, socket, state);\n\n  // We are consuming socket, so it won't get any actual data\n  socket.on('resume', onSocketResume);\n  socket.on('pause', onSocketPause);\n\n  // Override on to unconsume on `data`, `readable` listeners\n  socket.on = socketOnWrap;\n\n  // We only consume the socket if it has never been consumed before.\n  var external = socket._handle._externalStream;\n  if (!socket._handle._consumed && external) {\n    parser._consumed = true;\n    socket._handle._consumed = true;\n    parser.consume(external);\n  }\n  parser[kOnExecute] =\n    onParserExecute.bind(undefined, this, socket, parser, state);\n\n  socket._paused = false;\n}\n\n```\n\n未完...\n\n\n参考链接：\n```\nhttps://yjhjstz.gitbooks.io/deep-into-node/chapter1/\nhttp://blog.csdn.net/wuji3390/article/details/71276849\nhttps://feclub.cn/post/content/wq_node\n```\n"
  },
  {
    "path": "chapter1/chapter1-1.md",
    "content": "### NodeJS源码解析 - HTTP Server模块\n\n\nhttp是nodejs中重要的模块之一，有必要了解它的运行原理\n\n回到helloWorld ,当node在收到一个http请求，会创建一个http.Server，注册并监听request。\n\n```js\nvar http = require('http');\nhttp.createServer((req, res) => {\n  res.writeHead(200, { 'Content-Type': 'text/plain' });\n  res.end('Hello World\\n');\n}).listen(port, hostname, () => {\n  console.log(`Server running at http://${hostname}:${port}/`);\n});\n```\n\n#### HTTP模块\n1. 打开node-v8.9.3/lib/http.js \n\n首先引入的是http模块,模块抛出公共方法调用createServer实际上是返回Server实例，\n\ncreateServer里面的回调函数（参数requestListener）\n\n直接作为了Server的参数requestListener,而这个Server实际上是require('_http_server')\n```js\n'use strict';\n\nconst agent = require('_http_agent');\nconst { ClientRequest } = require('_http_client');\nconst common = require('_http_common');\nconst incoming = require('_http_incoming');\nconst outgoing = require('_http_outgoing');\n//引入私有_http_server模块\nconst server = require('_http_server');\n\nconst { Server } = server;\n//创建server, 将回调函数作为参数\nfunction createServer(requestListener) {\n  return new Server(requestListener);\n}\n\nfunction request(options, cb) {\n  return new ClientRequest(options, cb);\n}\n\nfunction get(options, cb) {\n  var req = request(options, cb);\n  req.end();\n  return req;\n}\n//http模块暴露的所有公共方法\nmodule.exports = { \n  _connectionListener: server._connectionListener,\n  METHODS: common.methods.slice().sort(),\n  STATUS_CODES: server.STATUS_CODES,\n  Agent: agent.Agent,\n  ClientRequest,\n  globalAgent: agent.globalAgent,\n  IncomingMessage: incoming.IncomingMessage,\n  OutgoingMessage: outgoing.OutgoingMessage,\n  Server,\n  ServerResponse: server.ServerResponse,\n  createServer, \n  get,\n  request\n};\n```\n\n打开文件node-v8.9.3/lib/_http_server.js 260行\n\n实际上是为这个requestListener函数与'request'事件绑定到了一起，而'request '是方法parserOnIncoming里面抛出的一个事件\n   \n```js\nfunction Server(requestListener) {\n  if (!(this instanceof Server)) return new Server(requestListener);\n  net.Server.call(this, { allowHalfOpen: true }); \n \n  //如果有回调函数，对当前实例进行监听，若request有事件触发则调用回调\n  if (requestListener) {\n    this.on('request', requestListener);\n  }\n\n  // Similar option to this. Too lazy to write my own docs.\n  // http://www.squid-cache.org/Doc/config/half_closed_clients/\n  // http://wiki.squid-cache.org/SquidFaq/InnerWorkings#What_is_a_half-closed_filedescriptor.3F\n  this.httpAllowHalfOpen = false;\n  //当启动server实例时，观察者建立connect事件\n  this.on('connection', connectionListener);\n\n  this.timeout = 2 * 60 * 1000;\n  this.keepAliveTimeout = 5000;\n  this._pendingResponseData = 0;\n  this.maxHeadersCount = null;\n}\n\n//net.Server继承Server\nutil.inherits(Server, net.Server);\n```\n\n#### res过程？\n调用emit方法，将request事件发送给每一个监听的实例，并且传入req,res\n\nserver.emit('request', req, res); 这个事件也会同时抛出req和res两个对象\n\nreq变量与另一个叫做shouldKeepAlive的变量作参同时传入此函数parserOnIncoming\n\n_http_server.js  592行 602行\n```js\n//处理具体解析完毕的请求\nfunction parserOnIncoming(server, socket, state, req, keepAlive) {\n  resetSocketTimeout(server, socket, state);\n\n  state.incoming.push(req);\n\n  // If the writable end isn't consuming, then stop reading\n  // so that we don't become overwhelmed by a flood of\n  // pipelined requests that may never be resolved.\n  if (!socket._paused) {\n    var ws = socket._writableState;\n    if (ws.needDrain || state.outgoingData >= ws.highWaterMark) {\n      socket._paused = true;\n      // We also need to pause the parser, but don't do that until after\n      // the call to execute, because we may still be processing the last\n      // chunk.\n      socket.pause();\n    }\n  }\n  //服务器通过ServerResponse实例，来个请求方发送数据。包括发送响应表头，发送响应主体\n  var res = new ServerResponse(req);\n  res._onPendingData = updateOutgoingData.bind(undefined, socket, state);\n\n  res.shouldKeepAlive = keepAlive;\n  DTRACE_HTTP_SERVER_REQUEST(req, socket);\n  LTTNG_HTTP_SERVER_REQUEST(req, socket);\n  COUNTER_HTTP_SERVER_REQUEST();\n\n  if (socket._httpMessage) {\n    // There are already pending outgoing res, append.\n    state.outgoing.push(res);\n  } else {\n    res.assignSocket(socket);\n  }\n\n  // When we're finished writing the response, check if this is the last\n  // response, if so destroy the socket.\n  res.on('finish',\n         resOnFinish.bind(undefined, req, res, socket, state, server));\n\n  if (req.headers.expect !== undefined &&\n      (req.httpVersionMajor === 1 && req.httpVersionMinor === 1)) {\n    if (continueExpression.test(req.headers.expect)) {\n      res._expect_continue = true;\n\n      if (server.listenerCount('checkContinue') > 0) {\n        server.emit('checkContinue', req, res);\n      } else {\n        res.writeContinue();\n        //送给每一个监听器的实例并传入req&res\n        server.emit('request', req, res);\n      }\n    } else if (server.listenerCount('checkExpectation') > 0) {\n      server.emit('checkExpectation', req, res);\n    } else {\n      res.writeHead(417);\n      res.end();\n    }\n  } else {\n    //送给每一个监听器的实例并传入req&res\n    // res实际上是ServerResponse的实例\n    // var res = new ServerResponse(req);\n    server.emit('request', req, res);\n  }\n  return false; // Not a HEAD response. (Not even a response!)\n}\n```\n\nServerResponse 实现了 Writable Stream interface，内部也是通过socket来发送信息。\nres，发现为ServerResponse（）的实例并传入req\n```js\nfunction ServerResponse(req) {\n  OutgoingMessage.call(this);\n\n  if (req.method === 'HEAD') this._hasBody = false;\n\n  this.sendDate = true;\n  this._sent100 = false;\n  this._expect_continue = false;\n\n  if (req.httpVersionMajor < 1 || req.httpVersionMinor < 1) {\n    this.useChunkedEncodingByDefault = chunkExpression.test(req.headers.te);\n    this.shouldKeepAlive = false;\n  }\n}\n//继承自OutgoingMessage,为OM的一个子类，所以回调函数里的res也是OM的一个实例\n//来自_http_outgoing私有模块  \n//const OutgoingMessage = require('_http_outgoing').OutgoingMessage;\nutil.inherits(ServerResponse, OutgoingMessage);\n```\n\n到此res线找到，res为ServerMessage的实例，也是OutgoingMessage的实例\n```js\nfunction OutgoingMessage() {\n  Stream.call(this);\n  \n  //返回一些与服务器有关的属性\n  // Queue that holds all currently pending data, until the response will be\n  // assigned to the socket (until it will its turn in the HTTP pipeline).\n  this.output = []; \n  this.outputEncodings = []; \n  this.outputCallbacks = []; \n\n  // `outputSize` is an approximate measure of how much data is queued on this\n  // response. `_onPendingData` will be invoked to update similar global\n  // per-connection counter. That counter will be used to pause/unpause the\n  // TCP socket and HTTP Parser and thus handle the backpressure.\n  this.outputSize = 0;\n\n  this.writable = true;\n\n  this._last = false;\n  this.upgrading = false;\n  this.chunkedEncoding = false;\n  this.shouldKeepAlive = true;\n  this.useChunkedEncodingByDefault = true;\n  this.sendDate = false;\n  this._removedConnection = false;\n  this._removedContLen = false;\n  this._removedTE = false;\n\n  this._contentLength = null;\n  this._hasBody = true;\n  this._trailer = ''; \n\n  this.finished = false;\n  this._headerSent = false;\n\n  this.socket = null;\n  this.connection = null;\n  this._header = null;\n  this[outHeadersKey] = null;\n\n  this._onPendingData = noopPendingOutput;\n}\nutil.inherits(OutgoingMessage, Stream); //继承自Stream  \n```\n流程图演示：\n\n![image](images/node-server-res.png)\n\n\n#### req 过程\nreq，在parserOnIncoming()作为参数传入\n\nparserOnIncoming()在哪里被调用?\n```js\n// _http_server.js 345行\nfunction connectionListener(socket) {\n    ...\n    var parser = parsers.alloc();\n    parser.onIncoming = parserOnIncoming.bind(undefined, this, socket, state);\n    ...\n}\n```\n\nparsers在_http_common.js抛出\nonIncoming在skipBody = parser.onIncoming(parser.incoming, shouldKeepAlive)中调用\n```\n function parserOnHeadersComplete(...) {\n     ...\n     //IncomingMessage的实例并将套接字作为参数传入 ,来自_http_common.js模块\n     parser.incoming = new IncomingMessage(parser.socket);\n     parser.incoming.httpVersionMajor = versionMajor;\n     parser.incoming.httpVersionMinor = versionMinor;\n     parser.incoming.httpVersion = `${versionMajor}.${versionMinor}`;\n     parser.incoming.url = url;\n     ...\n     //onIncoming 这里被调用 parser.incoming相当于req\n     skipBody = parser.onIncoming(parser.incoming, shouldKeepAlive);\n     ...\n }\n ```\n 流程图演示：\n![image](images/node-server-req.png)\n\n\n#### Listen 过程\n基于ner.js模块\nServer Connection事件在net.Server.call(this, { allowHalfOpen: true })触发\n\nconnection会在onconnection中触发handle\n```js\nfunction onconnection(err, clientHandle) {\n  var handle = this;\n  var self = handle.owner;\n\n  debug('onconnection');\n\n  if (err) {\n    self.emit('error', errnoException(err, 'accept'));\n    return;\n  }\n\n  if (self.maxConnections && self._connections >= self.maxConnections) {\n    clientHandle.close();\n    return;\n  }\n\n  var socket = new Socket({\n    handle: clientHandle,\n    allowHalfOpen: self.allowHalfOpen,\n    pauseOnCreate: self.pauseOnConnect\n  });  \n  socket.readable = socket.writable = true;\n\n\n  self._connections++;\n  socket.server = self;\n  socket._server = self;\n\n  DTRACE_NET_SERVER_CONNECTION(socket);\n  LTTNG_NET_SERVER_CONNECTION(socket);\n  COUNTER_NET_SERVER_CONNECTION(socket);\n  \n  self.emit('connection', socket);\n}\n```\n\nlisten2调用setupListenHandle方法，注册onconnection\n```js\nfunction setupListenHandle(address, port, addressType, backlog, fd) {\n    ...\n    this._handle.onconnection = onconnection\n    ...\n}\n```\n_listen2注册handle, 在listen里被调用\n```js\nServer.prototype._listen2 = setupListenHandle;\nserver._listen2(address, port, addressType, backlog, fd);\n```\n\nlisten在Server原型上，所以在代码里的http.createServer()实例上有listen()方法\n```js\nServer.prototype.listen = function(...args) {\n    ...\n    if (options instanceof TCP) {\n      this._handle = options;\n      this[async_id_symbol] = this._handle.getAsyncId();\n      listenInCluster(this, null, -1, -1, backlogFromArgs);\n      return this;\n    }\n    ...\n```\n\n```js\nSocket.prototype.listen = function() {\n  debug('socket.listen');\n  this.on('connection', arguments[0]);\n  listenInCluster(this, null, null, null);\n};\n\n```\nListen流程图：\n![image](images/node-server-listen.png)\n\n```\n参考链接：\nhttps://yjhjstz.gitbooks.io/deep-into-node/chapter10/chapter10-1.html\nhttps://www.cnblogs.com/chyingp/p/node-learning-guide-http.html\nhttp://blog.csdn.net/sinat_22996989/article/details/51496010\n```\n"
  },
  {
    "path": "chapter1/chapter1-2.md",
    "content": "### Node Stream模块分析\n\n\nStream在平时业务开发时很少用到， 但是很多模块都是基于stream实现的，引用官方文档的解释：\n\n流（stream）在 Node.js 中是处理流数据的抽象接口（abstract interface）。\n\nstream 模块提供了基础的 API 。使用这些 API 可以很容易地来构建实现流接口的对象。\n\n流可以是可读的、可写的，或是可读写的。所有的流都是 EventEmitter 的实例。\n\n\n\n#### 为什么应该使用Stream\n\n先来看一段代码:\n\n这段代码有什么问题， 看似是没有问题的。\n\n如果data.txt文件体积非常大，nodejs读入内存当中，然后全部取出\n\n这样会对性能造成很大影响\n\n```js\nvar http = require('http');\nvar fs = require('fs');\n\nvar server = http.createServer(function (req, res) {\n    fs.readFile(__dirname + '/data.txt', function (err, data) {\n        res.end(data);\n    });\n});\nserver.listen(8000);\n```\n\n\n经过优化后代码如下:\n\n这段将data.txt一段一段的发送到用户端\n\n这样减少了很多的服务器压力\n\n```js\nvar http = require('http');\nvar fs = require('fs');\n\nvar server = http.createServer(function (req, res) {\n    let stream = fs.createReadStream(__dirname + '/data.txt');//创造可读流\n    stream.pipe(res);//将可读流写入response\n});\nserver.listen(8000);\n```\n\n\n### 管道流Pipe\n管道提供了一个输出流到输入流的机制, 从获取到数据传入另外一个流\n无论哪一种流，都会使用.pipe()方法来实现输入和输出。\n\n读取input.txt文件流 \n\nhello World \n\n```js\nvar fs = require(\"fs\");\n\n// 创建一个可读流\nvar readerStream = fs.createReadStream('input.txt');\n\n// 创建一个可写流\nvar writerStream = fs.createWriteStream('output.txt');\n\n// 管道读写操作\n// 读取 input.txt 文件内容，并将内容写入到 output.txt 文件中\nreaderStream.pipe(writerStream);\n\nconsole.log(\"程序执行完毕\");\n```\n查看输出文件流output.txt\n\nhello World\n\n\n\n#### 如果多文件还可进行链式操作：\n\n代码如下：\n```js\na.pipe(b);\nb.pipe(c);\nc.pipe(d);\n\n//上面代码等价于这样\na.pipe(b).pipe(c).pipe(d)\n```\n\n### readable 可读操作\nReadable流可以产出数据流，你可以将这些数据传送到一个writable，transform， duplex，并调用pipe()方法:\n\n```js\nvar Readable = require('stream').Readable;\n\nvar rs = new Readable;\nrs.push('beep ');\nrs.push('boop\\n');\nrs.push(null);\n\nrs.pipe(process.stdout); //输出： beep boop\n```\n在上面的代码中rs.push(null)的作用是告诉rs输出数据应该结束了。\n\n\n```js\nvar Readable = require('stream').Readable;\nvar rs = Readable();\n\nvar c = 97;\nrs._read = function () {\n    rs.push(String.fromCharCode(c++));\n    if (c > 'z'.charCodeAt(0)) rs.push(null);\n};\n\nrs.pipe(process.stdout);//输出 abcdefghijklmnopqrstuvwxyz\n```\n\n还可以通过监听事件readable，触发时手工读取chunk数据:\n\n一旦注册了readable事件，必须手工读取read数据，否则数据就会流失\n\n```js\nvar Read = require('stream').Readable;\nvar r = new Read();\n\nr.push('hello');\nr.push('world');\nr.push(null);\n\nr.on('readable', function () {\n    var chunk = r.read();\n    console.log('get data by readable event: ', chunk.toString())\n});\n\n// get data by readable event:  hello world!\n```\n#### 注意：process.stdout之前已经将内容推送进readable流rs中，但是所有的数据依然是可写的\n\n### Readable Stream的模式\n\nReadable Stream 存在两种模式(flowing mode 与 paused mode)，\n这两种模式决定了chunk数据流动的方式---自动流动还是手工流动。那如何触发这两种模式呢：\n\nflowing mode: 注册事件data、调用resume方法、调用pipe方法\n\npaused mode: 调用pause方法(没有pipe方法)、移除data事件 && unpipe所有pipe\n```js\n// data事件触发flowing mode\nReadable.prototype.on = function(ev, fn) {\n    ...\n    if (ev === 'data' && false !== this._readableState.flowing) {\n        this.resume();\n      }\n      ...\n}\n\n// resume触发flowing mode\nReadable.prototype.resume = function() {\n    var state = this._readableState;\n    if (!state.flowing) {\n           debug('resume');\n           state.flowing = true;\n    resume(this, state);\n  }\n  return this;\n}\n\n// pipe方法触发flowing模式\nReadable.prototype.resume = function() {\n    if (!state.flowing) {\n        this.resume()\n    }\n}\n```\n结论\n\n两种方式取决于一个flowing字段：true --> flowing mode；false --> paused mode\n\n三种方式最后均是通过resume方法，将state.flowing = true\n\n```\n参考资料：\nhttps://yjhjstz.gitbooks.io/deep-into-node/chapter8/chapter8-1.html\nhttp://www.runoob.com/nodejs/nodejs-stream.html\nhttps://nodejs.org/api/stream.html\n```\n"
  }
]