大型门户网站建设是什么,网站开发的设计与实现,自动化的网站建设,公司简介宣传册模板ppt背景痛点#xff1a;大促“三杀”——连接暴涨、消息乱序、服务雪崩 去年双十一#xff0c;我们团队把智能客服从外包 SDK 切到自研#xff0c;结果 0 点刚过#xff0c;QPS 直接翻 40 倍#xff1a; 连接暴涨#xff1a;单实例 4C8G#xff0c;TCP 连接数 30 s 内从 …背景痛点大促“三杀”——连接暴涨、消息乱序、服务雪崩去年双十一我们团队把智能客服从外包 SDK 切到自研结果 0 点刚过QPS 直接翻 40 倍连接暴涨单实例 4C8GTCP 连接数 30 s 内从 2 k→18 w直接把ulimit -n顶满新用户进不来。消息乱序HTTP 轮询模式客户端 200 ms 刷一次结果因 CDN 节点回源延迟先发后至用户看见“已发货”在前、“付款成功”在后疯狂投诉。服务雪崩老架构是“无状态本地缓存”一台宕机网关重试打爆剩余实例CPU 100 % 又触发新一轮宕机差点把整条链路带走。痛定思痛我们决定把“多多智能客服 API”彻底重构目标只有一个百万级在线、99.9 % 消息时延 200 ms、宕机秒级自愈。下面把全过程拆给你看。架构对比轮询、SSE、WebSocket 实测数据实验室环境4 台 16C32G 压测机一台 8C16G 目标服务器千兆网卡。指标定义吞吐量成功回包数/秒延迟RTT P99资源单连接平均内存。方案吞吐量 (万/秒)P99 延迟 (ms)单连接内存 (KB)备注REST 轮询2.13806.8大量 304空转严重SSE4.72208.2半双工浏览器兼容坑WebSocket9.3984.5全双工内核零拷贝友好结论一目了然WebSocket 延迟减半、吞吐翻倍、内存更省于是拍板“长连接 事件驱动”。核心实现三条代码搞定百万连接1. Netty 服务端连接池Javapublic final class WsServer { // 防御性构造器私有避免随意 new private WsServer(){} public static void main(String[] args){ int port 9888; EventLoopGroup boss new NioEventLoopGroup(1); EventLoopGroup worker new NioEventLoopGroup(0); // 02*CPU try{ ServerBootstrap b new ServerBootstrap(); b.group(boss, worker) .channel(NioServerSocketChannel.class) .childOption(ChannelOption.TCP_NODELAY, true) .childOption(ChannelOption.SO_KEEPALIVE, false) // 自研心跳更灵活 .childHandler(new ChannelInitializerSocketChannel(){ Override protected void initChannel(SocketChannel ch){ ch.pipeline().addLast( new HttpServerCodec(), new HttpObjectAggregator(65536), new WebSocketServerProtocolHandler(/ws), new IdleStateHandler(20, 15, 0, TimeUnit.SECONDS), new WsFrameHandler() // 业务逻辑 ); } }); ChannelFuture f b.bind(port).sync(); f.channel().closeFuture().sync(); } catch (InterruptedException e){ Thread.currentThread().interrupt(); } finally { boss.shutdownGracefully(); worker.shutdownGracefully(); } } }连接池管理靠DefaultChannelGroup内部用ConcurrentHashMap时间复杂度 O(1)下线时channelGroup.flushAndClose()保证批量推送离线消息。2. 消息 ID 生成——雪花算法改进版public class IdGen { private final long workerIdBits 10L; // 支持 1024 实例 private final long seqBits 12L; private final long maxWorkerId ~(-1L workerIdBits); private long workerId; private long sequence 0L; private long lastTs -1L; public synchronized long nextId(){ long ts System.currentTimeMillis(); if (ts lastTs) throw new IllegalStateException(Clock moved backwards); if (ts lastTs) { sequence (sequence 1) ((1 seqBits) - 1); if (sequence 0) ts tilNextMillis(lastTs); // 自旋 } else sequence 0L; lastTs ts; return (ts (workerIdBits seqBits)) | (workerId seqBits) | sequence; } // 防御性阻塞到下一毫秒避免重复 private long tilNextMillis(long last){ long ts; do { ts System.currentTimeMillis(); } while (ts last); return ts; } }时间复杂度 O(1)单实例 1 ms 可生成 4096 个 ID百万并发也扛得住。3. 分布式会话Redis 本地二级缓存热数据最近 5 min放 Caffeine本地命中 50 µs。冷数据降级到 Redis Cluster一致性用Lua脚本保证GETSET原子。下线时先写 Redis再广播UserOffline事件其他节点消费后清本地缓存实现最终一致。避坑指南掉进去一次再也不想回忆心跳超时 ≠ TCP KeepaliveKeepalive 默认 2 h对秒级故障无感自研心跳 20 s 无响应即触发channel.close()配合IdleStateHandler零额外线程。消息重试与幂等客户端带msgId去重服务端用Redis SETNX做去重窗口 30 s防御性即使重复推送也回ACK但不落库避免“重复发货”舆情。灰度迁移新集群预热 30 % 连接后在网关层按userId % 100切流旧连接不下线待其自然心跳超时全程用户无感知。性能优化把硬件榨到最后一滴Off-Heap 内存Netty 默认PooledByteBufAllocator开启-Dio.netty.allocator.typepooled-XX:MaxDirectMemorySize8g把缓冲区搬到堆外GC 停顿从 120 ms→20 ms。Linux 内核调优ulimit -n 1000000打开文件句柄net.core.somaxconn 32768扩大全连接队列net.ipv4.tcp_tw_reuse 1快速回收 TIME_WAIT。编解码使用Protobuf替代 JSON包大小降 60 %CPU 降 25 %对超大消息64 KB开启CompressionHandler阈值动态可配。延伸思考Serverless 时代连接怎么管Serverless 的“实例随时冻结”天然与长连接冲突但也不是无解把连接状态外置到 Redis Stream函数实例只负责计算不 hold 连接用 Event-Bridge 做事件总线WebSocket 网关独立常驻函数按需弹缩冷启动提前预热 池化 Proxy可将“连接漂移”降到 5 s 内。目前我们还在 PoC 阶段欢迎一起踩坑交流。写完回头再看这套“多多智能客服 API”已稳定跑过 618、双 11 两场大促峰值 120 万在线P99 延迟 180 ms全年可用性 99.97 %。代码和压测脚本都开源在内部 GitLab如果你也在做高并发长连接希望这份笔记能帮你少走点弯路。