springboot整合redis过期key监听实现订单过期操作

简介: springboot整合redis过期key监听实现订单过期操作

业务场景说明

对于订单问题,那些下单了但是没有去支付的(占单情况),不管对于支付宝还是微信都有订单的过期时间设置,但是对于我们自己维护的订单呢。两种方案:被动修改,主动修改。这里仅仅说明对于主动修改的监听实现

  • 修改redis的配置文件redis.conf(好像不改也可以) image.png
K:keyspace事件,事件以__keyspace@<db>__为前缀进行发布;        
E:keyevent事件,事件以__keyevent@<db>__为前缀进行发布;        
g:一般性的,非特定类型的命令,比如del,expire,rename等;       
$:字符串特定命令;        
l:列表特定命令;        
s:集合特定命令;        
h:哈希特定命令;        
z:有序集合特定命令;        
x:过期事件,当某个键过期并删除时会产生该事件;        
e:驱逐事件,当某个键因maxmemore策略而被删除时,产生该事件;        
A:g$lshzxe的别名,因此”AKE”意味着所有事件
  • pom依赖坐标的引入
<!--版本号说明。这里我使用的是<jedis.version>2.9.3</jedis.version>,<spring.boot.version>2.3.0.RELEASE</spring.boot.version>-->
<dependency>
  <groupId>redis.clients</groupId>
  <artifactId>jedis</artifactId>
  <version>${jedis.version}</version>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
  <version>${spring.boot.version}</version>
</dependency>

这里可能会存在的依赖冲突问题是与io.netty

  • RedisConfig的配置
package test.bo.work.config.redis;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import test.bo.work.util.StringUtil;
/**
 * @author xiaobo
 */
@Configuration
@EnableAutoConfiguration
public class JedisConfig {
    private static final Logger LOGGER = LoggerFactory.getLogger(JedisConfig.class);
    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private int port;
    @Value("${spring.redis.password}")
    private String password;
    @Value("${spring.redis.timeout}")
    private int timeout;
    @Value("${spring.redis.jedis.pool.max-active}")
    private int maxActive;
    @Value("${spring.redis.jedis.pool.max-wait}")
    private int maxWait;
    @Value("${spring.redis.jedis.pool.max-idle}")
    private int maxIdle;
    @Value("${spring.redis.jedis.pool.min-idle}")
    private int minIdle;
    @Bean
    public JedisPool redisPoolFactory() {
        try {
            JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
            jedisPoolConfig.setMaxIdle(maxIdle);
            jedisPoolConfig.setMaxWaitMillis(maxWait);
            jedisPoolConfig.setMaxTotal(maxActive);
            jedisPoolConfig.setMinIdle(minIdle);
            // JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, password);
            String pwd = StringUtil.isBlank(password) ? null : password;
            JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, pwd,6);
            LOGGER.info("初始化Redis连接池JedisPool成功!地址: " + host + ":" + port);
            return jedisPool;
        } catch (Exception e) {
            LOGGER.error("初始化Redis连接池JedisPool异常:" + e.getMessage());
        }
        return null;
    }
    @Bean
    public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        return container;
    }
}
  • 监听器实现
package test.bo.work.config.redis;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
/**
 * @author xiaobo
 */
@Component
@Slf4j
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
  public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
    super(listenerContainer);
    log.info("Redis Key Expiration Listener has been initialized.");
  }
  @Override
  public void onMessage(Message message, byte[] pattern) {
    String expiredKey = message.toString();
    log.error(expiredKey);
      // 判断是否是想要监听的过期key
      if (expiredKey.startsWith(KuoCaiConstants.ORDER_REDIS_PREFIX)) {
        // 根据过期redisKey获取订单号
        String transactionOrderId = expiredKey.substring(KuoCaiConstants.ORDER_REDIS_PREFIX.length());
        transactionOrderService.updateTransactionOrderStatusByRedis(Long.valueOf(transactionOrderId));
}
  }
}

【注意】可能存在的问题(监听事件失效)

  • 自己自定义了库,如下代码
@Override
    protected void doRegister(RedisMessageListenerContainer listenerContainer) {
        // 针对db6进行监听
        listenerContainer.addMessageListener(this, new PatternTopic("__keyevent@6__:expired"));
    }

这种情况,如果你存入的key不在db6,那么你就看不到监听触发事件(这里并不是失效,只是说可能出现的你认为失效的情况)

  • 使用了默认的连接工厂,但是配置文件中又没有相关定义

首先解释一下,默认情况下,Spring Boot使用Jedis作为Redis客户端,并且会自动根据application.properties或application.yml配置文件中的spring.redis属性来创建连接工厂。如果没有指定这些属性,则会使用默认的localhost:6379作为Redis服务器地址。

如果你想要连接到其他的Redis服务器,可以在配置文件中设置spring.redis.hostspring.redis.port属性来指定Redis服务器的地址和端口号。

  • image.png
  • 对于以上情况可以自定义工厂然后注入即可(上面的JedisConfig.java文件)
@Bean
public JedisConnectionFactory jedisConnectionFactory() {
  RedisStandaloneConfiguration redisStandaloneConfiguration =
    new RedisStandaloneConfiguration();
  redisStandaloneConfiguration.setHostName(host);
  redisStandaloneConfiguration.setDatabase(db);
  redisStandaloneConfiguration.setPassword(RedisPassword.of(password));
  redisStandaloneConfiguration.setPort(port);
  return new JedisConnectionFactory(redisStandaloneConfiguration);
}
@Bean
public RedisMessageListenerContainer container(JedisConnectionFactory jedisConnectionFactory) {
  RedisMessageListenerContainer container = new RedisMessageListenerContainer();
  container.setConnectionFactory(jedisConnectionFactory);
  return container;
}

【警告】存在的问题

  • Redis的Key事件通知机制默认是异步的,即Redis会在Key过期时发送事件通知给所有监听者,但是不能保证监听者一定会及时接收到通知。如果您的应用程序需要在Key过期后立即处理相关操作,可能需要使用其他方式来实现。
  • 在Redis中,Key的过期时间只是一个近似的时间,它并不是精确的,因此不能保证过期时间到达时就一定会立即过期。如果您需要在Key过期后立即处理相关操作,建议您使用其他方式来实现,例如使用定时任务或轮询方式检查过期Key。
  • 在Redis中,Key的过期时间不能被取消或重置。如果您在设计时考虑到Key的过期时间可能需要修改,建议您使用其他方式来实现。
  • 当Redis中的Key被持久化到磁盘上时,过期时间可能会受到影响,因为过期时间的计算是基于系统时间的,如果系统时间发生变化,过期时间可能会出现不准确的情况。因此,建议您使用其他方式来处理需要精确过期时间的场景。
相关文章
|
5月前
|
NoSQL Java 网络安全
SpringBoot启动时连接Redis报错:ERR This instance has cluster support disabled - 如何解决?
通过以上步骤一般可以解决由于配置不匹配造成的连接错误。在调试问题时,一定要确保服务端和客户端的Redis配置保持同步一致。这能够确保SpringBoot应用顺利连接到正确配置的Redis服务,无论是单机模式还是集群模式。
516 5
|
NoSQL Java 关系型数据库
微服务——SpringBoot使用归纳——Spring Boot 中集成Redis——Redis 介绍
本文介绍在 Spring Boot 中集成 Redis 的方法。Redis 是一种支持多种数据结构的非关系型数据库(NoSQL),具备高并发、高性能和灵活扩展的特点,适用于缓存、实时数据分析等场景。其数据以键值对形式存储,支持字符串、哈希、列表、集合等类型。通过将 Redis 与 Mysql 集群结合使用,可实现数据同步,提升系统稳定性。例如,在网站架构中优先从 Redis 获取数据,故障时回退至 Mysql,确保服务不中断。
432 0
微服务——SpringBoot使用归纳——Spring Boot 中集成Redis——Redis 介绍
|
6月前
|
NoSQL Java 调度
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
分布式锁是分布式系统中用于同步多节点访问共享资源的机制,防止并发操作带来的冲突。本文介绍了基于Spring Boot和Redis实现分布式锁的技术方案,涵盖锁的获取与释放、Redis配置、服务调度及多实例运行等内容,通过Docker Compose搭建环境,验证了锁的有效性与互斥特性。
555 0
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
|
9月前
|
机器学习/深度学习 数据采集 人机交互
springboot+redis互联网医院智能导诊系统源码,基于医疗大模型、知识图谱、人机交互方式实现
智能导诊系统基于医疗大模型、知识图谱与人机交互技术,解决患者“知症不知病”“挂错号”等问题。通过多模态交互(语音、文字、图片等)收集病情信息,结合医学知识图谱和深度推理,实现精准的科室推荐和分级诊疗引导。系统支持基于规则模板和数据模型两种开发原理:前者依赖人工设定症状-科室规则,后者通过机器学习或深度学习分析问诊数据。其特点包括快速病情收集、智能病症关联推理、最佳就医推荐、分级导流以及与院内平台联动,提升患者就诊效率和服务体验。技术架构采用 SpringBoot+Redis+MyBatis Plus+MySQL+RocketMQ,确保高效稳定运行。
673 0
|
12月前
|
存储 人工智能 NoSQL
SpringBoot整合Redis、ApacheSolr和SpringSession
本文介绍了如何使用SpringBoot整合Redis、ApacheSolr和SpringSession。SpringBoot以其便捷的配置方式受到开发者青睐,通过引入对应的starter依赖,可轻松实现功能整合。对于Redis,可通过配置RedisSentinel实现高可用;SpringSession则提供集群Session管理,支持多种存储方式如Redis;整合ApacheSolr时,借助Zookeeper搭建SolrCloud提高可用性。文中详细说明了各组件的配置步骤与代码示例,方便开发者快速上手。
224 11
|
NoSQL Java API
微服务——SpringBoot使用归纳——Spring Boot 中集成Redis——Spring Boot 集成 Redis
本文介绍了在Spring Boot中集成Redis的方法,包括依赖导入、Redis配置及常用API的使用。通过导入`spring-boot-starter-data-redis`依赖和配置`application.yml`文件,可轻松实现Redis集成。文中详细讲解了StringRedisTemplate的使用,适用于字符串操作,并结合FastJSON将实体类转换为JSON存储。还展示了Redis的string、hash和list类型的操作示例。最后总结了Redis在缓存和高并发场景中的应用价值,并提供课程源代码下载链接。
2638 0
|
NoSQL Java Redis
微服务——SpringBoot使用归纳——Spring Boot 中集成Redis——Redis 安装
本教程介绍在 VMware 虚拟机(CentOS 7)或阿里云服务器中安装 Redis 的过程,包括安装 gcc 编译环境、下载 Redis(官网或 wget)、解压安装、修改配置文件(如 bind、daemonize、requirepass 等设置)、启动 Redis 服务及测试客户端连接。通过 set 和 get 命令验证安装是否成功。适用于初学者快速上手 Redis 部署。
371 0
|
存储 SQL 消息中间件
springboot整合redis
redis是一个支持key-value的数据库,数据全部在内存中处理,在在一定时间间隔中将数据固化到磁盘。因为是内存操作,所以速度特别快。(这里我们主要介绍redis作为缓存使用)
336 0
springboot整合redis
|
存储 缓存 NoSQL
SpringBoot整合Redis
SpringBoot整合Redis
712 0
SpringBoot整合Redis