当前位置: 首页 > news >正文

抽丝剥茧,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的连接状态。

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

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

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

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

具体代码如下

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

@Component
@Slf4j
public class RedisStatusListener {@Autowiredprivate ClientResources clientResources;public RedisClient redisClient() {RedisClient client = RedisClient.create(clientResources);EventBus eventBus = client.getResources().eventBus();eventBus.get().subscribe(e -> {RedisCheckConfig.redisConnected = e instanceof ConnectionActivatedEvent ? true : false;log.info("EventBus获取Redis是否连接 "+RedisCheckConfig.redisConnected);});return client;}
}

一种另类方法——AOP

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

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

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

具体实现

@Component
@Aspect
@Slf4j
public class RedisOperationAspect {@Around("execution(* com.o2o.shop.util.RedisOperator.*(..))")public Object monitorRedisCommandExecution(ProceedingJoinPoint joinPoint) throws Throwable {// 获取方法信息MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();Method method = methodSignature.getMethod();String methodName = method.getName();// 执行超时时间,单位mslong timeout = 1000;ExecutorService executor = new ThreadPoolExecutor(1, 1,1000, TimeUnit.MILLISECONDS,new ArrayBlockingQueue<>(1));// 创建Future对象Future<Object> future = executor.submit(() -> {try {return joinPoint.proceed();} catch (Throwable throwable) {log.error("Redis AOP 执行 "+joinPoint+"异常");throw new RuntimeException(throwable);}});// 执行方法并等待结果,设置超时时间try {return future.get(timeout, TimeUnit.MILLISECONDS);} catch (TimeoutException e) {// 超时处理log.warn("Redis command execution timed out");// 中断方法执行future.cancel(true);// 返回特定结果Class<?> returnType = method.getReturnType();if (returnType.isPrimitive()) {if (returnType == boolean.class) {return false;} else if (returnType == char.class) {return '\0';} else if (returnType == byte.class || returnType == short.class ||returnType == int.class || returnType == long.class) {return 0;} else if (returnType == float.class || returnType == double.class) {return 0.0f;}}return null;} finally {// 关闭线程池executor.shutdown();}}}

相关文章:

抽丝剥茧,Redis使用事件总线EventBus或AOP优化健康检测

目录 前言 Lettuce 什么是事件总线EventBus&#xff1f; Connected Connection activated Disconnected Connection deactivated Reconnect failed 使用 一种另类方法—AOP 具体实现 前言 在上一篇深入浅出&#xff0c;SpringBoot整合Quartz实现定时任务与Redis健康…...

【Tailwind CSS】当页面内容过少,怎样让footer保持在屏幕底部?

footer通常写版权信息等&#xff0c;显示在页面底部。如果页面内容过少&#xff0c;则footer会出现在屏幕中间位置&#xff0c;很尴尬。在 Tailwind 中&#xff0c;你可以使用flex来实现footer保持在屏幕或页面底部。 代码&#xff1a; <div class"flex flex-col min…...

Docker基础管理

这里写目录标题 Docker基础管理一.Docker 概述1.Docker介绍2.Docker与虚拟机的区别3.容器在内核中支持2种重要技术4.Docker核心概念 二.安装Docker1.安装依赖包2.配置文件及相关 三.Docker操作1.镜像操作2.容器操作 Docker基础管理 一.Docker 概述 1.Docker介绍 Docker是一个…...

基于YOLOv8模型的烟雾目标检测系统(PyTorch+Pyside6+YOLOv8模型)

摘要&#xff1a;基于YOLOv8模型的烟雾目标检测系统可用于日常生活中检测与定位烟雾目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的目标检测&#xff0c;另外本系统还支持图片、视频等格式的结果可视化与结果导出。本系统采用YOLOv8目标检测算法训练数据集…...

【代码随想录01】数组总结

抄去吧&#xff0c;保存去吧&#xff01;...

(SpringBoot)第二章:Spring创建和使用

文章目录 一:Sring创建(1)创建一个Maven项目(2)添加Spring框架支持(3)添加启动类二:存储Bean(1)创建Bean(2)将Bean注册到Spring中三:获取并使用Bean(1)创建Spring上下文(2)获取指定Bean(3)使用Bean注意:在Java中对象也叫做Bean,所以后续文章中用Bean代替对…...

力扣刷题 day56:10-26

1.解码异或后的数组 未知 整数数组 arr 由 n 个非负整数组成。 经编码后变为长度为 n - 1 的另一个整数数组 encoded &#xff0c;其中 encoded[i] arr[i] XOR arr[i 1] 。例如&#xff0c;arr [1,0,2,1] 经编码后得到 encoded [1,2,3] 。 给你编码后的数组 encoded 和原…...

『第四章』一见倾心:初识小雨燕(上)

在本篇博文中,您将学到如下内容: 1. 基本数据类型2. 基本操作符3. 枚举和结构4. 类和 Actor5. 属性、方法与访问控制6. 聚集总结夜月一帘幽梦,春风十里柔情。 无声交谈情意深,一见心曲绕梁成。 1. 基本数据类型 无论是 macOS 还是 iOS 上的开发,Swift 基础类型和功能都内置于…...

elasticsearch-7.9.3 单节点启动配置

一、elasticsearch-7.9.3 单节点启动配置 node.name: node-1 network.host: 192.168.227.128 http.port: 9200 discovery.seed_hosts: ["192.168.227.128"] node.max_local_storage_nodes: 1 discovery.type: single-node二、kibana-7.9.3-linux-x86_64 单节点启动配…...

【2024秋招】2023-10-9 同花顺后端笔试题

1 Hashmap mp new hashmap&#xff08;50&#xff09;的大小扩充了几次 初时应该就给了这么多空间&#xff0c;在不考虑添加元素&#xff0c;所以扩容为0次 2 算数表达式的中缀为ab*c-d/e&#xff0c;后缀为abc*de/-&#xff0c;前缀是&#xff1f; 3 50M电信带宽&#xff…...

完美的错误处理:Go 语言最佳实践分享

Go 语言是一门非常流行的编程语言&#xff0c;由于其高效的并发编程和出色的网络编程能力&#xff0c;越来越受到广大开发者的青睐。在任何编程语言中&#xff0c;错误处理都是非常重要的一环&#xff0c;它关系到程序的健壮性和可靠性。Go 语言作为一门现代化的编程语言&#…...

vue首页多模块布局(标题布局)

<template><div class"box"><div class"content"><div class"box1" style"background-color: rgb(245,23,156)">第一个</div><div class"box2" style"background-color: rgb(12,233,…...

嵌入式系统>嵌入式硬件知识

AI芯片的特点包括 &#xff1a;新型计算范式AI芯片的关键特征&#xff1a; 1、新型的计算范式 AI 计算既不脱离传统计算&#xff0c;也具有新的计算特质&#xff0c;如处理的内容往往是非结构化数据&#xff08;视频、图片等&#xff09;。处理的过程通常需要很大的计算量&am…...

LeetCode 1402. 做菜顺序【排序,动态规划;贪心,前缀和,递推】1679

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…...

【多线程】探索Java中的多线程编程

标题&#xff1a;探索Java中的多线程编程 摘要&#xff1a; Java是一种广泛使用的编程语言&#xff0c;具有强大的多线程编程能力。本文将深入探讨Java中的多线程编程&#xff0c;包括线程的创建、同步与互斥、线程池的使用以及常见的多线程编程模式。通过示例代码和详细解释&…...

【算法题】翻转对

题目&#xff1a; 给定一个数组 nums &#xff0c;如果 i < j 且 nums[i] > 2*nums[j] 我们就将 (i, j) 称作一个重要翻转对。 你需要返回给定数组中的重要翻转对的数量。 示例 1: 输入: [1,3,2,3,1] 输出: 2 示例 2: 输入: [2,4,3,5,1] 输出: 3 注意: 给定数组的长…...

适用于 Mac 或 Windows 的 4 种最佳 JPEG/PNG图片 恢复软件

您的计算机或外部存储驱动器上很可能有大量 JPEG /PNG图片照片&#xff0c;但不知何故&#xff0c;您意识到一些重要的 JPEG /PNG图片文件丢失或被删除&#xff0c;它们对您来说意义重大&#xff0c;您想要找回它们. 4 种最佳 JPEG/PNG图片 恢复软件 要成功执行 JPEG /PNG图片…...

位置信息API

位置信息API 一、获取当前位置&#xff1a;wx.getLocation(object)二、选择位置&#xff1a;wx.chooseLocation(object)三、打开位置&#xff1a;wx.openLocation(object)四、监听位置事件五、地图组件控制API六、收货地址API&#xff1a;wx.chooseAddress(object) 一、获取当前…...

MySQL——九、SQL编程

MySQL 一、触发器1、触发器简介2、创建触发器3、一些常见示例 二、存储过程1、什么是存储过程或者函数2、优点3、存储过程创建与调用 三、存储函数1、存储函数创建和调用2、修改存储函数3、删除存储函数 四、游标1、声明游标2、打开游标3、使用游标4、关闭游标游标案例 一、触发…...

threejs(4)-纹理材质高级操作

一、纹理重复_缩放_旋转_位移操作 // 导入threejs import * as THREE from "three"; // 导入轨道控制器 import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js"; // 导入lil.gui import { GUI } from "three/examples/jsm/l…...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

04-初识css

一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

html-<abbr> 缩写或首字母缩略词

定义与作用 <abbr> 标签用于表示缩写或首字母缩略词&#xff0c;它可以帮助用户更好地理解缩写的含义&#xff0c;尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时&#xff0c;会显示一个提示框。 示例&#x…...

大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计

随着大语言模型&#xff08;LLM&#xff09;参数规模的增长&#xff0c;推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长&#xff0c;而KV缓存的内存消耗可能高达数十GB&#xff08;例如Llama2-7B处理100K token时需50GB内存&a…...

代理篇12|深入理解 Vite中的Proxy接口代理配置

在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

Java 二维码

Java 二维码 **技术&#xff1a;**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...

算法:模拟

1.替换所有的问号 1576. 替换所有的问号 - 力扣&#xff08;LeetCode&#xff09; ​遍历字符串​&#xff1a;通过外层循环逐一检查每个字符。​遇到 ? 时处理​&#xff1a; 内层循环遍历小写字母&#xff08;a 到 z&#xff09;。对每个字母检查是否满足&#xff1a; ​与…...

STM32---外部32.768K晶振(LSE)无法起振问题

晶振是否起振主要就检查两个1、晶振与MCU是否兼容&#xff1b;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容&#xff08;CL&#xff09;与匹配电容&#xff08;CL1、CL2&#xff09;的关系 2. 如何选择 CL1 和 CL…...

LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)

在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...