Skip to content

Node 洋葱模型 #58

Open
Open
@myLightLin

Description

@myLightLin

什么是洋葱模型

image

洋葱模型就是说,从表皮切入,先从外至内一层层穿过,直到中心层;然后再从内至外,一层层穿出去。进来的时候穿过多少层,出去的时候就相应穿出多少层。这个过程有点类似栈的数据结构,它符合「先进后出」的特点。

中间件

洋葱模型在 Node 的体现就是中间件,其中的关键就是 next 函数,以热门框架 Express 为例:

const express = require('express')
const app = express()

app.use((req, res, next) => {
  console.log('enter1')
  next()
  console.log('exit1')
})

app.use((req, res, next) => {
  console.log('enter2')
  next()
  console.log('exit2')
})

app.use((req, res, next) => {
  console.log('enter3')
  next()
  console.log('exit3')
})

上面这段代码执行,输出结果的顺序如下:

enter1
enter2
enter3
exit3
exit2
exit1

以上就是中间件的大致执行过程。

express 中间件的原理

要理解中间件的原理,就是要知道 app.use 执行后发生了什么。app.use 源码存放于 express/lib/application.js 里,进到这个文件后,全局搜索 app.use 就可以找到函数,这里摘取一部分源码:
image

当调用 app.use 时,进行一系列处理后,最终调用的是 router.use 。而 router.use 存放于 express/lib/router 目录下,核心代码如下:
image

这里面做的东西就是,遍历传入的中间件函数 fn,然后创建 layer 对象,将它 push 到栈里面。那下面我们就要去找,这个函数是在哪里,什么时候从栈里面弹出来执行的,这个链路是这样子:

请求进来 --> app.handler --> router.handler -->  proto.handle

express/lib/router/index.js 里找到 proto.handle 函数,它的核心代码如下:
image

这里面调用了 layer. handle_request 进行处理,这个函数的源码如下:
image

可以看到,里面就是 try...catch 执行了 fn 函数,然后把中间件函数 next 作为 fn 的第三个参数传递下去,这就是 express 中间件的执行过程。

koa 中间件的原理

koa 中间件的核心就是 koa-compose 模块中的这段代码:

return function (context, next) {
    // last called middleware #
    let index = -1
    return dispatch(0)
    function dispatch (i) {
      if (i <= index) return Promise.reject(new Error('next() called multiple times'))
      index = i
      let fn = middleware[i]
      if (i === middleware.length) fn = next
      if (!fn) return Promise.resolve()
      try {
        return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
      } catch (err) {
        return Promise.reject(err)
      }
    }
  }

核心就是从第 0 个 middleware 函数开始,然后利用递归的特性,先深入各个中间件函数执行,然后在「归」回来的时候,接着执行剩余代码。

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions