1. 程式人生 > 實用技巧 >golang 中的中介軟體技術

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一般包含了真正的處理邏輯。