SpringBoot Redis 扩展高级功能

环境: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() ;     }  }}

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

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

相关文章

有哪些地图采集软件可以采集商家数据导出功能?

1.国内商家采集 寅甲地图数据采集软件 寅甲地图数据采集软件一款多关键词多城市同时采集百度地图、360地图、高德地图、搜狗地图、腾讯地图、图吧地图、天地图商家、公司、店铺的手机、座机、地址、坐标等数据信息的软件。 2.国外商家采集 寅甲谷歌地图数据采集软件 专为做…

Java学习路线思维导图

目录 Java学习流程1.学习大纲2.Java开发中常用的DOS命令 Java入门学习思维导图 Java学习流程 通过大纲了解学习的重点&#xff0c;通过目录依次深入【注&#xff1a;Java环境的搭建百度&#xff0c;提升自己百度的能力】 1.学习大纲 学习流程如下&#xff1a; Java基础语法 …

操作系统3_作业与处理机调度

操作系统3_作业与处理机调度 文章目录 操作系统3_作业与处理机调度1. 作业的概念与组成2. 作业的建立及状态3. 处理机调度相关概念3.1 调度级别3.2 调度队列模型3.3 选择准则4. 作业调度与进程调度5. 典型处理机调度算法5.1 先来先服务算法FCFS5.2 短作业优先算法SJF5.3 优先级…

Python中别再用 ‘+‘ 拼接字符串了!

大家好&#xff0c;在 Python 编程中&#xff0c;我们常常需要对字符串进行拼接。你可能会自然地想到用 操作符将字符串连接起来&#xff0c;毕竟这看起来简单明了。 在 Python 中&#xff0c;字符串是不可变的数据类型&#xff0c;这意味着一旦字符串被创建&#xff0c;它就…

HarmonyOS 鸿蒙应用开发 DevEco Studio环境搭建 (值得收藏哦)

目录 1、华为开发者官网下载 DevEco Studio 2、安装DevEco Studi 3、安装过程具体步骤 4、认证华为开发者账号 5、编写第一个鸿蒙应用 1、华为开发者官网下载 DevEco Studio 前往&#xff1a;华为开发者官网地址 下载&#xff0c;我这里下载的 deveco-studio-3.1.0.500版…

VSCode SAP Systems配置HTTPS访问SAP

第一次访问提示&#xff0c;Self-Signed 证书 解决办法&#xff1a;https访问SAP Fiori网站&#xff0c;导出SSL证书为DER格式保存到硬盘上 双击DER文件&#xff0c;导入到系统 退出VSCode&#xff0c;再次启动 Test Connection, 提示 The system URL is using a hostname …

“高考钉子户”唐尚珺决定再战2024年高考

“高考钉子户”唐尚珺决定在2024年再次参加高考&#xff0c;这个选择确实很特别也很有趣。十几年连续参加高考&#xff0c;他已经积累了大量的备考经验和应试技巧。这样的经验对于高考辅导机构来说无疑是非常宝贵的资源&#xff0c;他如果选择去辅导机构当老师&#xff0c;应该…

二百三十七、Hive——DWS层生成每个清洗字段的异常情况记录

一、目的 在Hive中对每种业务数据的清洗字段的异常数据进行记录 例如这张图&#xff0c;上面是原始数据&#xff0c;下面是每台雷达每天的异常字段的记录 二、实施步骤 &#xff08;一&#xff09;建表 create table if not exists dws_data_clean_record_queue(data_ty…

基于Matlab卷积神经网络人脸识别

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景与意义 人脸识别作为计算机视觉领域的关键技术之一&#xff0c;具有广泛的应用前景&#xff0c;如安全…

如何参与github开源项目并提交PR

&#x1f47d;System.out.println(“&#x1f44b;&#x1f3fc;嗨&#xff0c;大家好&#xff0c;我是代码不会敲的小符&#xff0c;目前工作于上海某电商服务公司…”); &#x1f4da;System.out.println(“&#x1f388;如果文章中有错误的地方&#xff0c;恳请大家指正&…

window环境下QT5开发环境的搭建

1、安装visual Stusio 15 生成工具2012 2、安装Visual studio Enterprise 2017 3、Visual studio Enterprise 2017安装完成之后&#xff0c; 修改&#xff1a;选择桌面调试&#xff0c;如下&#xff1a; 4、打开QTcreator&#xff0c;选项中&#xff0c;配置编译器&#xff…

LeetCode 264 —— 丑数 II

阅读目录 1. 题目2. 解题思路3. 代码实现 1. 题目 2. 解题思路 第一个丑数是 1 1 1&#xff0c;由于丑数的质因子只包含 2 、 3 、 5 2、3、5 2、3、5&#xff0c;所以后面的丑数肯定是前面的丑数分别乘以 2 、 3 、 5 2、3、5 2、3、5 后得到的数字。 这样&#xff0c;我…

【全开源】答题考试系统源码(FastAdmin+ThinkPHP+Uniapp)

答题考试系统源码&#xff1a;构建高效、安全的在线考试平台 引言 在当今数字化时代&#xff0c;在线考试系统已成为教育机构和企业选拔人才的重要工具。一个稳定、高效、安全的答题考试系统源码是构建这样平台的核心。本文将深入探讨答题考试系统源码的关键要素&#xff0c;…

SQLmap学习以及题解运用

1.简介 SQLmap是一款开源的SQL注入工具&#xff0c;用于检测和利用Web应用程序的SQL注入漏洞。SQLmap支持多种数据库管理系统&#xff0c;包括MySQL、Oracle、PostgreSQL、Microsoft SQL Server、SQLite等&#xff0c;并支持各种不同的操作系统和平台。 这里主要分为四大部分…

抖音运营_如何开抖店

截止20年8月&#xff0c;抖音的日活跃数高达6亿。 20年6月&#xff0c;上线抖店 &#xff08;抖音官方电商&#xff09; 一 抖店的定位和特色 1 一站式经营 帮助商家进行 商品交易、店铺管理、客户服务 等全链路的生意经营 2 多渠道拓展 抖音、今日头条、西瓜、抖音火山版…

解读乐得瑞LDR6020 PD协议芯片:开启智能快充新时代

在如今电子产品日新月异&#xff0c;功能不断增强的时代&#xff0c;充电技术的革新也显得尤为重要。为了满足用户对高效、安全、便捷的充电需求&#xff0c;乐得瑞公司凭借其深厚的技术积累和创新能力&#xff0c;推出了一款名为LDR6020的PD协议芯片&#xff0c;为智能快充领域…

探索python列表处理:偶数筛选的两种方法

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言 二、不使用列表生成式的偶数筛选 1. 读取输入列表 2. 筛选偶数 三、使用列表生…

GpuMall智算云:Ubuntu 实例桌面版

基于 ubuntu18.04 安装的桌面版本&#xff0c;桌面使用 xfce4 &#xff0c;集成了 Pytorch2.3.0、cuda11.8、Python3.10、VNC、noVNC、VSCode-Server。 在 镜像市场 选择xfce4-desktop镜像&#xff0c;然后进行创建实例 GpuMall智算云 | 省钱、好用、弹性。租GPU就上GpuMall…

打造AI虚拟伴侣 - 优化方案

第一部分:框架优化概述 1、精确定位: 构建一个高度灵活且用户友好的平台,旨在通过无缝集成多种大型语言模型(LLMs)后端,为用户创造沉浸式的角色交互体验。不仅适配电脑端,还特别优化移动端体验,满足二次元AI虚拟伴侣市场的特定需求。 2、核心功能强化: 增强后端兼容…

吉时利2401新款(keithley)2410数字源表 原装二手

吉时利2401数字源表 Keithley 2401 数字源表 Keithley吉时利数字源表 先进电气测试仪器与系统的吉时利仪器公司发布了专为低电压测试而优化的低成本方案&#xff0c;扩展了其广受工程师赞誉的2400系列数字源表产品线。与所有吉时利SMU&#xff08;源测量单元&#xff09;仪器…