【项目实战】Redis的key值过期提醒功能使用
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
一、使用背景
很多业务场景例如订单过期自动删除订单几天后自动好评这些常用操作可以通过定时任务数据库轮询做但是订单量大的情况可能会对数据库产生大的压力。
二、Redis的key过期推送功能原理分析
Redis的key过期推送功能打开后程序通过监听器RedisKeyExpirationListener.java监听到每个key过期的事件再做相应处理。
三、打开Redis的key过期推送的正确方式
3.1 为什么要打开
事件通过 Redis 的订阅与发布功能pub/sub来进行分发故需要开启 redis 的事件监听与发布。
3.2 如何打开
可以改 redis.conf 文件 打开 notify-keyspace-events Ex 的注释开启过期通知功能
比如可以使用 config set notify-keyspace-events Exe
命令来打开 Redis 的 key 过期推送通知。其中Ex 表示所有事件都通知e 表示只通知 key 过期事件。
执行该命令后可以使用订阅类型的命令如 PSUBSCRIBE订阅通知。
四、功能实战1 待支付订单自动取消功能
步骤1将该订单相关信息存入Redis
生成新订单时将该订单相关信息存入redis并设置过期时间
//加入redis30分钟自动取消
String keyRedis = String.valueOf(StrUtil.format("{}{}:{}",MallConstants.REDIS_ORDER_KEY_IS_PAY_0,TenantContextHolder.getTenantId(),orderInfo.getId()));
//设置过期时间
redisTemplate.opsForValue().set(keyRedis, orderInfo.getOrderNo() , orderTimeOut , TimeUnit.MINUTES);
步骤2编写监听器判断订单到期逻辑
当此key值的订单到期时redis通知到监听器RedisKeyExpirationListener.java监听器再判断处理该订单是否需要取消
@Override
public void onMessage(Message message, byte[] bytes) {
RedisSerializer<?> serializer = redisTemplate.getValueSerializer();
String channel = String.valueOf(serializer.deserialize(message.getChannel()));
String body = String.valueOf(serializer.deserialize(message.getBody()));
//key过期监听
if(StrUtil.format("__keyevent@{}__:expired", redisConfigProperties.getDatabase()).equals(channel)){
//订单自动取消
if(body.contains(MallConstants.REDIS_ORDER_KEY_IS_PAY_0)) {
body = body.replace(MallConstants.REDIS_ORDER_KEY_IS_PAY_0, "");
String[] str = body.split(":");
String wxOrderId = str[1];
TenantContextHolder.setTenantId(str[0]);
OrderInfo orderInfo = orderInfoService.getById(wxOrderId);
if(orderInfo != null && CommonConstants.NO.equals(orderInfo.getIsPay())){//只有待支付的订单能取消
orderInfoService.orderCancel(orderInfo);
}
}
......
}
}
五、功能实战2 订单自动收货
步骤1定义常量
/**
* 订单自动收货时间天
*/
long ORDER_TIME_OUT_2 = 7;
步骤2编写业务逻辑
@Slf4j
@Service
@AllArgsConstructor
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {
private final OrderItemService orderItemService;
private final OrderLogisticsService orderLogisticsService;
private final RedisTemplate<String, String> redisTemplate;
private final MallConfigProperties mallConfigProperties;
@Override
@Transactional(rollbackFor = Exception.class)
public boolean updateById(OrderInfo entity) {
if (StrUtil.isNotBlank(entity.getLogistics()) && StrUtil.isNotBlank(entity.getLogisticsNo())) {// 发货。更新快递单号
entity.setDeliveryTime(LocalDateTime.now());
OrderLogistics orderLogistics = orderLogisticsService
.getOne(Wrappers.<OrderLogistics>lambdaQuery().eq(OrderLogistics::getId, entity.getLogisticsId()));
// 第一次发货调起收到倒计时
boolean sendRedis = false;
if (StrUtil.isBlank(orderLogistics.getLogistics()) && StrUtil.isBlank(orderLogistics.getLogisticsNo())) {
sendRedis = true;
}
orderLogistics.setLogistics(entity.getLogistics());
orderLogistics.setLogisticsNo(entity.getLogisticsNo());
orderLogistics.setStatus(OrderLogisticsEnum.STATUS_1.getValue());
mallConfigProperties.getLogistics().forEach(logistics -> {
if (StrUtil.equals(logistics.getCode(), entity.getLogistics())) {
orderLogistics.setLogisticsDesc(logistics.getName());
}
});
orderLogisticsService.updateById(orderLogistics);
if (sendRedis) {
// 加入redis7天后自动确认收货
String keyRedis = String.valueOf(StrUtil.format("{}{}:{}", MallConstants.REDIS_ORDER_KEY_STATUS_2,
TenantContextHolder.getTenantId(), entity.getId()));
redisTemplate.opsForValue().set(keyRedis, entity.getOrderNo(), MallConstants.ORDER_TIME_OUT_2,
TimeUnit.DAYS);// 设置过期时间
}
}
return super.updateById(entity);
}