SpringBoot集成本地缓存性能之王Caffeine示例详解
SpringBoot 缓存之 @Cacheable介绍
接口内的rpc调用报错,error级别的日志被监控平台报警。
@Cacheable(cacheManager = RedisKeyConstants.CACHE_MANAGER_LOCAL, value = RedisKeyConstants.Gpc.certificate.CACHE_NAME, key = RedisKeyConstants.Gpc.certificate.CACHE_KEY) public User getData(Object obj) { User user = new User(); try { Response res = aliyunClient.doRpcCall(obj); if (!resp.getCode().equals("success!")) { log.error("error happening, code:{}, msg:{}", resp.getCode(), resp.getMsg()); return null; } if (res.getData() == null) { log.error("error happening. rpc return a Null Object, code:{}, msg:{}", resp.getCode(), resp.getMsg()); return null; } } catch (Exception e) { log.error("rpc调用异常", e); return user; } return user; } (1)主要有两个,一个是当rpc调用异常的时候,会被try捕获并且直接return一个实例化的user对象,然后触发@Cacheable注解定义的本地缓存机制,导致异常调用的时候,对象也被缓存,随后直至本地缓存中该条目过期,后面的请求才会发起rpc请求更新缓存,所以对于rpc调用时获取的错误,应该不缓存。
(2)rpc调用成功但是返回了一个空对象,这个时候我直接返回的是null值,导致本地缓存没有存储这个空值,随后当流量走getData这个方法时,因为没有查到缓存,就会一直走rpc调用,造成cpu时间浪费,同时会延长响应时间,如果某一波大流量打进来,rpc服务甚至会挂掉,这个时候会发生缓存击穿。
@Cacheable(cacheManager = RedisKeyConstants.CACHE_MANAGER_LOCAL, value = RedisKeyConstants.Gpc.certificate.CACHE_NAME, key = RedisKeyConstants.Gpc.certificate.CACHE_KEY,unless = "#result==null") public User getData(Object obj) { User user = new User(); try { Response res = aliyunClient.doRpcCall(obj); if (!resp.getCode().equals("success!")) { log.error("error happening, code:{}, msg:{}", resp.getCode(), resp.getMsg()); return user; } if (res.getData() == null) { log.error("error happening. rpc return a Null Object, code:{}, msg:{}", resp.getCode(), resp.getMsg()); return user; } } catch (Exception e) { log.error("rpc调用异常", e); return null; } user.setXXXX(res.getData().getXXXX()); .... return user; } 在@Cacheable注解上添加unless = "#result==null"属性
(1)首先既然rpc异常了就不应该对null值进行缓存,后续的查询不能一直用null缓存。
(2)rpc是暂时故障,被调用方会进行迅速(秒级)的故障转移,比如重启、切换,在故障切换期间
对于RPC服务,可以实现服务降级策略,当检测到RPC服务负载过高或出现异常时,可以临时返回一些兜底数据或执行简化的逻辑以减轻服务压力。
对于高频请求,尤其是批量操作导致的数据访问,可以通过限流策略来控制请求速率,避免短时间内对后端服务(包括缓存和RPC服务)造成过大压力。
当缓存失效后,为了防止同时有大量对同一数据的访问请求打到后端服务,可以引入分布式锁的机制。在更新缓存前通过获取分布式锁来确保同一时间只有一个请求去调用RPC服务更新数据,并将结果写入缓存。其他的请求只需等待缓存更新即可获取数据,这种方式需要确保锁的获取与释放正确管理,以防止死锁或服务延迟。