Golang中間件設(shè)計示例詳解
什么是中間件
中間件:將這些非業(yè)務(wù)邏輯代碼抽象出來,封裝好,提供接口給控制器使用
裝飾器模式:將最核心的代碼一層層裝飾,返回的時候一層層出來
動手設(shè)計中間件
首先我們從之前的Controller開始,之前寫了一個可以超時的controller但是那是寫在了代碼里,我們能不能變成中間件為我們自動去判斷超時呢!
首先在framework/timeout.go
寫下我們的中間件方法:
// 包裝所有注冊的Controller 然后前置方法加上錯誤panic和超時控制 func TimeOutController(fun ControllerHandler, d time.Duration) ControllerHandler { return func(c *Context) error { finish := make(chan struct{}, 1) panicChan := make(chan interface{}, 1) context, cancel := context.WithTimeout(c, d) defer cancel() c.Request.WithContext(context) go func() { defer func() { if p := recover(); p != nil { panicChan <- p } }() fun(c) finish <- struct{}{} }() select { case p := <-panicChan: log.Println(p) c.ResponseWriter.WriteHeader(500) case <-finish: log.Println("finish") case <-context.Done(): c.SetHasTimeOut() c.ResponseWriter.Write([]byte("time out")) } return nil } }
但是這樣的調(diào)用的話就變成了
core.Get("/user/login", framework.TimeOutController(UserLoginController,time.Second*2))
如果有新的中間件要包裹,那豈不是顯示寫出來會很長,一層一層的!而且這個實現(xiàn),也只能為一個controller需要的時候去包裹一下,只是省了寫多個超時的代碼,但還是要自己顯示的調(diào)用!
使用流水線模式
當不要嵌套之后,那我們就用一個數(shù)組將這些中間件都存起來然后順序自己執(zhí)行,這樣既不用調(diào)用,也不用擔心嵌套了
需要處理的地方:
- 第一個是在此次請求處理的入口處,即 Core 的 ServeHttp;
- 第二個是在每個中間件的邏輯代碼中,用于調(diào)用下個中間件。
代碼處理
framework/core.go
修改了Core
中添加了一個handlers包含中間件的方法
type Core struct { router map[string]*Tree handles []ControllerHandler // 因為包含中間件 所以是多個集合 } // 在請求邏輯處理的函數(shù)中,添加上我們的next循環(huán)處理中間件的方法 func (c Core) ServeHTTP(writer http.ResponseWriter, request *http.Request) { ctx := NewContext(request, writer) handlers := c.FindRouteByRequest(request) if handlers== nil { ctx.Json(404, "router not found ") return } // 先設(shè)置該core添加的中間件方便next去調(diào)用執(zhí)行 ctx.SetHandler(handlers) if err:=ctx.Next();err!=nil{ ctx.Json(500, "server Interval") return } }
上面不僅是加上了這個結(jié)構(gòu)體成員變量,還要處理一下Get
等處理方法
將接收的函數(shù)ControllerHandler
改為可以接受多個!,然后處理過程將所有中間件都傳給addroute
中去設(shè)置在最后一個映射節(jié)點下!**這樣在我們請求對應(yīng)的路由方法時,就回去執(zhí)行路由最后一個節(jié)點下的所有中間件方法!**下面是例子Get
方法:同理Post,Put,Delete
// 注冊Get方法 func (c *Core) Get(pattern string, handler ...ControllerHandler) { str :="" if strings.HasPrefix(pattern,"/"){ strs:=strings.SplitN(pattern,"/",2) str=strs[1] log.Println("去除首字符/",str) } url := strings.ToUpper(str) allHandlers:=append(c.handles,handler...) log.Println("進來了",url) if err := c.router[GET].AddRoute(url, allHandlers); err != nil { log.Fatal("add router error:", err) } }
還要加一個方法方便我們在注冊函數(shù)的使用去使用中間件!
func (c *Core)Use(middlewares ...ControllerHandler){ c.handles=middlewares }
framework/group.go
其實和上面的core修改方向差不多,都是加上成員變量中間件集合!
type Group struct { core *Core // perfix string // 自身前綴 handler []ControllerHandler } //IGroup 代表前綴分組 type IGroup interface { Get(string, ...ControllerHandler) Post(string, ...ControllerHandler) Delete(string, ...ControllerHandler) Put(string, ...ControllerHandler) Use(middlewares ...ControllerHandler) } // 獲得中間件集合 func (g Group)getMiddlewares()[]ControllerHandler{ if g.handler==nil{ g.handler=make([]ControllerHandler,0) } return g.handler } // 支持傳入多個中間件 func (g Group) Get(s string, handler ...ControllerHandler) { url := g.perfix + s allHandlers := append(g.getMiddlewares(), handler...) g.core.Get(url, allHandlers...) }
也添加一個支持添加組的中間件
func (g *Group)Use(middlewares ...ControllerHandler){ g.handler=middlewares }
framework/node.go
因為支持了可以傳入多個中間件集合,那么node存放的處理器方法也需要改為切片,那么對應(yīng)下面有以下改動!
// 代表節(jié)點 type node struct { isLast bool // 代表這個節(jié)點是否可以成為最終的路由規(guī)則。 該節(jié)點是否能成為一 segment string // url 中的字符串,代表這個節(jié)點表示的路由中某個段的字符串 handler []ControllerHandler // 代表這個節(jié)點中包含的控制器,用于最終加載調(diào)用 childes []*node // 代表這個節(jié)點下的子節(jié)點 }
將對應(yīng)的handler之前的代碼修改為支持切片多個即可!
func (tree *Tree) AddRoute(url string, handler []ControllerHandler) error {<!--{C}%3C!%2D%2D%20%2D%2D%3E-->...}
framework/context.go
下面來看看context中的Next方法是如何寫的?
type Context struct { Request *http.Request ResponseWriter http.ResponseWriter hasTimeOut bool // 是否超時標記位 writerMux *sync.Mutex // 當前請求的handler鏈條 handlers []ControllerHandler index int // 請求調(diào)用到的方法下標 每執(zhí)行一個向后+1 } /* Next() 函數(shù)會在框架的兩個地方被調(diào)用: 這里要注意,index 下標表示當前調(diào)用 Next 要執(zhí)行的控制器序列,它的初始值應(yīng)該為-1,每次調(diào)用都會自增 1,這樣才能保證第一次調(diào)用的時候 index 為 0,定位到控制器鏈條的下標為 0 的控制器,即第一個控制器。 在框架文件夾 context.go 的初始化 Context 函數(shù)中,代碼如下: 第一個是在此次請求處理的入口處,即 Core 的 ServeHttp; 第二個是在每個中間件的邏輯代碼中,用于調(diào)用下個中間件 */ func (ctx *Context) Next() error { ctx.index++ if ctx.index < len(ctx.handlers) { if err := ctx.handlers[ctx.index](ctx); err != nil { return err } } return nil } // 設(shè)置可可執(zhí)行方法 func (ctx *Context)SetHandler(fn []ControllerHandler){ if ctx.handlers==nil{ ctx.handlers=make([]ControllerHandler,0) } ctx.handlers=append(ctx.handlers,fn...) }
中間件例子
新建一個middlerware文件夾,然后里面創(chuàng)建文件寫下我們的文件夾
比如我們這個處理錯誤的中間件,
// recovery機制,將協(xié)程中的函數(shù)異常進行捕獲 func Recovery() framework.ControllerHandler { // 使用函數(shù)回調(diào) return func(c *framework.Context) error { // 核心在增加這個recover機制,捕獲c.Next()出現(xiàn)的panic defer func() { if err := recover(); err != nil { c.Json(500, err) } }() // 使用next執(zhí)行具體的業(yè)務(wù)邏輯 c.Next() return nil } } // 例子1 func Test1()framework.ControllerHandler{ return func(c *framework.Context) error { log.Println("middleware test1") c.Next() log.Println("middleware end test1") return nil } } // 例子2 func Test2()framework.ControllerHandler{ return func(c *framework.Context) error { log.Println("middleware test2") c.Next() log.Println("middleware end test2") return nil } }
實際使用
可以如同像gin
框架一樣進行用use
注冊進我們的中間件!實戰(zhàn)結(jié)束~
func registerRouter(core *framework.Core) { print(111) // 設(shè)置控制器 core.Use(middleware.Recovery()) core.Get("/foo", FooController) core.Get("/user/login", framework.TimeOutController(UserLoginController,time.Second*2)) core.Use( middleware.Test1(), middleware.Test2(), ) subjectApi := core.Group("/subject") subjectApi.Use(middleware.Test2()) { subjectApi.Get("/list/all", SubjectListController) subjectApi.Post("/add", SubjectListController) subjectApi.Delete("/:id", SubjectListController) subjectApi.Put("/:id", SubjectListController) subjectApi.Get("/:id", SubjectListController) } }
到此這篇關(guān)于Golang中間件設(shè)計提升HTTP服務(wù)的文章就介紹到這了,更多相關(guān)Golang中間件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang中的關(guān)鍵字(defer、:=、go?func())詳細解讀
這篇文章主要介紹了Golang中的關(guān)鍵字(defer、:=、go?func())詳細解讀,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-04-04Go標準庫http與fasthttp服務(wù)端性能對比場景分析
這篇文章主要介紹了Go標準庫http與fasthttp服務(wù)端性能比較,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-06-06簡化Go開發(fā)提高生產(chǎn)力的強大工具及使用詳解
作為?Go?開發(fā)人員,應(yīng)該都知道維持簡潔高效開發(fā)工作流程的重要性,為了提高工作效率和代碼質(zhì)量,簡化開發(fā)流程并自動執(zhí)行重復(fù)性任務(wù)至關(guān)重要,在本文中,我們將探討一些強大的工具和技術(shù),它們將簡化?Go?開發(fā)過程,助力您的編碼之旅2023-10-10Go語言HTTPServer開發(fā)的六種方式小結(jié)
Golang的Server開發(fā)顯得非常簡單,有很多種方式,本文就介紹了Go語言HTTPServer開發(fā)的六種方式,具有一定的參考價值,感興趣的可以了解一下2021-11-11golang hack插件開發(fā)動態(tài)鏈接庫實例探究
這篇文章主要為大家介紹了golang hack插件開發(fā)動態(tài)鏈接庫實例探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2024-01-01Windows10系統(tǒng)下安裝Go環(huán)境詳細步驟
Go語言是谷歌推出的一款全新的編程語言,可以在不損失應(yīng)用程序性能的情況下極大的降低代碼的復(fù)雜性,這篇文章主要給大家介紹了關(guān)于Windows10系統(tǒng)下安裝Go環(huán)境的詳細步驟,需要的朋友可以參考下2023-11-11Go語言kube-scheduler深度剖析開發(fā)之scheduler初始化
這篇文章主要介紹了Go語言kube-scheduler深度剖析開發(fā)之scheduler初始化實現(xiàn)過程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-04-04