背景介绍
项目基于Spring Cloud Netfilx一套的微服务架构,大约有10来个微服务,每个服务都会用到一些基础公用数据(类似于字典之类的),这份数据统一由基础服务进行维护,每个服务要用到时通过RPC调用基础服务获取相关数据,为了减少RPC的调用于是就设计出一套本地缓存方案,各个服务本地缓存一套基础公用数据,但是为了保证本地缓存数据与基础服务的一致性,就需要定时的进行同步,并且每次服务重启后,还需要全量拉取一次。
问题
背景介绍完了,接下来说一下问题,基于这套方案,目前每次同步或者全量拉取时都会造成内存和CPU持续告警。
1、因为一次性拉取的数据太大了(几百M),所有服务全部向基础服务请求,基础服务奄奄一息。
2、各自服务拉回数据后,会发生大量的Json解析,造成CPU告警。
3、各自服务需要把数据在各自集群中进行同步传输,又造成二次伤害。
4、一次性拉回的数据瞬间占据双倍内存,容易导致FGC。
如何解决
问题本身解决起来非常简单,换Redis完事,但是受实际项目约束,要想一次性把所有服务的本地缓存都替换掉,也是挺麻烦的,但是问题又迫在眉睫,所以基于综合考虑,决定先解燃眉之急,再做长远打算。
1、拆分
把一次性拉取拆分成多次小数据量拉取
2、服务交错拉取
各个服务把同步的时间错开,避免同时请求。
3、控制全量同步
取消所有服务重启即拉取的方式,而是通过接口调用,手动控制。
最终解决
通过上述改进方式,可以暂时缓解当前问题,并且容易实施,风险较小。
但是最终要彻底解决这个问题,就是去掉本地缓存的方式,而是采用缓存中间件,比如Redis,既减少了内存的浪费,又避免了全量同步和实时同步的麻烦。
问题回顾
这个问题产生的原因还是因为技术上选型的错误,对于使用本地缓存考虑不足造成的,对于一些占用较大内存的数据不应该设计成本地缓存,否则无论是对于内存的管理、缓存的一致性都会带来一些困扰,比如本文中遇到的这些问题。