四种Golang实现middleware框架的方式小结

2024-04-18 0 1,071
目录
  • 写在前面
  • 方案一:数组递归调用
  • 方案二:顺序实现
  • 方式三:链式调用
  • 方案四:for循环实现
  • 总结

写在前面

middleware是一般框架里面常用的形式,比如web框架、rpc框架,通过middleware在流量入口和出口做一些公共事情,包括鉴权、日志、埋点、统计、限流、参数处理、异常处理等等。

在工作中经常会用到,在阅读web框架(gin,beego)的时候也会遇到,今天总结一下middleware有哪些实现方式。

方案一:数组递归调用

package middleware

import \”context\”

// 处理函数
type Handler func(ctx context.Context,
msg string) error

// 插件类型
type MiddleWareFunc func(ctx context.Context,
msg string, next Handler) error

type MiddlewareManager struct {
handler Handler
middlewares []MiddleWareFunc
}

func NewMiddlewareManager(handler Handler) *MiddlewareManager {
return &MiddlewareManager{
handler: handler,
}
}

func (m *MiddlewareManager) Register(middlewares …MiddleWareFunc) {
m.middlewares = append(m.middlewares, middlewares…)
}

func (m *MiddlewareManager) Exec(ctx context.Context, msg string) error {
handlerFunc := func(ctx context.Context, msg string, next Handler) error {
return m.handler(ctx, msg)
}
m.middlewares = append(m.middlewares, handlerFunc)

callChain := m.mkCallChain(m.middlewares)
return callChain(ctx, msg)
}

func (m *MiddlewareManager) mkCallChain(
middlewares []MiddleWareFunc) Handler {
if len(middlewares) <= 0 {
return nil
}

return func(ctx context.Context, msg string) error {
return middlewares[0](ctx, msg, m.mkCallChain(middlewares[1:]))
}
}

在MiddlewareManager结构体中定义业务处理函数handler和插件数组middlewares,在执行函数Exec里面,将业务处理函数handler封装成一个middleware放到middlewares后面,然后递归调用内部函数mkCallChain。这个内部函数mkCallChain返回的是一个函数,将所有middleware一层一层包裹起来,最终callChain := m.mkCallChain(m.middlewares)得到的是一个调用链。

这段代码有点绕,需要细品。

测试方案一

// 方案一
fmt.Println(\”===方案一 begin\”)
m1 := middleware.NewMiddlewareManager(HandlerMsg)
m1.Register(middleware.TimeCostMW, middleware.FilterMW, middleware.LoggerMW)
if err := m1.Exec(context.Background(), \”hello chain\”); err != nil {
panic(err)
}
fmt.Println(\”===方案一 end\”)

结果

===方案一 beginTimeCost beforeFinlterMW beginLoggerMW beforeHandlerMsg: hello chainLoggerMW endFinlterMW endTimeCostMW:cost 1000428754===方案一 end

方案二:顺序实现

package middlewarecontext

type MiddleWareFunc func(ctx *MyContext) error

type MyContext struct {
middlewares []MiddleWareFunc
idx int
maxIdx int
}

func NewMyContext() *MyContext {
return &MyContext{
middlewares: make([]MiddleWareFunc, 0),
}
}

// 执行下一个middleware
func (m *MyContext) Next() error {
if m.idx < m.maxIdx-1 {
m.idx += 1
return m.middlewares[m.idx](m)
}

return nil
}

// 终止middleware
func (m *MyContext) Abort() {
m.idx = m.maxIdx
}

func (m *MyContext) Register(middlewares …MiddleWareFunc) {
m.middlewares = append(m.middlewares, middlewares…)
m.maxIdx = len(m.middlewares)
}

func (m *MyContext) Exec() error {
// 从第一个middleware开始执行
return m.middlewares[0](m)
}

核心代码是这段

type MyContext struct {
middlewares []MiddleWareFunc
idx int
maxIdx int
}

自己定义一个context将所有middleware作为数组放在context中,执行Exec()的时候就执行第一个middleware,并且将context传进去。其他middlewaer中通过调用Next()函数来触发下一个middleware。

这种方式看起来逻辑简单,容易理解。gin框架的middleware就是这样实现的。这个方式是作者对gin框架的middleware的总结和抽象。

测试方案二

fmt.Println(\”===方案二 begin\”)
m2 := middlewarecontext.NewMyContext()
m2.Register(
middlewarecontext.TimeCostMW,
middlewarecontext.FilterMW,
middlewarecontext.LoggerMW)
if err := m2.Exec(); err != nil {
panic(err)
}
fmt.Println(\”===方案二 end\”)

结果

===方案二 beginTimeCost beforeFinlterMW beginLoggerMW beforeLoggerMW endFinlterMW endTimeCostMW:cost 1000588399===方案二 end

方式三:链式调用

package middlewarechain

import \”context\”

type Handler func(ctx context.Context) error

type MiddleWareFunc func(ctx context.Context, next Handler) Handler

这段代码逻辑很简单,它就是将上一个middleweare作为next参数传到当前middleware,形成链式调用。

看到这个定义你会不会觉得很奇怪,怎么这么点代码?

是的,它的代码就是这么少。有句话说的好“哪有什么岁月静好,不过是有人替你负重前行,生活从来都不容易”,定义的地方代码少了,调用的时候肯定就复杂了。

下面看看测试用例

fmt.Println(\”===方案三 begin\”)
ctx := context.Background()
m3 := middlewarechain.TimeCostMW(ctx, func(ctx context.Context) error {
PrintMsg(\”test\”)
return nil
})
m4 := middlewarechain.FilterMW(ctx, m3)
m5 := middlewarechain.LoggerMW(ctx, m4)
if err := m5(ctx); err != nil {
fmt.Println(err)
}
fmt.Println(\”===方案三 end\”)

结果

===方案三 beginLoggerMW beforeFinlterMW beginTimeCost beforePrintMsg:testTimeCostMW:cost 6130FinlterMW endLoggerMW end===方案三 end

可见,在定义middleweare的时候,要将上一个middleeware传入当前middleeware的定义。跟其他几种方案相比,其实它就是将middleware的注册去掉了,没有地方维护所有的middleware。

方案四:for循环实现

package middlewarefor

import \”context\”

type Handler func(ctx context.Context) error

type Middleware func(next Handler) Handler

type MiddlewareManager struct {
middlewares []Middleware
}

func NewMiddlewareManager(middlewares …Middleware) *MiddlewareManager {
return &MiddlewareManager{
middlewares: middlewares,
}
}

func (m *MiddlewareManager) Register(middlewares …Middleware) {
m.middlewares = append(m.middlewares, middlewares…)
}

func (m *MiddlewareManager) Exec(ctx context.Context) error {
handler := defaultHandler
for i := range m.middlewares {
handler = m.middlewares[len(m.middlewares)-i-1](handler)
}

return handler(ctx)
}

func defaultHandler(ctx context.Context) error {
return nil
}

它跟方案一很像,都是定义一个MiddlewareManager结构体,内部维护一个middlewares数组,在调用Exec的时候,循环执行middlewares

测试方案四

fmt.Println(\”===方案四 begin\”)
ctx = context.Background()
middleware4 := middlewarefor.NewMiddlewareManager(
middlewarefor.RecoveryMW,
middlewarefor.LoggerMW,
middlewarefor.TimeCostMW,
)

middleware4.Exec(ctx)
fmt.Println(\”===方案四 end\”)

结果

===方案四 begin2023/01/15 15:27:09 [RecoveryMW] befor2023/01/15 15:27:09 [LoggerMW] befor2023/01/15 15:27:09 [TimeCostMW] cost:0.000000s2023/01/15 15:27:09 [LoggerMW] end2023/01/15 15:27:09 [RecoveryMW] end===方案四 end

总结

上面四种方案,都能实现middleware,好坏不予评价,你喜欢用哪种方式就用哪种。

本文及github上的代码实现主要是用于学习和总结,如果你想用某种方式到自己的项目中,直接复制过去就行,不建议引用本代码仓库。

github代码仓库:github.com/ZBIGBEAR/middleware

到此这篇关于四种Golang实现middleware框架的方式小结的文章就介绍到这了,更多相关Go middleware框架内容请搜索悠久资源网以前的文章或继续浏览下面的相关文章希望大家以后多多支持悠久资源网!

您可能感兴趣的文章:

  • Django框架之中间件MiddleWare的实现
  • Django框架教程之中间件MiddleWare浅析
  • Django框架中间件(Middleware)用法实例分析
  • Go gRPC服务进阶middleware使用教程
  • Golang中实现简单的Http Middleware

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

悠久资源 Golang 四种Golang实现middleware框架的方式小结 https://www.u-9.cn/jiaoben/golang/187572.html

四种Golang实现middleware框架的方式小结
下一篇:

已经没有下一篇了!

常见问题

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务