《后端程序猿 · 基于 Lettuce 实现缓存容错策略》

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗

🌻 近期刚转战 CSDN,会严格把控文章质量,绝不滥竽充数,如需交流,欢迎留言评论。👍

文章目录

写在前面的话

笔者所在公司由于存在大量业务场景,是借助Redis缓存实现的,因此缓存的健康状态对框架整体的影响较大。

针对这一问题,在框架封装过程中,采用了基于 Lettuce 事件总线实现的缓存容错策略和环境监测。

本篇博文鉴于此方案,展开描述,让我们絮絮道来。


背景说明

在高并发场景中,通常会引入Reids缓存机制,以此减轻数据库的查询压力,增加系统吞吐量。

例如,以Spring开发框架为例,开发者通常会选择采用@Cacheable等注解方式,便捷实现缓存的快速利用。

在传统的开发模式下,当Redis缓存宕机的时候,该注解所标注的方法会直接抛出异常,对功能调用方不太友好,并且针对缓存环境恢复的情况下,没有自动切换缓存策略的机制。开发人员往往要对单独接口进行缓存降级策略的编写,往往容易出现疏漏。开发者即使采用其他缓存客户端代码的使用方式,都不可避免的需要面对这个问题。

同时,由于Redis集群部署成本较高,部分企业并不会采用集群模式,往往不能保障Redis的高可用。

综上所述,Redis在宕机情况下的容错应对机制,是框架设计人员必须要考虑的。

【@Cacheable 技术简介】

@Cacheable 是 Spring 自带的注解,便捷操作缓存的。对于使用 @Cacheable 标注的方法,Spring在每次执行前都会检查Cache中是否存在相同key的缓存元素,如果存在就不再执行该方法,而是直接从缓存中获取结果进行返回,否则才会执行并将返回结果存入指定的缓存中。

【关于缓存容错策略增强】

传统使用Cacheable注解Redis方法时,如果Redis服务器挂了,就直接抛出异常了,

java.net.ConnectException: Connection refused: connect

新框架对Spring的@Cacheable功能进行了增强,支持毫秒级故障自动降级与自动恢复已支持组件。


方案介绍

本方案通过自定义拓展 SpringRedisCacheManager 缓存处理器,设置不同环境的缓存处理策略,并且基于 LettuceEventBus 机制实现缓存环境监测和策略切换,以此增强单机模式下的 Redis容错性。通过这种方式,解决了传统缓存运用的弊端,开发人员可以正常使用@Cacheable,当缓存宕机的话,可以根据项目级配置,指定想要的缓存策略,比如按包装异常抛出策略、或执行原逻辑策略等。当缓存恢复的时候,会自动恢复切换到正常的缓存策略,不影响用户操作,切换不需要人工干预。当然,这一过程,对业务开发人员也是不需要特别关注的,开发人员只需要正常使用缓存场景,不需要为单独接口考虑缓存降级策略。同时,本方案支持采用延迟任务模式,不断确保缓存健康状态下缓存处理器的正确性,并做出相应响应。


具体实现

本方案自定义拓展缓存处理器,基于 Lettuce 的 EventBus 机制实现缓存环境监测和缓存策略切换。完成了对缓存宕机情况下,可以定制缓存容错策略。同时可以根据缓存环境的健康,自动切换缓存策略,增强了系统的稳定性,减少了人工运维成本。

本方案的缓存服务端不局限于Redis,缓存操作方式也不局限于Spring的@Cacheable方式,为方便理解,下文暂以这两种模式声明。

Step1、自定义缓存管理器,继承Spring自带的CacheManager,在其构造函数中,设置失败时的缓存处理器,成功时缓存处理器,当前激活缓存处理器等属性,并重写获取缓存的相关方法,同时新增同步缓存处理器的切换方法;

Step2、定义失败时缓存处理器,读取服务的项目级策略配置,示例代码如下,根据指定的缓存策略枚举,激活对应的缓存容错策略;

yaml 复制代码
xxxx:
  cache:
    # degrade=自动降级
    # 当Redis服务出现故障时,不再查询Redis缓存,直接执行原有的方法逻辑
    # fail_fast=快速失败
    # 当Redis服务出现故障时,调用被标记了@Cacheable的方法将直接抛出 
    cache-fail-strategy: degrade  

Step3、同理,类似步骤2,定义成功时的缓存处理器;

Step4、在程序初始化的时候,利用Lettuce的客户端API,检查缓存当前健康状态,选择对应的缓存处理器为当前激活的缓存处理器;

Step5、在程序初始化的时候,利用Lettuce的EventBus事件总线监听机制,包含但不限于监听ConnectionActivatedEvent和ConnectionDeactivatedEvent等事件,并做出相应处理;

Step6、当监听到ConnectionDeactivatedEvent事件时,代表当前缓存状态异常,此时将触发步骤1的自定义缓存管理器的up方法,将失败时缓存处理器设定为当前激活状态,此时若逻辑执行到@Cacheable等注解方式标注的方法,将根据缓存容错策略,例如进行异常报错或直接执行原方法等;

Step7、当监听到ConnectionActivatedEvent事件时,代表当前缓存状态健康,此时将触发步骤1的自定义缓存管理器的down方法,将失败时缓存处理器设定为当前激活状态,此时若逻辑执行到@Cacheable等注解方式标注的方法,将继续正常优先从缓存加载数据;

Step8、同时,在事件监听逻辑中,利用ScheduledThreadPoolExecutor线程池的scheduleAtFixedRate方法,开启间隔延迟任务,不断检查缓存状态,确保上述缓存切换的准确性,并将结果输出日志;


总结陈词

上文介绍了博主所在公司采用的缓存容错策略方案,仅供参考。

从内容上看,主要是方案介绍(PS:因为是技术交底书口吻编写),有需要源代码的欢迎留言交流。

💗 后续会逐步分享企业实际开发中的实战经验,有需要交流的可以联系博主。

相关推荐
魔道不误砍柴功8 分钟前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
NiNg_1_2348 分钟前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
闲晨11 分钟前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
测开小菜鸟2 小时前
使用python向钉钉群聊发送消息
java·python·钉钉
P.H. Infinity3 小时前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天3 小时前
java的threadlocal为何内存泄漏
java
caridle3 小时前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express
^velpro^3 小时前
数据库连接池的创建
java·开发语言·数据库
苹果醋33 小时前
Java8->Java19的初步探索
java·运维·spring boot·mysql·nginx
秋の花3 小时前
【JAVA基础】Java集合基础
java·开发语言·windows