优惠券平台(六):基于RocketMQ5.x延时消息设置优惠券结束状态

业务背景

思考这样一个问题:如果说优惠券模板有效期结束了之后,我们的模板状态依然是生效中。基于这个功能点考虑,我们需要在定时任务和定时消息中进行选择,以此来满足精准关闭优惠券模板功能。

调度器实现

  • 只需配置定时任务逻辑,无需依赖其他基础设施。
  • 实时性较差:定时任务的运行频率决定了更新的时效性(例如,每分钟扫描一次可能会延迟最多一分钟)。
  • 资源浪费:即使没有过期的优惠券,任务依然会运行,占用资源。

RocketMQ 延迟消息实现

  • 延迟消息:在优惠券模板创建时,发送一条延迟消息,延迟时间等于模板的有效期。

消息队列介绍

1. 什么是消息队列?

消息队列是一种用于异步通信的机制,允许不同的系统组件或服务之间交换信息。它的主要作用是将消息从发送者传递到接收者,同时解耦这两个组件的直接依赖。

 2. 什么是 RocketMQ?

  • 高吞吐量和低延迟:RocketMQ 设计用于处理大量的消息,并提供低延迟的消息传递服务,适合需要高性能的场景。

  • 分布式架构:RocketMQ 使用分布式架构来支持大规模的消息传递。它可以水平扩展,以处理更大的数据量和更高的并发需求。

  • 消息可靠性:RocketMQ 支持消息持久化和多副本机制,确保在系统故障时不会丢失消息。这使得消息的可靠性和一致性得到了保障。

  • 高可用性和容错:RocketMQ 提供了高可用性的解决方案,包括多主多从等架构方案,确保系统的稳定性和连续性。

消息队列都有哪些作用?

异步解耦

最常见的一个场景是用户注册后,需要发送注册邮件和短信通知,以告知用户注册成功。假设每个任务串行执行,每个任务耗时分别为 50ms,则用户需要在注册页面等待总共 150ms 才能登录。

 并行形式:对于用户来说,注册功能实际只需要注册系统存储用户的账户信息后,该用户便可以登录,后续的注册短信和邮件不是即时需要关注的步骤。其他的操作放入对应的 RocketMQ 中然后马上返回用户结果,由 RocketMQ 异步地进行这些操作。

 削峰填谷

在秒杀或团队抢购活动中,由于用户请求量较大,导致流量暴增,秒杀的应用在处理如此大量的访问流量后,下游的通知系统无法承载海量的调用量,甚至会导致系统崩溃等问题而发生漏通知的情况。为解决这些问题,可在应用和下游通知系统之间加入 RocketMQ。

 RocketMQ5.x延时消息设置优惠券结束状态

生产者核心代码

@Override
public void createCouponTemplate(CouponTemplateSaveReqDTO requestParam) {
    // ......
    // 使用 RocketMQ5.x 发送任意时间延时消息
    // 定义 Topic
    String couponTemplateDelayCloseTopic = "one-coupon_merchant-admin-service_coupon-template-delay_topic${unique-name:}";
​
    // 通过 Spring 上下文解析占位符,也就是把咱们 VM 参数里的 unique-name 替换到字符串中
    couponTemplateDelayCloseTopic = configurableEnvironment.resolvePlaceholders(couponTemplateDelayCloseTopic);
​
    // 定义消息体
    JSONObject messageBody = new JSONObject();
    messageBody.put("couponTemplateId", couponTemplateDO.getId());
    messageBody.put("shopNumber", UserContext.getShopNumber());
​
    // 设置消息的送达时间,毫秒级 Unix 时间戳
    Long deliverTimeStamp = couponTemplateDO.getValidEndTime().getTime();
​
    // 构建消息体
    String messageKeys = UUID.randomUUID().toString();
    Message<JSONObject> message = MessageBuilder
            .withPayload(messageBody)
            .setHeader(MessageConst.PROPERTY_KEYS, messageKeys)
            .build();
​
    // 执行 RocketMQ5.x 消息队列发送&异常处理逻辑
    SendResult sendResult;
    try {
        sendResult = rocketMQTemplate.syncSendDeliverTimeMills(couponTemplateDelayCloseTopic, message, deliverTimeStamp);
        log.info("[生产者] 优惠券模板延时关闭 - 发送结果:{},消息ID:{},消息Keys:{}", sendResult.getSendStatus(), sendResult.getMsgId(), messageKeys);
    } catch (Exception ex) {
        log.error("[生产者] 优惠券模板延时关闭 - 消息发送失败,消息体:{}", couponTemplateDO.getId(), ex);
    }
}
  1. 设置执行的Topic

  2. 定义消息体,优惠券ID和店铺编号

  3. 设置Unix时间戳(也就是目标时间)通过getValidEndTime()获取传入的结束字段

  4. 构建消息体并使用rocket发送延迟消息

 消费者核心代码

@Override
    public void onMessage(JSONObject message) {
        // 开头打印日志,平常可 Debug 看任务参数,线上可报平安(比如消息是否消费,重新投递时获取参数等)
        log.info("[消费者] 优惠券模板定时执行@变更模板表状态 - 执行消费逻辑,消息体:{}", message.toString());
​
        // 修改指定优惠券模板状态为已结束
        LambdaUpdateWrapper<CouponTemplateDO> updateWrapper = Wrappers.lambdaUpdate(CouponTemplateDO.class)
                .eq(CouponTemplateDO::getShopNumber, message.getLong("shopNumber"))
                .eq(CouponTemplateDO::getId, message.getLong("couponTemplateId"))
                .set(CouponTemplateDO::getStatus, CouponTemplateStatusEnum.ENDED.getStatus());
        couponTemplateService.update(updateWrapper);
    }
  1. 添加 @RocketMQMessageListener 注解,其中加上 Topic 和消费者组定义。

  2. 实现 RocketMQListener 消息监听接口,泛型的类型是我们生产者发送消息的类定义。

  3. 修改指定优惠券模板状态为已结束

最终的日志输入类似于:

2024-08-22T19:23:17.456+08:00  INFO 78983 --- [cg-mading0924_2] CouponTemplateDelayExecuteStatusConsumer : [消费者] 优惠券模板定时执行@变更模板表状态 - 执行消费逻辑,消息体:{"couponTemplateId":1826580899668439042,"shopNumber":1810714735922956666}

本章逻辑流程图

 完结散花,这里只是简单实现了rocket延迟消息,很多细节还没有掌握,继续学习!

    部分图片和内容引用知识星球《拿个offer》牛券项目-https://nageoffer.com/onecoupon/  

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值