Gin 增加超時控制中間件

背景

目標

方案

代碼:

package main

import (
 "context"
 "fmt"
 "log"
 "net/http"
 "time"

 "github.com/gin-gonic/gin"
)

// 超時控制中間件
func timeoutMiddleware(timeout time.Duration) func(c *gin.Context) {
 return func(c *gin.Context) {
  // 用超時context wrap request的context
  ctx, cancel := context.WithTimeout(c.Request.Context(), timeout)

  defer func() {
   // 檢查是否超時
   if ctx.Err() == context.DeadlineExceeded {
    c.Writer.WriteHeader(http.StatusGatewayTimeout)
    c.Abort()
   }

   //清理資源
   cancel()
  }()

  // 替換
  c.Request = c.Request.WithContext(ctx)
  c.Next()
 }
}

func timedHandler(duration time.Duration) func(c *gin.Context) {
 return func(c *gin.Context) {
  // 獲取替換之後的context 它具備了超時控制
  ctx := c.Request.Context()

  // 定義響應struct
  type responseData struct {
   status int
   body   map[string]interface{}
  }

  // 創建一個done chan表明request要完成了
  doneChan := make(chan responseData)
  // 模擬API耗時的處理
  go func() {
   time.Sleep(duration)
   doneChan <- responseData{
    status: 200,
    body:   gin.H{"hello""world"},
   }
  }()

  // 監聽兩個chan誰先到達
  select {

  // 超時
  case <-ctx.Done():
   return

   // 請求完成
  case res := <-doneChan:
   c.JSON(res.status, res.body)
  }
 }
}

func main() {
 engine := gin.New()

 // 不用超時控制
 group := engine.Group("/normal")
 {
  group.GET("/short", timedHandler(time.Second))
 }

 // 需要超時控制 時間控制在2s
 timeoutGroup := engine.Group("/timeout")
 timeoutGroup.Use(timeoutMiddleware(time.Second * 2))
 {
  // 延遲5s
  timeoutGroup.GET("/long", timedHandler(time.Second*5))
 }

 // run the server
 log.Fatal(engine.Run(":5090"))
}

驗證

  1. 正常請求

  2. 超時請求

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/Hp_QvCjHmsQqIgcoVDQasw