Gin 增加超時控制中間件
背景
- 有時候很多 API 有業務超時要求,就是數據必須在幾秒內返回,超時的話立刻結束,不用卡死在某一個頁面上。
目標
- 對這一類 API 特定處理,增加超時控制,不要影響別的 API
方案
- 方案很簡單,就是利用 go 提供的 context 技術對 gin 框架的 request 對象進行改造,使其具備超時控制機能。
代碼:
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"))
}
驗證
-
正常請求
-
超時請求
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/Hp_QvCjHmsQqIgcoVDQasw