知识卡片

Akka日志组件的瞬时峰值坑

普通读书笔记卡

内容

雪球IM系统最初的设计目标是提供聊天功能(Netty加自定义网络协议、每个在线Client对应Akka的一个actor、Client在线时用推模式、支持单账号多端同步),但移动互联网时代除了微信QQ之外几乎所有IM都转型成了推送通道,核心指标变成了瞬间峰值性能,原有架构在很多地方就不太合适了,因此做了一系列优化:分配更多资源(专门的推送账号actor池)、精简业务逻辑(重复消息只存ID、实时提醒内容不推给历史设备、不更新非活跃设备的session列表)、把拉黑等无法精简的业务逻辑迁移到本地缓存、以及优化代码(异步加密存储、去除不合理的Akka使用)。其中最有价值的一个具体教训是Akka自带的log adapter踩过的坑:Akka内部用一个专门的actor来处理所有的log event stream,这套机制平时运转正常,但当瞬间峰值到来时,这个event stream会一下子被堵上百万条log,导致GC颠簸非常严重——因为所有日志事件都要排队等这一个actor处理,日志处理速度跟不上日志产生速度,积压的日志对象本身又不断产生GC压力,形成恶性循环。最终的解决办法是绕过Akka自带的log adapter,直接使用logback的appender——优化后线上记录显示,5万/秒(主动限速)的推送持续3分钟,p99性能指标没有明显变化。这条教训揭示的一般性原理是:框架自带的某个子组件(这里是日志),即便在正常负载下工作良好,也可能在其内部设计上暗藏一个隐蔽的串行瓶颈(这里是”所有日志都排队给单个actor处理”),这类瓶颈只有在真正的峰值流量冲击下才会暴露出来;遇到框架自带组件在极端场景下失效的情况,绕过它、直接使用更底层或更专用的实现,往往比试图在框架层面调优更直接有效。

参考来源

- 位置:《高可用架构(第1卷)》第1章《高可用架构案例精选》"1.5 雪球在股市风暴下的高可用架构改造分享"节,"1.5.3 雪球架构优化历程"(源文件:_epub-src/OEBPS/Text/Chapter1_5_4.xhtml) - 结论依据:原文说明"Akka有一个自己的log adapter,内部使用一个actor来处理所有的log event stream。当瞬间峰值到来的时候,这个event stream一下子就堵了上百万条log,导致GC颠簸非常严重。最后的解决办法是,绕过Akka的log adapter,直接使用logback的appender",直接支撑本卡片结论。 - 原始内容:Akka有一个自己的log adapter,内部使用一个actor来处理所有的log event stream。当瞬间峰值到来的时候,这个event stream一下子就堵了上百万条log,导致GC颠簸非常严重。最后的解决办法是,绕过Akka的log adapter,直接使用logback的appender。