首頁 > 軟體

一文詳解Golang的中介軟體設計模式

2023-03-07 06:01:40

背景

記錄一下自己在go開發和學習上的一些筆記

最近在看一些rpc框架的使用原理和原始碼的時候,對中介軟體的實現非常感興趣,然後也看了一下grpc的中介軟體的用法,也看了別的框架的中介軟體的設計,感覺grpc的還算是比較容易弄懂,於是記錄一下這個常用中介軟體的實現的一個原理的demo(吐槽一下其他的rpc框架分為inbound和outbound的middleware感覺好像有點複雜化了,所以我也不知道哪種設計會比較好,樓主是java出身,所以對反射走aop的那種模式比較熟悉,對鏈式呼叫的middleware有點不太熟悉,當然現在已經熟悉了)

Demo

所以接下來我們就來看看demo吧 首先先定義好中介軟體的型別,這裡我就簡單定義為以下的格式

type middleware func(ctx context.Context, req interface{}, handler endpoint) (resp interface{}, err error)
  • ctx: 協程間通訊帶著
  • req: 請求的格式,這裡圖簡便,直接interface{}型別
  • resp: 同req
  • err: error

handler: endpoint型別,真正用來發起請求的一個處理方法或者是經過N層中介軟體包裝的後的發起請求的處理方法

type endpoint func(ctx context.Context, req interface{}) (resp interface{}, err error)
//ctx: 協程間通訊帶著 

//req: 請求的格式,這裡圖簡便,直接interface{}型別
//resp: 同req  
//err: error

然後既然我們要將上方的endpoint進行包裝然後產生一個新的endpoint那麼也就是需要一個函數去做一步的事情,input是endpoint,ouput也是endpoint

type warp func(endpoint) endpoint //就是這個warp函數

然後我們通過每次呼叫這個warp的定義去生成一個新的endpoint就可以產生一個類似於dfs鏈式呼叫的一箇中介軟體的過程,因為將會一層套一層的endpoint下去,然後當最後一層有返回了以後就可以接著返回了,然後不斷的彈棧回去最開始的地方,因為我們中介軟體的實現必然是要呼叫handler的

func(ctx context.Context, req interface{}, handler endpoint) (resp interface{}, err error) {
		fmt.Printf("before1n")
		resp, err = handler(ctx, req)
		fmt.Printf("end1n")
		return
}

// handler0
var handler endpoint = func(ctx context.Context, req interface{}) (resp interface{}, err error) {
	fmt.Printf("make msgn")
	return nil, nil
} 
// middleware
var md Middleware = func(ctx context.Context, req interface{}, handler endpoint) (resp interface{}, err error) {
		fmt.Printf("before1n")
		resp, err = handler(ctx, req)
		fmt.Printf("end1n")
		return
}
// warp ->> handler1
handler = warp(func(e endpoint) endpoint {
		return func(ctx context.Context, req interface{}) (resp interface{}, err error) {
				return md(ctx, req, e)
			}
		})(handler)

驗證結論

package main

import (
	"context"
	"fmt"
)

type endpoint func(ctx context.Context, req interface{}) (resp interface{}, err error)

type middleware func(ctx context.Context, req interface{}, handler endpoint) (resp interface{}, err error)

type warp func(endpoint) endpoint

func main() {
	mds := []middleware{}
	mds = append(mds, func(ctx context.Context, req interface{}, handler endpoint) (resp interface{}, err error) {
		fmt.Printf("before1n")
		resp, err = handler(ctx, req)
		fmt.Printf("end1n")
		return
	})
	mds = append(mds, func(ctx context.Context, req interface{}, handler endpoint) (resp interface{}, err error) {
		fmt.Printf("before2n")
		resp, err = handler(ctx, req)
		fmt.Printf("end2n")
		return
	})
	var handler endpoint = func(ctx context.Context, req interface{}) (resp interface{}, err error) {
		fmt.Printf("make msgn")
		return nil, nil
	}
	for i := len(mds) - 1; i >= 0; i-- {
		handler = warp(func(e endpoint) endpoint {
			// 由於go的機制問題如果不用tmp去存下當前的i,那麼mds[i]就會取最終的那一個,就會溢位,所以在return前先儲存一下i的量,然後每一個stack去存的變數就是對的
			cur := i
			return func(ctx context.Context, req interface{}) (resp interface{}, err error) {
				return mds[cur](ctx, req, e)
			}
		})(handler)
	}
	resp, err := handler(context.Background(), "ster")
	if resp != nil && err != nil {
		return
	}
}

結果是

before1
before2
make msg
end2
end1

結論:感覺有函數指標的語言都可以用這一套去實現一箇中介軟體

以上就是一文詳解Golang的中介軟體設計模式的詳細內容,更多關於Golang中介軟體設計模式的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com