Redisson实现简单消息队列:优雅解决缓存清理冲突

在项目中,缓存是提高应用性能和响应速度的关键手段之一。然而,当多个模块在短时间内发布工单并且需要清理同一个接口的缓存时,容易引发缓存清理冲突,导致缓存失效的问题。为了解决这一难题,我们采用Redisson的消息队列功能,实现了一个简单而高效的消息队列,优雅地解决了缓存清理冲突问题。本文将为您详细介绍Redisson实现简单消息队列的方案,以及如何在项目中使用它来优化缓存清理。

第一部分:缓存清理冲突的挑战

在高并发场景下,多个模块可能同时发布工单,并且这些工单可能会导致同一个接口的缓存失效。当多个模块同时发起缓存清理请求时,可能会造成数据库压力增大及缓存不一致的问题,降低应用程序的性能和稳定性。这种情况下,我们需要一种优雅的解决方案来协调缓存清理的行为。

第二部分:Redisson简介

Redisson是一个功能强大的Java库,基于Redis构建,它提供了分布式数据结构和服务,以及异步处理的解决方案。其中,消息队列是Redisson提供的一项强大功能,用于在多个模块之间实现高效的消息传递和处理。

第三部分:使用Redisson消息队列解决方案

为了解决缓存清理冲突问题,我们选择使用Redisson的消息队列功能,具体步骤如下:

创建Redisson客户端: 首先,我们需要创建一个Redisson客户端,用于连接到Redis服务器。

创建消息队列: 接下来,我们创建一个Redisson的队列,用于存放缓存清理请求消息。

发布缓存清理请求: 在每个模块发布工单时,我们将对应的缓存清理请求添加到Redisson队列中。

消费缓存清理请求: 我们创建一个定时任务,周期性的从Redisson队列中获取缓存清理请求消息。

处理缓存清理冲突: 合并消息,并执行缓存清理操作,避免多个模块同时清理同一个接口的缓存。

第三部分:代码示例

redisson配置文件

# 项目相关Spring redis配置
spring:
  redisson:
    # 地址
    clusters: 192.168.10.106:6479,192.168.10.106:6579,192.168.10.106:6679
    # 密码
    password: 123456
    # 连接超时时间
    timeout: 10s

客户端连接类: RedissonManager

package cn.xj.redis;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.StringCodec;
import org.redisson.config.Config;
import org.redisson.config.ReadMode;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


/**
 * description: redisson初始化
 *
 * @author xj
 * <p>
 * create on 2020-04-09 17:21
 **/
@Configuration
public class RedissonManager {

    @Value("${spring.redisson.clusters}")
    private  String cluster;
    @Value("${spring.redisson.password}")
    private String password;




    @Bean(name="cacheCluster")
    public RedissonClient getRedissonCluster(){
        String[] nodes = cluster.split(",");
        //redisson版本是3.5,集群的ip前面要加上“redis://”,不然会报错,3.2版本可不加
        for(int i=0;i<nodes.length;i++){
            nodes[i] = "redis://"+nodes[i];
        }
        RedissonClient redisson = null;
        Config config = new Config();
        //设置
        config.setCodec(new StringCodec())
                //这是用的集群server
                .useClusterServers()
                //设置集群状态扫描时间
                .setScanInterval(2000)
                .addNodeAddress(nodes)
                .setPassword(password)
                .setReadMode(ReadMode.MASTER);;
        redisson = Redisson.create(config);
        return redisson;
    }

}

redis工具类:RedissonCache


package cn.xj.redis;


import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.Set;


/**
 * redisson工具类
 * company
 * @author
 * @data 2020-04-01
 */
@Component
@Slf4j
public class RedissonCache {



    @Autowired
    @Qualifier("cacheCluster")
    private RedissonClient cacheCluster;

    public boolean cacheAdd(String key, Map<String,Set<String>> message){
        String msgJsonstr = JSONObject.toJSONString(message);
        RQueue<String> queue = cacheCluster.getQueue(key);
        return queue.add(msgJsonstr);
    }

    public Map<String, Set<String>> cachePoll(String key){
        RQueue<String> queue = cacheCluster.getQueue(key);
        String msgJsonstr = queue.poll();
        return JSON.parseObject(msgJsonstr, new TypeReference<Map<String, Set<String>>>() {});
    }

}

功能示例类:QueueController

package cn.xj.redis;

import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

@RestController
public class QueueController {

    private static final String KEY= "xj_test_queue";
    @Autowired
    private RedissonCache redissonCache;

    /**
     * 添加消息队列
     * @param params
     */
    @PostMapping("/redis/queue/add")
    public void add(@RequestBody Map<String, Set<String>> params){
        redissonCache.cacheAdd(KEY,params);
    }


    /**
     * 消费队列中的消息
     * @return
     */
    @GetMapping("/redis/queue/poll")
    public List<Map<String, Set<String>>> poll(){
        List<Map<String, Set<String>>> result = new ArrayList<>();
        //每次最大消费条数
        int batchSize = 200;
        //获取消费的所有消息
        Map<String, Set<String>> msgMap = redissonCache.cachePoll(KEY);
        while (batchSize > 0 && !ObjectUtils.isEmpty(msgMap)) {
            result.add(msgMap);
            batchSize--;
            msgMap = redissonCache.cachePoll(KEY);
        }

        try {
            //消息合并、处理逻辑

            //此处省略代码一万行
        } catch (Exception e){
            
            //消息处理失败的逻辑
        }
        return result;
    }
}

我们调用模拟添加接口可以看到。每次有新的消息都是给queque的尾部添加

_20230724204722.png

每次消费的时候是queque的头部开始取数据

第五部分:总结和应用场景及注意事项

通过Redisson的消息队列功能,我们成功实现了一个简单而高效的缓存清理解决方案。该方案有效解决了多个模块同时发布工单导致缓存清理冲突的问题,提高了应用程序的性能和稳定性。

适用场景:

  • 多个模块在短时间内发布工单,并需要清理同一个接口的缓存。

  • 消息队列可以将不同模块或不同组件之间的通信解耦,实现系统的高内聚和低耦合。同时,在系统面临突发大量请求时,消息队列可以进行削峰填谷,保护系统不受过载影响。

注意事项:

  • 在使用队列时,要考虑队列的容量,避免队列过大导致内存压力过大或队列过小导致消息丢失。

  • 在使用消息队列时,需要小心处理异常情况。例如,如果消息处理失败,可以将消息重新排队或将其放入一个死信队列,以便稍后进行处理。

总结

Redisson的消息队列是解决缓存清理冲突问题的优雅方案,通过其强大的功能,我们可以简单地实现消息传递和处理,从而优化应用程序的性能。在日常开发中,合理应用Redisson的消息队列功能,能够帮助我们处理更多类似的并发问题,提升应用程序的可靠性和扩展性。

希望本文能够为读者提供有益的参考,让您在项目中更加灵活和高效地使用Redisson实现简单消息队列。愿您的应用程序在缓存清理中更上一层楼,助您的项目更加稳健发展!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/44864.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【MCU学习】RTthread工程介绍

RT-Thread架构 RT-Thread诞生于2006年&#xff0c;是一款以开源、中立、社区化发展起来的物联网操作系统。 RT-Thread主要采用 C 语言编写&#xff0c;浅显易懂&#xff0c;且具有方便移植的特性&#xff08;可快速移植到多种主流 MCU 及模组芯片上&#xff09;。RT-Thread把面…

cocosCreator 之 ScrollView

版本&#xff1a;3.4.0 参考&#xff1a;ScrollView组件 简介 ScrollView组件作为滚动容器来使用&#xff0c;它的实现通过ScrollBar组件来展示内容的位置和Mask组件显示指定区域&#xff0c;来保证有限的区域内显示更多的内容。 它的构成部分&#xff1a; ScrollBar滚动条相…

03 shell 编程

变量 语言型 编译型语言 解释型语言 shell脚本语言是解释型语言shell脚本的本质&#xff1a;shell命令的有序集合 shell 编程的基本过程 基本过程分为三步&#xff1a; step1. 建立 shell 文件 包含任意多行操作系统命令或shell命令的文本文件; step2. 赋予shell文件执行…

23 自定义控件

案例&#xff1a;组合Spin Box和Horizontal Slider实现联动 新建Qt设计师界面&#xff1a; 选择Widget&#xff1a; 选择类名&#xff08;生成.h、.cpp、.ui文件&#xff09; 在smallWidget.ui中使用Spin Box和Horizontal Slider控件 可以自定义数字区间&#xff1a; 在主窗口w…

脑电信号处理与特征提取——1. 脑电、诱发电位和事件相关电位(胡理)

目录 一、 脑电、诱发电位和事件相关电位 1.1 EEG基本知识 1.2 经典的ERPs成分及研究 1.2.1 ERPs命名规则及分类 1.2.2 常见的脑电成分 1.2.3 P300及Oddball范式 1.2.4 N400成分 一、 脑电、诱发电位和事件相关电位 1.1 EEG基本知识 EEG(Electroencephalogram)&#x…

MFC第二十天 数值型关联变量 和单选按钮与复选框的开发应用

文章目录 数值型关联变量数值型关联变量的种类介绍 单选按钮与复选框单选按钮的组内选择原理解析单选按钮和复选框以及应用数值型关联变量的开发CMainDlg.cppCInputDlg.hCInputDlg.cpp 附录 数值型关联变量 数值型关联变量的种类介绍 1、 数值型关联变量&#xff1a; a)控件型…

全志F1C200S嵌入式驱动开发(解决spi加载过慢的问题)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 之前的几个章节当中,我们陆续解决了spi-nor驱动的问题、uboot支持spi-nor的问题。按道理来说,下面要做的应该就是用uboot的loady命令把kernel、dtb、rootfs这些文件下载到ddr,然…

执行 yum install gcc 报 【-bash: $‘yum\302\240install\302\240gcc‘: 未找到命令】

执行 yum install gcc 报错 找了一圈&#xff0c;执行&#xff1a;sudo apt-get install yum 执行&#xff1a;wget http://yum.baseurl.org/download/3.2/yum-3.2.28.tar.gz 在线下载yum完成 对其进行解压&#xff1a;tar zxvf yum-3.2.28.tar.gz 解压后如下&#xff1a; 执行…

Tiny Player (js) - 轻量好用、免费开源的 web 视频播放开发组件,内置硬解、软解视频功能

一款简单好用的 JS 视频播放器&#xff0c;完美解决我遇到的移动端播放视频的需求&#xff0c;安利给各位。 关于 Tiny Player Tiny Player 是一个极简的视频播放器 JS 库&#xff0c;内置硬解、软解视频功能&#xff0c;支持原生控件样式以及自定义控件样式&#xff0c;小巧…

Android Service启动ANR原理

一、前言 在Service组件StartService()方式启动流程分析文章中&#xff0c;针对Context#startService()启动Service流程分析了源码&#xff0c;其实关于Service启动还有一个比较重要的点是Service启动的ANR&#xff0c;因为因为线上出现了上百例的"executing service &quo…

vue三级路由的写法

{path: "/trafficmanagement",component: Layout,redirect: "/trafficmanagement",alwaysShow: true,meta: {title: "通行模块",icon: "excel",},children: [{path: "carline",name: "carline",alwaysShow: true,…

OpenCV图像处理-视频分割静态背景-MOG/MOG2/GMG

视频分割背景 1.概念介绍2. 函数介绍MOG算法MOG2算法GMG算法 原视频获取链接 1.概念介绍 视频背景扣除原理&#xff1a;视频是一组连续的帧&#xff08;一幅幅图组成&#xff09;&#xff0c;帧与帧之间关系密切(GOP/group of picture)&#xff0c;在GOP中&#xff0c;背景几乎…

LeetCode116. 填充每个节点的下一个右侧节点指针

116. 填充每个节点的下一个右侧节点指针 文章目录 [116. 填充每个节点的下一个右侧节点指针](https://leetcode.cn/problems/populating-next-right-pointers-in-each-node/)一、题目二、题解方法一&#xff1a;迭代方法二&#xff1a;递归 一、题目 给定一个 完美二叉树 &…

【nginx】nginx中root与alias的区别:

文章目录 root与alias主要区别在于nginx如何解释location后面的uri&#xff0c;这会使两者分别以不同的方式将请求映射到服务器文件上。 root的处理结果是&#xff1a;root路径&#xff0b;location路径 alias的处理结果是&#xff1a;使用alias路径替换location路径 alias是一…

Qt Core学习日记——第七天QMetaObject(上)

每一个声明Q_OBJECT的类都具有QMetaObject对象 Q_OBJECT宏源代码&#xff1a; #define Q_OBJECT \ public: \ QT_WARNING_PUSH \ Q_OBJECT_NO_OVERRIDE_WARNING \ static const QMetaObject staticMetaObject; \ virtual const QMetaObject *metaObject() const; \ vir…

istio安装部署总结

istio安装部署总结 大纲 istio基础概念版本选择安装istio核心主件卸载istiokiali安装 istio基础概念 https://istio.io/latest/zh/docs/ 中文文档 istio是一个服务治理平台&#xff0c;治理服务间的访问&#xff0c;&#xff08;例如流量控制&#xff0c;安全策略&#xf…

【C++链表】

目录 前言一、搭建链表实现的框架二、链表的构造函数三、链表的尾插四、链表的遍历(重点)迭代器的遍历const修饰的迭代器 五、代码实现 前言 最近用C写了一下list的基本功能&#xff0c;感触颇深。本以为会跟之前用C写list一样会很轻松&#xff0c;没想到更难了。要考虑的东西…

代码随想录| 图论04 查并集 ●查并集理论知识 ●1971.寻找图中是否存在路径 ●684.冗余连接 ●685.冗余连接II

#查并集理论知识 并查集用处&#xff1a;解决连通性问题 将两个元素添加到一个集合中。判断两个元素在不在同一个集合 思路&#xff1a;将三个元素A&#xff0c;B&#xff0c;C &#xff08;分别是数字&#xff09;放在同一个集合&#xff0c;其实就是将三个元素连通在一起&a…

k8s常见的资源对象使用

目录 一、kubernetes内置资源对象 1.1、kubernetes内置资源对象介绍 1.2、kubernetes资源对象操作命令 二、job与cronjob计划任务 2.1、job计划任务 2.2、cronjob计划任务 三、RC/RS副本控制器 3.1、RC副本控制器 3.2、RS副本控制器 3.3、RS更新pod 四、Deployment副…

IBM:2023 年数据泄露的平均成本将达到 445 万美元

IBM 发布年度《数据泄露成本报告》&#xff0c;显示 2023 年全球数据泄露平均成本达到 445 万美元&#xff0c;比过去 3 年增加了 15%。创下该报告的历史新高。 报告显示&#xff0c;企业在计划如何应对日益增长的数据泄露频率和成本方面存在分歧。研究发现&#xff0c;虽然 95…