前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >浅析洋葱模型

浅析洋葱模型

作者头像
前端进击者
发布2021-07-27 14:36:28
9350
发布2021-07-27 14:36:28
举报
文章被收录于专栏:前端有的玩前端有的玩

如果你愿意一层一层 一层地剥开我的心 你会发现 你会讶异 你是我 最压抑 最深处的秘密 如果你愿意一层一层 一层地剥开我的心 你会鼻酸 你会流泪 只要你能 听到我 看到我的全心全意

前言

使用过koa的小伙伴们都应该对洋葱模型有所了解,koa的独特的中间件流程控制就是通过洋葱模型来实现的,那么洋葱模型是什么,又是如何实现的呢?

洋葱模型介绍

介绍

上图是洋葱模型比较经典的一个图,通过这个图可以看到,洋葱模型就像一个洋葱一个,是分成多层的,而一个请求进入的时候,会从外到内依次经过每一层,到最内侧之后又从内到外依次经过每一次,而我们就可以在这每一层上面做自己需要做的操作。

比如一个请求,在koa接收到请求的时候,首先需要鉴权,然后需要对请求参数解析等等,完成请求之后,需要处理异常,添加请求头等等操作,而这些操作就可以放到洋葱模型的每一层上面做处理。

示例代码

如下代码为koa的一段示例代码:

const Koa = require('koa');
const app = new Koa();

app.use(async (ctx, next) => {
  console.log(1);
  await next();
  console.log(2);
});

app.use(async (ctx, next) => {
  console.log(3);
  await next();
  console.log(4);
});
app.use(async (ctx, next) => {
  console.log(5);
  await next();
  console.log(6);
});

app.listen(8000);

执行上面的代码,会发现输出的数字顺序为1,3,5,6,4,2,与上面介绍的洋葱模型的执行顺序是一致的。

洋葱模型实现

首先我们先分析一下上面的代码app.use传入了一个异步的函数,而且app.use可以被使用多次,而这些函数在请求进入的时候会依次被调用,这是不是与发布订阅者模式是一致的,那首先我们来实现一个app.use

实现一个app.use

export interface Middleware {
  (...rest: any): Promise<any>;
}

export default class Onion {
  middlewares: Middleware[] = [];
  constructor(middlewares: Middleware[] = []) {
    this.middlewares = middlewares;
  }

  use(middleware: Middleware) {
    this.middlewares.push(middleware);
  }
}

上面代码我们定义了一个Onion类,通过这个类我们就可以将订阅函数进行收集,如下代码所示

const onion = new Onion()
onion.use(async(params, next)=> {
   console.log(1)
   await next()
   console.log(2)
})

发布者在收集完订阅函数后需要有触发的时机,这时候就需要再给Onion添加一个执行函数

完善Onion

有小伙伴想到发布订阅者的实现代码,可能就会想到这样做:

export default class Onion {
  middlewares: Middleware[] = [];
  constructor(middlewares: Middleware[] = []) {
    this.middlewares = middlewares;
  }

  use(middleware: Middleware) {
    this.middlewares.push(middleware);
  }

  execute(params: any) {
    this.middlewares.forEach(fn => {
          fn(params)
       })
  }
}

但是这样做的话,就无法满足洋葱模型的先入后出的顺序,那我们应该怎么做呢?

1.

定义 compose函数

function compose(middlewares: Array<Middleware>) {
if (!Array.isArray(middlewares)) {
 throw new Error('中间件必须是数组');
}
for (let i = 0; i < middlewares.length; i++) {
 if (typeof middlewares[i] !== 'function') {
   throw new Error('中间件的每一项都必须是函数');
 }
}

return (params: any) => {
 let index = 0;
 function dispatch(fn: Middleware | undefined) {
   if (!fn) {
     return Promise.resolve();
   }
   const next = () => dispatch(middlewares[++index]);
   return Promise.resolve(fn(params, next));
 }
 return dispatch(middlewares[index]);
};
}

2.

实现execute

export default class Onion {
middlewares: Middleware[] = [];
constructor(middlewares: Middleware[] = []) {
 this.middlewares = middlewares;
}

use(middleware: Middleware) {
 this.middlewares.push(middleware);
}

execute(params: any) {
 const fn = compose(this.middlewares);
 return fn(params);
}
}

通过定义componse函数,可以将中间件函数依次按照顺序来执行。

const onion = new Onion();
onion.use(async (params: any, next: Middleware) => {
 console.log(1);
 await next();
 console.log(2);
});

onion.use(async (params: any, next: Middleware) => {
 console.log(3);
 await next();
 console.log(4);
});

onion.use(async (params: any, next: Middleware) => {
 console.log(5);
 await next();
 console.log(6);
});

onion.execute({});

这样我们就实现了一个简易版的洋葱模型。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-06-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端有的玩 微信公众号,前往查看

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

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

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