feat: 支持设置请求超时中间件

This commit is contained in:
2026-01-04 11:16:36 +08:00
parent 1830f0a571
commit 5e2964b5af
6 changed files with 70 additions and 14 deletions

13
go.mod
View File

@@ -7,21 +7,21 @@ toolchain go1.24.2
require (
git.zhangdeman.cn/zhangdeman/api-doc v1.0.3-0.20251013152001-868ee8955623
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20260102134357-189dc7f57c1a
git.zhangdeman.cn/zhangdeman/dynamic-struct v0.0.0-20251013092857-dcf591d4e8a8
git.zhangdeman.cn/zhangdeman/dynamic-struct v0.0.0-20251207071238-9aa24be3d708
git.zhangdeman.cn/zhangdeman/exception v0.0.0-20250510123912-a0d52fc093ab
git.zhangdeman.cn/zhangdeman/graceful v0.0.0-20250529070945-92833db6f3a4
git.zhangdeman.cn/zhangdeman/logger v0.0.0-20251031042950-416e962cbf3b
git.zhangdeman.cn/zhangdeman/network v0.0.0-20251013095944-5b89fff39bde
git.zhangdeman.cn/zhangdeman/logger v0.0.0-20251231045117-f05cb1e30afe
git.zhangdeman.cn/zhangdeman/network v0.0.0-20251220063845-8403861d36e5
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20251013044511-86c1a4a3a9dd
git.zhangdeman.cn/zhangdeman/trace v0.0.0-20251013092356-b7b9fb5f8a76
git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20251014035305-c0ec06fa6dff
git.zhangdeman.cn/zhangdeman/util v0.0.0-20260103142706-1f6145405ec4
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20251225094759-09c64ba2541c
github.com/gin-contrib/pprof v1.5.3
github.com/gin-gonic/gin v1.11.0
github.com/go-playground/validator/v10 v10.30.1
github.com/jedib0t/go-pretty/v6 v6.7.8
github.com/mcuadros/go-defaults v1.2.0
go.uber.org/zap v1.27.0
go.uber.org/zap v1.27.1
)
require (
@@ -38,6 +38,7 @@ require (
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 // indirect
github.com/gabriel-vasile/mimetype v1.4.12 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect
github.com/gin-contrib/timeout v1.1.0 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-openapi/jsonpointer v0.22.1 // indirect
github.com/go-openapi/jsonreference v0.21.2 // indirect

15
go.sum
View File

@@ -6,6 +6,8 @@ git.zhangdeman.cn/zhangdeman/consts v0.0.0-20260102134357-189dc7f57c1a h1:ppfx1Y
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20260102134357-189dc7f57c1a/go.mod h1:5p8CEKGBxi7qPtTXDI3HDmqKAfIm5i/aBWdrbkbdNjc=
git.zhangdeman.cn/zhangdeman/dynamic-struct v0.0.0-20251013092857-dcf591d4e8a8 h1:Pw981jG3hH9ZHrB3s1xPpsZafgX3USuMAjnGi2GEP9Y=
git.zhangdeman.cn/zhangdeman/dynamic-struct v0.0.0-20251013092857-dcf591d4e8a8/go.mod h1:xtCw3om5DRrG30EfQd/lfQPXgptfK7l9oBSt4Kdhjok=
git.zhangdeman.cn/zhangdeman/dynamic-struct v0.0.0-20251207071238-9aa24be3d708 h1:yVr38JAgPwS/6JeYdHLDa8PXU3fTa2dnENL1JGdu3ns=
git.zhangdeman.cn/zhangdeman/dynamic-struct v0.0.0-20251207071238-9aa24be3d708/go.mod h1:iwegDT/6mX2INyxMAYxsL9pZdpEixiu1GdpHIG2It1k=
git.zhangdeman.cn/zhangdeman/easylock v0.0.0-20230731062340-983985c12eda h1:bMD6r9gjRy7cO+T4zRQVYAesgIblBdTnhzT1vN5wjvI=
git.zhangdeman.cn/zhangdeman/easylock v0.0.0-20230731062340-983985c12eda/go.mod h1:dT0rmHcJ9Z9IqWeMIt7YzR88nKkNV2V3dfG0j9Q6lK0=
git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20241101082529-28a6c68e38a4 h1:s6d4b6yY+NaK1AzoBD1pxqsuygEHQz0Oie86c45geDw=
@@ -16,8 +18,12 @@ git.zhangdeman.cn/zhangdeman/graceful v0.0.0-20250529070945-92833db6f3a4 h1:d1B3
git.zhangdeman.cn/zhangdeman/graceful v0.0.0-20250529070945-92833db6f3a4/go.mod h1:faaKb5d5tz3NmQQ+NrTnBDa1G/tN9/CVuKun1V4WBIE=
git.zhangdeman.cn/zhangdeman/logger v0.0.0-20251031042950-416e962cbf3b h1:+ca511XlP+HoBa2zB/ERSIE6yTiqiXpsgqvs64kRAZA=
git.zhangdeman.cn/zhangdeman/logger v0.0.0-20251031042950-416e962cbf3b/go.mod h1:tlQR2nfdP291Lug+b3BSu0q+kTPoSVNRL5dPq2nv9zQ=
git.zhangdeman.cn/zhangdeman/logger v0.0.0-20251231045117-f05cb1e30afe h1:/AeCVO9xqsERJbhsSA6Uw+F5Dc+shR6MQbG30i4OS7c=
git.zhangdeman.cn/zhangdeman/logger v0.0.0-20251231045117-f05cb1e30afe/go.mod h1:mzy5JiGVcx8KnT8hUaXjngboHQm1k/b5JymO01ujZSI=
git.zhangdeman.cn/zhangdeman/network v0.0.0-20251013095944-5b89fff39bde h1:+3zIOditaUwzSpl2ybM1PYN4OYTIKiemMBt+pNv3Yko=
git.zhangdeman.cn/zhangdeman/network v0.0.0-20251013095944-5b89fff39bde/go.mod h1:Ewh0UYOqXxEh0khgHj9bDz1rbnd7cCCsJrcOTFX/8wg=
git.zhangdeman.cn/zhangdeman/network v0.0.0-20251220063845-8403861d36e5 h1:Dx5xvkIx3GHF6gIPhrJnANFYNWG514/9+PHhdGowNfc=
git.zhangdeman.cn/zhangdeman/network v0.0.0-20251220063845-8403861d36e5/go.mod h1:HG6674mvHXXADavdVRHebkcBOz6Zq6kCGKnTN0ukAXE=
git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20251013024601-da007da2fb42 h1:VjYrb4adud7FHeiYS9XA0B/tOaJjfRejzQAlwimrrDc=
git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20251013024601-da007da2fb42/go.mod h1:VHb9qmhaPDAQDcS6vUiDCamYjZ4R5lD1XtVsh55KsMI=
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20251013044511-86c1a4a3a9dd h1:kTZOpR8iHx27sUufMWVYhDZx9Q4h80j7RWlaR8GIBiU=
@@ -26,10 +32,14 @@ git.zhangdeman.cn/zhangdeman/trace v0.0.0-20251013092356-b7b9fb5f8a76 h1:Y1GME5d
git.zhangdeman.cn/zhangdeman/trace v0.0.0-20251013092356-b7b9fb5f8a76/go.mod h1:avN/muzbNjUM2qamRZeY4fuLEmA9cmfslk1dkdtRQBE=
git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e h1:Q973S6CcWr1ICZhFI1STFOJ+KUImCl2BaIXm6YppBqI=
git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e/go.mod h1:VpPjBlwz8U+OxZuxzHQBv1aEEZ3pStH6bZvT21ADEbI=
git.zhangdeman.cn/zhangdeman/util v0.0.0-20260103142706-1f6145405ec4 h1:x/xEkukbXS8WNN0N2A79C/q1Gh+fm3XrAdr1d/nEcXI=
git.zhangdeman.cn/zhangdeman/util v0.0.0-20260103142706-1f6145405ec4/go.mod h1:OKI+RVVfRTUf/ox9uDMydtos2YDFr+/UuU4FFZKgPYY=
git.zhangdeman.cn/zhangdeman/websocket v0.0.0-20251013144324-313024336a6f h1:InxNoLPHBLwCLblW0lsL2P1ZBYoUEzw9Yh+KNLt4Xmg=
git.zhangdeman.cn/zhangdeman/websocket v0.0.0-20251013144324-313024336a6f/go.mod h1:Pbs7tusW6RNcqrNCVcLE2zrM8JfPaO7lBJQuRiAzzLs=
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20251014035305-c0ec06fa6dff h1:ym1Qs4diJe27CK/0K6vy7RvgH90mXgslWA++L8mXaKE=
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20251014035305-c0ec06fa6dff/go.mod h1:mBvTwcdqHRF3QIkAh92j/JRhru2LzyJ2LBqolxjzzKE=
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20251225094759-09c64ba2541c h1:lTShwLIIBojEInQlQYmKvpoio5Srasn05bPyBIEXBsY=
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20251225094759-09c64ba2541c/go.mod h1:oSmLgHs4EausBSmH4GdpGUtjubPek8hLXTAUc4cPCb0=
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
@@ -45,6 +55,7 @@ github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gE
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 h1:CaO/zOnF8VvUfEbhRatPcwKVWamvbYd8tQGRWacE9kU=
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4=
github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
@@ -55,6 +66,8 @@ github.com/gin-contrib/pprof v1.5.3 h1:Bj5SxJ3kQDVez/s/+f9+meedJIqLS+xlkIVDe/lcv
github.com/gin-contrib/pprof v1.5.3/go.mod h1:0+LQSZ4SLO0B6+2n6JBzaEygpTBxe/nI+YEYpfQQ6xY=
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
github.com/gin-contrib/timeout v1.1.0 h1:WAmWseo5gfBUbMrMJu5hJxDclehfSJUmK2wGwCC/EFw=
github.com/gin-contrib/timeout v1.1.0/go.mod h1:NpRo4gd1Ad8ZQ4T6bQLVFDqiplCmPRs2nvfckxS2Fw4=
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
@@ -208,6 +221,8 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg=

34
middleware/timeout.go Normal file
View File

@@ -0,0 +1,34 @@
// Package middleware ...
//
// Description : middleware ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2026-01-04 10:17
package middleware
import (
"net/http"
"time"
"github.com/gin-contrib/timeout"
"github.com/gin-gonic/gin"
)
// Timeout 超时处理中间件
func Timeout(ttl int64) func(ctx *gin.Context) {
if ttl <= 0 {
// 未设置超时
return func(ctx *gin.Context) {
ctx.Next()
}
}
// 设置超时中间件, 单位: ms
return timeout.New(
timeout.WithTimeout(time.Duration(ttl)*time.Millisecond),
timeout.WithResponse(func(ctx *gin.Context) {
ctx.JSON(http.StatusRequestTimeout, gin.H{"code": http.StatusRequestTimeout, "msg": "请求超时"})
ctx.Abort()
}),
)
}

View File

@@ -153,7 +153,7 @@ func (c controllerParser) setUriMeta(metaField reflect.StructField, cfg *UriConf
cfg.NoLogin = boolMetaParse(TagNameNoLogin) // 是否需要登录
// 最大执行时间
cfg.MaxExecTime = uint(0)
cfg.MaxExecTime = int64(0)
if tagVal := strings.ToLower(metaField.Tag.Get(TagNameMaxExecTime)); tagVal != "" {
if err := util.ConvertAssign(&cfg.MaxExecTime, tagVal); nil != err {
panic(cfg.Path + " : 最大执行时间配置错误(配置的值必须是无符号整型), 请检查配置 : " + err.Error())

View File

@@ -44,7 +44,7 @@ type UriConfig struct {
IsSse bool `json:"is_sse"` // 是否 SSE 连接
IsWebsocket bool `json:"is_ws"` // 是否 websocket 连接
OutputStrict bool `json:"output_strict"` // 接口是否为严格模式 : 不配置,可返回任意类型, 配置, 必须返回结构体或者map
MaxExecTime uint `json:"max_exec_time"` // 接口最大执行时间, 单位: ms, 配置为0则不验证, 注意, 超时后不会报错, 会打印一条warn日志, 如果配置了报警策略, 也会发送报警信息
MaxExecTime int64 `json:"max_exec_time"` // 接口最大执行时间, 单位: ms, 配置为0则不验证, 注意, 超时后不会报错, 会打印一条warn日志, 如果配置了报警策略, 也会发送报警信息
HookSync bool `json:"hook_sync"` // 接口主逻辑执行完成之后hook是否同步执行, 默认异步执行
NoLogin bool `json:"no_login"` // 接口是否需要登录(无需登录, 则有token就验证, 无token不验证)
ParamIsPtr bool `json:"param_is_ptr"` // 参数是否指针类型

View File

@@ -203,6 +203,8 @@ func (s *server) Group(routerPrefix string, middlewareList []gin.HandlerFunc, co
} else {
apiMiddlewareList = append(apiMiddlewareList, runtime.FuncForPC(reflect.ValueOf(s.RequestHandler).Pointer()).Name())
}
// 设置 超时 函数描述
apiMiddlewareList = append(apiMiddlewareList, runtime.FuncForPC(reflect.ValueOf(middleware.Timeout).Pointer()).Name())
// 设置 logic 函数描述
apiMiddlewareList = append(apiMiddlewareList, runtime.FuncForPC(itemUriCfg.ApiLogicFunc.Func.Pointer()).Name())
@@ -231,21 +233,25 @@ func (s *server) Group(routerPrefix string, middlewareList []gin.HandlerFunc, co
// registerRouter 注册路由
func (s *server) registerRouter(routerGroup *gin.RouterGroup, method string, itemUriCfg UriConfig, handleFunc gin.HandlerFunc) {
funcList := []gin.HandlerFunc{
middleware.Timeout(itemUriCfg.MaxExecTime), // 超时处理
handleFunc,
}
switch method {
case http.MethodGet:
routerGroup.GET(itemUriCfg.Path, handleFunc)
case http.MethodHead:
routerGroup.HEAD(itemUriCfg.Path, handleFunc)
routerGroup.HEAD(itemUriCfg.Path, funcList...)
case http.MethodPost:
routerGroup.POST(itemUriCfg.Path, handleFunc)
routerGroup.POST(itemUriCfg.Path, funcList...)
case http.MethodPut:
routerGroup.PUT(itemUriCfg.Path, handleFunc)
routerGroup.PUT(itemUriCfg.Path, funcList...)
case http.MethodPatch:
routerGroup.PATCH(itemUriCfg.Path, handleFunc)
routerGroup.PATCH(itemUriCfg.Path, funcList...)
case http.MethodDelete:
routerGroup.DELETE(itemUriCfg.Path, handleFunc)
routerGroup.DELETE(itemUriCfg.Path, funcList...)
case http.MethodOptions:
routerGroup.OPTIONS(itemUriCfg.Path, handleFunc)
routerGroup.OPTIONS(itemUriCfg.Path, funcList...)
case http.MethodTrace:
panic(`method Trace is not supported`)
default: