Gin Context超时与截止时间
超时控制是保障服务稳定性的关键,防止请求长时间阻塞占用资源。
WithTimeout设置超时
Go
func TimeoutMiddleware(timeout time.Duration) gin.HandlerFunc {
return func(c *gin.Context) {
ctx, cancel := context.WithTimeout(c.Request.Context(), timeout)
defer cancel()
c.Request = c.Request.WithContext(ctx)
done := make(chan struct{})
go func() {
c.Next()
close(done)
}()
select {
case <-done:
// 正常完成
case <-ctx.Done():
c.JSON(504, gin.H{"error": "request timeout"})
c.Abort()
}
}
}
WithDeadline设置截止时间
Go
func DeadlineMiddleware(deadline time.Time) gin.HandlerFunc {
return func(c *gin.Context) {
ctx, cancel := context.WithDeadline(c.Request.Context(), deadline)
defer cancel()
c.Request = c.Request.WithContext(ctx)
c.Next()
}
}
// 使用示例
func setupRouter() *gin.Engine {
r := gin.New()
// 设置每天23:59:59为截止时间
deadline := time.Date(2026, 5, 18, 23, 59, 59, 0, time.Local)
r.Use(DeadlineMiddleware(deadline))
return r
}
超时检测模式
处理器中超时检测
Go
func slowHandler(c *gin.Context) {
ctx := c.Request.Context()
resultCh := make(chan Result)
errCh := make(chan error)
go func() {
result, err := slowOperation()
if err != nil {
errCh <- err
return
}
resultCh <- result
}()
select {
case <-ctx.Done():
c.JSON(504, gin.H{"error": "operation timeout"})
return
case err := <-errCh:
c.JSON(500, gin.H{"error": err.Error()})
return
case result := <-resultCh:
c.JSON(200, result)
}
}
数据库操作超时
Go
func dbQueryWithTimeout(c *gin.Context) {
ctx, cancel := context.WithTimeout(c.Request.Context(), 3*time.Second)
defer cancel()
var users []User
if err := db.WithContext(ctx).Find(&users).Error; err != nil {
if errors.Is(err, context.DeadlineExceeded) {
c.JSON(504, gin.H{"error": "database query timeout"})
return
}
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, users)
}
超时与截止时间对比
| 特性 | WithTimeout | WithDeadline |
|---|---|---|
| 设置方式 | 相对时间 | 绝对时间 |
| 使用场景 | 操作限时 | 定点截止 |
| 典型应用 | API请求超时 | 定时任务截止 |
超时时间获取
Go
func handler(c *gin.Context) {
ctx := c.Request.Context()
if deadline, ok := ctx.Deadline(); ok {
remaining := time.Until(deadline)
log.Printf("剩余时间: %v", remaining)
}
c.Next()
}
分层超时控制
Go
// 全局超时:30秒
func GlobalTimeout() gin.HandlerFunc {
return func(c *gin.Context) {
ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second)
defer cancel()
c.Request = c.Request.WithContext(ctx)
c.Next()
}
}
// 路由组超时:10秒
func GroupTimeout() gin.HandlerFunc {
return func(c *gin.Context) {
parentCtx := c.Request.Context()
if _, ok := parentCtx.Deadline(); !ok {
ctx, cancel := context.WithTimeout(parentCtx, 10*time.Second)
defer cancel()
c.Request = c.Request.WithContext(ctx)
}
c.Next()
}
}
注意:子Context超时不能超过父Context的截止时间。
要点总结
- WithTimeout使用相对时间,WithDeadline使用绝对时间
- 超时后ctx.Done()通道关闭,触发取消信号
- 数据库、HTTP调用都应传入Context实现超时控制
- 子Context超时时间不能超过父Context
- 必须调用cancel函数释放资源
📝 发现内容有误?点击此处直接编辑