前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【十七】RabbitMQ基础篇(延迟队列和死信队列实战)

【十七】RabbitMQ基础篇(延迟队列和死信队列实战)

作者头像
小z666
发布2024-06-21 17:49:06
890
发布2024-06-21 17:49:06
举报
文章被收录于专栏:javajava

本章将通过学习rabbitMQ基础中的延时队列和死信队列,然后写一个demo实现一个小例子,在商城购物时,先下单创建订单记录,然后可以选择进行立即支付或者不支付,若30秒后不支付,则删除订单。下面针对这个例子进行学习。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCPeuKZgA==,size_20,color_FFFFFF,t_70,g_se,x_16
watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCPeuKZgA==,size_20,color_FFFFFF,t_70,g_se,x_16

首先展示一下最终效果,并进行效果讲解,如下所示:

  • 正常购买流程
73770f44d71741b5894023aa8d56788f.gif
73770f44d71741b5894023aa8d56788f.gif

描述:点击购买,创建订单记录,在倒计时内支付成功的话,正常完成购买流程。

  • 未支付流程
8f69d31704b84a4ebc807114f62228d4.gif
8f69d31704b84a4ebc807114f62228d4.gif

描述:点击购买,创建订单记录,在倒计时内未支付成功的话,删除该笔订单。


下面为了满足上述效果,进行实现。

一、分析例子

为了满足上面的效果,可以通过很多方法实现,最简单的就是定时任务,创建一个定时任务,定时去请求数据,查看状态为未支付的订单,并删除。当然还可以通过redis或者其他办法,本章当然是通过RabbitMQ的方式实现,将通过延时队列和死信队列实现,逻辑关系如下:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCPeuKZgA==,size_20,color_FFFFFF,t_70,g_se,x_16
watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCPeuKZgA==,size_20,color_FFFFFF,t_70,g_se,x_16

所以需要创建如下几个东西:

  • 延时交换机
  • 延时队列
  • 延时队列绑定关系
  • 死信交换机
  • 死信队列
  • 死信队列绑定关系
  • 消费者端死信队列监听器
  • 生产者端创建订单接口(内含发送消息到延时队列)
  • 生产者端支付订单接口

分析完毕,下面开整。

建表语句

代码语言:javascript
复制
CREATE TABLE `dingdan` (
  `id` varchar(255) COLLATE utf8_bin NOT NULL,
  `name` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `type` int DEFAULT NULL COMMENT '0:未支付,1:已支付,默认0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin;

二、编写前端代码

上面测试效果是随便整的,过于简便,代码我直接贴出来:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCPeuKZgA==,size_14,color_FFFFFF,t_70,g_se,x_16
watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCPeuKZgA==,size_14,color_FFFFFF,t_70,g_se,x_16
代码语言:javascript
复制
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>订单</title>
	</head>
	<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
	<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
	<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
	
	<body>
		<div style="width: 100%;height: 300px;display: flex;justify-content: center;margin-top: 200px;">
			<div class="card" style="width: 15rem;">
			  <img class="card-img-top" style="" src="./img/apple.jpg" alt="Card image cap">
			  <div class="card-body">
			    <h5 class="card-title">苹果售价:100¥</h5>
			    <a onclick="fun1()" style="margin-top: 20px;margin-left: 70px;" class="btn btn-primary">购买</a>
			  </div>
			</div>
		</div>
	</body>
	
	<script type="text/javascript">
		function fun1(){
			console.log("点击");
				
			$.ajax({
				url:"http://localhost:7778/dingdanController/insertDingdan",
				data:null,
				type:"post",
				dataType: "json",
				success: function(data) { 
					console.log("返回值:"+data.id);
					window.location.href="支付.html?id="+data.id
				}
			});
		}
	</script>
</html>
代码语言:javascript
复制
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>支付</title>
	</head>
	<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
	<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
	<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
	
	<body>
		<div style="width: 100px;height: 30px;text-align: center;line-height: 30px;margin-top: 100px;margin-left: 50%;background-color: aliceblue;">
			支付倒计时:
		</div>
		<div id="time" style="width: 100px;height: 30px;text-align: center;line-height: 30px;margin-top: 10px;margin-left: 50%;background-color: aliceblue;">
			
		</div>
		<button onclick="fun1()" style="margin-top: 10px;margin-left: 50%;" type="button" class="btn btn-primary">确定支付</button>
	</body>
	<script type="text/javascript">
		
		var id = getid('id');
		console.log(id);
		// alert(id);
		
		var i=15;
		$("#time").html(i);
		var time1 =setInterval(function(){
			if(i>0){
				i=i-1;
				$("#time").html(i);
			}else{
				clearInterval(time1);
			}
		},1000)
		
		var time2 = setInterval(function(){
			var i = $("#time").html();
			if(i==0){
				window.location.href="订单.html"
			}
		},1000)
		
		//支付
		function fun1(){
			alert("支付成功");
			clearInterval(time1);
			clearInterval(time2);
			$.ajax({
				url:"http://localhost:7778/dingdanController/pay",
				data:{id:id},
				type:"post",
				dataType: "json",
				success: function(data) { 
					
				}
			});
			
		}
		//取值
		function getid(names, urls) {
			urls = urls || window.location.href;
			urls && urls.indexOf("?") > -1 ? urls = urls
					.substring(urls.indexOf("?") + 1) : "";
			var reg = new RegExp("(^|&)" + names + "=([^&]*)(&|$)", "i");
			var r = urls ? urls.match(reg) : window.location.search.substr(1)
					.match(reg);
			if (r != null && r[2] != "")
				return unescape(r[2]);
			return null;
		};
	</script>
</html>

用的ajax和bootstrap。


三、整理模块

下面开始本章的核心改造,首先补一下前面章节的坑,建立父子工程时,直接在父工程的maven进行打包会出异常,因为其中含有一个common公共模块,其他服务有使用commo模块的东西,为了避免编译整个父工程时再报错,在功能模块代码的pom文件,增加如下代码:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCPeuKZgA==,size_20,color_FFFFFF,t_70,g_se,x_16
watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCPeuKZgA==,size_20,color_FFFFFF,t_70,g_se,x_16

这样,下次在父工程直接编译就不会再找不到各个模块对commom模块的依赖了。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCPeuKZgA==,size_20,color_FFFFFF,t_70,g_se,x_16
watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCPeuKZgA==,size_20,color_FFFFFF,t_70,g_se,x_16

然后本次测试会涉及到数据库的操作,所以直接引入mybatis-plus,便于操作,关于mybatis-plus的操作,前面springboot整合篇有讲到。

此处再简单讲一下整合mybatis-plus简要步骤

  1. 导入依赖到父工程
watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCPeuKZgA==,size_20,color_FFFFFF,t_70,g_se,x_16
watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCPeuKZgA==,size_20,color_FFFFFF,t_70,g_se,x_16
  1. 修改provider模块和consumer模块业务模块的配置类yml文件
watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCPeuKZgA==,size_20,color_FFFFFF,t_70,g_se,x_16
watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCPeuKZgA==,size_20,color_FFFFFF,t_70,g_se,x_16

两个模块的yml配置文件记得都要修改。

  1. 关于报错,数据库版本问题可能导致依赖版本存在一些不一致,会出现一系列问题,根据报错百度一下即可解决

四、改造common公共模块

接下来改造common模块,为了方便管理,每个模块公用的东西都放到了common子工程,上一章有将topic主题模式消息队列涉及到的常量放到common的RabbitMQConstant类中,但是本章为了方便看,就直接不将常量信息放到其中。

  1. 创建实体类
watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCPeuKZgA==,size_20,color_FFFFFF,t_70,g_se,x_16
watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCPeuKZgA==,size_20,color_FFFFFF,t_70,g_se,x_16

id使用mybatis-plus的UUID雪花算法自动生成。

  1. 本来想将mapper也放到其中,但是放到其中后,其他模块使用时会导致接口无法访问,问题还未解决

五、改造provider服务提供方

还是展示一下改造后的provider服务的最终目录结构,如下:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCPeuKZgA==,size_20,color_FFFFFF,t_70,g_se,x_16
watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCPeuKZgA==,size_20,color_FFFFFF,t_70,g_se,x_16

框选部分为新增代码,下面开整。

  1. 新增mapper类
watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCPeuKZgA==,size_20,color_FFFFFF,t_70,g_se,x_16
watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCPeuKZgA==,size_20,color_FFFFFF,t_70,g_se,x_16
  1. 新增订单操作接口
watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCPeuKZgA==,size_20,color_FFFFFF,t_70,g_se,x_16
watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCPeuKZgA==,size_20,color_FFFFFF,t_70,g_se,x_16

简要描述:

1、创建一个支付接口,一个创建订单接口。

2、创建订单接口:创建订单记录,并发送消息(传订单id)到延时交换机,并将id返回前端方便前端调用支付接口。

3、支付接口:根据订单id,改变订单已支付状态,避免被消息监听器处理。

六、改造consumer服务消费方

接着改造consumer服务,最终目录结构如下:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCPeuKZgA==,size_20,color_FFFFFF,t_70,g_se,x_16
watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCPeuKZgA==,size_20,color_FFFFFF,t_70,g_se,x_16

框选处为新增代码。

  1. 新增mapper,和生产者服务一样,本来应该可以放到common,但是我还未解决问题,只能先这样处理。
watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCPeuKZgA==,size_20,color_FFFFFF,t_70,g_se,x_16
watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCPeuKZgA==,size_20,color_FFFFFF,t_70,g_se,x_16
  1. 新增延时队列和死信队列的配置类TopicDelayConfig。
watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCPeuKZgA==,size_20,color_FFFFFF,t_70,g_se,x_16
watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCPeuKZgA==,size_20,color_FFFFFF,t_70,g_se,x_16

简要描述:

类似原来的主题模式的常规配置,只不过此处的延时队列创建时有所不同,需要先设置好各个参数再创建,注释有说明,此处为了方便操作,直接使用的路由模式,没有使用主题模式。

  1. 创建延时交换机
  2. 创建死信交换机
  3. 创建延时队列,并设置延时时间以及,成为死信后进入哪一个交换机并设置路由键
  4. 创建死信队列
  5. 创建死信队列和死信交换机的绑定关系
  6. 创建延时队列和延时交换机的绑定关系

3. 新增死信队列监听器

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCPeuKZgA==,size_20,color_FFFFFF,t_70,g_se,x_16
watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCPeuKZgA==,size_20,color_FFFFFF,t_70,g_se,x_16

简要描述:

根据从队列接收到的消息处理具体的逻辑,根据订单id查询订单记录,若存在则判断是否已支付,若未支付则删除。

七、演示

演示效果如本章开篇所展示的一致,此处就不展示了,感兴趣的朋友可以尝试一下,over。

——————————————————完毕——————————————————
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-03-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、分析例子
  • 二、编写前端代码
  • 三、整理模块
  • 四、改造common公共模块
  • 五、改造provider服务提供方
  • 六、改造consumer服务消费方
  • 七、演示
    • ——————————————————完毕——————————————————
    相关产品与服务
    消息队列 CMQ
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档