基于Redis实现订单倒计时自动关闭——Java

1.场景:

    电商系统或者购票系统都必须具备订单功能,生成订单后一段时间不支付订单会自动关闭。最简单的想法是设置定时任务轮询,    但是每个订单的创建时间不一样,定时任务的规则无法设定,如果将定时任务执行的间隔设置的过短,太影响效率。还有一种想法,在用户进入订单界面的时候,判断时间执行相关操作。方式可能有很多,在这里介绍一种监听Redis键值对过期时间来实现订单自动关闭。

2.思路:

    在生成订单时,向Redis中增加一个KV键值对,K为订单号,或者订单id,保证通过K能定位到数据库中的某个订单即可,V可为任意值(后边会解释为什么V可为任意值)。

    假设,生成订单时向Redis中存放K为订单号,V也为订单号的键值对,并设置过期时间为30分装,如果该键值对在30分钟过期后能够发送给程序一个通知,或者执行一个方法,那么即可解决订单关闭问题。

    实现:通过监听Redis提供的过期队列来实现,监听过期队列后,如果Redis中某一个KV过期了,那么将向监听者发送消息,监听者可以获取到该键值对的K,注意,是获取不到V的,因为已经过期了,这就是上面所提到的,为什么要保证能通过K来定位到订单,而V为任意值即可。拿到K后,通过K定位订单,并判断其状态,如果是未支付,更新为关闭,或者取消状态即可。

3.实现:

    1.项目为SSM框架基础架构。

    2.使用SpringDataRedis来操作Redis(SDR)。

    3.创建一个监听者类,实现SDR提供的监听者接口。

    

public class TopicMessageListener implements MessageListener {
    @Override
    public void onMessage(Message message, byte[] bytes) { 
        byte[] body = message.getBody();// 请使用valueSerializer
        byte[] channel = message.getChannel();
        //设置监听频道
        String topic = new String(channel);
        //key
        String itemValue = new String(body);
        System.out.println("频道topic:"+topic);
        System.out.println("过期的键值对的K:"+itemValue);
   }
}

    4.在spring配置文件中,增加监听者的配置

    

        <!-- redis 连接工厂 -->
	<bean id="redisConnectionFactory"
		class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
		p:port="6379" p:host-name="127.0.0.1" p:password=""/>

	<!-- 用来格式化 KV  的 类 -->
	<bean id="stringSer" class="org.springframework.data.redis.serializer.StringRedisSerializer" />
	<bean id="jdkSer" class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />

	<!-- Spring操作redis的模板类 -->
	<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
		p:connectionFactory-ref="redisConnectionFactory">
		<property name="keySerializer" ref="stringSer" />
		<property name="valueSerializer" ref="jdkSer" />
	</bean>
        <!-- 将监听者类 放入Spring容器 -->
	<bean id="messageListener" class="org.springframework.data.redis.listener.adapter.MessageListenerAdapter">
		<constructor-arg>
			<bean class="com.meilong.common.utils.TopicMessageListener"/>
		</constructor-arg>
	</bean>

        <!-- 配置监听者容器 -->
	<bean id="redisContainer" class="org.springframework.data.redis.listener.RedisMessageListenerContainer">
		<property name="connectionFactory" ref="redisConnectionFactory"/>
		<property name="messageListeners">
			<map>
				<entry key-ref="messageListener">
					<bean class="org.springframework.data.redis.listener.ChannelTopic">
						<constructor-arg value="__keyevent@0__:expired"/>
					</bean>
				</entry>
			</map>
		</property>
	</bean>

    5.都是一些使用SDR的常用配置,简单解释一下如下配置

    

    6.这个配置,配置的是监听的频道,格式为固定,Redis有16个库,配置中0代表监听第0个库,如果要监听所有库,可将0改为*,星号,如果监听其他库,将0改为库的编号即可0-15。keyevent代表监听的事件类型,expired表示,监听的时间为过期事件,也就是当第0个库中如果有KV过期,那么,监听者类将接受到消息。注意,配置中有两处出现了下划线,每一处下划线均有两个下划线组成,一定要注意  这是一个下划线  _   这是两个下划线  __ 。

    7.修改Redis配置文件,开启过期通知功能

        

    标记1处原来是被注释掉的,打开注释。

    标记2处原来是没有注解的,将其注释掉。这两处为开始Redis的过期通知功能,保证跟图中的注释一致即可。

    还有要保证程序能够连接上Redis,该配置中0.0.0.0表示任意ip都可连接Redis。


    8.那么到这里其实重要的配置已经完成,可以启动项目,进行测试。打开Redis客户端,存放KV并设置过期时间,如set testKey testValue Ex 5。存放一个键值对,过期时间为5秒,那么5秒后监听者类就会收到消息。

    

    9.已经可以成功的监听到过期频道,并且能够接受到过期消息,实际业务根据需求拓展即可。

    

        

### Spring Boot 实现未付款订单倒计时功能 在电子商务平台中,实现未付款订单倒计时功能可以有效提升用户体验并优化订单管理流程。以下是几种常见的解决方案: #### 使用 Redis 的键过期事件机制 这种方法利用了 Redis 提供的强大缓存能力和灵活的时间控制特性。每当新订单被创建时,在数据库保存的同时也会向 Redis 中写入一条记录,并设定相应的超时期限。 ```java @Service public class OrderService { @Autowired private StringRedisTemplate redisTemplate; /** * 创建订单的方法 */ public void createOrder(Order order) { // 将订单信息保存到数据库 saveOrderToDB(order); // 同步地往 Redis 设置带有 TTL (生存时间) 的 key-value 对, // 这里的 TTL 即是我们想要设置的等待支付时限 redisTemplate.opsForValue().set( "order:" + order.getId(), order.getId(), 30, TimeUnit.MINUTES); } /** * 当某个特定前缀下的 Key 到达其有效期结束时刻后触发此函数执行。 * 需要在应用程序启动期间注册监听器来捕获此类事件。 */ @EventListener public void handleKeyExpirationEvent(KeyExpiredEvent event) { String expiredKeyId = event.getKey(); if (!expiredKeyId.startsWith("order:")) return; Long orderId = Long.parseLong(expiredKeyId.split(":")[1]); cancelOrder(orderId); } } ``` 上述代码展示了如何使用 `StringRedisTemplate` 来操作 Redis 数据库中的字符串类型的键值对[^4]。同时定义了一个名为 `handleKeyExpirationEvent()` 的方法用于接收来自 Redis 发布订阅系统的消息通知——即当某条代表订单状态的数据项因达到预设存活期限而失效时所发出的通知;随后依据接收到的信息判断是否为预期的目标对象(本例中是指定格式开头的订单 ID),若是则进一步调用业务逻辑层内的取消订单接口完成后续动作。 #### 定时任务调度方式 另一种较为简单的做法就是借助于 Java 自带的任务计划工具或是第三方组件如 Quartz 等来进行周期性的扫描工作。这种方式虽然相对容易理解但也存在一定的局限性,比如无法做到精确到秒级别的延迟处理以及可能带来较高的性能开销等问题。 ```java @Configuration @EnableScheduling public class SchedulerConfig {} @Component @RequiredArgsConstructor(onConstructor_=@Autowired) @Slf4j public class UnpaidOrdersCleanerJob implements SchedulingConfigurer { private final OrderRepository repository; @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { taskRegistrar.addCronTask(() -> log.info("Checking unpaid orders..."), "0 0/30 * ? * *"); // 每半小时一次 taskRegistrar.addFixedRateTask(this::checkUnpaidOrders, 1800000L); // 或者每三十分钟检查一遍 } private void checkUnpaidOrders() { LocalDateTime nowMinusThirtyMinutes = LocalDateTime.now().minusMinutes(30); List<Order> overdueOrders = repository.findByStatusAndCreatedAtBeforeOrderByCreatedAtAsc( PaymentStatus.UNPAID.name(), nowMinusThirtyMinutes.toLocalDateTime()); for (var o : overdueOrders) { try { o.setStatus(PaymentStatus.CANCELLED.name()); repository.save(o); sendNotificationEmail(o.getUserEmail()); // 取消成功发送邮件给客户 } catch (Exception e) { logger.error("Failed to process order cancellation", e); } } } ... } ``` 这段示例说明了怎样通过配置类开启定时任务支持[@EnableScheduling][^1], 并且自定义实现了具体的清理作业逻辑。这里采用了两种不同的间隔策略分别对应 cron 表达式和固定频率模式(`addFixedRateTask`)。实际开发过程中可以根据需求选择合适的方式。
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值