skynet性能优化学习
常见问题消息堆积 / 服务延迟飙高现象某个服务 mq长度持续增长skynet.stat看到 mqlen很大同节点其他服务也跟着抖。根因单条 callback 太重在 skynet.dispatch里做了同步重计算协议编解码、JSON 序列化、大表遍历阻塞了 Worker 线程这条 mq 里的后续消息只能排队。skynet.call链式 / 广播滥用A→B→C→D 一长串 call或者一广播就是几十个服务发送方被全程挂起接收方 mq 瞬间打满。跨节点 call 没设 timeout对方节点抖动skynet.call的 coroutine 一直挂session 映射表和 mq 都堆。优化重计算拆出去CPU 密集型活丢给专用的计算服做 skynet.send 异步回传或切 C 模块用 skynet_worker如果有甚至外部进程。分帧 / forkLua 层大循环 for i1,1e6 do … end改成每帧处理一段skynet.fork让出。call 必带 timeoutskynet.call(addr, “lua”, …, 100)0.6 语法或外层用 pcall timeout兜底。广播用 multicast或 clusterd 批量别自己 for 循环 send。Lua GC 压力大现象服务 cpu占用里 GC 占比 30%每隔几秒一次微卡顿时延 P99 跳内存缓慢涨不降根因每服务一个 LuaVMSkynet 的 Lua 服务本质是 snlua每个都独立 VM、独立 GC。服务多了几千个 agentGC 总开销线性放大。临时对象疯狂造skynet.call每次生成新 session 表、closure协议 unpack 每消息 new 一个 table字符串拼接用 …。热服务消息 QPS 高网关、聊天、排行榜这类每秒上千消息的服GC 被反复触发。优化对象复用协议层用 protobuf/sproto的 decode复用 table或手写 C 解码。session/coroutine 映射表定期清理避免闭包捕获大对象。LuaJIT 开起来确认编译开了 LUAJIT_ENABLEJIT 能把热循环编成机器码GC 压力也间接降。合批处理网关类服务把多条消息攒一波再 dispatch减少 per-message 的 Lua 调用次数。超大服拆小单服 agent 过多时按 uid 区间拆多个 snlua 进程GC 并行度反而上来了。全局消息队列global_queue争抢现象Worker 线程数开得多≥ 8时吞吐量不再涨甚至略降perf top看到 spinlock/ atomic相关指令占比高根因global_queue 是无锁循环数组 CAS 实现的3.x 后改成了更轻量的版本但高并发下仍有争抢。当Worker 数 ≈ 核数 × 2单节点 QPS 极高 50w msg/s大量服务 mq 频繁入队Timer/Socket 两个生产者狂 push多个 Worker 同时 pop Timer/Socket 同时 pushCAS 失败重试变多。优化Worker 数 CPU 物理核数别超超了反而自旋空转。热点服务拆分到不同节点让 global_queue 的入队源少一个 Socket/Timer 集中点。3.x 之后确认用的是新版本老版 2.x 的 global_queue 锁更重。Socket 层瓶颈现象连接数上了 1wCPU 花在 socket_server上多收发包延迟涨但业务服 mq 并不满根因每一帧 Socket 线程把所有 fd 事件转成消息 push 到 snlua-gate的 mqgate 服瞬间被冲。socket.write大包阻塞单个包 64KB 或 send buffer 满时Socket 线程会自旋等。TCP_NODELAY / 缓冲区没调小包频发时 Nagle Lua 层频繁 write 来回折腾。优化gate 服分片按连接 ID % N 拆多个 gate 服务每个管一部分 fd。合包 / 限流Lua 层协议加长度前缀避免半包反复 callback上行消息频率做简单 throttle。socket 参数调优socket.limit控制 send buffer内核侧 somaxconn、tcp_mem一起调。Timer 模块抖动现象大量短周期 timer比如每 100ms 一个 agent 心跳某帧突然卡 10msskynet.timeout精度漂移根因Skynet 定时器是四级时间轮近似 Linux 实现单线程Timer 线程扫。当同一时刻到期 timer 太多比如 1w 个 agent 同时加的 1s 超时timer 回调里又 skynet.call或做重活Timer 线程这一帧就拖长间接影响全局消息生产节奏。优化timer 回调只做轻量事发一条消息给 Worker 服去干活别在 timer 线程里 call。同类 timer 合并1w 个 agent 各自 1s 心跳 → 改成一个心跳服 1s 触发一次再 send通知所有 agentagent 只注册一次用个计数器就行。长周期用 cron思路别用大量 100ms 短 timer 轮询。跨节点Cluster性能问题现象跨节点 call延迟比本地高一个数量级集群间带宽打满根因序列化开销cluster 默认用 Lua Table 序列化跨机每消息编解码一次。connection 单 TCP 连接默认两个节点间只有一条 TCP所有服务共享头部争抢 串行化。没做 batch高频小消息一条一条发TCP 小包多。优化换 sproto / protobuf 做 cluster 序列化比原生 table 序列化省 CPU 也省带宽。高频通道拆独立 connection3.x 支持多 harbor 或多 TCP 通道。批量 call多个小请求攒一波一次 send 一次回包。排查工具链skynet.stat()—— 看 mqlen、cpu、gc 时间skynet.mem_limit()—— 看内存profile模块 —— Lua 层函数级耗时debug控制台list、stat、mem、netstatperf top/ perf record—— C 层热点global_queue 自旋、socket_server 等jeprofLuaJIT —— 内存泄漏定位