理解 Koa 框架中间件原理

Posted 我就是程序员

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了理解 Koa 框架中间件原理相关的知识,希望对你有一定的参考价值。

团队文化:进取,分享,快乐,责任!

团队愿景:做最好的产品,打造有影响力的团队!

一个热爱技术,气氛活跃,开放分享的团队,长期招聘高级java开发工程师,高级前端开发工程师数名,期待你的加入,简历投递:panjian@jd.com

阅读这篇文章,你不需要听过 koa 框架,你甚至不需要使用过 node,你只需简单看懂 js。
相信每一个具有 web 开发经验的人都能轻松理解。

Node 主要用在开发 Web 应用,koa 是目前 node 里最流行的 web 框架。

在 Node 开启一个 http 服务简直易如反掌,官网 demo。

 
   
   
 
  1. const http = require("http");

  2. const server = http.createServer((req, res) => {

  3.  res.statusCode = 200;

  4.  res.setHeader("Content-Type", "text/plain");

  5.  res.end("Hello World ");

  6. });

  7. const hostname = "127.0.0.1";

  8. const port = 3000;

  9. server.listen(port, hostname, () => {

  10.  console.log(`Server running at http://${hostname}:${port}/`);

  11. });

  • 引入 http 模块, http 的 createServer 方法创建了一个 http.Server 的实例。

  • server 监听 3000 端口。

  • 我们传入到 createServer里的函数实际是监听 request事件的回调,每当请求进来,监听函数就会执行。

  • request 事件的监听函数,其函数接受两个参数,分别是 req 和 res。其中 req 是一个可读流, res 是一个可写流。我们通过 req 获取 http 请求的所有信息,同时将数据写入到 res 来对该请求作出响应。

koa 应用

koa 如何创建一个 server, 直接上个官网的例子

 
   
   
 
  1. const Koa = require("koa");

  2. const app = new Koa();

  3. // x-response-time

  4. app.use(async (ctx, next) => {

  5.  const start = Date.now();

  6.  await next();

  7.  const ms = Date.now() - start;

  8.  ctx.set("X-Response-Time", `${ms}ms`);

  9. });

  10. // logger

  11. app.use(async (ctx, next) => {

  12.  const start = Date.now();

  13.  await next();

  14.  const ms = Date.now() - start;

  15.  console.log(`${ctx.method} ${ctx.url} - ${ms}`);

  16. });

  17. // response

  18. app.use(async ctx => {

  19.  ctx.body = "Hello World";

  20. });

  21. app.listen(3000);

中间件概念在编程中使用广泛, 不管是前端还是后端, 在实际编程中或者框架设计都有使用到这种实用的模型。

基本上,Koa 所有的功能都是通过中间件实现的。

每个中间件默认接受两个参数,第一个参数是 Context 对象,第二个参数是 next 函数。只要调用 next 函数,就可以把执行权转交给下一个中间件。

如果中间件内部没有调用 next 函数,那么执行权就不会传递下去。

多个中间件会形成一个栈结构(middle stack),以“先进后出”(first-in-last-out)的顺序执行。整个过程就像,先是入栈,然后出栈的操作。

上面代码的执行顺序是:

 
   
   
 
  1. 请求 ==> x-response-time 中间件 ==> logger 中间件 ==> response中间件 ==> logger 中间件 ==> response-time 中间件 ==> 响应

理解 Koa 的中间件机制(源码分析)

阅读源码,化繁为简,我们看看 koa 的中间件系统是如何实现的。

 
   
   
 
  1. class Application extends Emitter {

  2.  constructor() {

  3.    super();

  4.    this.middleware = [];

  5.  },

  6.  use(fn) {

  7.    this.middleware.push(fn);

  8.    return this;

  9.  },

  10.  callback() {

  11.    const fn = compose(this.middleware);

  12.    return function(req, res) {

  13.      return fn(ctx);

  14.    };

  15.  },

  16.  listen(...args) {

  17.    const server = http.createServer(this.callback());

  18.    return server.listen(...args);

  19.  }

  20. }

好了,精简结束,一不小心,去枝末节,最后只剩下不到 20 行代码。

这就是框架的核心,简化后的代码非常清晰,有点不可思议,但核心就是这么简单。

我们先分析以上代码做了什么事。

(1)我们定义了一个 middleware数组来存储中间件。

(2)我们定一个了一个 use方法来注册一个中间件。其实就是简单的 push 到自身的 mideware 这个数组中。

(3)我们还使用了一个 compose方法,传入 middleware,应该是做了一些处理,返回了一个可执行的方法。

你一定对中间的 compose 方法很好奇,初此之外的代码都容易理解,唯独这个 compose 不太知道究竟做了什么。

其实, compose就是整个中间件框架的核心。

compose之外,代码已经很清楚的定义了

(1)中间件的存储

(2)中间件的注册

compose 方法做了最为重要的一件事

  • 中间件的执行

核心源码 compose

先上码

 
   
   
 
  1. function compose(middleware) {

  2.  return function(context, next) {

  3.    // last called middleware #

  4.    let index = -1;

  5.    return dispatch(0);

  6.    function dispatch(i) {

  7.      if (i <= index)

  8.        return Promise.reject(new Error("next() called multiple times"));

  9.      index = i;

  10.      let fn = middleware[i];

  11.      if (i === middleware.length) fn = next;

  12.      if (!fn) return Promise.resolve();

  13.      try {

  14.        return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));

  15.      } catch (err) {

  16.        return Promise.reject(err);

  17.      }

  18.    }

  19.  };

  20. }

我试图去简化一下这个方法,但方法本身已经足够简洁。

代码很简洁。

通过 next()传递 实现中间件调用, 结合 Promise 采用 递归调用 的通知机制。

看图

理解 Koa 框架中间件原理


种形式的控制流让整个 Koa 框架中间件的访问呈现出 自上而下的中间件流 + 自下而上的response数据流的形式。

Koa 本身做的工作仅仅是定制了中间件的编写规范,而不内置任何中间件。一个 web request 会通过 Koa 的中间件栈,来动态完成 response 的处理。

koa 在中间件语法上面采用了 async+ await 语法来生成 Promise 形式的程序控制流。

总结

koa 是非常精简的框架, 其中的精粹思想就是洋葱模型(中间件模型), koa 框架的中间件模型非常好用并且简洁, 但是也有自身的缺陷, 一旦中间件数组过于庞大, 性能会有所下降,我们需要结合自身的情况与业务场景作出最合适的选择.

理解 Koa 框架中间件原理

好文推荐






以上是关于理解 Koa 框架中间件原理的主要内容,如果未能解决你的问题,请参考以下文章

koa2中间件koa和koa-compose源码分析原理

玩转Koa -- 核心原理分析

深入解析Koa之核心原理

koa入坑及其中间件原理

最新Node.js框架:Koa 2 实用入门

Koa中间件(middleware)级联原理