最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

四種Golang實(shí)現(xiàn)middleware框架的方式小結(jié)

 更新時(shí)間:2024年03月24日 08:26:58   作者:Alex  
middleware是一般框架里面常用的形式,比如web框架、rpc框架等,本文為大家詳細(xì)介紹了四種實(shí)現(xiàn)middleawre的方式,感興趣的可以了解一下

寫(xiě)在前面

middleware是一般框架里面常用的形式,比如web框架、rpc框架,通過(guò)middleware在流量入口和出口做一些公共事情,包括鑒權(quán)、日志、埋點(diǎn)、統(tǒng)計(jì)、限流、參數(shù)處理、異常處理等等。

在工作中經(jīng)常會(huì)用到,在閱讀web框架(gin,beego)的時(shí)候也會(huì)遇到,今天總結(jié)一下middleware有哪些實(shí)現(xiàn)方式。

方案一:數(shù)組遞歸調(diào)用

package middleware

import "context"

// 處理函數(shù)
type Handler func(ctx context.Context,
	msg string) error

// 插件類(lèi)型
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結(jié)構(gòu)體中定義業(yè)務(wù)處理函數(shù)handler和插件數(shù)組middlewares,在執(zhí)行函數(shù)Exec里面,將業(yè)務(wù)處理函數(shù)handler封裝成一個(gè)middleware放到middlewares后面,然后遞歸調(diào)用內(nèi)部函數(shù)mkCallChain。這個(gè)內(nèi)部函數(shù)mkCallChain返回的是一個(gè)函數(shù),將所有middleware一層一層包裹起來(lái),最終callChain := m.mkCallChain(m.middlewares)得到的是一個(gè)調(diào)用鏈。

這段代碼有點(diǎn)繞,需要細(xì)品。

測(cè)試方案一

	// 方案一
	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")

結(jié)果

===方案一 begin
TimeCost before
FinlterMW begin
LoggerMW before
HandlerMsg: hello chain
LoggerMW end
FinlterMW end
TimeCostMW:cost 1000428754
===方案一 end

方案二:順序?qū)崿F(xiàn)

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),
	}
}

// 執(zhí)行下一個(gè)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 {
	// 從第一個(gè)middleware開(kāi)始執(zhí)行
	return m.middlewares[0](m)
}

核心代碼是這段

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

自己定義一個(gè)context將所有middleware作為數(shù)組放在context中,執(zhí)行Exec()的時(shí)候就執(zhí)行第一個(gè)middleware,并且將context傳進(jìn)去。其他middlewaer中通過(guò)調(diào)用Next()函數(shù)來(lái)觸發(fā)下一個(gè)middleware。

這種方式看起來(lái)邏輯簡(jiǎn)單,容易理解。gin框架的middleware就是這樣實(shí)現(xiàn)的。這個(gè)方式是作者對(duì)gin框架的middleware的總結(jié)和抽象。

測(cè)試方案二

	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")

結(jié)果

===方案二 begin
TimeCost before
FinlterMW begin
LoggerMW before
LoggerMW end
FinlterMW end
TimeCostMW:cost 1000588399
===方案二 end

方式三:鏈?zhǔn)秸{(diào)用

package middlewarechain

import "context"

type Handler func(ctx context.Context) error

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

這段代碼邏輯很簡(jiǎn)單,它就是將上一個(gè)middleweare作為next參數(shù)傳到當(dāng)前middleware,形成鏈?zhǔn)秸{(diào)用。

看到這個(gè)定義你會(huì)不會(huì)覺(jué)得很奇怪,怎么這么點(diǎn)代碼?

是的,它的代碼就是這么少。有句話說(shuō)的好“哪有什么歲月靜好,不過(guò)是有人替你負(fù)重前行,生活從來(lái)都不容易”,定義的地方代碼少了,調(diào)用的時(shí)候肯定就復(fù)雜了。

下面看看測(cè)試用例

	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")

結(jié)果

===方案三 begin
LoggerMW before
FinlterMW begin
TimeCost before
PrintMsg:test
TimeCostMW:cost 6130
FinlterMW end
LoggerMW end
===方案三 end

可見(jiàn),在定義middleweare的時(shí)候,要將上一個(gè)middleeware傳入當(dāng)前middleeware的定義。跟其他幾種方案相比,其實(shí)它就是將middleware的注冊(cè)去掉了,沒(méi)有地方維護(hù)所有的middleware。

方案四:for循環(huán)實(shí)現(xiàn)

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
}

它跟方案一很像,都是定義一個(gè)MiddlewareManager結(jié)構(gòu)體,內(nèi)部維護(hù)一個(gè)middlewares數(shù)組,在調(diào)用Exec的時(shí)候,循環(huán)執(zhí)行middlewares

測(cè)試方案四

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

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

結(jié)果

===方案四 begin
2023/01/15 15:27:09 [RecoveryMW] befor
2023/01/15 15:27:09 [LoggerMW] befor
2023/01/15 15:27:09 [TimeCostMW] cost:0.000000s
2023/01/15 15:27:09 [LoggerMW] end
2023/01/15 15:27:09 [RecoveryMW] end
===方案四 end

總結(jié)

上面四種方案,都能實(shí)現(xiàn)middleware,好壞不予評(píng)價(jià),你喜歡用哪種方式就用哪種。

本文及github上的代碼實(shí)現(xiàn)主要是用于學(xué)習(xí)和總結(jié),如果你想用某種方式到自己的項(xiàng)目中,直接復(fù)制過(guò)去就行,不建議引用本代碼倉(cāng)庫(kù)。

github代碼倉(cāng)庫(kù):github.com/ZBIGBEAR/middleware

到此這篇關(guān)于四種Golang實(shí)現(xiàn)middleware框架的方式小結(jié)的文章就介紹到這了,更多相關(guān)Go middleware框架內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go內(nèi)置零值標(biāo)識(shí)符zero

    Go內(nèi)置零值標(biāo)識(shí)符zero

    大家學(xué)習(xí)和使用?Go?語(yǔ)言時(shí),有一個(gè)神奇的概念:零值(zero-values),所以本文想給大家分享一個(gè)關(guān)于零值的新提案,目測(cè)已經(jīng)八九不離十了
    2023-08-08
  • Go語(yǔ)言O(shè)RM包中使用worm構(gòu)造查詢條件的實(shí)例詳解

    Go語(yǔ)言O(shè)RM包中使用worm構(gòu)造查詢條件的實(shí)例詳解

    worm是一款方便易用的Go語(yǔ)言O(shè)RM庫(kù)。worm支Model方式(持結(jié)構(gòu)體字段映射)、原生SQL以及SQLBuilder三種模式來(lái)操作數(shù)據(jù)庫(kù),并且Model方式、原生SQL以及SQLBuilder可混合使用,本文通過(guò)一些例子來(lái)說(shuō)明如何使用worm來(lái)構(gòu)造查詢條件,感興趣的朋友一起看看吧
    2022-07-07
  • GO語(yǔ)言短變量聲明的實(shí)現(xiàn)示例

    GO語(yǔ)言短變量聲明的實(shí)現(xiàn)示例

    在Go語(yǔ)言中,短變量聲明是一種簡(jiǎn)潔的變量聲明方式,使用 := 運(yùn)算符,可以自動(dòng)推斷變量類(lèi)型,下面就來(lái)具體介紹一下如何使用,感興趣的可以了解一下
    2025-08-08
  • 使用Go語(yǔ)言實(shí)現(xiàn)敏感詞過(guò)濾功能

    使用Go語(yǔ)言實(shí)現(xiàn)敏感詞過(guò)濾功能

    敏感詞過(guò)濾,算是一個(gè)比較常見(jiàn)的功能,尤其是在內(nèi)容、社交類(lèi)應(yīng)用中更是如此,本文介紹如何使用Go語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單的敏感詞過(guò)濾功能,文中通過(guò)代碼示例介紹的非常詳細(xì),需要的朋友可以參考下
    2023-12-12
  • golang防止內(nèi)存逃逸的方法小結(jié)

    golang防止內(nèi)存逃逸的方法小結(jié)

    在Go語(yǔ)言中,內(nèi)存逃逸是指在函數(shù)中分配的變量在函數(shù)結(jié)束后仍然被引用,防止內(nèi)存逃逸有助于提高程序的性能,因?yàn)闂I戏峙涞膬?nèi)存可以更快地被回收,本文給大家總結(jié)了一些防止內(nèi)存逃逸的方法,需要的朋友可以參考下
    2024-02-02
  • Golang使用http協(xié)議實(shí)現(xiàn)心跳檢測(cè)程序過(guò)程詳解

    Golang使用http協(xié)議實(shí)現(xiàn)心跳檢測(cè)程序過(guò)程詳解

    這篇文章主要介紹了Golang使用http協(xié)議實(shí)現(xiàn)心跳檢測(cè)程序過(guò)程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧
    2023-03-03
  • Go操作Kafka的實(shí)現(xiàn)示例(kafka-go)

    Go操作Kafka的實(shí)現(xiàn)示例(kafka-go)

    本文介紹了使用kafka-go庫(kù)在Go語(yǔ)言中與Kafka進(jìn)行交互,涵蓋了kafka-go的安裝、API使用、消息發(fā)送與消費(fèi)方法,以及如何通過(guò)DockerCompose快速搭建Kafka環(huán)境,文章還比較了其他兩個(gè)常用的Kafka客戶端庫(kù),感興趣的可以了解一下
    2024-10-10
  • Go?channel實(shí)現(xiàn)批量讀取數(shù)據(jù)

    Go?channel實(shí)現(xiàn)批量讀取數(shù)據(jù)

    Go中的?channel?其實(shí)并沒(méi)有提供批量讀取數(shù)據(jù)的方法,需要我們自己實(shí)現(xiàn)一個(gè),使用本文就來(lái)為大家大家介紹一下如何通過(guò)Go?channel實(shí)現(xiàn)批量讀取數(shù)據(jù)吧
    2023-12-12
  • golang websocket 服務(wù)端的實(shí)現(xiàn)

    golang websocket 服務(wù)端的實(shí)現(xiàn)

    這篇文章主要介紹了golang websocket 服務(wù)端的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • Go語(yǔ)言中的os.Truncate函數(shù)用法解讀

    Go語(yǔ)言中的os.Truncate函數(shù)用法解讀

    這篇文章主要介紹了Go語(yǔ)言中的os.Truncate函數(shù)用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-07-07

最新評(píng)論

元阳县| 桑日县| 仙桃市| 清水河县| 孟村| 甘德县| 夹江县| 静安区| 获嘉县| 富锦市| 大庆市| 义乌市| 大厂| 泾阳县| 神农架林区| 新闻| 柯坪县| 沅陵县| 曲阜市| 杭锦旗| 壤塘县| 淄博市| 台东市| 株洲市| 深水埗区| 彭州市| 山东| 竹山县| 且末县| 株洲县| 临洮县| 南康市| 天等县| 天津市| 扶沟县| 大方县| 右玉县| 上栗县| 咸丰县| 陆川县| 华坪县|