koa洋葱模型

一. 什么是洋葱模型?
先看🌰

const Koa = require('koa');

const app = new Koa();
const PORT = 3000;

// #1
app.use(async (ctx, next)=>{
    console.log(1)
    await next();  //暂停当前程序
    console.log(1)
});
// #2
app.use(async (ctx, next) => {
    console.log(2)
    await next();
    console.log(2)
})

app.use(async (ctx, next) => {
    console.log(3)
})

app.listen(PORT);
console.log(`http://localhost:${PORT}`);


// 输出
1
2
3
2
1

在 koa 中,app.use注册的中间件的回调按照洋葱模型迭代的。中间件被 next()方法分成了两部分。next()方法上面部分会先执行。next()方法下面的部分会在后面的中间件执行全部结束后执行。结合下图可以直观的看出:
请添加图片描述

洋葱内的每一层都表示一个独立的中间件,用于实现不同的功能,比如异常处理、缓存处理等。每次请求都会从左侧开始一层层地经过每层的中间件,当进入到最里层的中间件之后,就会从最里层的中间件开始逐层返回。因此对于每层的中间件来说,在一个 请求和响应 周期中,都有两个时机点来添加不同的处理逻辑。
中间件执行顺序:
在这里插入图片描述

洋葱模型可以干什么?
比如计算一次请求的耗时,且获取其他中间件的信息,如果没有洋葱模型是没有办法实现的。

二. koa原理
深入学习koa原理,通过查阅koa源码,我们看到有下面这些方法。

  1. use方法:维护得到 middleware 中间件数组
use (fn) {
    if (typeof fn !== 'function') throw new TypeError('middleware must be a function!')
    debug('use %s', fn._name || fn.name || '-')
    this.middleware.push(fn)
    return this
}
  1. listen方法:Node.js 原生 http 模块 createServer 方法创建了一个服务。
  2. callback方法:服务的回调就是callback方法,返回是一个Promise函数。
listen (...args) {
    debug('listen')
    // 创建一个服务
    const server = http.createServer(this.callback())
    return server.listen(...args)
}

callback () {
    // 返回一个函数
    const fn = compose(this.middleware)

    if (!this.listenerCount('error')) this.on('error', this.onerror)

    const handleRequest = (req, res) => {
      // 创建 ctx 上下文环境
      const ctx = this.createContext(req, res)
      return this.handleRequest(ctx, fn)
    }
    // 执行 compose 函数中返回的 Promise 函数并返回结果。
    return handleRequest
}

compose来自koa-compose模块,方法中的dispatch遍历整个middleware,然后将context和dispatch(i + 1)传给middleware中的方法。
主要目的【洋葱模型的核心】:
1). 将context一路传下去给中间件
2). 将middleware中的下一个中间件fn作为未来next的返回值

function compose(middleware) {
  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, function next() {
          return dispatch(i + 1)
        }))
      } catch (err) {
        return Promise.reject(err)
      }
    }
  }

在看createContext方法和handleRequest方法,是把ctx和中间件进行绑定

handleRequest (ctx, fnMiddleware) {
    const res = ctx.res
    res.statusCode = 404
    const onerror = err => ctx.onerror(err)
    const handleResponse = () => respond(ctx)
    onFinished(res, onerror)
    return fnMiddleware(ctx).then(handleResponse).catch(onerror)
}

createContext (req, res) {
    const context = Object.create(this.context)
    const request = context.request = Object.create(this.request)
    const response = context.response = Object.create(this.response)
    context.app = request.app = response.app = this
    context.req = request.req = response.req = req
    context.res = request.res = response.res = res
    request.ctx = response.ctx = context
    request.response = response
    response.request = request
    context.originalUrl = request.originalUrl = req.url
    context.state = {}
    return context
}

三. 参考资料

  1. https://juejin.cn/post/7012031464237694983
  2. https://juejin.cn/post/7046713578547576863
  3. https://juejin.cn/post/6890259747866411022
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值