一文詳解Golang的中間件設(shè)計模式
背景
記錄一下自己在go開發(fā)和學(xué)習(xí)上的一些筆記
最近在看一些rpc框架的使用原理和源碼的時候,對中間件的實(shí)現(xiàn)非常感興趣,然后也看了一下grpc的中間件的用法,也看了別的框架的中間件的設(shè)計,感覺grpc的還算是比較容易弄懂,于是記錄一下這個常用中間件的實(shí)現(xiàn)的一個原理的demo(吐槽一下其他的rpc框架分為inbound和outbound的middleware感覺好像有點(diǎn)復(fù)雜化了,所以我也不知道哪種設(shè)計會比較好,樓主是java出身,所以對反射走aop的那種模式比較熟悉,對鏈?zhǔn)秸{(diào)用的middleware有點(diǎn)不太熟悉,當(dāng)然現(xiàn)在已經(jīng)熟悉了)
Demo
所以接下來我們就來看看demo吧 首先先定義好中間件的類型,這里我就簡單定義為以下的格式
type middleware func(ctx context.Context, req interface{}, handler endpoint) (resp interface{}, err error)
- ctx: 協(xié)程間通信帶著
- req: 請求的格式,這里圖簡便,直接interface{}類型
- resp: 同req
- err: error
handler: endpoint類型,真正用來發(fā)起請求的一個處理方法或者是經(jīng)過N層中間件包裝的后的發(fā)起請求的處理方法
type endpoint func(ctx context.Context, req interface{}) (resp interface{}, err error) //ctx: 協(xié)程間通信帶著 //req: 請求的格式,這里圖簡便,直接interface{}類型 //resp: 同req //err: error
然后既然我們要將上方的endpoint
進(jìn)行包裝然后產(chǎn)生一個新的endpoint
那么也就是需要一個函數(shù)去做一步的事情,input是endpoint
,ouput也是endpoint
type warp func(endpoint) endpoint //就是這個warp函數(shù)
然后我們通過每次調(diào)用這個warp的定義去生成一個新的endpoint
就可以產(chǎn)生一個類似于dfs鏈?zhǔn)秸{(diào)用的一個中間件的過程,因?yàn)閷粚犹滓粚拥?code>endpoint下去,然后當(dāng)最后一層有返回了以后就可以接著返回了,然后不斷的彈?;厝プ铋_始的地方,因?yàn)槲覀冎虚g件的實(shí)現(xiàn)必然是要調(diào)用handler的
func(ctx context.Context, req interface{}, handler endpoint) (resp interface{}, err error) { fmt.Printf("before1\n") resp, err = handler(ctx, req) fmt.Printf("end1\n") return }
// handler0 var handler endpoint = func(ctx context.Context, req interface{}) (resp interface{}, err error) { fmt.Printf("make msg\n") return nil, nil } // middleware var md Middleware = func(ctx context.Context, req interface{}, handler endpoint) (resp interface{}, err error) { fmt.Printf("before1\n") resp, err = handler(ctx, req) fmt.Printf("end1\n") 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)
驗(yàn)證結(jié)論
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("before1\n") resp, err = handler(ctx, req) fmt.Printf("end1\n") return }) mds = append(mds, func(ctx context.Context, req interface{}, handler endpoint) (resp interface{}, err error) { fmt.Printf("before2\n") resp, err = handler(ctx, req) fmt.Printf("end2\n") return }) var handler endpoint = func(ctx context.Context, req interface{}) (resp interface{}, err error) { fmt.Printf("make msg\n") return nil, nil } for i := len(mds) - 1; i >= 0; i-- { handler = warp(func(e endpoint) endpoint { // 由于go的機(jī)制問題如果不用tmp去存下當(dāng)前的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 } }
結(jié)果是
before1
before2
make msg
end2
end1
結(jié)論:感覺有函數(shù)指針的語言都可以用這一套去實(shí)現(xiàn)一個中間件
以上就是一文詳解Golang的中間件設(shè)計模式的詳細(xì)內(nèi)容,更多關(guān)于Golang中間件設(shè)計模式的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
go語言實(shí)現(xiàn)簡單http服務(wù)的方法
這篇文章主要介紹了go語言實(shí)現(xiàn)簡單http服務(wù)的方法,涉及Go語言http操作技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-02-02利用Go語言實(shí)現(xiàn)簡單Ping過程的方法
相信利用各種語言實(shí)現(xiàn)Ping已經(jīng)是大家喜聞樂見的事情了,網(wǎng)絡(luò)上利用Golang實(shí)現(xiàn)Ping已經(jīng)有比較詳細(xì)的代碼示例,但大多是僅僅是實(shí)現(xiàn)了Request過程,而對Response的回顯內(nèi)容并沒有做接收。而Ping程序不僅僅是發(fā)送一個ICMP,更重要的是如何接收并進(jìn)行統(tǒng)計。2016-09-09golang 如何用反射reflect操作結(jié)構(gòu)體
這篇文章主要介紹了golang 用反射reflect操作結(jié)構(gòu)體的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-04-04使用Go語言寫一個Http?Server的實(shí)現(xiàn)
本文主要介紹了使用Go語言寫一個Http?Server的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-04-04