惊魂48小时,阿里工程师如何紧急定位线上内存泄露?

  • 时间:
  • 浏览:1
  • 来源:大发五分时时彩—大发分分时时彩

因为前端Proxy最主要的内存开销是基于订阅实现的高效地址缓存,因此,亲戚亲戚大家首先通过gdb查看得人维护了缓存的unordered_map大小,结果你你这个 大小是符合预期的(正如监控指标显示的,估算下来你你这个 空间占用太多再超过1GB),远远达只能太多再 撑起没法了内存泄漏的地步。这点亲戚亲戚大家进一步通过strings core文件也得到了证实,string对象空间存在一种多,一时间,亲戚亲戚大家的调查陷入了困境。

没法了新的哪些的什么的问题来了,哪些Followers为哪些不回复轻量的心跳请求呢?这次没法了直接的日志来解答亲戚亲戚大家的疑惑,还好,有间接信息:出哪些的什么的问题前Follower的日志输出存在了长时间的中断(超过了触发退出Quorum的阈值),你你这个 在对分布式协调服务有着频繁请求访问的某业务集群中几乎是不可想象的!亲戚亲戚大家更太多再 相信后端程序hang住了,而与非 压根没法了用户请求打过来。

亲戚亲戚大家首先排除了网络哪些的什么的问题,通过tsar命令查看机器上网络各项指标正常,通过內部的网络平台查看机器上联网络设备以及网络链路也均是健康请况。回到日志来分析,亲戚亲戚大家从Leader日志中找到了线索,上述报警时间点,均有“Leader主动关闭了与Follower的通信通道”没法了另另4个 事件。

该风险隐患在2019年10月下旬某天开始浮现,只能24小时的时间里,值班同学陆续收到多个线上电话报警,显示某业务集群中分布式协调服务程序异常:

稳定性工作只能从细节处入手,只能亲戚亲戚大家针对线上服务的每二根报警因为是服务指标的一丝异常,太多再 追根溯源,找到root cause,并持续跟进风险修复,原本一定太多再 锤炼出更加稳定的分布式系统。“路漫漫其修远兮,吾将上下而求索”,与诸君共勉。

1)业务方停止错误访问行为,解决分布式协调服务前端Proxy持续走到错误路径,触发内存泄露;

2)原本端Proxy代码层面彻底修复掉你你这个 bug,因此对线上分布式协调服务Proxy做一轮升级;

有关你你这个 对象的大面积泄露,定位到最终因为着实是跟亲戚亲戚大家对Proxy日志分析有关,亲戚亲戚大家在日志中发现了少许非法访问请求:客户端尝试解析某个角色注册的服务地址,因此却使用错误的集群名参数。在单个Proxy机器上1s时间里最多刷出10000+原本的错误日志,没法了会太多再因为持续走到原本错误路径因为的对象内存泄露呢?

原文发布时间:2019-12-20

作者:朱云锋

本文来自阿里云相互相互合作伙伴“阿里技术”,了解相关信息太多再 关注“阿里技术”。

阿里妹导读:云计算场景下的大规模分布式系统中,网络异常、磁盘IO异常、时钟跳变、操作系统异常乃至软件一种因为存在bugs等,均给分布式系统正确运行带来了挑战。持续的监控报警完善是打造稳定高可用分布式系统过程中非常重要的工作,你你这个 也就要求亲戚亲戚大家研发同学从细节处入手,本文将介绍的场景是针对线上报警的一丝异常,抽丝剥茧找到内存泄露的root cause,全程48小时,跟进修复了潜在风险隐患,并进一步富足完善监控报警体系的过程。

在明确了分布式协调服务Proxy程序存在内存泄露风险以后,亲戚亲戚大家紧急巡检了线上其它集群,发现该哪些的什么的问题一种个例。大促在即,你你这个 风险隐患只太多再 留到双十一的时间点,在gcore了前端Proxy现场以后,亲戚亲戚大家做了紧急变更,逐台重启了上述风险集群的前端Proxy程序,一种先缓解了线上风险。

对照这块的代码,亲戚亲戚大家仔细研究了一下,你以为,CheckCall对象正常是会走到执行逻辑的(common::closure在执行以后自动会析构掉,释放内存),因此在异常路径下,譬如中间的非法集群名,没法了你你这个 函数会直接return掉,相应的CheckCall对象太多再被析构,随着业务持续访问,也就持续产生内存泄露。

进一步查看系统日志,发现每段机器上前端Proxy程序因为存在过因为内存过高 的OOM错误而被系统KILL的事件,至此哪些的什么的问题初步定位,亲戚亲戚大家开始转向调查前端Proxy内存泄露的哪些的什么的问题。

当然,根本的修复土办法 还是要原本端Proxy针对CheckCall的异常路径下的解决,亲戚亲戚大家的修复土办法 是遵循函数实现单一出口原则,在异常路径下也同样执行该closure,在执行逻辑中间判断错误码直接return,即不执行实际的CheckCall逻辑,只触发自我析构的行为。该修复在双十一以后将发布上线。

下图展示了该分布式协调服务的系统架构,后端是基于Paxos实现的一致性维护功能模块,前端代理客户端与一致性服务单元的通信,支持服务能力水平扩展性。因为后端分布式一致性服务单元由5台Master机器组成,太多再 容忍一起去2台机器挂掉,因此上述报警均没法了发现对服务可用性产生影响。因此,在短时间之内频繁存在单个Master服务程序异常,你你这个 对于服务稳定性是个极大隐患,有点是对于作业调度强依赖分布式协调服务的某业务。由此,亲戚亲戚大家开始集中人力重点调查你你这个 哪些的什么的问题。

很自然地,亲戚亲戚大家想知道为哪些会频繁存在Leader关闭与Follower通信通道的事件,答案同样在日志中:Follower长时间没法了发送请求给Leader,包括Leader发给过来的心跳包的回复,因此被Leader认定为异常Follower,进而关闭与之通信通道,将其踢出当前Quorum。

方案二的动静比较大,大促以后因为没法了足够的升级、灰度窗口,最终亲戚亲戚大家选取了方案一,根据日志中持续出现的你你这个 非法访问路径,亲戚亲戚大家联系了业务方,协助调查确认业务哪些客户端程序在使用错误集群名访问分布式协调服务,进一步找到了因为。最终业务方通过紧急上线hotfix,消除了错误集群名的访问行为,该业务线分布式协调服务前端Proxy程序内存泄露趋势因此得以控制,风险解除。

在没法了其它更多调查思路的请况下,基于后端分布式一致性服务单元是基于java实现的事实,亲戚亲戚大家查看得人Follower存在哪些的什么的问题时间段的gc日志,结果找到了因为:java gc相关的ParNew耗时太多(当天日志因为被清理,下图是该机器上的类似日志),亲戚亲戚大家知道java gc过程是有个STW(Stop-The-World)机制的,除了垃圾收集器,其余程序完整篇 挂起,你你这个 就太多再 解释为哪些后端Follower程序会短时hang住。

大神提供了另另4个 内存泄露排查工具(说明一下,你你这个 工具基于规整的tcmalloc的内存管理土办法 来分析的),通过符号表找到每个vtable,因此太多再 知道虚表地址,即每个虚函数类的对象前8字节的内容,你你这个 工具厉害的地方在于摆脱了gdb依赖,直接根据程序申请的所有内存块分析,找到所有泄露内存块地址,进一步统计出每个虚表对应类的对象数目。具体你你这个 工具实现细节不再赘述,最终亲戚亲戚大家统计出来的所有出现频率超过10W的虚表信息,找到了罪魁祸首:你你这个 common::closure的对象泄露了高达16亿+。

继续回来调查哪些的什么的问题,亲戚亲戚大家在重启Proxy程序以后,gcore保留了现场(这里要强调一下,线上gcore一定要谨慎,有点是内存占用没法了大的程序,很容易造成请求解决hang住,亲戚亲戚大家基于的考虑是该分布式协调服务的客户端是有超时重试机制的,因此太多再 承受一定时长的gcore操作)。

你你这个 哪些的什么的问题的rootcause定位以后,摆在亲戚亲戚大家转过身的修复土办法 有另另4个 :

好了,现在太多再 直观地解释触发报警因为了:Follower长时间与Leader失联,触发了退出Quorum逻辑(因为退出Quorum过程不难 一段话,进一步会触发直接退出程序逻辑,快速恢复)。

按照你你这个 思路,亲戚亲戚大家进一步在Follower机器上使用top命令查看程序内存占用请况,结果发现机器上混合部署的前端Proxy程序使用的内存因为达到整机66%+(此时后端一致性程序实际占用的物理内存也因为达到1000%左右)。

这时亲戚亲戚大家想到了兄弟团队某大神的大作,介绍了在线上环境调查C/C++程序内存泄露哪些的什么的问题(因为会有同学提到valgrind你你这个 工具干嘛太多再?首先你你这个 神器在测试环境是必备的,因此终究因为存在太多漏掉的场景发布上线了因为线上内存泄露。另外,大型项目中会暴露valgrind运行太慢的哪些的什么的问题,甚至因为程序只能正常工作),这里提供了原本厚度来调查内存泄露:虚表。每个有虚函数的类与非 个虚表,同另另4个 类的所有对象与非 指针指向同另另4个 虚表(通常是每个对象的前8个字节),因此统计每个虚表指针出现的频度就太多再 知道原本的对象被分配了有十有几个 ,数量异常一段话没法了就存在内存泄露的因为。

该业务对分布式协调服务的服务发现功能是重度依赖的。以本次调查的业务集群为例,单集群注册的服务地址数达到240K,解析地址的活跃会话数总量达到41000W,因此,分布式协调服务的稳定性直接影响着集群内业务作业的健康运行。

根据closure的参数类型信息,亲戚亲戚大家减慢定位到了具体的类CheckCall:

着实亲戚亲戚大家的java程序申请的初始内存较大,因此实际分配的是虚拟内存,ParNew耗时太多另另4个 很大因为性是机器上实际物理内存过高 了。