springboot 使用 redis 监听 key 的过期回调( 模拟设置订单号超时时间, 触发修改订单状态业务逻辑)

本文Demo地址:https://gitee.com/wslxm/spring-boot-redis

一、redis 配置文件 redis.conf 修改如下

 notify-keyspace-events ""  修改为  notify-keyspace-events Ex

具体参考
在这里插入图片描述

二、springboot 使用redis 设置key失效监听配置

1、配置监听beng

package cn.ws.config;


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;

@Configuration
public class RedisListenerConfig {

    @Bean
    RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {

        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        return container;
    }
}

2、配置redis-key 失效监听类

package cn.ws.config;


import cn.ws.constants.OrderConstants;
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;

@Component
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {

    public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
        super(listenerContainer);
    }

    /**
     * Redis-key失效监听事件,所有key 失效都会走此方法
     *
     * @param message
     * @param pattern
     */
    @Override
    public void onMessage(Message message, byte[] pattern) {
        //  获取失效的key
        String expiredKey = message.toString();
      
        //  指定key 的前缀=xxxxxx,处理对应业务逻辑,可看步骤三生成订单号:public static final String   ORDER_OVER_TIME_KEY= "order-over-time:";
        if (expiredKey.indexOf(OrderConstants.ORDER_OVER_TIME_KEY) != -1) {
            String[] orderOn = expiredKey.split(":");
            System.out.println("订单号: "+orderOn[1] +"超时"  );
        }
        System.out.println(expiredKey);
    }
}

如果只想配置监听, 请无视下方内容

三、Redis 订单号生成分布式唯一订单号工具类

此工具类每秒可生成999999 个订单号,订单号位数为20位

  • 实现思路:使用14位时间戳 + 4位自增Id (位数不够自动填充0)
  • 获得时间戳:new SimpleDateFormat(“yyyyMMddHHmmss”).format(new Date());
  • 获取redis自增id:opsForValue().increment(newKey, delta)
  • 补位填充0到4位数:String.format("%04d", increment);
  • redis集群:使用命令设置自增歩长,如步长=2,自增值= 1,3,5,7,9
  • redis宕机处理:采用14位时间戳+ 4位随机数生成订单号

1、订单相关常量类

package cn.ws.constants;

/**
 * TODO  订单常量类
 *
 * @author ws
 * @mail [email protected]
 * @date 2020/2/21 0021 16:02
 */
public class OrderConstants {


    /**
     * 订单生成redis-key前缀
     */
   public static final String ORDER_ON = "order-on";
    /**
     * 订单生成redis-key前缀
     */
   public static final String   ORDER_OVER_TIME_KEY= "order-over-time:";
    /**
     * 订单有效期(秒)
     */
   // public static final Long ORDER_OVER_TIME = 30L * 60L;
    public static final Long ORDER_OVER_TIME = 10L;
}

2、订单号生成工具 RedisOrderIDGenerate

package cn.ws.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * TODO  分布式架构获取唯一订单号(基于redis),如果redis 集群,需设置自增歩长,使用命令设置
 *
 * @author ws
 * @mail [email protected]
 * @date 2020/2/20 0020 15:02
 */
@Configuration
public class RedisOrderIDGenerate {


    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * TODO 获取唯一Id(20位)
     *
     * @param key   redis-key前缀
     * @param delta 默认初始自增值
     * @return java.lang.String
     * @author ws
     * @mail [email protected]
     * @date 2020/2/20 0020 15:59
     */
    public String getOrderId(String key, Long delta) {
        try {
            // delta为空默认值1
            if (null == delta) {
                delta = 1L;
            }
            // 生成14位的时间戳(每秒使用新的时间戳当key)
            String timeStamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
            // 获得redis-key
            String newKey = key + ":" + timeStamp;
            // 获取自增值(时间戳+自定义key)
            Long increment = redisTemplate.opsForValue().increment(newKey, delta);
            // 设置时间戳生成的key的有效期为2秒
            redisTemplate.expire(newKey, 2, TimeUnit.SECONDS);
            // 获取订单号,时间戳 + 唯一自增Id( 6位数,不过前方补0)
            return timeStamp + String.format("%06d", increment);
        } catch (Exception e) {
            // redis 宕机时采用时间戳加随机数
            String timeStamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
            Random random = new Random();
            //14位时间戳到秒 + 4位随机数
            timeStamp += random.nextInt(10) + random.nextInt(10) + random.nextInt(10) + random.nextInt(10);
            return timeStamp;
        }
    }
}

3、订单号生成参数controller


/**
 * TODO  生成订单号并测试并添加订单过期时间
 *
 * @author ws
 * @mail [email protected]
 * @date 2020/2/21 0021 16:03
 */
@RestController
public class TestController {

    @Autowired
    private RedisOrderIDGenerate redisOrderIDGenerate;
  
    // redis 工具类,本次不做说明
    @Autowired
    private RedisUtil redisUtil;


    @GetMapping("/test")
    public String test() {
        // 获取订单号
        String orderOn = redisOrderIDGenerate.getOrderId(OrderConstants.ORDER_ON, 1L);
        // 设置订单有效期30秒
        redisUtil.set(OrderConstants.ORDER_OVER_TIME_KEY + orderOn, 0, OrderConstants.ORDER_OVER_TIME);
        return orderOn;
    }
}
发布了200 篇原创文章 · 获赞 30 · 访问量 7万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章