Netty支撑百万WebSocket连接的关键在于IdleStateHandler超时参数需匹配NAT实际超时(readerIdleTime≤90秒)、EventLoop线程数合理设置(Worker≤16、Boss=1–2)、PooledByteBufAllocator精细化调优(禁用线程缓存、设maxOrder=11)及Linux内核参数调优(ulimit -n≥1048576、somaxconn=65535、fs.file-max≥2097152)。

Netty 能撑住百万 WebSocket 连接,但默认配置一上线就崩——关键不在“能不能”,而在 IdleStateHandler 怎么配、ByteBuf 怎么复用、EventLoopGroup 线程数怎么压。
为什么加了 IdleStateHandler 还会大量假死连接?
常见现象是:客户端断网后 5 分钟内服务端才触发 userEventTriggered,期间所有发往该 Channel 的消息都静默丢弃,监控看不到异常,但业务已失联。
根本原因是超时参数没对齐网络真实路径。运营商 NAT 超时普遍在 2–5 分钟,而你设了 readerIdleTime=300(5 分钟),等于主动等断连发生后再处理。
-
readerIdleTime必须 ≤ 120(秒),建议设为90,留出 30 秒给网络抖动和重传 -
writerIdleTime设为0,禁用写空闲检测——WebSocket 是服务端主动推的场景,写不写不反映连接活性 -
allIdleTime不要设,它会覆盖前两者逻辑,且触发条件模糊,容易漏判 - 必须在自定义
ChannelInboundHandler中重写userEventTriggered,且对IdleStateEvent做显式channel.close(),不能只打日志
EventLoopGroup 线程数设多少才不翻车?
很多项目直接照抄文档写 new NioEventLoopGroup(0),结果 CPU 跑满、GC 频繁——0 表示用 Runtime.getRuntime().availableProcessors() * 2,在 64 核机器上就是 128 个线程,远超 Netty 实际需要。
Netty 的 Worker EventLoop 只负责 I/O 事件分发,不是业务执行线程。线程过多反而加剧缓存行竞争和上下文切换开销。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- Worker 线程数建议固定为
Math.min(16, Runtime.getRuntime().availableProcessors()),超过 16 之后吞吐几乎不涨 - Boss 线程数严格设为
1或2,它只做 accept,再多毫无意义 - 绝对不要在
channelRead里执行耗时操作(如 DB 查询、HTTP 调用),必须用ctx.executor().submit()切到业务线程池 - 检查
ChannelOption.SO_BACKLOG是否 ≥ 1024,否则高并发建连时会直接拒绝新连接
内存暴涨、频繁 GC 的真实元凶是 PooledByteBufAllocator 配置错
默认开启池化,但若没调优,每个连接分配的堆外缓冲区(DirectByteBuffer)会碎片化严重,JVM 无法及时回收,Old Gen 在 1 小时内就涨满。
这不是代码写错了,而是 PooledByteBufAllocator 的 chunk 和 page 大小与你的消息体分布不匹配。
- 禁用线程本地缓存(
useThreadLocalCache = false),避免多线程反复申请释放导致缓存污染 - 设置
maxOrder = 11(对应 2MB chunk),适配直播弹幕、教育题库等中等消息体(1–50KB) - 显式关闭堆内缓冲:
config.setAllocator(PooledByteBufAllocator.DEFAULT),别用UnpooledByteBufAllocator - 用
netstat -s | grep "packet receive errors"辅助判断是否因内存不足丢包——这是比 GC 日志更早的预警信号
连接数卡在 65535 上不去?别怪 Netty,先查 ulimit -n 和 net.ipv4.ip_local_port_range
单机百万连接不是靠堆机器,而是让每个连接尽可能轻。但如果你的 Linux 内核参数没调,哪怕 Netty 写得再好,也卡死在 65535(一个 IP 可用端口上限)。
这不是应用层问题,是系统资源配额没放开。很多团队压测失败后花两周查 Netty 源码,其实改两行 sysctl 就能突破。
- 执行
ulimit -n 1048576并写入/etc/security/limits.conf,否则 JVM 进程打不开足够文件描述符 - 修改
/proc/sys/net/core/somaxconn至65535,提升 listen 队列长度 - 调整
/proc/sys/net/ipv4/ip_local_port_range为1024 65535→1024 65535不够,改成1024 65535是无效的,正确值应为1024 65535?不对——实际要设成1024 65535是旧认知,现需设为1024 65535?等等,这里必须纠正:真正起作用的是net.ipv4.ip_local_port_range = 1024 65535仅提供客户端源端口范围,服务端监听端口固定(如 8080),所以单 IP 支持连接数 ≈ 文件描述符上限 × 客户端 IP 数量。因此,重点永远是ulimit -n和fs.file-max - 确认
fs.file-max≥2097152,并写入/etc/sysctl.conf
这些参数不生效,netty-all-4.1.97.Final 再稳也扛不住 10 万连接。真正的瓶颈,往往藏在 /proc/sys/ 下面,而不是你的 Handler 里。

















