三步为你的Springboot应用集成Actuator
|
M消息作为闲鱼用户重要的交易咨询工具,核心目标有两点,第一是保证用户的消息不丢失,第二是保证用户的消息及时送达接收方。IM消息根据消息的接收方设备是否在线,分为离线和在线推送,数据显示目前闲鱼每天有超过一半以上的IM消息是走在线通道的,而在线消息的到达率、及时性是直接影响用户体验的,本文将着重分析优化在线通道的稳定性,保证用户消息及时到达。 面临哪些问题 端内长连接中断
在IM场景中,用户与云端通信频繁,且为了实现用户的消息及时到达,往往采用云端下推消息的方式触达用户,所以用户在线时设备与云端会维持一条TCP长连接通道,可以更轻量级的与服务端进行交互,现代IM即时通讯的下行消息都是通过长连下发的,闲鱼消息使用的是ACCS长连接,ACCS是淘宝无线提供的全双工、低延时、高安全的通道服务。但是由于用户设备网络状态的不确定性,可能会发生各种各样的网络异常情况导致长连接通道中断,长连接一旦意外中断,就会导致用户无法及时收到在线消息,所以我们需要尽可能及时的感知到长连中断并尝试重连。 当时内存泄漏的场景是,用本地缓存(公司基础架构组自己研发的框架)存放了商品数据,商品数量不算太多,几十万的样子。如果只存热点商品,内存占用不会太大,但是如果存放全量商品,内存就不够了。 初期我们给每个缓存记录都加了7天的过期时间,这样就可以保证缓存中绝大部分都是热点商品。不过后来本地缓存框架经过一次重构,过期时间被去掉了。没有了过期时间,日积月累本地缓存越来越大,很多冷数据也被加载到了缓存。 直到有一天接到告警短信,提示堆内存过高。赶紧通过jmap( jmap -dump:format=b,file=文件名 [pid] )下载了堆内存快照,然后用eclipse的mat工具分析快照,发现了本地缓存中有大量的商品记录。定位问题后赶紧让架构组加上了过期时间,然后逐个节点重启了服务。 亏了我们加了服务器内存和JVM堆内存监控,及时发现了内存泄漏的问题。否则随着泄漏问题日积月累,如果哪天真的OOM就惨了。 所以技术团队除了做好CPU,内存等运维监控,JVM监控也非常重要。 故障三:幂等问题很多年前,笔者在一家大型电商公司做Java程序员,当时开发了积分服务。当时的业务逻辑是,用户订单完结后,订单系统发送消息到消息队列,积分服务接到消息后给用户积分,在用户现有的积分上加上新产生的积分。 由于网络等原因会有消息重复发送的情况,这样也就导致了消息的重复消费。当时笔者还是个初入职场的小菜鸟,并没有考虑到这种情况。所以上线后偶尔会出现重复积分的情况,也就是一个订单完结后会给用户加两次或多次积分。 后来我们加了一个积分记录表,每次消费消息给用户增加积分前,先根据订单号查一遍积分记录表,如果没有积分记录才给用户增加积分。这也就是所谓的“幂等性”,即多次重复操作不影响最终的结果。 实际开发中很多需要重试或重复消费的场景都要实现幂等,以保证结果的正确性。例如,为了避免重复支付,支付接口也要实现幂等。 故障四:缓存雪崩我们经常会遇到需要初始化缓存的情况。比如,我们曾经经历过用户系统重构,用户系统表结构发生了变化,缓存信息也要变。重构完成后上线前,需要初始化缓存,将用户信息批量存入Reids。 每条用户信息缓存记录过期时间是1天,记录过期后再从数据库查询最新的数据并拉取到Redis中。灰度上线时一切正常,所以很快就全量发布了。整个上线过程非常顺利,码农们也很开心。 不过,第二天,灾难发生了!到某一个时间点,各种报警纷至沓来。用户系统响应突然变得非常慢,甚至一度没有任何响应。查看监控,用户服务 CPU突然飙高(IO wait很高),MySQL访问量激增, MySQL 服务器压力也随之暴增,Reids缓存命中率也跌到了极点。 依赖于我们强大的监控系统(运维监控,数据库监控,APM全链路性能监控),很快定位了问题。原因就是Reids中大量用户记录集中失效,获取用户信息的请求在Redis中查不到用户记录,导致大量的请求穿透到数据库,瞬间给数据库带来巨大压力。同时用户服务和相关联的其他服务也都受到了影响。 这种缓存集中失效,导致大量请求同时穿透到数据库的情况,就是所谓的“缓存雪崩”。如果没到缓存失效时间点,性能测试也测不出问题。所以一定要引起大家注意。 所以,需要初始化缓存数据时,一定 要保证每个缓存记录过期时间的离散性。 例如,我们给这些用户信息设置过期时间, 可以采用一个较大的固定值加上一个较小的随机值。比如过期时间可以是:24小时 + 0到3600秒的随机值。 故障五:磁盘IO导致线程阻塞问题发生在2017年下半年,有一段时间地理网格服务时不常的会响应变慢,每次持续几秒钟到几十秒钟就自动恢复。 如果响应变慢是持续的还好办,直接用jstack抓线程堆栈,基本可以很快定位问题。关键持续时间只有最多几十秒钟,而且是偶发的,一天只发生一两次,有时几天才发生一次,发生时间点也不确定,人盯着然后用jstack手工抓线程堆栈显然不现实。 好吧,既然手工的办法不现实,咱们就来自动的,写一个shell脚本自动定时执行jstack,5秒执行一次jstack,每次执行结果放到不同日志文件中,只保存20000个日志文件。
Shell脚本如下: (编辑:广元站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |

