码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 抽丝剥茧,Redis使用事件总线EventBus或AOP优化健康检测


    目录

    前言

    Lettuce

    什么是事件总线EventBus?

    Connected

    Connection activated

    Disconnected

    Connection deactivated

    Reconnect failed

    使用

    一种另类方法—AOP

    具体实现


    前言

    在上一篇深入浅出,SpringBoot整合Quartz实现定时任务与Redis健康检测(二)_往事如烟隔多年的博客-CSDN博客

    文章中,通过在SpringBoot中使用Quartz实现了Redis健康状态的检测,虽然一定程度上解决了Redis的连接问题,但仍存在一些问题,比如当Redis在定时检测的间隔时间内断开连接时,此时有用户请求进入时仍然会出现服务不可用的状态,那么有办法能够在Redis断开时通知到SpringBoot程序,进而实现Redis到MySQL的切换呢?本文将在之前的基础上使用Redis的事件总线机制来解决该问题。

    Lettuce

    在SpringBoot 2.x版本中默认使用的Redis客户端为Lettuce,与早期的Jedis不同,Lettuce底层由Netty构建,异步IO驱动提升效率,支持高并发,且线程安全。由下图可以看出项目中引入了Redis依赖后使用的客户端为Lettuce。

    ​

    什么是事件总线EventBus?

    虽然引入了Lettuce,但是面临的问题应该如何作以解决呢?这里就得讲一下Redis的EventBus了,可以通过Lettuce官方文档了解相关信息,

    Connection Events · lettuce-io/lettuce-core Wiki · GitHub

    由于客户端使用了lettuce6.1.10,因此我们关注内容如下Connection Events · lettuce-io/lettuce-core Wiki · GitHub

    ​

    ​

    不难看出自lettuce提供了一个用于使用连接事件的实例代码,通过订阅事件总线可以监听到当前Redis的连接事件状态,连接事件状态分为如下五种:

    Connected

    传输协议连接建立,即TCP三次握手完成,Socket通信建立,事件类型ConnectedEvent。

    Connection activated

    逻辑连接建立,当前处于活动状态并且可以开始分发Redis命令,此时ping命令已经发送并收到回应,可以正常使用Redis命令。事件类型ConnectionActivatedEvent。

    Disconnected

    连接关闭,传输协议关闭或重置。该事件发生在定期关闭和连接中断时,事件类型Disconnected。

    Connection deactivated

    连接停用,逻辑连接停用,内部的处理状态被重置,并且isOpen()标志被设置为false,该事件发生在定期关闭和连接中断时,事件类型Connection deactivated。

    Reconnect failed

    重连失败(自5.3版起),尝试重连失败。包含重连失败和重连计数器。事件类型RecconectFailedEvent。

    如上五种类型中需要重点关注的是Connection activated,当该连接事件发生时,将Redis连接标识置为true即可。

    使用

    根据前文提到的官方文档给出的代码,使用后会发现并未监听到Redis的连接状态。

    1. RedisClient client = RedisClient.create()
    2. EventBus eventBus = client.getresources().eventBus();
    3. eventBus.get().subscribe(e -> System.out.println(event));
    4. ...
    5. client.shutdown();

    此时进入create()方法内部查看情况,可以看到默认无参的构造函数创建了一个RedisClient对象,其中第一个参数为clientResources对象,第二个参数为一个字符串,即空的URI,想必在这里已经猜到为什么无法监听连接了。

    由于并没有传入对应的Redis资源或URI路径,导致无法进行正常的事件监听。

    ​

    在源码中不难发现有其它同名含参的构造方法, 此处使用第三个含有ClientResource对象的方法,因为可以通过Spring注入该对象,从而省去手工构造的繁琐步骤。

    ​

    具体代码如下

    通过创建事件总线对象,进而订阅Redis的连接事件,当Reids处于非连接状态时会实时修改RedisCheckConfig的连接状态标识。即使在Redis定时检测任务的间隔中也能保证服务的正常运行。

    1. @Component
    2. @Slf4j
    3. public class RedisStatusListener {
    4. @Autowired
    5. private ClientResources clientResources;
    6. public RedisClient redisClient() {
    7. RedisClient client = RedisClient.create(clientResources);
    8. EventBus eventBus = client.getResources().eventBus();
    9. eventBus.get().subscribe(e -> {
    10. RedisCheckConfig.redisConnected = e instanceof ConnectionActivatedEvent ? true : false;
    11. log.info("EventBus获取Redis是否连接 "+RedisCheckConfig.redisConnected);
    12. });
    13. return client;
    14. }
    15. }

    一种另类方法——AOP

    如果并不想使用事件总线的方式来解决,这里再提供一种其它的思路。可以通过AOP的方式来完成判断。

    通过对Redis操作类中的所有操作指令进行检测,即当Redis操作命令执行超过1s(可调节)时则认为此时Redis出现了连接异常。

    这里由于需要统计执行时间需要用到线程池,通过AOP的环绕通知来实现业务逻辑拦截,即当Redis命令操作超过指定时间则返回数据为其对应存储数据的默认类型。

    具体实现

    1. @Component
    2. @Aspect
    3. @Slf4j
    4. public class RedisOperationAspect {
    5. @Around("execution(* com.o2o.shop.util.RedisOperator.*(..))")
    6. public Object monitorRedisCommandExecution(ProceedingJoinPoint joinPoint) throws Throwable {
    7. // 获取方法信息
    8. MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
    9. Method method = methodSignature.getMethod();
    10. String methodName = method.getName();
    11. // 执行超时时间,单位ms
    12. long timeout = 1000;
    13. ExecutorService executor = new ThreadPoolExecutor(1, 1,
    14. 1000, TimeUnit.MILLISECONDS,new ArrayBlockingQueue<>(1));
    15. // 创建Future对象
    16. Future future = executor.submit(() -> {
    17. try {
    18. return joinPoint.proceed();
    19. } catch (Throwable throwable) {
    20. log.error("Redis AOP 执行 "+joinPoint+"异常");
    21. throw new RuntimeException(throwable);
    22. }
    23. });
    24. // 执行方法并等待结果,设置超时时间
    25. try {
    26. return future.get(timeout, TimeUnit.MILLISECONDS);
    27. } catch (TimeoutException e) {
    28. // 超时处理
    29. log.warn("Redis command execution timed out");
    30. // 中断方法执行
    31. future.cancel(true);
    32. // 返回特定结果
    33. Class returnType = method.getReturnType();
    34. if (returnType.isPrimitive()) {
    35. if (returnType == boolean.class) {
    36. return false;
    37. } else if (returnType == char.class) {
    38. return '\0';
    39. } else if (returnType == byte.class || returnType == short.class ||
    40. returnType == int.class || returnType == long.class) {
    41. return 0;
    42. } else if (returnType == float.class || returnType == double.class) {
    43. return 0.0f;
    44. }
    45. }
    46. return null;
    47. } finally {
    48. // 关闭线程池
    49. executor.shutdown();
    50. }
    51. }
    52. }
    53. 相关阅读:
      FL Studio21免许可证完整版数字音频工作站(DAW)
      kubernetes集群yaml文件与kubectl工具
      中断下半部之 work queue
      WSL中部署xampp及swoole
      【网络安全 --- 任意文件上传漏洞(2)】带你了解学习任意文件上传漏洞
      基于量子随机游走的图像加密算法
      学习黑马程序员JavaScript总结
      老电脑升级内存、固态硬盘、重新装机过程记录
      通俗易懂分析:Vite和Webpack的区别
      模型评估与选择
    54. 原文地址:https://blog.csdn.net/mdzz14/article/details/134032066
      • 最新文章
      • 攻防演习之三天拿下官网站群
        数据安全治理学习——前期安全规划和安全管理体系建设
        企业安全 | 企业内一次钓鱼演练准备过程
        内网渗透测试 | Kerberos协议及其部分攻击手法
        0day的产生 | 不懂代码的"代码审计"
        安装scrcpy-client模块av模块异常,环境问题解决方案
        leetcode hot100【LeetCode 279. 完全平方数】java实现
        OpenWrt下安装Mosquitto
        AnatoMask论文汇总
        【AI日记】24.11.01 LangChain、openai api和github copilot
      • 热门文章
      • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
        奉劝各位学弟学妹们,该打造你的技术影响力了!
        五年了,我在 CSDN 的两个一百万。
        Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
        面试官都震惊,你这网络基础可以啊!
        你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
        心情不好的时候,用 Python 画棵樱花树送给自己吧
        通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
        13 万字 C 语言从入门到精通保姆级教程2021 年版
        10行代码集2000张美女图,Python爬虫120例,再上征途
      Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
      正则表达式工具 cron表达式工具 密码生成工具

      京公网安备 11010502049817号