Gin 本身不处理长连接,优化重点在 http.Server 配置、操作系统参数和连接生命周期管理;用 gin.Default() 在长连接场景下必然出问题。因其默认启用同步 I/O 的 Logger 和 Recovery 中间件,高并发下引发锁竞争,且 ReadTimeout、WriteTimeout、IdleTimeout 全为 0,导致空闲连接永不释放、文件描述符耗尽。

直接说结论:Gin 本身不处理长连接,优化重点在 http.Server 配置、操作系统参数和连接生命周期管理;用 gin.Default() 在长连接场景下必然出问题。
为什么 gin.Default() 必须禁用
gin.Default() 默认启用 gin.Logger() 和 gin.Recovery(),这两个中间件在每次请求中都做同步 I/O(比如写日志到文件、刷缓冲区),高并发下会成为锁竞争热点。更致命的是它完全没设任何超时——ReadTimeout、WriteTimeout、IdleTimeout 全为 0,导致空闲 HTTP/1.1 keep-alive 连接永不释放,文件描述符持续堆积,直到 ulimit -n 耗尽。
- 必须改用
gin.New()初始化引擎 - 日志输出要异步(例如走
zerolog.New(os.Stdout).With().Timestamp().Logger()的异步 writer) - 禁用默认
Recovery,改用带 context 超时控制的 panic 捕获逻辑
http.Server IdleTimeout 是生命线
HTTP 长连接是否“可控”,全看 IdleTimeout。客户端不主动断连时,服务端必须靠它强制清理空闲连接。不设这个值,10 万连接可能几分钟就卡死。
-
IdleTimeout建议设为 15–45 秒,略大于客户端心跳间隔(如心跳 20s,这里设 30s) -
ReadTimeout应略大于最长业务处理时间(比如最慢接口耗时 3s,则设 5s),避免半开连接堆积 - 不要设
ReadHeaderTimeout过短(如 - 示例配置:
srv := &http.Server{
Addr: ":8080",
Handler: router,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 30 * time.Second,
MaxHeaderBytes: 1 <h3>操作系统与 Go 运行时联动调优</h3><p>代码再优化,Linux 默认参数也会拦住你。10 万连接不是 Gin 写得对不对的问题,是系统能不能撑住的问题。</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/gongju/2602" title="Gin框架 1.9.0"><img
src="https://img.php.cn/upload/manual/001/589/237/6a731f5b72949207.png" alt="Gin框架 1.9.0" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/gongju/2602" title="Gin框架 1.9.0" class="overflowclass">Gin框架 1.9.0</a>
<p class="overflowclass">Gin框架 1.9.0版本源码包下载,版本号 1.9.0,适合需要 sonic JSON 支持、路由修复和内容协商改进的 Go Web 开发场景。</p>
</div>
<a rel="nofollow" href="/xiazai/gongju/2602" title="Gin框架 1.9.0" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
- 启动前执行
ulimit -n 1048576;若用 systemd,必须在 service 文件里加LimitNOFILE=1048576 -
sysctl -w net.core.somaxconn=65535,并写入/etc/sysctl.conf持久化 - Go 程序启动时显式设置
runtime.GOMAXPROCS(runtime.NumCPU()),避免调度器争抢 - WebSocket 场景下,
gorilla/websocket的Upgrader要设好ReadBufferSize和WriteBufferSize(通常 4096 足够),避免频繁 alloc
WebSocket 升级必须绕过 Gin 默认中间件链
WebSocket 连接建立后,后续通信不走 HTTP 请求流程,但升级阶段仍经过 Gin 的中间件。如果中间件里有耗时操作(如鉴权查 DB、写日志),会拖慢握手速度,甚至触发 IdleTimeout 断连。
- WebSocket 路由应注册在独立分组或前置拦截,避免被全局日志/恢复中间件包裹
- 升级前建议做轻量校验(如 token 解析、IP 白名单),别在
Upgrade后才查用户信息 - 升级成功后,
ws.ReadMessage()和ws.WriteMessage()调用需配超时控制(用SetReadDeadline/SetWriteDeadline),否则单个卡死连接会阻塞整个 goroutine
真正决定长连接上限的,从来不是 Gin 的路由树多快,而是你有没有亲手把 IdleTimeout 设上、ulimit 调高、Upgrader 缓冲区配对——这些地方一漏,百万连接就是幻觉。










