golang 中的中间件技术
golang中很多网络相关的库都使用到了一种 middleware
的编程技巧,包括 rpc 和 http。但是这种技巧刚接触很容易搞不清楚概念,在这里记录一下我的理解。
以rpc框架 kite 为例 github地址
kite 作为一个rpc框架,提供了 middleware 的接口,保证多个 rpc 请求可以进行共同的一些配置,包括 超时,LB,日志记录等等。具体代码如下:
// EndPoint represent one method for calling from remote.
type EndPoint func(ctx context.Context, req interface{}) (resp interface{}, err error)
// Middleware deal with input EndPoint and output EndPoint
type Middleware func(EndPoint) EndPoint
// Chain connect middlewares into one middleware.
func Chain(outer Middleware, others ...Middleware) Middleware {
return func(next EndPoint) EndPoint {
for i := len(others) - 1; i >= 0; i-- {
next = others[i](next)
}
return outer(next)
}
}
然后是我自己写的一个例子:
func MW1(next EndPoint) EndPoint {
return func(ctx context.Context, req interface{}) (resp interface{}, err error) {
ctx, cancel := context.WithTimeout(ctx, time.Second)
fmt.Printf("Enter MW1\n")
defer cancel()
return next(ctx, req)
}
}
func MW2(next EndPoint) EndPoint {
return func(ctx context.Context, req interface{}) (resp interface{}, err error) {
fmt.Printf("Enter MW2\n")
return next(ctx, req)
}
}
func real(ctx context.Context, req interface{}) (resp interface{}, err error) {
fmt.Printf("Enter real. req: %v\n", req)
return 1, nil
}
func main() {
var mw = Chain(MW1, MW2)
fmt.Printf("finish calling chain\n")
var resp, err = mw(real)(context.Background(), "hello")
fmt.Printf("resp: %v error: %v\n", resp, err)
}
最终的打印结果:
finish calling chain
Enter other 0: 0x10a0040
Enter MW1
Enter MW2
Enter real. req: hello
resp: 1 error: <nil>
这里的 EndPoint 和 Middleware 可以这么理解:
EndPoint 是一个处理单元,而 Middleware 可以理解为对一串处理单元的包装,最终适配另一个 EndPoint。如图:
其中 Middleware1 就是蓝色的,它包含了两个 EndPoint,Middleware2 是绿色的,它包含了后面两个 EndPoint,Middleware3 是黄色的,它包含了 Middleware1 和 Middleware2 两个合起来的处理逻辑,也就是说可以通过调用 Chain(Middleware1, Middleware2)
得到 Middleware3。
千万不能将 EndPoint 之间的线认为是 Middleware,因为一个 Middleware 是包含了多个 EndPoint 的。同时,从编码的角度来说,一般中间节点的 EndPoint 逻辑都会写在 Middleware 的匿名函数里面。
最终调用一个中间件时,需要传入一个“叶子”EndPoint,也就是上图中的 EndPoint n,这个EndPoint一般包含了真正的处理逻辑。