SpringBoot Redis 扩展高级功能

news2024/11/18 4:20:27

环境:SpringBoot2.7.16 + Redis6.2.1



1. Redis消息发布订阅

Spring Data 为 Redis 提供了专用的消息传递集成,其功能和命名与 Spring Framework 中的 JMS 集成类似。Redis 消息传递大致可分为两个功能区域:

  • 信息发布

  • 信息订阅


这是一个通常被称为发布/订阅(Publish/Subscribe,简称 Pub/Sub)模式的示例。RedisTemplate 类用于消息生成。

Redis消息机制最大的2个缺点:

  • 没有 Ack 机制,也不保证数据的连续性:生产者传递过来一个消息,Redis 会直接找到相应的消费者传递过去。如果没有一个消费者,那么消息会被直接丢弃。如果开始有三个消费者,其中一个突然挂掉了,过了一会儿等它再重连时,那么重连期间的消息对于这个消费者来说就彻底丢失了。

  • 不持久化消息:如果 Redis 停机重启,发布订阅的消息是不会持久化的,毕竟 Redis 宕机就相当于一个消费者都没有,所有的消息都会被直接丢弃。


示例:

消息监听器

@Componentpublic class MsgMessageListener implements MessageListener {
  @Override  public void onMessage(Message message, byte[] pattern) {    System.out.printf("从通道【%s】, 接收到消息: %s%n", new String(message.getChannel()), new String(message.getBody())) ;  }
}

配置消息监听容器

@Configurationpublic class MessageReceiverConfig {
  @Bean  RedisMessageListenerContainer redisMessageListenerContainer(      RedisConnectionFactory connectionFactory,      MessageListener listener) {    RedisMessageListenerContainer container = new RedisMessageListenerContainer();    container.setConnectionFactory(connectionFactory);    // 这里监听了2个通道msg和chat    container.addMessageListener(listener, Arrays.asList(ChannelTopic.of("msg"), ChannelTopic.of("chat"))) ;    return container;  }}

消息发送

@Componentpublic class MessageSender {  @Autowired  private StringRedisTemplate stringRedisTemplate;
  public void sendMessage(String channel, String message) {    stringRedisTemplate.convertAndSend(channel, message);  }}


2. Redis事务

Redis通过multi、exec和discard命令提供对事务的支持。这些操作可以在RedisTemplate上使用。但是,RedisTemplate不能保证使用相同的连接运行事务中的所有操作。

编程式事务

Spring Data Redis 提供了 SessionCallback 接口,供需要使用同一连接执行多个操作(如使用 Redis 事务)时使用:

@Servicepublic class RedisTransactionService {    private final StringRedisTemplate stringRedisTemplate ;  public RedisTransactionService(StringRedisTemplate stringRedisTemplate) {    this.stringRedisTemplate = stringRedisTemplate ;  }    public void multiOperator() {    List<Object> txResults = stringRedisTemplate.execute(new SessionCallback<List<Object>>() {      public List<Object> execute(RedisOperations operations) throws DataAccessException {        operations.multi();                operations.opsForHash().put("users:666", "name", "张三") ;        operations.opsForValue().set("id", "666") ;                return operations.exec() ;      }    });    for (Object ret : txResults) {      System.out.println(ret) ;    }  }  }

正常输出如下

truetrue

使用声明式事务

启用事务支持后,RedisConnection 会绑定到由 ThreadLocal 支持的当前事务。如果事务无误完成,Redis 事务将通过 EXEC 提交,否则将通过 DISCARD 回滚。Redis 事务是面向批处理的。正在进行的事务中发出的命令会排队,只有在提交事务时才会应用。Spring Data Redis 会区分正在进行的事务中的只读命令和写命令。只读命令(如 KEYS)通过管道连接到新的(非线程绑定)RedisConnection,以允许读取。写入命令由 RedisTemplate 队列处理,并在提交时应用。

@BeanStringRedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {  StringRedisTemplate template = new StringRedisTemplate(redisConnectionFactory) ;  // 手动开启事务功能  template.setEnableTransactionSupport(true);                return template;}

测试

@Transactionalpublic void multiOperator2() {  stringRedisTemplate.opsForValue().set("oo", "xx") ;  Set<String> keys = stringRedisTemplate.keys("*") ;  // 输出[]。读操作必须在空闲(不感知事务)连接上运行  System.out.println(keys) ;  String value = stringRedisTemplate.opsForValue().get("oo") ;  // 返回null,因为在事务中设置的值不可见  System.out.println(value) ;}

如果事务生效,那么上面的输出如下:

​​​​​​​​​​​​​​

[]null


3. 管道

Redis 提供管道支持,即向服务器发送多条命令而不等待回复,然后一次性读取回复。当你需要连续发送多条命令时,例如向同一个 List 添加多个元素时,管道操作可以提高性能。

Spring Data Redis 提供了多种 RedisTemplate 方法,用于在管道中运行命令。如果不关心管道操作的结果,可以使用标准的 execute 方法,并为管道参数传递 true。executePipelined 方法在管道中运行所提供的 RedisCallback 或 SessionCallback,并返回结果,如下例所示:

@Servicepublic class RedisPipeliningService {
  private final StringRedisTemplate stringRedisTemplate ;    public RedisPipeliningService(StringRedisTemplate stringRedisTemplate) {    this.stringRedisTemplate = stringRedisTemplate ;  }    public List<Object> pipe() {    List<Object> results = stringRedisTemplate.executePipelined(      new RedisCallback<Object>() {        public Object doInRedis(RedisConnection connection) throws DataAccessException {          StringRedisConnection stringRedisConn = (StringRedisConnection)connection;          for(int i = 0; i < 10; i++) {            stringRedisConn.rPush("i" + i, String.valueOf(i)) ;          }        return null;      }    });    return results ;  }  }

输出结果

图片


4. Redis LUA脚本

Redis 2.6 及更高版本支持通过 eval 和 evalsha 命令运行 Lua 脚本。Spring Data Redis 为运行脚本提供了高级抽象,可处理序列化并自动使用 Redis 脚本缓存。示例如下:

LUA脚本

这是一个分布式锁的简单实现

加锁

-- 判断锁key是否存在if (redis.call('EXISTS', KEYS[1]) == 0) then  -- 不存在  redis.call('HINCRBY', KEYS[1], ARGV[1], 1) ;  redis.call('PEXPIRE', KEYS[1], 300000) ;  return 1; end ;-- 判断hash中是否存在ARGV[1]字段(该字段用来统计重入次数,一般使用threadId唯一标识)if (redis.call('HEXISTS', KEYS[1], ARGV[1]) == 1) then  redis.call('HINCRBY', KEYS[1], ARGV[1], 1) ;  redis.call('PEXPIRE', KEYS[1], 300000) ;  return 1;end ;return 0 ;

解锁

-- 判断锁key是否存在if (redis.call('EXISTS', KEYS[1]) == 0) then  return nil ;end ;local counter = redis.call('HINCRBY', KEYS[1], ARGV[1], -1) ;if (counter > 0) then  redis.call('PEXPIRE', KEYS[1], 30000) ;  return 1 ;else   redis.call('DEL', KEYS[1]) ;  return 1 ;end ;return 0 ;

锁实现

public interface PLock {  boolean lock() ;  void unlock() ;}public class ProductRedisLock implements PLock {    private StringRedisTemplate stringRedisTemplate ;  private String key ;  private String id ;    public ProductRedisLock(String key, StringRedisTemplate stringRedisTemplate) {    this.key = key ;    this.id = key.hashCode() + ":";    this.stringRedisTemplate = stringRedisTemplate ;  }    @Override  public boolean lock() {    long threadId = Thread.currentThread().getId() ;    return this.stringRedisTemplate.execute(RedisScript.of(new ClassPathResource("lock.lua"), Boolean.class),         Arrays.asList(this.key), this.id + threadId) ;  }
  @Override  public void unlock() {    long threadId = Thread.currentThread().getId() ;    this.stringRedisTemplate.execute(RedisScript.of(new ClassPathResource("unlock.lua"), Boolean.class),         Arrays.asList(this.key), this.id + threadId) ;  }
}

使用

public void updateProduct(Long id) {

  String key = "lock:product:" + id ;  PLock lock = new ProductRedisLock(key, stringRedisTemplate) ;  if (lock.lock()) {    try {      System.out.println("更新商品:" + id) ;      this.configProduct(id) ;    } finally {      lock.unlock() ;     }  }}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[XYCTF新生赛]-Reverse:你是真的大学生吗?解析(汇编异或逆向)

无壳 查看ida 没有办法反汇编&#xff0c;只能直接看汇编了。 这里提示有输入&#xff0c;输入到2F地址后&#xff0c;然后从后往前异或&#xff0c;其中先最后一个字符与第一个字符异或。这里其实也有字符串的长度&#xff0c;推测应该是cx自身异或之后传给了cx 完整exp&am…

三分钟轻松搞定内容,2024视频号最新AI自动生成影视解说,,百分之百过原创, 月入1万+

在这个数字时代&#xff0c;我们有幸见证了AI技术对创新的推动。现如今&#xff0c;一个崭新的平台出现了&#xff0c;它能让你用AI软件在短短3分钟内制作完成一段影视解说&#xff0c;而且由于这个平台尚属于新兴&#xff0c;竞争者稀少&#xff0c;提供了一个广阔的机遇天地。…

如何将云服务器上操作系统由centos切换为ubuntu

本文将介绍如何将我们购买的云服务器上之前装的centos切换为ubuntu&#xff0c;云服务器以华为云为例&#xff0c;要切换的ubuntu版本为ubuntu20.04。 参考官方文档&#xff1a;切换操作系统_弹性云服务器 ECS (huaweicloud.com) 首先打开华为云官网&#xff0c;登录后点击右…

多模态MLLM都是怎么实现的(9)-时序LLM是怎么个事儿?

时序预测这东西大家一般不陌生,随便举几个例子 1- 金融,比如预测股票(股市有风险,入市需谨慎),纯用K线做,我个人不太推荐 2- 天气,比如预测云图,天气预报啥的 3- 交通,早晚高峰,堵车啥的,车啥时候加油,啥时候充电之类的 4- 医疗,看你病史和喝酒的剂量建模,看你会…

斯坦福大学ALOHA家务机器人团队发布了最新研究成果—YAY Robot语言交互式操作系统

ALOHA YAY 演示视频-智能佳 斯坦福的ALOHA家务机器人团队&#xff0c;发布了最新研究成果—Yell At Your Robot&#xff08;简称YAY&#xff09;&#xff0c;有了它&#xff0c;机器人的“翻车”动作&#xff0c;只要喊句话就能纠正了&#xff01; 标ALOHA2协作平台题 而且机器…

Shell脚本基本命令

文件名后缀.sh 编写shell脚本一定要说明一下在#&#xff01;/bin/bash在进行编写。命令选项空格隔开。Shell脚本是解释的语言&#xff0c;bash 文件名即可打印出编写的脚本。chmod给权限命令。如 chmod 0777 文件名意思是给最高权限。 注意:count赋值不能加空格。取消变量可在变…

基于Spring 框架中的@Async 注解实现异步任务

Async 是 Spring 框架中的一个注解&#xff0c;用于实现方法级别的异步执行。使用 Async 可以让你的代码在非当前线程中执行&#xff0c;从而提高应用的并发性能。 1、 启用异步支持 在 Spring 应用的主配置类或任何其他配置类上添加 EnableAsync 注解来开启异步任务的支持 …

13.Redis之数据库管理redis客户端JAVA客户端

1.数据库管理 mysql 中有一个重要的概念,database 1个 mysql 服务器上可以有很多个 database1个 database 上可以有很多个 表mysql 上可以随心所欲的 创建/删除 数据库~~ Redis 提供了⼏个⾯向 Redis 数据库的操作&#xff0c;分别是 dbsize、select、flushdb、flushall 命令…

2024年中国金融行业网络安全市场全景图

网络安全一直是国家安全的核心组成部分&#xff0c;特别是在金融行业&#xff0c;金融机构拥有大量的敏感数据&#xff0c;包括个人信息、交易记录、财务报告等&#xff0c;这些数据的安全直接关系到消费者的利益和金融市场的稳定&#xff0c;因此金融行业在网络安全建设领域一…

Java—选择排序

选择排序是一种简单但高效的排序算法。它的基本思想是从未排序的部分中选择最小&#xff08;或最大&#xff09;的元素&#xff0c;并将其放置在已排序部分的末尾。 实现步骤 具体实现选择排序的步骤如下&#xff1a; 遍历数组&#xff1a;从数组的第一个元素开始&#xff0…

cesium绘制区域编辑

npm 安装也是可以的 #默认安装最新的 yarn add cesium#卸载插件 yarn remove cesium#安装指定版本的 yarn add cesium1.96.0#安装指定版本到测试环境 yarn add cesium1.96.0 -D yarn install turf/turf <template><div id"cesiumContainer"></div&…

从零开始学React--环境搭建

React官网 快速入门 – React 中文文档 1.搭建环境 下载nodejs,双击安装 nodejs下载地址 更新npm npm install -g npm 设置npm源&#xff0c;加快下载速度 npm config set registry https://registry.npmmirror.com 创建一个react应用 npx create-react-app react-ba…

QT 自定义协议TCP传输文件

后面附带实例的下载地址 一、将文件看做是由:文件头+文件内容组成,其中文件头包含文件的一些信息:文件名称、文件大小等。 二、文件头单独发送,文件内容切块发送。 三、每次发送信息格式:发送内容大小、发送内容类型(文件头或是文件块内容)、文件块内容。 四、效果展…

项目十三:搜狗——python爬虫实战案例

根据文章项目十二&#xff1a;简单的python基础爬虫训练-CSDN博客的简单应用&#xff0c;这一次来升级我们的技术&#xff0c;那么继续往下看&#xff0c;希望对技术有好运。 还是老样子&#xff0c;按流程走&#xff0c;一条龙服务&#xff0c;嘿嘿。 第一步&#xff1a;导入…

设置AXI主寄存器切片和AXI数据FIFO

设置AXI主寄存器切片和AXI数据FIFO 打开MHS文件&#xff0c;并为每个AXI主机设置启用寄存器切片/启用数据FIFO。到 确定正确的设置&#xff0c;使用下表中的信息搜索MHS。 进行搜索时&#xff0c;将<intf_name>替换为相关的BUS_INTERFACE名称。 例如&#xff0c;BUS_INTE…

开发远程遥控情趣玩具软件,提供现成程序源码应具备哪些基础功能

以“东莞梦情智能”为参考&#xff0c;其提供的现成情趣玩具遥控软件程序源码&#xff0c;所具备哪些基础功能&#xff0c;看看它们如何让情趣玩具变得更加丰富多彩。 一、设备连接 设备连接是情趣玩具遥控软件的基础功能之一。“东莞梦情智能”的现成源码支持多种连接方式&am…

迅狐跨境商城系统源码

在当今全球化的商业环境中&#xff0c;跨境电商的兴起为商家提供了无限的可能性。为了满足这一需求&#xff0c;跨境商城系统源码的开发显得尤为重要。本文将探讨跨境商城系统源码的优势&#xff0c;以及如何利用这些优势来构建一个成功的跨境电商平台。 独立开发&#xff0c;…

双指针技巧,链表

双指针链表 虚拟头节点双指针&#xff0c;都要用虚拟1头节点 合并两个有序链表 设置双指针&#xff0c;都指向虚拟头节点 ListNode list1 代表的是头节点 class Solution {public ListNode mergeTwoLists(ListNode list1, ListNode list2) {ListNode dummynew ListNode(-1…

pytorch比较操作

文章目录 常用的比较操作1.torch.allclose()2.torch.argsort()3.torch.eq()4.torch.equal()5.torch.greater_equal()6.torch.gt()7.torch.isclose()8.torch.isfinite()9.torch.isif()10.torch.isposinf()11.torch.isneginf()12.torch.isnan()13.torch.kthvalue()14.torch.less_…

常见排序算法之选择排序

目录 一、选择排序 1.1 什么是选择排序&#xff1f; 1.2 思路 1.2.1 思路一 1.2.2 优化思路 1.3 C语言源码 1.3.1 思路一 1.3.2 优化思路 二、堆排序 2.1 调整算法 2.1.2 向上调整算法 2.1.3 向下调整算法 2.2 建堆排序 一、选择排序 1.1 什么是选择排序&#xf…