前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 ># 中间件引擎

# 中间件引擎

作者头像
九旬
发布2023-10-19 17:24:59
1860
发布2023-10-19 17:24:59
举报
文章被收录于专栏:九旬大爷九旬大爷

# 中间件引擎

# 前言

在使用Koa.js过程中,会发现中间件的使用都是这样子的,如以下代码所示。

代码语言:javascript
复制
const Koa = require('koa');
let app = new Koa();

const middleware1 = async (ctx, next) => { 
  console.log(1); 
  await next();  
  console.log(6);   
}

const middleware2 = async (ctx, next) => { 
  console.log(2); 
  await next();  
  console.log(5);   
}

const middleware3 = async (ctx, next) => { 
  console.log(3); 
  await next();  
  console.log(4);   
}

app.use(middleware1);
app.use(middleware2);
app.use(middleware3);
app.use(async(ctx, next) => {
  ctx.body = 'hello world'
})

app.listen(3001)

// 启动访问浏览器
// 控制台会出现以下结果
// 1
// 2
// 3
// 4
// 5
// 6

为什么会出现以上的结果, 这个主要是Koa.js的一个中间件引擎 koa-compose模块来实现的,也就是Koa.js实现洋葱模型的核心引擎。

# 中间件原理

洋葱模型可以看出,中间件的在 await next() 前后的操作,很像数据结构的一种场景——“栈”,先进后出。同时,又有统一上下文管理操作数据。综上所述,可以总结出一下特性。

  • 有统一 context
  • 操作先进后出
  • 有控制先进后出的机制 next
  • 有提前结束机制

这样子我们可以单纯用 Promise 做个简单的实现如下

代码语言:javascript
复制
let context = {
  data: []
};

async function middleware1(ctx, next) {
  console.log('action 001');
  ctx.data.push(1);
  await next();
  console.log('action 006');
  ctx.data.push(6);
}

async function middleware2(ctx, next) {
  console.log('action 002');
  ctx.data.push(2);
  await next();
  console.log('action 005');
  ctx.data.push(5);
}

async function middleware3(ctx, next) {
  console.log('action 003');
  ctx.data.push(3);
  await next();
  console.log('action 004');
  ctx.data.push(4);
}

Promise.resolve(middleware1(context, async() => {
  return Promise.resolve(middleware2(context, async() => {
    return Promise.resolve(middleware3(context, async() => {
      return Promise.resolve();
    }));
  }));
}))
  .then(() => {
    console.log('end');
    console.log('context = ', context);
  });

// 结果显示
// "action 001"
// "action 002"
// "action 003"
// "action 004"
// "action 005"
// "action 006"
// "end"
// "context = { data: [1, 2, 3, 4, 5, 6]}"

# 引擎实现

通过上一节中的中间件原理,可以看出,单纯用Promise 嵌套可以直接实现中间件流程。虽然可以实现,但是Promise嵌套会产生代码的可读性和可维护性的问题,也带来了中间件扩展问题。

所以需要把Promise 嵌套实现的中间件方式进行高度抽象,达到可以自定义中间件的层数。这时候需要借助前面几章提到的处理 Promise嵌套的神器async/await

我们先理清楚需要的步骤

  • 中间件队列
  • 处理中间件队列,并将上下文context传进去
  • 中间件的流程控制器next
  • 异常处理

根据上一节分析中间的原理,我们可以抽象出

  • 每一个中间件需要封装一个 Promise
  • 洋葱模型的先进后出操作,对应Promise.resolve的前后操作

源码地址 https://github.com/chenshenhai/koajs-design-note/tree/master/demo/chapter-01-05open in new window

代码语言:javascript
复制
function compose(middleware) {

  if (!Array.isArray(middleware)) {
    throw new TypeError('Middleware stack must be an array!');
  }

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

试用中间件引擎

代码语言:javascript
复制
let middleware = [];
let context = {
  data: []
};

middleware.push(async(ctx, next) => {
  console.log('action 001');
  ctx.data.push(2);
  await next();
  console.log('action 006');
  ctx.data.push(5);
});

middleware.push(async(ctx, next) => {
  console.log('action 002');
  ctx.data.push(2);
  await next();
  console.log('action 005');
  ctx.data.push(5);
});

middleware.push(async(ctx, next) => {
  console.log('action 003');
  ctx.data.push(2);
  await next();
  console.log('action 004');
  ctx.data.push(5);
});

const fn = compose(middleware);

fn(context)
  .then(() => {
    console.log('end');
    console.log('context = ', context);
  });
  
// 结果显示
// "action 001"
// "action 002"
// "action 003"
// "action 004"
// "action 005"
// "action 006"
// "end"
// "context = { data: [1, 2, 3, 4, 5, 6]}"
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • # 中间件引擎
    • # 前言
      • # 中间件原理
      相关产品与服务
      消息队列 TDMQ
      消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档