golang – middleware设计模式

在开发框架中应用了大量的设计模式,其中middleware中间件模式就是一种常见的实用模式。

下面通过一个简短的例子,给大家分享一下如何实现中间件模式。

模拟请求

做web开发肯定需要与请求打交道,所以这里设计一个request结构。

从代码可以看出,中间件模式实现时就是N个回调函数组成的数组,当一个request到来之后需要依次经过每一个中间件函数的加工与处理,最终request才到达用户编写的业务处理函数。

大家应该会问,业务处理函数保存在哪个字段呢?其实业务处理函数也是中间件,只不过它放在middlewares数组的末尾,最后才会被调用。

所谓的”中间”件,意思就是这些回调函数夹在入口和业务处理函数之间被调用,代表了执行的位置。

创建新请求

我们没有web环境,所以提供一个方法可以模拟创建了一个新请求:

这个请求还没注册中间件函数和业务处理函数,middlewares数组为空。

index字段的作用稍后解释。

注册中间件到请求

请求到来后,需要依次经过中间件的处理,因此我们接下来提供1个方法注册中间件到request上下文中。

下面的函数将注册中间件函数到middlewares数组,注册的顺序就是最终中间件被执行的顺序。

执行请求处理

中间件注册完成后,我们就要执行请求处理了。

request会依次经过middlewares数组中各个回调函数的处理,但是注意中间件模式和插件模式是完全不同的,下面我们看看中间件模式具体是怎么实现的。

这里我们终于看到了request.index字段的用处。

中间件模式是递归的,这是与插件模式的重要区别。

第一个中间件函数的实现中,需要递归的调用request.Next唤起下一个中间件函数,如此不断的递归向后,直到request.index到达request.middlewares的末尾,则递归终止。

递归的好处就是靠前的中间件是包裹着靠后的中间件的,这种自顶向下的结构可以实现非常灵活的框架设计。(这里我想补充一张图片,会更加直观)

实际测试

我的测试主程序如下:

我给request注册了2个中间件:

  • Logger:用于打印请求日志
  • Recovery:用于捕获业务代码中抛出的panic异常,避免程序崩溃

在最后是一个业务处理函数,里面实现业务逻辑。

中间件的执行关系是自顶向下的,递归包含的:

那么我们看看Logger干了什么:

它排在request.middlewares数组首位,首先被request.Next()唤起。

它在调用下一个middleware之前打印了一行日志,在下一个middleware返回之后打印了另外一行日志。

然后看看Recovery干了什么:

在调用下一个中间件之前,首先defer注册了一个延迟函数。

也就是说即便request.Next()执行的后续middlewares抛出了panic异常,defer函数也一定在离开Recovery函数前执行,从而可以通过runtime的recover函数捕获panic异常,避免panic异常继续向上抛出。

因此Logger中间件中是不可能遇到panic异常的,因为已经被下层的Recovery捕获了。

最后

完整代码我放在了这里:https://github.com/owenliang/go-middlewares

如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~