返回顶部
首页 > 资讯 > 精选 >Redis监听过期的key实现流程是什么
  • 672
分享到

Redis监听过期的key实现流程是什么

2023-07-05 07:07:02 672人浏览 薄情痞子
摘要

本篇内容介绍了“Redis监听过期的key实现流程是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、简介我们来个最简单的集群架构,如下

本篇内容介绍了“Redis监听过期的key实现流程是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

    一、简介

    我们来个最简单的集群架构,如下图:

    Redis监听过期的key实现流程是什么

    我们上面图中看到是服务A和服务B就是同一个服务的不同实例。

    二、Maven依赖

    pom.xml

    <?xml version="1.0" encoding="UTF-8"?><project xmlns="Http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <parent>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-parent</artifactId>        <version>2.6.0</version>        <relativePath/> <!-- lookup parent from repository -->    </parent>    <groupId>com.alian</groupId>    <artifactId>expiration</artifactId>    <version>0.0.1-SNAPSHOT</version>    <name>expiration</name>    <description>Redis-key-expiration-listener</description>    <properties>        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>        <project.package.directory>target</project.package.directory>        <java.version>1.8</java.version>        <!--com.fasterxml.jackson 版本-->        <jackson.version>2.9.10</jackson.version>        <!--阿里巴巴fastJSON 版本-->        <fastjson.version>1.2.68</fastjson.version>    </properties>    <dependencies>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-WEB</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-test</artifactId>        </dependency>        <!--redis依赖-->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-data-redis</artifactId>            <version>${parent.version}</version>        </dependency>        <!--用于序列化-->        <dependency>            <groupId>com.fasterxml.jackson.core</groupId>            <artifactId>jackson-databind</artifactId>            <version>${jackson.version}</version>        </dependency>        <!--java 8时间序列化-->        <dependency>            <groupId>com.fasterxml.jackson.datatype</groupId>            <artifactId>jackson-datatype-jsr310</artifactId>            <version>${jackson.version}</version>        </dependency>        <dependency>            <groupId>com.alibaba</groupId>            <artifactId>fastjson</artifactId>            <version>1.2.68</version>        </dependency>        <dependency>            <groupId>org.projectlombok</groupId>            <artifactId>lombok</artifactId>            <version>1.16.14</version>        </dependency>        <dependency>            <groupId>junit</groupId>            <artifactId>junit</artifactId>            <version>4.13.2</version>            <scope>test</scope>        </dependency>    </dependencies>    <build>        <plugins>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>            </plugin>        </plugins>    </build></project>

    三、编码实现

    3.1、application.properties

    # 端口
    server.port=8090
    # 上下文路径
    server.servlet.context-path=/expiration

    # Redis数据库索引(默认为0)
    spring.redis.database=0
    # Redis服务器地址
    spring.redis.host=192.168.0.193
    #spring.redis.host=127.0.0.1
    # Redis服务器连接端口
    spring.redis.port=6379
    # Redis服务器连接密码(默认为空)
    spring.redis.passWord=
    # 连接池最大连接数(使用负值表示没有限制)
    spring.redis.jedis.pool.max-active=20
    # 连接池中的最小空闲连接
    spring.redis.jedis.pool.min-idle=10
    # 连接池中的最大空闲连接
    spring.redis.jedis.pool.max-idle=10
    # 连接池最大阻塞等待时间(使用负值表示没有限制)
    spring.redis.jedis.pool.max-wait=20000
    # 读时间(毫秒)
    spring.redis.timeout=10000
    # 连接超时时间(毫秒)
    spring.redis.connect-timeout=10000

    3.2、Redis配置类

    RedisConfig

    package com.alian.expiration.config;import com.fasterxml.jackson.annotation.JsonAutoDetect;import com.fasterxml.jackson.annotation.PropertyAccessor;import com.fasterxml.jackson.databind.ObjectMapper;import com.fasterxml.jackson.databind.SerializationFeature;import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.listener.RedisMessageListenerContainer;import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;import org.springframework.data.redis.serializer.RedisSerializer;import org.springframework.data.redis.serializer.StringRedisSerializer;import java.time.LocalDate;import java.time.LocalDateTime;import java.time.LocalTime;import java.time.fORMat.DateTimeFormatter;@Configurationpublic class RedisConfig {        @Bean    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {        // 实例化redisTemplate        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();        //设置连接工厂        redisTemplate.setConnectionFactory(redisConnectionFactory);        // key采用String的序列化        redisTemplate.seTKEySerializer(keySerializer());        // value采用jackson序列化        redisTemplate.setValueSerializer(valueSerializer());        // Hash key采用String的序列化        redisTemplate.setHashKeySerializer(keySerializer());        // Hash value采用jackson序列化        redisTemplate.setHashValueSerializer(valueSerializer());        // 支持事务        // redisTemplate.setEnableTransactionSupport(true);        //执行函数,初始化RedisTemplate        redisTemplate.afterPropertiesSet();        return redisTemplate;    }        private RedisSerializer<String> keySerializer() {        return new StringRedisSerializer();    }        private RedisSerializer<Object> valueSerializer() {        //设置jackson序列化        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);        //设置序列化对象        jackson2JsonRedisSerializer.setObjectMapper(getMapper());        return jackson2JsonRedisSerializer;    }        private ObjectMapper getMapper() {        ObjectMapper mapper = new ObjectMapper();        //设置可见性        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);        //默认键入对象        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);        //设置Java 8 时间序列化        JavaTimeModule timeModule = new JavaTimeModule();        timeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));        timeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));        timeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));        timeModule.aDDDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));        timeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));        timeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")));        //禁用把时间转为时间戳        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);        mapper.reGISterModule(timeModule);        return mapper;    }    @Bean    RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {        RedisMessageListenerContainer container = new RedisMessageListenerContainer();        container.setConnectionFactory(connectionFactory);        return container;    }}

    和我们之前整合redis差不多,只不过在最后增加了一个redis消息监听监听容器RedisMessageListenerContainer

    3.3、监听器

    RedisKeyExpirationListener

    package com.alian.expiration.listener;import com.alian.expiration.service.RedisExpirationService;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.connection.Message;import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;import org.springframework.data.redis.listener.RedisMessageListenerContainer;import org.springframework.stereotype.Component;@Slf4j@Componentpublic class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {    @Autowired    private RedisExpirationService redisExpirationService;// 把我们上面一步配置的bean注入进去    public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {        super(listenerContainer);    }        @Override    public void onMessage(Message message, byte[] pattern) {        // 用户做自己的业务处理即可,注意message.toString()可以获取失效的key        String expiredKey = message.toString();        log.info("onMessage --> redis 过期的key是:{}", expiredKey);        try {            // 对过期key进行处理            redisExpirationService.processingExpiredKey(expiredKey);            log.info("过期key处理完成:{}", expiredKey);        } catch (Exception e) {            e.printStackTrace();            log.error("处理redis 过期的key异常:{}", expiredKey, e);        }    }}

    实现的步骤如下:

    • 继承KeyExpirationEventMessageListener

    • 把redis消息监听监听容器RedisMessageListenerContainer 注入到密钥空间事件消息侦 听器中

    • 重写onMessage方法

    • 通过Message 的 toString() 方法就可以获取到过期的key

    • 对key中关键信息进行业务处理,比如 id

    3.4、服务类

    RedisExpirationService

    package com.alian.expiration.service;import com.alian.expiration.util.SignUtils;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;@Slf4j@Servicepublic class RedisExpirationService {    @Autowired    private RedisTemplate<String, Object> redisTemplate;    public void processingExpiredKey(String expiredKey) {        // 如果是优惠券的key(一定要规范命名)        if (expiredKey.startsWith("com.mall.coupon.id")) {            // 临时key,此key可以在业务处理完,然后延迟一定时间删除,或者不处理            String tempKey = SignUtils.md5(expiredKey, "UTF-8");            // 临时key不存在才设置值,key超时时间为10秒(此处相当于分布式的应用)            Boolean exist = redisTemplate.opsForValue().setIfAbsent(tempKey, "1", 10, TimeUnit.SECONDS);            if (Boolean.TRUE.equals(exist)) {                log.info("Business Handing...");                // 比如截取里面的id,然后关联数据库进行处理            } else {                log.info("Other service is handing...");            }        } else {            log.info("Expired keys without processing");        }    }}

    基本流程如下:

    • 判断是否是需要处理的key,一般这种key通过命名规范加以处理

    • 以当前key生成一个新的key作为分布式key

    • 如果redis中不存在这个新的key,则为新的key设置一个值,达到分布式服务处理(核心)

    • 设置成功的,进行业务处理;设置失败了,说明其他服务正在处理这个key

    • 根据 key 的关键信息(比如截取id),进行业务处理

    3.5、工具

    SignUtils

    package com.alian.expiration.util;import java.security.MessageDigest;public class SignUtils {    public static final String md5(String s, String charset) {        char[] hexDigits = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};        try {            byte[] btInput = s.getBytes(charset);            MessageDigest mdInst = MessageDigest.getInstance("MD5");            mdInst.update(btInput);            byte[] md = mdInst.digest();            int j = md.length;            char[] str = new char[j * 2];            int k = 0;            for (byte byte0 : md) {                str[k++] = hexDigits[byte0 >>> 4 & 15];                str[k++] = hexDigits[byte0 & 15];            }            return new String(str);        } catch (Exception var11) {            return "";        }    }}

    四、测试

    4.1、测试类

    简单模拟下发送一个优惠券数据到redis,然后设置超时时间

    package com.alian.expiration;import lombok.extern.slf4j.Slf4j;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import java.util.HashMap;import java.util.Map;import java.util.concurrent.TimeUnit;@Slf4j@RunWith(SpringJUnit4ClassRunner.class)@SpringBootTestpublic class RedisKeyExpirationTest {    @Autowired    private RedisTemplate<String, Object> redisTemplate;    @Test    public void keyExpiration() {        // 优惠券信息        String id = "2023021685264735";        Map<String, String> map = new HashMap<>();        map.put("id", id);        map.put("amount", "1000");        map.put("type", "1001");        map.put("describe", "满减红包");        // 缓存到redis        redisTemplate.opsForHash().putAll("com.mall.coupon.id." + id, map);        // 设置过期时间        redisTemplate.expire("com.mall.coupon.id." + id, 10, TimeUnit.SECONDS);    }}

    4.2、单实例

    单实例就是服务只部署了一份,我们启动一份,端口是8090,然后通过上面的测试类,发送一个消息,结果如下:

    39 701 INFO [container-2]:onMessage --> redis 过期的key是:com.mall.coupon.id.2023021685264735
    10:23:39 988 INFO [container-2]:Business Handing...
    10:23:39 989 INFO [container-2]:过期key处理完成:com.mall.coupon.id.2023021685264735
    10:23:50 005 INFO [container-3]:onMessage --> redis 过期的key是:450FCC35415BADC16805962CA5BC7E12
    10:23:50 005 INFO [container-3]:Expired keys without processing
    10:23:50 005 INFO [container-3]:过期key处理完成:450FCC35415BADC16805962CA5BC7E12

    4.3、多实例

    多实例就是服务部署了多份,比如我们启动两份,端口分别为8090和8091,然后通过上面的测试类,发送一个消息,8090端口的服务结果如下(Business Handing&hellip;):

    06 691 INFO [container-2]:onMessage --> redis 过期的key是:com.mall.coupon.id.2023021685264735
    11:39:06 707 INFO [container-2]:Business Handing...
    11:39:06 707 INFO [container-2]:过期key处理完成:com.mall.coupon.id.2023021685264735
    11:39:16 796 INFO [container-3]:onMessage --> redis 过期的key是:450FCC35415BADC16805962CA5BC7E12
    11:39:16 796 INFO [container-3]:Expired keys without processing
    11:39:16 796 INFO [container-3]:过期key处理完成:450FCC35415BADC16805962CA5BC7E12

    8091端口的服务结果如下(Other service is handing&hellip;):

    06 691 INFO [container-2]:onMessage --> redis 过期的key是:com.mall.coupon.id.2023021685264735
    11:39:06 707 INFO [container-2]:Other service is handing...
    11:39:06 707 INFO [container-2]:过期key处理完成:com.mall.coupon.id.2023021685264735
    11:39:16 796 INFO [container-3]:onMessage --> redis 过期的key是:450FCC35415BADC16805962CA5BC7E12
    11:39:16 796 INFO [container-3]:Expired keys without processing
    11:39:16 796 INFO [container-3]:过期key处理完成:450FCC35415BADC16805962CA5BC7E12

    结果分析:

    • 多实例的情况下,每个实例都会收到过期key通知

    • 通过redis分布式锁,实现只有一个实例会进行业务处理,防止重复

    • 使用分布式锁会有一个新的key过期,并且收到该key的通知,你可以业务执行完延迟一定时间(避免重复执行),再删除,也可以不处理(因为本就不是要处理业务的key)

    “Redis监听过期的key实现流程是什么”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

    --结束END--

    本文标题: Redis监听过期的key实现流程是什么

    本文链接: https://lsjlt.com/news/350252.html(转载时请注明来源链接)

    有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

    猜你喜欢
    • Redis监听过期的key实现流程是什么
      本篇内容介绍了“Redis监听过期的key实现流程是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、简介我们来个最简单的集群架构,如下...
      99+
      2023-07-05
    • Redis监听过期的key实现流程详解
      目录一、简介二、maven依赖三、编码实现3.1、application.properties3.2、Redis配置类3.3、监听器3.4、服务类3.5、工具类四、测试4.1、测试类...
      99+
      2023-02-28
      Redis监听过期的key Redis监听key
    • 怎么在Redis集群中监听过期key
      这篇文章将为大家详细讲解有关怎么在Redis集群中监听过期key,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。redis.host1: 10.11...
      99+
      2024-04-02
    • redis监听key过期事件的详细步骤
      目录1、配置Redis.conf文件2、 配置一个key过期事件的监听器3、订阅key过期事件4、发起订阅1、配置redis.conf文件 配置文件默认是#注释了的,改为notify-keyspace-even...
      99+
      2022-08-10
      redis监听key过期事件 redis监听key
    • redis过期key处理的方法是什么
      Redis过期key的处理方法有以下几种:1. 被动删除:当客户端尝试访问一个已经过期的key时,Redis会立即删除该key,并返...
      99+
      2023-09-11
      redis
    • SpringMVC拦截器如何实现监听session是否过期
      这篇文章给大家分享的是有关SpringMVC拦截器如何实现监听session是否过期的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。具体如下:一、拦截器配置<mvc:interceptors> ...
      99+
      2023-05-30
      java
    • Redis的过期策略是什么
      本篇内容介绍了“Redis的过期策略是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!保存过期时间Redis可以为每个key设置过期时间,...
      99+
      2023-06-22
    • JavaWeb的监听器和过滤器是什么
      这篇文章主要为大家展示了“JavaWeb的监听器和过滤器是什么”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“JavaWeb的监听器和过滤器是什么”这篇文章吧。1.监听器---->Conte...
      99+
      2023-06-29
    • redis通过key查询的方法是什么
      在Redis中,通过key查询的方法是使用`GET`命令。以下是使用`GET`命令查询key的示例:```bashGET key_n...
      99+
      2023-09-12
      redis
    • Redis中的过期策略是什么
      这篇文章将为大家详细讲解有关Redis中的过期策略是什么,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。Redis的过期策略redis主要有2种过期删除策略惰性删除惰性删除...
      99+
      2024-04-02
    • vue监听是否切屏和开启小窗的实现过程
      目录前言切屏监控方法一方法二小窗监控方法一方法二方法三结语前言 在做自己的项目的时候有用到判断设备是否有切屏,一般用的多的地方就是考试系统,切屏我们都知道,一般可以很容易的进行监控,...
      99+
      2023-05-17
      vue 监听切屏和开启小窗 vue 监听切屏
    • java中的监听器和过滤器是什么
      本篇内容介绍了“java中的监听器和过滤器是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!目录介绍:作用域对象:Servt规范扩展---...
      99+
      2023-06-20
    • redis过期键未释放的原因是什么
      本篇内容主要讲解“redis过期键未释放的原因是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“redis过期键未释放的原因是什么”吧!背景:1、redis ...
      99+
      2023-01-05
      redis
    • redis过期数据清理的方法是什么
      Redis过期数据的清理主要通过以下两种方法来实现: 定时过期数据清理:Redis会在数据设置过期时间时记录该数据的过期时间,并...
      99+
      2024-04-02
    • Redis过期键删除策略的原理是什么
      这篇“Redis过期键删除策略的原理是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“R...
      99+
      2024-04-02
    • spring整合redis消息监听通知使用的方法是什么
      本篇内容介绍了“spring整合redis消息监听通知使用的方法是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!问题引入在电商系统中,秒...
      99+
      2023-06-22
    • php秒杀实现的流程是什么
      PHP秒杀的实现流程通常如下: 创建数据库表:创建一个用于存储商品信息的数据库表,包括商品ID、名称、库存数量等字段。 设置...
      99+
      2023-10-28
      php
    • Java带有过期时间的LRU实现方法是什么
      本篇内容主要讲解“Java带有过期时间的LRU实现方法是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java带有过期时间的LRU实现方法是什么”吧!一、什么是LRULRU全称是Least ...
      99+
      2023-06-16
    • DHCP服务器的实现过程是什么
      DHCP服务器的实现过程是:1、客户端发送discover广播报文,DHCP服务器进行响应;2、DHCP服务器对discover报文进行解析;3、客户端收到offer报文后,发送request请求报文给服务器端;4、服务器端对客户端的请求报...
      99+
      2024-04-02
    • Vue3侦听器的实现原理是什么
      侦听响应式对象前面我们聊到计算属性,它可以自动计算并缓存响应式数据的值。而如果我们仅需要在响应式数据变化时,执行一些预设的操作,就可以使用watch侦听器。我们还是先来实现一个最简单的例子,然后来一点一点扩充它。const data = {...
      99+
      2023-05-16
      Vue3
    软考高级职称资格查询
    编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
    • 官方手机版

    • 微信公众号

    • 商务合作