Redis实战(12)-基于Redis的Key失效和定时任务调度实现订单支付超时自动失效(延时队列)

摘要:“商城平台用户下单”这一业务场景相信很多小伙伴并不陌生,在正常的情况下,用户在提交完订单/下完单之后,应该是前往“收银台”选择支付方式进行支付,之后只需要提供相应的密码即可完成整个支付过程;然而,“非正常的情况”也总是会有的,即用户在提交完订单之后在“规定的时间内”迟迟没有支付,这个时候我们就需要采取一些措施了,本文就是讲解如何基于Redis的Key失效,即TTL + 定时任务调度 实现这一业务场景的功能。

内容:前面篇章中,我们基本上给各位小伙伴介绍完了缓存中间件Redis各种典型且常见的数据结构及其典型的应用场景,这些数据结构包括字符串String、列表List、集合Set、有序集合SortedSet以及哈希Hash,其常见的业务场景包括“实体对象信息的存储”、“商品列表有序存储”、“List队列特性实现消息的广播通知”、“重复提交”、“随机获取试卷题目列表”、“排行榜”以及“数据字典的实时触发缓存存储”,可以说,真正地做到了技术的学以致用!

本文我们将给大家介绍一个目前在“电商平台”比较常见、典型的业务场景,即“用户在下单之后,支付超时而自动失效该订单”的功能!对于这一功能的实现,如果有小伙伴撸过我的那套“消息中间件RabbitMQ实战视频教程”的课程,那么肯定知晓如何实现!没错,就是利用“死信队列”来实现的!

而现在,我们要介绍的并非RabbitMQ的死信队列,而是想如何基于缓存中间件Redis来实现这一功能!我们知道在使用Redis的缓存功能时,无非就是SET Key Value,这是最为“常规的操作”,但千万要记住,Redis提供的功能的还远不止于此,像设置Key的失效时间,即SET Key Value TTL,其作用就是“设置某个Key的值为Value,同时设置了它在缓存Redis中能存活的时间”。

有些小伙伴听到“能存活的时间”,可能脑袋会灵机一动,“这不跟RabbitMQ死信队列中的消息能存活的时间TTL差不多是一个意思吗?”哈哈,确实是差不多那个意思,我们只需要将用户下单成功得到的“订单号”塞入缓存Redis,并设置其TTL即可(就像我们在RabbitMQ的死信队列设置“订单号”这一消息的TTL一样!)

但有这个还不够,因为 “Redis的Key的TTL一到就自动从缓存中剔除” 这个过程是Redis底层自动触发的,而在我们的程序、代码里是完全感知不到的,因为我们得借助某种机制来帮助我们主动地去检测Redis缓存中那些Key已经失效了,而且,我们希望这种检测可以是“近实时”的!

故而我们将基于Redis的Key失效/存活时间TTL + 定时任务调度(用于主动定时的触发,去检测缓存Redis那些失效了的Key,而且希望Cron可以设置得足够合理,实现“近实时”的功效)!

现在我们基本已经确实了这一功能的实现方案了,等待着我们要去做的无非就是撸码实战了,当然啦,在开始施展我们的代码才华之前,我们有必要给大家贴一下这一业务场景的整体业务流程图!整个业务流程可以说包含两大功能模块,即“用户提交订单/下订单模块”、“定时任务调度定时检测Redis的订单存活时间+自动失效订单记录模块”

一、用户提交订单的核心流程

对于“用户下订单”的功能模块,其实也不是很复杂,就是将前端用户提交过来的信息经过处理生成相应的订单号,然后将该订单记录插入数据库、插入缓存Redis,并设置对应的Key的存活时间TTL,其完整的业务流程如下图所示:

下面我们就进入代码实战环节。

(1)工欲善其事,必先利其器,我们首先仍然需要建立一张数据库表user_order,用于记录用户的下单记录,其DDL定义如下所示:

CREATE TABLE `user_order` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) DEFAULT NULL COMMENT '用户id',
  `order_no` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '订单编号',
  `pay_status` tinyint(255) DEFAULT '1' COMMENT '支付状态(1=未支付;2=已支付;3=已取消)',
  `is_active` tinyint(255) DEFAULT '1' COMMENT '是否有效(1=是;0=否)',
  `order_time` datetime DEFAULT NULL COMMENT '下单时间',
  `update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COMMENT='用户下单记录表';

然后,基于Mybatis的逆向工程或者代码生成器生成该数据库表的实体类Entity、Mapper操作接口及其对应的用于写动态SQL的Mapper.xml,在这里我们只贴出两个Mapper操作接口吧,如下所示:  

//TODO:查询有效+未支付的订单列表    
List<UserOrder> selectUnPayOrders();

//TODO:失效订单记录
int unActiveOrder(@Param("id") Integer id);

其对应的动态SQL是在对应的Mapper.xml中实现的,如下所示:  

<!--查询未支付的用户订单列表-->
  <select id="selectUnPayOrders" resultType="com.boot.debug.redis.model.entity.UserOrder">
    SELECT
        <include refid="Base_Column_List"/>
    FROM
        user_order
    WHERE
        is_active = 1
    AND pay_status = 1
    ORDER BY
        order_time DESC
  </select>

  <!--失效订单-->
  <update id="unActiveOrder">
    update user_order
    set is_active = 0
    where id = #{id} and is_active = 1 and pay_status = 1
  </update>

(2)之后,我们开发一个UserOrderController,用于接收前端过来的请求参数,并在UserOrderService实现“用户下单”的整个业务逻辑,其完整的源代码如下所示:  

/**用户下单controller
 * @Author:debug (SteadyJack)
 * @Link: weixin-> debug0868 qq-> 1948831260**/
@RestController
@RequestMapping("user/order")
public class UserOrderController {
    private static final Logger log= LoggerFactory.getLogger(UserOrderController.class);

    @Autowired
private UserOrderService userOrderService;

    //下单
    @RequestMapping(value = "put",method = RequestMethod.POST,consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public BaseResponse put(@RequestBody @Validated UserOrder userOrder, BindingResult result){
        String checkRes=ValidatorUtil.checkResult(result);
        if (StrUtil.isNotBlank(checkRes)){
            return new BaseResponse(StatusCode.InvalidParams.getCode(),checkRes);
        }
        BaseResponse response=new BaseResponse(StatusCode.Success);
        try {
            log.info("--用户下单:{}",userOrder);

            String res=userOrderService.putOrder(userOrder);
            response.setData(res);
        }catch (Exception e){
            log.error("--用户下单-发生异常:",e.fillInStackTrace());
            response=new BaseResponse(StatusCode.Fail.getCode(),e.getMessage());
        }
        return response;
    }
}

更多请见:http://www.mark-to-win.com/tutorial/51064.html 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值