当前位置: 首页 > news >正文

【Go】Gin 超时中间件的坑:fatal error: concurrent map writes

Gin 社区超时中间件的坑:导致线上 Pod 异常重启

在最近的项目中,我们遇到了因为 Gin 超时中间件(timeout 引发的生产事故:Pod 异常退出并重启

问题现场

pod无故重启,抓取标准输出日志,问题指向超时中间件
在这里插入图片描述

堆栈报错信息如下
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

为什么会并发写入呢? 报错指向Go社区的超时中间件,社区搜索相关issue, 果然有相关问题 ttps://github.com/gin-contrib/timeout/pull/55

我们的代码封装

func timeoutMiddleWare(timeoutInt int) gin.HandlerFunc {return timeout.New(timeout.WithTimeout(time.Duration(timeoutInt)*time.Second),timeout.WithResponse(func(c *gin.Context) {c.JSON(http.StatusGatewayTimeout, response.Failed(http.StatusGatewayTimeout, nil))}),)
}

问题复现与成因

先说原因:

超时中间件额外开了一个协程去执行业务逻辑,超时中间件的逻辑在另外的的协程中,当请求超时发生时会出现了两个 goroutine 同时对响应进行写操作,而gin的源码响应中有写入map的操作,这会导致 重复写入,并触发 map 并发写 错误(Go 的 map 在并发写时会直接 panic), 从而导致Pod 异常退出,K8s 会立刻重启容器。

源码分析:

// github.com/gin-contrib/timeout v1.0.1
var bufPool *BufferPool
const (defaultTimeout = 5 * time.Second
)
// New wraps a handler and aborts the process of the handler if the timeout is reached
func New(opts ...Option) gin.HandlerFunc {t := &Timeout{timeout:  defaultTimeout,handler:  nil,response: defaultResponse,}// Loop through each optionfor _, opt := range opts {if opt == nil {panic("timeout Option not be nil")}// Call the option giving the instantiatedopt(t)}if t.timeout <= 0 {return t.handler}bufPool = &BufferPool{}return func(c *gin.Context) {finish := make(chan struct{}, 1)panicChan := make(chan interface{}, 1)w := c.Writerbuffer := bufPool.Get()tw := NewWriter(w, buffer)c.Writer = twbuffer.Reset()// 这里开了一个协程去执行业务逻辑go func() {defer func() {if p := recover(); p != nil {panicChan <- p}}()t.handler(c)finish <- struct{}{}}()select {case p := <-panicChan:tw.FreeBuffer()c.Writer = wpanic(p)case <-finish:c.Next()tw.mu.Lock()defer tw.mu.Unlock()dst := tw.ResponseWriter.Header()for k, vv := range tw.Header() {dst[k] = vv}tw.ResponseWriter.WriteHeader(tw.code)if _, err := tw.ResponseWriter.Write(buffer.Bytes()); err != nil {panic(err)}tw.FreeBuffer()bufPool.Put(buffer)case <-time.After(t.timeout):c.Abort()tw.mu.Lock()defer tw.mu.Unlock()tw.timeout = truetw.FreeBuffer()bufPool.Put(buffer)// v1.0.1 报错的代码c.Writer = wt.response(c)c.Writer = tw// v1.1.0 修复后的PR代码cc := c.Copy() // 重新拷贝了一份gin.Context进行响应cc.Writer = wt.response(cc)}}// t.response 实际是调用gin.Context.String()
func defaultResponse(c *gin.Context) {c.String(http.StatusRequestTimeout, http.StatusText(http.StatusRequestTimeout))
}// gin源码 v1.10.0:
func (c *Context) String(code int, format string, values ...interface{}) {c.Render(code, render.String{Format: format, Data: values})
}// Render writes the response headers and calls render.Render to render data.
func (c *Context) Render(code int, r render.Render) {c.Status(code)if !bodyAllowedForStatus(code) {// 关键在这里 r.WriteContentType(c.Writer)c.Writer.WriteHeaderNow()return}if err := r.Render(c.Writer); err != nil {panic(err)}
}// WriteContentType (JSON) writes JSON ContentType.
func (r JSON) WriteContentType(w http.ResponseWriter) {writeContentType(w, jsonContentType)
}// !!!WriteContentType 最终会往header(map)中写入值,引发并发问题 !!!func writeContentType(w http.ResponseWriter, value []string) {header := w.Header()if val := header["Content-Type"]; len(val) == 0 {header["Content-Type"] = value}
}

社区修复

修复详情相关 PR:fix(response): conflict when handler completed and concurrent map writes by demouth · Pull Request #

在这里插入图片描述

解决办法

所有使用 go get github.com/gin-contrib/timeout 的项目需要升级:

  • Go 版本 ≥ 1.23.0
  • 拉取最新包: go get github.com/gin-contrib/timeout@v1.1.0
http://www.dtcms.com/a/328181.html

相关文章:

  • FPGA即插即用Verilog驱动系列——UART串口接收
  • 医疗智慧大屏系统 - Flask + Vue实现
  • nextTick和setTimeout的区别
  • Docker概述与安装Dockerfile文件
  • k8s-scheduler 解析
  • 1小时 MySQL 数据库基础速通
  • log4cplus的功能是什么,我们如何来使用它?
  • 调整UOS在VMware中的分辨率
  • Linux系统启动过程详解
  • CTO 如何从“干活的人”转变成“带方向的人”?
  • 需求沟通会议如何组织
  • 云手机在电商行业中的作用
  • 知名车企门户漏洞或致攻击者远程解锁汽车并窃取数据
  • C++ 学习与 CLion 使用:(二)using namespace std 语句详解,以及 std 空间的标识符罗列
  • 消防安全预警系统助力安全生产
  • 【工作笔记】win11系统docker desktop配置国内mirror不生效解决方案汇总整理
  • `SHOW PROCESSLIST;` 返回列详解(含义 + 单位)
  • django celery 动态添加定时任务后不生效问题
  • 【SDR课堂第35讲】通用软件无线电平台USRP7440- RFSOC NCO性能测试(一)
  • android 换肤框架详解3-自动换肤原理梳理
  • JDK 9~17 新特性及升级建议
  • 【154页PPT】某大型再生资源集团管控企业数字化转型SAP解决方案(附下载方式)
  • 麒麟信安“操作系统+云”双驱动,推进某市公安局智慧警务建设
  • 云部署 MCP 服务计费
  • Java 包
  • 飞算JavaAI:Java智能开发工具的技术解析、应用实践
  • 燕山大学计算机网络实验(2025最新)
  • Python科学计算与可视化领域工具TVTK、Mayavi、Mlab、Traits(附视频教程)
  • 【AI】Pycharm中要注意Python程序文件的位置
  • 【C#】正则表达式