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

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

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

相关文章

北斗短报文终端 | 什么是北斗短报文功能?如何实现北斗短报文通信?

北斗短报文功能是指通过北斗卫星进行短报文通信的功能。这种功能允许用户在没有移动通信信号覆盖的偏远山区、海洋、沙漠等地带&#xff0c;通过北斗短报文终端发送和接收文本信息&#xff0c;进行基本的数据通信。 北斗短报文功能是指北斗卫星导航系统特有的双向报文通信功能。…

urllib_post请求_百度翻译

打开百度翻译&#xff0c;并打开控制台&#xff0c;输入spider&#xff0c;然后在网络中找到对应的接口&#xff0c;可以看出&#xff0c;该url是post请求 在此案例中找到的接口为sug&#xff0c;依据为&#xff1a; 可以看到&#xff0c;传递的数据为kw : XXX&#xff0c; 所…

[Linux]服务管理

一.服务的概念&#xff0c;状态&#xff0c;查看系统服务 服务(service)本质就是进程 如(mysqld&#xff0c;sshd 防火墙等) 是运行在后台的&#xff0c;通常都会监听某个端口&#xff0c;等待其它程序的请求 -------比如mysqld&#xff0c;防火墙等&#xff0c;因此我们又称为…

3D瓦片地图组件上线|提供DEM数据接入,全方位呈现三维地图地形!

在用户调研中&#xff0c;我们了解到很多用户自身的可视化项目&#xff0c;需要在垂直空间上表现一些业务&#xff0c;例如&#xff1a;3D地形效果&#xff0c;数据底板建设等&#xff0c;而传统的地图效果不满足此用户需求。瓦片地图能够无限加载大地图&#xff0c;以更三维的…

【Linux】在Ubuntu 16.04上安装Gerrit + PostgreSQL + Apache服务

Gerrit是一个基于Git版本控制系统的运行于Web浏览器上的Code Review工具&#xff0c;本文叙述如何在Ubuntu 16.04上安装Gerrit服务。&#xff08;当然安装Gerrit的方法有很多&#xff0c;本文只是其中之一&#xff09; 文章目录 前提安装PostgreSQL数据库并创建用户下载、配置和…

【飞舞的花瓣】飞舞的花瓣代码||樱花代码||表白代码(完整代码)

关注微信公众号「ClassmateJie」有完整代码以及更多惊喜等待你的发现。 简介/效果展示 这段代码是一个HTML页面&#xff0c;其中包含一个canvas元素和相关的JavaScript代码。这个页面创建了一个飘落花瓣的动画效果。 代码【获取完整代码关注微信公众号「ClassmateJie」回复“…

一步将 CentOS 7.X 原地升级并迁移至 RHEL 7.9

《OpenShift / RHEL / DevSecOps 汇总目录》 在《在离线环境中将 CentOS 7.X 原地升级并迁移至 RHEL 7.9》一文中为了实现从 CentOS 7.X 原地升级并迁移至 RHEL 7.9&#xff0c;我们第一步先将一个测试环境 CentOS 7.5 升级到 CentOS 7.9&#xff0c;然后在第二步使用 convert2…

抖音运营_如何开抖店

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

【Python】用于发送电子邮件的标准库smtplib和构建邮件主体、添加附件、设置收件人的email

欢迎来到《小5讲堂》 这是《Python》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 插件介绍邮件代码扩展知识点文章推荐 插件介绍 smtplib 是 Pytho…

海外媒体发稿的关键步骤和投稿策略:如何撰写高质量的新闻稿?国外软文发布平台有哪些?

发布国外新闻稿件是一个涉及多步骤的过程&#xff0c;旨在确保您的新闻稿能够有效覆盖目标受众。以下是一些关键步骤和实用的技巧&#xff0c;帮助你实现海外媒体发稿。 1. 明确目标和受众 首先&#xff0c;明确您发布新闻稿的目标&#xff0c;是为了增加品牌曝光、推出新产品…

惊呆了!企业数字化转型竟如工厂生产?

在众多使用蚓链数字化生态系统解决方案实现数字化转型的企业&#xff0c;你能想象吗&#xff1f;如今的企业数字化转型&#xff0c;就如同一家工厂的生产过程&#xff01;数据成为了原材料&#xff0c;而数据资源则是场景化的零件&#xff0c;最终生产出满足市场需求的数据产品…

基于Kafka的日志采集

目录 前言 架构图 资源列表 基础环境 关闭防护墙 关闭内核安全机制 修改主机名 添加hosts映射 一、部署elasticsearch 修改limit限制 部署elasticsearch 修改配置文件 启动 二、部署filebeat 部署filebeat 添加配置文件 启动 三、部署kibana 部署kibana 修…

21-信号集处理函数

屏蔽信号集 屏蔽某些信号 手动自动 未处理信号集 信号如果被屏蔽&#xff0c;则记录在未处理信号集中 非实时信号&#xff08;1~31&#xff09;&#xff0c;不排队&#xff0c;只留一个实时信号&#xff08;34~64&#xff09;&#xff0c;排队&#xff0c;保留全部 信号集…

springmvc中HandlerMapping是干什么用的

HandlerMapping处理器映射器 作用是根据request找到相应的处理器Handler和Interceptors&#xff0c;然后封装成HandlerExecutionChain对象返回 HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception; 实现类 HandlerMapping帮助DispatcherServlet进…

【AD21】BOM表文件的输出

BOM表文件通常发给采购&#xff0c;采购可以购买BOM表中所需的元器件。 在菜单栏中点击报告->Bill of Materials。 在以下界面&#xff0c;点击Columns&#xff0c;对输出的BOM表中一些说明进行不显示&#xff0c;可以不显示Description和LibRef。值&#xff0c;位号&#…

文心智能体【焦虑粉碎机】——帮你赶走“坏”情绪

目录&#xff1a; 引言1.登录 文心智能体平台2.创建智能体3.配置智能体&#x1f337; 头像设置&#x1f337; 名称设置&#x1f337; 简介设置&#x1f337;指令设置&#x1f337; 开场白设置&#x1f337; 引导示例设置 4.使用智能体 引言 随着ChatGPT的爆火&#xff0c;人工智…

【Python】 掌握 Flask 请求数据获取的艺术

基本原理 在Web开发中&#xff0c;Flask是一个用Python编写的轻量级Web应用框架。它被广泛用于快速开发简单的Web应用。当用户通过浏览器或其他客户端向服务器发送请求时&#xff0c;Flask需要能够接收和解析这些请求中的数据。这些数据可以是GET请求的查询字符串、POST请求的…

【算法】分治 - 快速排序

快乐的流畅&#xff1a;个人主页 个人专栏&#xff1a;《算法神殿》《数据结构世界》《进击的C》 远方有一堆篝火&#xff0c;在为久候之人燃烧&#xff01; 文章目录 引言一、颜色分类二、排序数组三、数组中的第k个数四、最小的k个数总结 引言 本节主要介绍快速排序&#xf…

swust oj 1012: 哈希表(链地址法处理冲突)

直接采用二维数组模拟实现 #include <iostream> using namespace std; const int N 100; int arr[N][N]; int point[N];//计数int main() {int m, n,data;cin >> m >> n;for (int i 0; i < n; i){cin >> data;int key data % m;arr[key][point[…

对于高速信号完整性,一块聊聊啊(10)

本文包含的主要内容有: 过孔设计概述:从前面的各种基础知识到过孔设计,逐步对信号完整性有了初步了解,在过孔设计这里稍微做一个概述,也是个人的一些理解,算是一个小结。 过孔设计的必要性。 过孔结构的基础知识 实例:过孔设计仿真HFSS实例 过孔设计概述 通过前面…