欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Golang中間件設(shè)計(jì)示例詳解

 更新時(shí)間:2023年05月10日 09:26:42   作者:未來(lái)誰(shuí)可知  
這篇文章主要介紹了Golang中間件設(shè)計(jì)示例,中間件是在HTTP請(qǐng)求處理過程中,對(duì)請(qǐng)求進(jìn)行處理的一種模式,中間件可以在請(qǐng)求到達(dá)處理器之前或之后對(duì)請(qǐng)求進(jìn)行攔截,修改或者增強(qiáng),同時(shí)可以對(duì)響應(yīng)進(jìn)行處理,需要的朋友可以參考下

什么是中間件

中間件:將這些非業(yè)務(wù)邏輯代碼抽象出來(lái),封裝好,提供接口給控制器使用

裝飾器模式:將最核心的代碼一層層裝飾,返回的時(shí)候一層層出來(lái)

動(dòng)手設(shè)計(jì)中間件

首先我們從之前的Controller開始,之前寫了一個(gè)可以超時(shí)的controller但是那是寫在了代碼里,我們能不能變成中間件為我們自動(dòng)去判斷超時(shí)呢!

首先在framework/timeout.go寫下我們的中間件方法:

// 包裝所有注冊(cè)的Controller 然后前置方法加上錯(cuò)誤panic和超時(shí)控制
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))

如果有新的中間件要包裹,那豈不是顯示寫出來(lái)會(huì)很長(zhǎng),一層一層的!而且這個(gè)實(shí)現(xiàn),也只能為一個(gè)controller需要的時(shí)候去包裹一下,只是省了寫多個(gè)超時(shí)的代碼,但還是要自己顯示的調(diào)用!

使用流水線模式

當(dāng)不要嵌套之后,那我們就用一個(gè)數(shù)組將這些中間件都存起來(lái)然后順序自己執(zhí)行,這樣既不用調(diào)用,也不用擔(dān)心嵌套了

需要處理的地方:

  • 第一個(gè)是在此次請(qǐng)求處理的入口處,即 Core 的 ServeHttp;
  • 第二個(gè)是在每個(gè)中間件的邏輯代碼中,用于調(diào)用下個(gè)中間件。

代碼處理

framework/core.go

修改了Core中添加了一個(gè)handlers包含中間件的方法

type Core struct {
	router map[string]*Tree
	handles []ControllerHandler  // 因?yàn)榘虚g件 所以是多個(gè)集合
}
// 在請(qǐng)求邏輯處理的函數(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
	}
}

上面不僅是加上了這個(gè)結(jié)構(gòu)體成員變量,還要處理一下Get等處理方法

將接收的函數(shù)ControllerHandler改為可以接受多個(gè)!,然后處理過程將所有中間件都傳給addroute 中去設(shè)置在最后一個(gè)映射節(jié)點(diǎn)下!**這樣在我們請(qǐng)求對(duì)應(yīng)的路由方法時(shí),就回去執(zhí)行路由最后一個(gè)節(jié)點(diǎn)下的所有中間件方法!**下面是例子Get方法:同理Post,Put,Delete

// 注冊(cè)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("進(jìn)來(lái)了",url)
	if err := c.router[GET].AddRoute(url, allHandlers); err != nil {
		log.Fatal("add router error:", err)
	}
}

還要加一個(gè)方法方便我們?cè)谧?cè)函數(shù)的使用去使用中間件!

func (c *Core)Use(middlewares ...ControllerHandler){
   c.handles=middlewares
}

framework/group.go

其實(shí)和上面的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
}
// 支持傳入多個(gè)中間件
func (g Group) Get(s string, handler ...ControllerHandler) {
	url := g.perfix + s
	allHandlers := append(g.getMiddlewares(), handler...)
	g.core.Get(url, allHandlers...)
}

也添加一個(gè)支持添加組的中間件

func (g *Group)Use(middlewares ...ControllerHandler){
   g.handler=middlewares
}

framework/node.go

因?yàn)橹С至丝梢詡魅攵鄠€(gè)中間件集合,那么node存放的處理器方法也需要改為切片,那么對(duì)應(yīng)下面有以下改動(dòng)!

// 代表節(jié)點(diǎn)
type node struct {
   isLast  bool              // 代表這個(gè)節(jié)點(diǎn)是否可以成為最終的路由規(guī)則。 該節(jié)點(diǎn)是否能成為一
   segment string            // url 中的字符串,代表這個(gè)節(jié)點(diǎn)表示的路由中某個(gè)段的字符串
   handler []ControllerHandler // 代表這個(gè)節(jié)點(diǎn)中包含的控制器,用于最終加載調(diào)用
   childes []*node           // 代表這個(gè)節(jié)點(diǎn)下的子節(jié)點(diǎn)
}

將對(duì)應(yīng)的handler之前的代碼修改為支持切片多個(gè)即可!

func (tree *Tree) AddRoute(url string, handler []ControllerHandler) error {<!--{C}%3C!%2D%2D%20%2D%2D%3E-->...}

framework/context.go

下面來(lái)看看context中的Next方法是如何寫的?

type Context struct {
	Request        *http.Request
	ResponseWriter http.ResponseWriter
	hasTimeOut     bool // 是否超時(shí)標(biāo)記位
	writerMux      *sync.Mutex
	// 當(dāng)前請(qǐng)求的handler鏈條
	handlers []ControllerHandler
	index    int // 請(qǐng)求調(diào)用到的方法下標(biāo)   每執(zhí)行一個(gè)向后+1
}
/*
Next() 函數(shù)會(huì)在框架的兩個(gè)地方被調(diào)用:
這里要注意,index 下標(biāo)表示當(dāng)前調(diào)用 Next 要執(zhí)行的控制器序列,它的初始值應(yīng)該為-1,每次調(diào)用都會(huì)自增 1,這樣才能保證第一次調(diào)用的時(shí)候 index 為 0,定位到控制器鏈條的下標(biāo)為 0 的控制器,即第一個(gè)控制器。
在框架文件夾 context.go 的初始化 Context 函數(shù)中,代碼如下:
   第一個(gè)是在此次請(qǐng)求處理的入口處,即 Core 的 ServeHttp;
   第二個(gè)是在每個(gè)中間件的邏輯代碼中,用于調(diào)用下個(gè)中間件
*/
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...)
}

中間件例子

新建一個(gè)middlerware文件夾,然后里面創(chuàng)建文件寫下我們的文件夾

比如我們這個(gè)處理錯(cuò)誤的中間件,

// recovery機(jī)制,將協(xié)程中的函數(shù)異常進(jìn)行捕獲
func Recovery() framework.ControllerHandler { // 使用函數(shù)回調(diào)
   return func(c *framework.Context) error {
      // 核心在增加這個(gè)recover機(jī)制,捕獲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
	}
}

實(shí)際使用

可以如同像gin框架一樣進(jìn)行用use注冊(cè)進(jìn)我們的中間件!實(shí)戰(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è)計(jì)提升HTTP服務(wù)的文章就介紹到這了,更多相關(guān)Golang中間件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論