Mall脚手架总结(四) —— SpringBoot整合RabbitMQ实现超时订单处理

news2024/11/15 16:24:38

前言

        在电商项目中,订单因为某种特殊情况被取消或者超时未支付都是比较常规的用户行为,而实现该功能我们就要借助消息中间件来为我们维护这么一个消息队列。在mall脚手架中选择了RabbitMQ消息中间件,接下来荔枝就会根据功能需求来梳理一下超时订单处理功能以及相应的背景知识。希望对正在学习的小伙伴有帮助~~~


文章目录

前言

一、整合RabbitMQ实现延时消息

1.1 RabbitMQ管理界面的VirtualHost

1.2 回顾:枚举类的优点

1.3 划重点:Spring AMQP框架 

1.3.1 AMQPTemplate

1.3.2 Message

1.3.3 @RabbitListener注解

1.4 订单超时未支付取消订单的流程

总结


一、整合RabbitMQ实现延时消息

1.1 RabbitMQ管理界面的VirtualHost

        Virtual Host虚拟主机,相当于是一个个的相对独立的RabbitMQ服务器。每个虚拟主机都有自己独立的用户、权限、交换机(exchange)、队列(queue)和绑定关系。在RabbitMQ中,每个连接到服务器的客户端都必须选择一个虚拟主机进行操作。如果客户端没有指定虚拟主机,默认会使用/作为虚拟主机,也就是RabbitMQ默认的虚拟主机。为保证隔离性,这里声明了一个/mall的虚拟主机,与我们默认的用户虚拟主机/隔离开来。

1.2 回顾:枚举类的优点

枚举类Enum是一种特殊的数据库类型,用于表示固定数量的命名常量,枚举类定义了一个新的数据类型,该类型可以包含一组预定义的值。

优点:

  • 安全性:保证方法参数的类型的安全性,避免非法值的传入;
  • 避免魔法数字: 枚举类可以帮助你避免在代码中使用魔法数字(magic numbers)。魔法数字是指在代码中直接使用的、没有明确含义的数字。使用枚举类可以为这些常量提供有意义的名字。
  • 支持迭代器(Iteration): 枚举类提供了一种便捷的方式来遍历枚举值。

所以在脚手架中我们定义交换机名称、队列名称和routingKey就借助了枚举类。

/**
 * @auther lzddl
 * @description 消息队列枚举配置
 */
@Getter
public enum QueueEnum {
    /**
     * 消息通知队列
     */
    QUEUE_ORDER_CANCEL("mall.order.direct", "mall.order.cancel", "mall.order.cancel"),
    /**
     * 消息通知ttl队列
     */
    QUEUE_TTL_ORDER_CANCEL("mall.order.direct.ttl", "mall.order.cancel.ttl", "mall.order.cancel.ttl");

    /**
     * 交换机名称
     */
    private String exchange;
    /**
     * 队列名称
     */
    private String name;
    /**
     * 路由键
     */
    private String routeKey;

    QueueEnum(String exchange, String name, String routeKey) {
        this.exchange = exchange;
        this.name = name;
        this.routeKey = routeKey;
    }
}

1.3 划重点:Spring AMQP框架 

        Spring AMQP是一个基于AMQP协议的强大的消息中间件框架,它提供了一个简单的API来发送和接收异步、可靠的消息。AMQP是Spring框架的一部分,可以与Spring Boot和其他Spring项目一起使用。Spring AMQP支持多种消息协议,包括RabbitMQ、Apache ActiveMQ和Qpid等。它提供了一个高级的消息模型,包括消息确认、事务和消息监听器等功能,使得开发者可以轻松地编写可靠的消息应用程序。同时Spring AMQP还提供了一些高级特性,如消息转换器、消息路由、消息过滤和消息拦截等。总的来说,Spring AMQP 是对 Spring 基于 AMQP 的消息收发解决方案,在SpringBoot项目中操作消息中间件RabbitMQ的相关操作的时候,我们需要借助Spring提供的AMQP框架!

AMQP:高级消息队列协议,是面向消息的中间件的平台中立的线级协议。

Spring AMQP的核心组件:

  • ConnectionFactory:连接工厂接口,用于创建连接。

  • AmqpAdmin:封装了RabbitMQ的基础管理操作,比如对交换机、队列、绑定的声明和删除等。
  • Message:Spring AMQP 对消息的封装。两个重要的属性:body:消息内容;messageProperties:消息属性。
  • 消息模板AmqpTemplate: 用来简化消息的收发,支持消息的确认与返回。跟 JDBCTemplate一 样,它封装了创建连接 、创建消息信道、收发消息、消息格式转换、关闭信道、关闭连接等等操作。

  • 消息监听Messager Listener: Spring AMQP 异步消息投递的监听器接口,它只有一个方法onMessage,用于处理消息队列推送来的消息。

  • 转换器MessageConvertor:用来处理消息对象的序列化和反序列化的操作工具,它可以将消息对象转换为消息队列可以处理的格式,并将接收到的消息转换为Java对象。

1.3.1 AMQPTemplate

AMQPTemplate是Spring AMQP框架提供的一个接口,它定义了一系列用于发送和接收消息的方法。我们来看看源码并归类一下这些方法:

public interface AmqpTemplate {
    void send(Message var1) throws AmqpException;
    void send(String var1, Message var2) throws AmqpException;
    void send(String var1, String var2, Message var3) throws AmqpException;
 

    void convertAndSend(Object var1) throws AmqpException;
    void convertAndSend(String var1, Object var2) throws AmqpException;
    void convertAndSend(String var1, String var2, Object var3) throws AmqpException;
    void convertAndSend(Object var1, MessagePostProcessor var2) throws AmqpException;
    void convertAndSend(String var1, Object var2, MessagePostProcessor var3) throws AmqpException;
    void convertAndSend(String var1, String var2, Object var3, MessagePostProcessor var4) throws AmqpException;


    @Nullable
    Message receive() throws AmqpException;
    @Nullable
    Message receive(String var1) throws AmqpException;
    @Nullable
    Message receive(long var1) throws AmqpException;
    @Nullable
    Message receive(String var1, long var2) throws AmqpException;


    @Nullable
    Object receiveAndConvert() throws AmqpException;
    @Nullable
    Object receiveAndConvert(String var1) throws AmqpException;
    @Nullable
    Object receiveAndConvert(long var1) throws AmqpException;
    @Nullable
    Object receiveAndConvert(String var1, long var2) throws AmqpException;
    @Nullable
    <T> T receiveAndConvert(ParameterizedTypeReference<T> var1) throws AmqpException;
    @Nullable
    <T> T receiveAndConvert(String var1, ParameterizedTypeReference<T> var2) throws AmqpException;
    @Nullable
    <T> T receiveAndConvert(long var1, ParameterizedTypeReference<T> var3) throws AmqpException;
    @Nullable
    <T> T receiveAndConvert(String var1, long var2, ParameterizedTypeReference<T> var4) throws AmqpException;


    <R, S> boolean receiveAndReply(ReceiveAndReplyCallback<R, S> var1) throws AmqpException;
    <R, S> boolean receiveAndReply(String var1, ReceiveAndReplyCallback<R, S> var2) throws AmqpException;
    <R, S> boolean receiveAndReply(ReceiveAndReplyCallback<R, S> var1, String var2, String var3) throws AmqpException;
    <R, S> boolean receiveAndReply(String var1, ReceiveAndReplyCallback<R, S> var2, String var3, String var4) throws AmqpException;
    <R, S> boolean receiveAndReply(ReceiveAndReplyCallback<R, S> var1, ReplyToAddressCallback<S> var2) throws AmqpException;
    <R, S> boolean receiveAndReply(String var1, ReceiveAndReplyCallback<R, S> var2, ReplyToAddressCallback<S> var3) throws AmqpException;


    @Nullable
    Message sendAndReceive(Message var1) throws AmqpException;
    @Nullable
    Message sendAndReceive(String var1, Message var2) throws AmqpException;
    @Nullable
    Message sendAndReceive(String var1, String var2, Message var3) throws AmqpException;
   

    @Nullable
    Object convertSendAndReceive(Object var1) throws AmqpException;
    @Nullable
    Object convertSendAndReceive(String var1, Object var2) throws AmqpException;
    @Nullable
    Object convertSendAndReceive(String var1, String var2, Object var3) throws AmqpException;
    @Nullable
    Object convertSendAndReceive(Object var1, MessagePostProcessor var2) throws AmqpException;
    @Nullable
    Object convertSendAndReceive(String var1, Object var2, MessagePostProcessor var3) throws AmqpException;
    @Nullable
    Object convertSendAndReceive(String var1, String var2, Object var3, MessagePostProcessor var4) throws AmqpException;


    @Nullable
    <T> T convertSendAndReceiveAsType(Object var1, ParameterizedTypeReference<T> var2) throws AmqpException;
    @Nullable
    <T> T convertSendAndReceiveAsType(String var1, Object var2, ParameterizedTypeReference<T> var3) throws AmqpException;
    @Nullable
    <T> T convertSendAndReceiveAsType(String var1, String var2, Object var3, ParameterizedTypeReference<T> var4) throws AmqpException;
    @Nullable
    <T> T convertSendAndReceiveAsType(Object var1, MessagePostProcessor var2, ParameterizedTypeReference<T> var3) throws AmqpException;
    @Nullable
    <T> T convertSendAndReceiveAsType(String var1, Object var2, MessagePostProcessor var3, ParameterizedTypeReference<T> var4) throws AmqpException;
    @Nullable
    <T> T convertSendAndReceiveAsType(String var1, String var2, Object var3, MessagePostProcessor var4, ParameterizedTypeReference<T> var5) throws AmqpException;
}

从源码中我们可以了解到,其实该接口提供了八种类型的消息操作方法,因为不同方法都采用了重载所以看起来有点吓人,接下来我们大致根据方法名弄清楚这些方法的功能即可。

send()和receive()就不说了,根据给出的参数来发送消息和接收消息

  • convertAndSend:转化并发送消息;
  • receiveAndConvert:接收并转化消息
  • receiveAndReply:接收并回复消息
  • sendAndReceive:发送并接收消息
  • convertSendAndReceive:转化发送和接收
  • convertSendAndReceiveAsType:根据类型来发送接收

其实AMQPTemplate是一个比较抽象的接口,其中操作RabbitMQ更为具体的接口的实现类是RabbitMQTemplate。而关于RabbitMQTemplate的源码这里就不展示了(一千多行呢~)

 //给延迟队列发送消息
amqpTemplate.convertAndSend(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getExchange(), QueueEnum.QUEUE_TTL_ORDER_CANCEL.getRouteKey(), orderId, new MessagePostProcessor() {
    @Override
    public Message postProcessMessage(Message message) throws AmqpException {
        //给消息设置延迟毫秒值
        message.getMessageProperties().setExpiration(String.valueOf(delayTimes));
        return message;
    }
});

可以看到我们调用了RabbitMQTemplate实现的AMQPTemplate接口方法来发送消息。

1.3.2 Message

Message消息,是服务器与应用程序之间传递的数据,由Properties和Body组成, Properties可以对消息进行修饰,如消息的优先级、传输格式(如JSON)、延迟等高级特性,Body则就是消息体内容。

Message类的使用场景:

  • 发送消息时:通常使用最多的是编写消息体内容、设置过期时间、设置持久化;发送消息的类型分为两种情况:基本类型和对象; 
  • 接收消息时:实际是把二进制byte转为需要的类型,再进行数据传递和业务处理,接收消息的类型分为两种情况:基本类型和对象

在脚手架中我们看到使用了Message来反取消息对象的属性并设置相应的消息过期时间:

message.getMessageProperties().setExpiration(String.valueOf(delayTimes));

其余的核心组件具体内容可以参考这位大佬的博文: https://blog.csdn.net/weixin_45596022/article/details/113359009

1.3.3 @RabbitListener注解

        @RabbitListener注解是Spring AMQP框架提供的注解,用于简化RabbitMQ消息消费者的创建。当你在方法上使用@RabbitListener注解时,Spring会自动创建一个RabbitMQ消息监听器,用于监听指定队列上的消息,并在消息到达时调用被注解的方法来处理消息。

因此在脚手架中我们通过该注解来实现死信队列消费者的创建:

@Component
@RabbitListener(queues = "mall.order.cancel")
public class CancelOrderReceiver {
    private static Logger LOGGER =LoggerFactory.getLogger(CancelOrderReceiver.class);
    @Autowired
    private OmsPortalOrderService portalOrderService;
    @RabbitHandler
    public void handle(Long orderId){
        LOGGER.info("receive delay message orderId:{}",orderId);
        portalOrderService.cancelOrder(orderId);
    }
}

1.4 订单超时未支付取消订单的流程

        在mall脚手架中模拟了一个订单到期未支付取消下单的操作,首先用户在购买之后会创建订单(随之可能会有锁定库存、判断会员身份或者积分、优惠券等操作),Controller层中的generateOrder()就会创建一个带有过期时间的延时消息,这部分是通过一个之前已经定义好了的CancelOrderSender也就是延时消息的发送者类发送延时消息到死信队列中。这里我们来看一下配置死信队列的配置类方法:

    /**
     * 订单延迟队列(死信队列)
     */
    @Bean
    public Queue orderTtlQueue() {
        return QueueBuilder
                .durable(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getName())
                .withArgument("x-dead-letter-exchange", QueueEnum.QUEUE_ORDER_CANCEL.getExchange())//到期后转发的交换机
                .withArgument("x-dead-letter-routing-key", QueueEnum.QUEUE_ORDER_CANCEL.getRouteKey())//到期后转发的路由键
                .build();
    }

        由于我们定义了一个死信队列和一个延时队列,订单下单之后我们就会把消息发送到死信队列,由于用户迟迟不支付,所以死信队列中的消息一直没有被消费,等到TTL时间一到就会转发到普通队列中被消费者消费。前面在讲@RabbitListener注解的时候已经给出了消费者的demo,消费者监听的就是普通队列。当消费者消费后会触发取消订单的API进行订单取消的操作(释放库存、扣除优惠券或积分等操作)


总结

        RabbitMQ整合进脚手架的功能还是比较简单的哈哈,当然了脚手架只是为了让我们了解一些基础知识以便于快速上手项目,重点的还是要学习有关AMQP的操作以及相应的在RabbitMQ对应的接口实现类。梳理完后接下来的文章荔枝就可以开始整合MinIO了,一起加油吧~

今朝已然成为过去,明日依然向往未来!我是荔枝,在技术成长之路上与您相伴~~~

如果博文对您有帮助的话,可以给荔枝一键三连嘿,您的支持和鼓励是荔枝最大的动力!

如果博文内容有误,也欢迎各位大佬在下方评论区批评指正!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1080073.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SRE实战:如何低成本推进风险治理?稳定性与架构优化的3个策略

一分钟精华速览 SRE 团队每天面临着不可控的各类风险和重复发生的琐事&#xff0c;故障时疲于奔命忙于救火。作为技术管理者&#xff0c;你一直担心这些琐事会像滚雪球一样&#xff0c;越来越多地、无止尽地消耗你的团队&#xff0c;进而思考如何系统性地枚举、掌控这些风险&a…

ctf中ping命令执行绕过

相关wp参考&#xff1a;CTF中的命令执行绕过方式 - 知乎 CTFping命令绕过及符号用法_ctf ping-CSDN博客 在用linux命令时候,我们可以 一行执行多条命令 或者 有条件的执行下一条命令 linux命令中一些符号的用法 1. “;”分号用法 方式&#xff1a;command1 ; command…

【ccf-csp题解】第7次csp认证-第三题-路径解析超详细题解-字符串模拟

本题思路来源于acwing ccfcsp认证课 题目描述 思路分析 首先&#xff0c;为了处理路径中的反斜杠符号&#xff0c;我们可以实现一个get函数&#xff0c;把一个路径中每一对反斜杠之间的内容存到vector<string>中&#xff0c;如果有连续的多个反斜杠则只看成一个 举个例…

“.NET视频总结:认识框架的结构和组件,掌握开发工具的奥妙“一

目录 第一单元&#xff1a;二十一世纪程序执行 背景: 总结&#xff1a; 第二单元:对象导向与类别设计 背景: 总结&#xff1a; 第三单元&#xff1a;使用类别与基底类别库 总结: 第四单元:Windows开发程序 背景: 总结: 第五单元:防护式程序设计 背景: 总结: 第六…

数据库中的DECODE函数,SIGN函数

oracle中的if(),oracle中if/else的三种实现方式详解_电竞GO的博客-CSDN博客 DECODE(CONCAT(b.AZZ231,%),%,,CONCAT(b.AZZ231,%)) czwcl,

Acrel-6000电气火灾监控系统应用

安科瑞 崔丽洁 摘要 建筑电气火灾在建筑物火灾中占较大的比例&#xff0c;起火原因也很多&#xff0c;包括短路、过热、漏电、雷击和电气等故障&#xff0c;火灾危害也较大。因此&#xff0c;各种原因引起的火灾都应得到有效控制。目前&#xff0c;短路、过热、雷击等保护措施…

【Java网络编程】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 前言 Java是一种广泛应用于网络编程的编程语言。通过Java的网络编程能力&#xff0c;我们可以构建强大的网络应用程序。本文将介绍Java网络编程的基础知识、常用API和一些实…

nginx目录穿越

测试nginx版本为nginx/1.23.3 location /file {alias /home/;} 在/usr跟目录下新建a.txt测试文件 通过访问 http://{ip}:{端口}/file../test.txt 实现目录穿越 防护:location与alias的值都加上/或不加/

MongoDB实践

MongoDB学习 MongoDB简介 MongoDB 是一种流行的文档型 NoSQL 数据库&#xff0c;它具有以下特点和应用场景&#xff1a; 文档型数据库&#xff1a;MongoDB 使用 BSON&#xff08;Binary JSON&#xff09;格式的文档来存储数据。每个文档可以具有不同的字段&#xff0c;这使得…

山西电力市场日前价格预测【2023-10-12】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2023-10-12&#xff09;山西电力市场全天平均日前电价为506.23元/MWh。其中&#xff0c;最高日前电价为841.91元/MWh&#xff0c;预计出现在18: 30。最低日前电价为351.76元/MWh&#xff0c;预计…

关键词搜索速卖通商品列表接口,速卖通商品列表数据接口

在网页抓取方面&#xff0c;可以使用 Python、Java 等编程语言编写程序&#xff0c;通过模拟 HTTP 请求&#xff0c;获取速卖通网站上的商品页面。在数据提取方面&#xff0c;可以使用正则表达式、XPath 等方式从 HTML 代码中提取出有用的信息。值得注意的是&#xff0c;速卖通…

PowerDesinger导入保险业excel数据模型(代码亲测可用)

一、Excel表 按如下结构填充建表所需的数据(附 Excel 表模板): 二、PowerDesigner 新建模型 点击:File——>New Model——>Model types——>Physical Data Mode 三、PowerDesigner 导入 Excel 脚本 按住Ctrl+Shift+X进入脚本运行界面,导入以下脚本并运行: 注…

牛客 明明的随机数

HJ3 明明的随机数 原题思路代码运行截图收获 原题 HJ3 明明的随机数 思路 如果是C的话直接用set结构体就可以自动排序GO&#xff1a;用一个501的数组存储是否出现&#xff0c;最后从头开始输出出现过的数字 代码 #include <iostream> #include <set> using na…

css怎么实现文字描边

有时&#xff0c;我们会遇到UI稿有文字描边的效果&#xff0c;比如下图的效果。 一、给需要描边的文字加一个id选择器 例如&#xff1a; 二、css写法&#xff1a; number,{//这个是实现文字描边的关键&#xff0c;也就是‘空心文字’&#xff0c;这个是定义文字字符的描边的宽…

向量的运算

向量加法 实质是坐标加法。如下图&#xff1a; 向量减法 实质是坐标减法。如下图&#xff1a; 向量乘法 分为点乘和叉乘&#xff0c;点乘是计算平行四边形的面积&#xff0c;叉乘是计算投影&#xff0c;点乘为标量&#xff0c;叉乘为向量。

Git 使用技巧:5个提高效率的命令,不再只会pull和push

前言 使用 Git 作为代码版本管理&#xff0c;早已是现在开发工程师必备的技能。可大多数工程师还是只会最基本的保存、拉取、推送&#xff0c;遇到一些commit管理的问题就束手无策&#xff0c;或者用一些不优雅的方式解决。 本文分享我在开发工作中实践过的实用命令。这些都能…

【1】MongoDB的安装以及连接

今天是2023年10月11日&#xff0c;MongoDB最新版本是7.0.2 最近闲着没事学习一下MongoDB这个NoSQL数据库&#xff0c;有时间就顺手记录一下我学习的笔记吧~ 学习笔记来自黑马程序员《MongoDB基础入门到高级进阶&#xff0c;一套搞定mongodb》 配套资料&#xff1a;点此资料链接…

Android 面经总结分享(相当走心)

背景描述 这是来自一位粉丝朋友的面经分享&#xff0c;他在 「Android 开发行业」 摸爬滚打5年多的开发&#xff0c;呆过的互联网公司有三家&#xff0c;均从事的Android 开发的工作。最后离职的一家公司也是做的最久的一家&#xff0c;工作了将近3年多时光。 废话不多说了&a…

机器学习 - 似然函数:概念、应用与代码实例

目录 一、概要二、什么是似然函数数学定义似然与概率的区别重要性举例 三、似然函数与概率密度函数似然函数&#xff08;Likelihood Function&#xff09;定义例子 概率密度函数&#xff08;Probability Density Function, PDF&#xff09;定义 区别与联系 四、最大似然估计&am…

Haskell网络编程:从数据采集到图片分析

概述 爬虫技术在当今信息时代中发挥着关键作用&#xff0c;用于从互联网上获取数据并进行分析。本文将介绍如何使用Haskell进行网络编程&#xff0c;从数据采集到图片分析&#xff0c;为你提供一个清晰的指南。我们将探讨如何使用亿牛云爬虫代理来确保高效、可靠的数据获取&am…