资讯详情

资讯详情

skynet线上问题查证练习记录(更新中)

cpu暴涨类在匹配服务中新增一个死循环。-- 玩家请求开始匹配functionCMD.start_match(uid,elo_val,name,agent_addr)-- FAULT INJECTION (INJECTED): CPU 死循环 local_fault_counter0whiletruedo_fault_counter_fault_counter1end-- END FAULT INJECTION -- 防重复检查ifuid_bucket_map[uid]thenreturn{okfalse,errmsgalready in matching queue}end-- 找到对应的段位桶localbucket_nameelo.get_bucket(elo_val)localbucket_players_aget_bucket_players(bucket_name)table.insert(bucket_players_a,{uiduid,eloelo_val,namename,agent_addragent_addr,enter_timeskynet.now()/100,})uid_bucket_map[uid]bucket_name rpc.send(chat_mgr,update_player_state,uid,matching)skynet.error(string.format([match_mgr] player enter queue: uid%d name%s elo%d bucket%s total%d,uid,name,elo_val,bucket_name,get_total_queue_size()))return{oktrue}end步骤一、先查看正常情况下的skynet的正常状态服务列表消息队列和cpu内存占用情况查看cpu使用率# 2. 看 Battle 节点的 CPUtop-p$(pgrep-fchatroom.*battle|head-1)-bn1|head-5解析第一行系统概览top - 21:24:02 up 1:13, 0 users, load average: 4.99, 4.75, 4.65字段含义你的值21:24:02当前时间晚上 9:24up 1:13开机到现在 1 小时 13 分—0 users登录用户数0你可能是 ssh 进来的不算load average面试高频考点⚠️ 4.99 / 4.75 / 4.65load average 三个数字指过去 1 分钟、5 分钟、15 分钟的平均负载。含义是等待 CPU 等待 IO 的进程数。你的值 4.99 意味着平均有 5 个进程排队等资源。如果机器是 4 核那已经饱和了。 面试技巧load CPU 核心数 → CPU 瓶颈load 高但 CPU us 低 → IO 瓶颈。第二行进程统计任务: 1 total, 0 running, 1 sleeping, 0 stopped, 0 zombie字段含义total总计进程/线程running正在 CPU 上跑sleeping在等事件网络 IO、定时器等stopped被信号暂停zombie僵尸进程不为 0 就要查第三行CPU 分解 ⭐ 最重要%Cpu(s): 0.0 us, 1.6 sy, 0.0 ni, 98.4 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st这是瓶颈区分的核心依据缩写全称含义告警阈值ususer用户态 CPU你的业务代码 80% → 代码死循环/计算密集sysystem内核态 CPU系统调用 30% → 频繁 IO/锁竞争ninice低优先级进程的 CPU通常忽略ididle空闲接近 100% 没事waiowait等磁盘 IO 的 CPU 时间 10% → 磁盘瓶颈hihardware irq硬件中断通常 1%sisoftware irq软中断网络包处理 5% → 网络包太多ststeal虚拟机被宿主机偷走的 CPU云主机关注 5% 不正常你的当前状态us0.0, id98.4→ CPU 几乎空闲还没触发故障。故障触发后你会看到us99.0, id0.0→ 用户态代码吃满 CPU典型死循环。记忆口诀us 高 代码有 bug死循环、复杂计算 sy 高 系统调用太多频繁 IO、锁 wa 高 磁盘慢数据库、日志写盘 si 高 网络包太多DDoS、广播风暴第四、五行内存MiB Mem : 3876.0 total, 112.2 free, 2192.3 used, 1571.5 buff/cache MiB Swap: 2048.0 total, 2045.5 free, 2.5 used. 1408.1 avail Mem字段含义你的值total总内存3.8 GBfree完全空闲112 MB看着少但正常used已用2.2 GBbuff/cacheLinux 用空闲内存做文件缓存可随时释放1.5 GBavail Mem实际可用 free 可释放的 cache1.4 GB ✅Swap used用了多少交换分区2.5 MB几乎没用好事free低别紧张看available。Linux 会尽可能把空闲内存用作文件缓存这是正常的。只有available持续下降才说明内存不够。 排查时你只需要看这三个排查 CPU 问题 → 看 %Cpu 那行us 高 代码问题sy 高 系统调用 排查 IO 问题 → 看 %Cpu 那行wa 高 等磁盘 排查内存问题 → 看 Mem 那行available 持续下降 内存泄漏 排查负载 → 看 load average超过 CPU 核数 系统过载二、查看哪个进程cpu占用使用top看出是skynet的一个进程接着查看skynet是谁cat /proc/{PID}/cmdline | tr \0 PID填写top查询到的异常进程id确定是battle进程有问题# 对 battle 进程采样 10 秒看 CPU 时间花在哪里sudoperftop-p8999看收集结果cpu全在跑lua解释器由于我的linux无法使用pstack为了不折腾环境就是用gdb# gdb 是 pstack 的底层直接用它看 Lua 调用栈sudogdb-p$(pgrep-fbattle|head-1)-batch-exthread apply all bt2/dev/null|grep-A5lua输出中找包含luaD_precall或luaV_execute的栈帧附近会有关键线索。但实际还是看不出来于是就必须开启debug_console来查证问题dbgcmd10TASK但遇到问题使用dbgcmd指令会在cpu暴涨时卡住无法使用我试了下再正常情况是能正常输出.dbgcmd 10 TASK没输出意味着match_mgr 已经彻底卡死拒绝处理任何消息——包括调试命令。 核心学习点Skynet 的死循环服务 彻底失联。dbgcmd、inject、TASK都走skynet.call服务必须在消息循环中才能响应。死循环 消息循环被卡住 所有 debug 命令超时。我后续的解决方案就是引入metrics模块监控rpc组件哪个操作耗时非常高就直接定位。暂时没有找到其他查证手段。内存泄漏环境搭建运维监控机器内存业务支持内存监控组件目前我是开发了一个子服务需要监控的服务就在启动时创建memory_monitor然后定时调用该服务中的gc接口用于监控memory_monitor 核心逻辑架构每个节点一个实例battle / logic独立轮询本节点服务 │ ├─ timer.every(10s) → 回调 │ │ │ ├─ ① 遍历 watch_listskynet.call 每个服务的 diag_mem │ ├─ ② 汇总 total检查两档告警 │ ├─ ③ 上报 Prometheusgauge_set │ └─ ④ 输出日志基线 / 常规 / 告警 / 恢复 │ └─ 被监控服务只需暴露 CMD.diag_mem() 接口两档告警告警触发器防抖单服务超阈值kb ALERT.per_servicealert_state[svc]标记只告一次降回阈值以下发[RECOVER]10 分钟增长recent - oldest ALERT.delta_10min无防抖每次超都告滑动窗口 60 个采样点滑动窗口实现history { {tick, total}, {tick, total}, ... } ↓ 每次 tick: push 新采样点 → 移除 tick now-6000 的旧点 ↓ delta_10min history[#history].total - history[1].total关键设计点skynet.call会永久阻塞被监控服务如果不响应无CMD.diag_mem或 dispatch 报错定时器链断裂之后所有轮询停止。用pcall 失败日志兜底。service 名称必须用小写skynet.queryservice查的是.service本地注册表来自skynet.uniqueservice不是harbor.globalname的集群注册表——大小写不匹配会永久阻塞。collectgarbage(count)返回浮点数所有string.format用%.0f不能用%d。detail字段是诊断内存泄漏的关键被监控服务在diag_mem中返回各数据结构的条目数日志输出match_mgr68KB (match_history3, queue_players0)哪个字段独涨就定位到泄漏源。步骤第一层运维告警机器级Prometheus / Grafana 监控节点 RES/RSS → 单节点内存持续上涨不回落 → 触发运维告警通知研发介入这一层只告诉你哪个节点出了问题不涉及具体服务。第二层memory_monitor 定位服务服务级查看 memory_monitor 日志 → 常规行格式 battle total92KB ⬆ delta12KB d10m12KB | match_mgr92KB (match_history2, queue_players0, uid_mappings0)判定方法观察点结论total持续单调上涨确实有泄漏delta每轮都为正泄漏速率稳定[ALERT] per_service触发单服务越过阈值[ALERT] 10min delta触发长期趋势确认泄漏某服务显示OFFLINE服务挂了或被监控的服务没注册第三层detail 字段定位数据结构结构级match_mgr92KB (match_history2, queue_players0, uid_mappings0) ───────────┬────────── ──────┬────── ───────┬─────── ↑ 独涨 ↑ 归零 ↑ 归零 → 泄漏源就是 match_history判定规则在所有detail字段中正常字段随业务波动归零泄漏字段只增不减。把业务跑一轮再看第二轮的 detailtext第一轮 match 后: match_history1, queue_players0 第二轮 match 后: match_history2, queue_players0 ← 只有 match_history 在涨第四层代码 diff 定位提交代码级git log -- chatroom/service/match_mgr.lua → 找到近期改动的 commit → git diff HEAD~3..HEAD -- chatroom/service/match_mgr.lua → 重点关注新增的全局表、table.insert 没有对应 remove、闭包引用等常见泄漏模式速查模式特征detail 表现全局表只增不减table.insert无对应的table.remove某个 field 单调递增事件监听未解绑skynet.call或timer.every不 cancel内存缓慢增长detail 无明显特征闭包持有大对象匿名函数捕获了 table 引用不释放detail 看不出需用collectgarbage(count)手动对比协程泄漏skynet.fork创建的协程永不退出服务总内存涨但 detail 各字段正常总结四步闭环
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →