RabbitMQ常用的几种消息模型
第一种模型(HelloWorld)
上图来自官方文档
P代表生产者用来生产消息,发送给消费者C,中间的共色部分代表消息队列,用来缓存消息。
首先导入依赖
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.7.2</version>
</dependency>
工具类 RabbitmqUtil.java
public class RabbitmqUtil {
private static ConnectionFactory connectionFactory = new ConnectionFactory();
/**
* 配置连接端口,ip,用户名和密码
*/
static {
//绑定RabbitMQ主机地址
connectionFactory.setHost("192.168.1.6");
//绑定端口
connectionFactory.setPort(5672);
//输入用户名密码
connectionFactory.setUsername("rabbit");
connectionFactory.setPassword("rabbit");
}
/**
* 获取连接对象
* @return
*/
public static Connection getConnection() {
try {
return connectionFactory.newConnection();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 关闭资源
* @param channel
* @param connection
*/
public static void closeConnAndChannel(Channel channel,Connection connection){
try {
if (channel != null) {
channel.close();
}
if (connection != null) {
connection.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
生产者代码
Provider.java
public class Provider {
public static void main(String[] args) throws IOException, TimeoutException {
// 获取连接对象
Connection connection = RabbitmqUtil.getConnection();
// 创建通道
Channel channel = connection.createChannel();
//queueDeclare第一个参数是队列名称,第二个是是否持久化,如果是true,队列持久化,但是队列内容的持久化需要在basicProperties设置
//第三个是是否独占队列一般是false不独占,第四个是消费完成后是否自动删除 true代表删除,false代表不删除
//第五个参数是额外附加参数
channel.queueDeclare("hello",true,false,true,null);
//basicPublish 第一个参数代表交换机名称,第二是队列名称,第三个是额外的队列配置 第四个参数就是发送的消息
channel.basicPublish("","hello", MessageProperties.PERSISTENT_TEXT_PLAIN,"hello,world".getBytes());
//关闭管道和连接
RabbitmqUtil.closeConnAndChannel(channel,connection);
}
}
Consumer.java
public class Consumer {
public static void main(String[] args) throws IOException, TimeoutException {
// 通过工具类获取连接对象
Connection connection = RabbitmqUtil.getConnection();
// 创建通道
Channel channel = connection.createChannel();
// '参数1':用来声明通道对应的队列 hello
// '参数2':用来指定是否持久化队列 true
// '参数3':用来指定是否独占队列 false 一般都是不独站队列 让多个连接可以共同向一个队列生产消费消息
// '参数4':用来指定是否自动删除队列 false
// '参数5':对队列的额外配置 是一个Map类型
channel.queueDeclare("hello",true,false,true,null);
//参数一代表队列名,参数二是否开启自动确认机制,参数三,消费时的回调接口
channel.basicConsume("hello",true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("接收的消息是:"+new String(body));
}
});
}
}
第二种模型(Work queue)
C1和C2都是消费者,P代表生产者,中间红色的部分是消息队列。
Work queue
被称为任务队列。当消息处理比较耗时时,生产的速度大于消费的速度,长此以往,消息会在消息队列中越来愈多,无法及时处理。此时可以使用work模型,让多了消费者绑定到一个队列中,共同消费队列中的消息。
消息提供者
public class Provider {
public static void main(String[] args) throws IOException {
Connection connection = RabbitmqUtil.getConnection();
//建立通道
Channel channel = connection.createChannel();
//建立队列
channel.queueDeclare("work",false,false,false,null);
//生产消息
for (int i = 0; i < 10; i++) {
channel.basicPublish("","work",null,(i+":work queue").getBytes());
}
//关闭资源
RabbitmqUtil.closeConnAndChannel(channel,connection);
}
}
消费者1
public class Consumer1 {
public static void main(String[] args) throws IOException {
// 通过工具类获取连接对象
Connection connection = RabbitmqUtil.getConnection();
// 创建通道
Channel channel = connection.createChannel();
//每次只确认一条消息
channel.basicQos(1);
// '参数1':用来声明通道对应的队列 hello
// '参数2':用来指定是否持久化队列 true
// '参数3':用来指定是否独占队列 false 一般都是不独站队列 让多个连接可以共同向一个队列生产消费消息
// '参数4':用来指定是否自动删除队列 false
// '参数5':对队列的额外配置 是一个Map类型
channel.queueDeclare("work",false,false,false,null);
//参数一代表队列名,参数二是否开启自动确认机制,参数三,消费时的回调接口
channel.basicConsume("work",true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者 1 接收的消息是:"+new String(body));
//进行手动确认
channel.basicAck(envelope.getDeliveryTag(),false);
}
});
}
}
消费者2
public class Consumer2 {
public static void main(String[] args) throws IOException {
// 通过工具类获取连接对象
Connection connection = RabbitmqUtil.getConnection();
// 创建通道
Channel channel = connection.createChannel();
//一次只接受1条消息进行 确认
channel.basicQos(1);
// '参数1':用来声明通道对应的队列 hello
// '参数2':用来指定是否持久化队列 true
// '参数3':用来指定是否独占队列 false 一般都是不独站队列 让多个连接可以共同向一个队列生产消费消息
// '参数4':用来指定是否自动删除队列 false
// '参数5':对队列的额外配置 是一个Map类型
channel.queueDeclare("work",false,false,false,null);
//参数一代表队列名,
// 参数二是否开启自动确认机,
// 在开启自动确认消息机制时,RabbitMQ会认为只要消费者从队列中拿走消息就认为已经消费完成,就会将队列中的消息标记为已消费实际消费过程中有可能出现
//开启自动确认机制有时会造成消息丢失,如果一个消费者在执行过程中宕机了那他未完成的消息也会丢失,我们想让宕机后未消费的消息转移到正常运行的消费者上进行消费,
// 所以需要关闭自动确认机制(设置成false),进行手动确认
// 参数三,消费时的回调接口
channel.basicConsume("work",false,new DefaultConsumer(channel){
@lombok.SneakyThrows
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
// 消费者2消费的速度小于消费者1
Thread.sleep(2000);
System.out.println("消费者 2 接收的消息是:"+new String(body));
//进行手动确认消息 false代表不开启多次确认
channel.basicAck(envelope.getDeliveryTag(),false);
}
});
}
}
上面的代码是进行手动确认消息,默认是自动确认消息,如果是默认的自动消息确认机制,那么消息提供者会将消息平均发送给消费者1和消费者2。也就是说消费者收到的消息数量是相同的,有序的,这种消费发送方式也被叫做循环。
但是我们通常会根据消费者处理的效率不同,让消费快的消费者处理多点消息;消费效率低的消费者处理少量的消息。
第三种模型(Fanout)
扇出(fanout)又称广播
广播模式下,消息发送流程
- 可以有多个消费者
- 每个消费者都有自己的队列
- 每个队列都要绑定到Exchange(交换机)
- 生产者发送消息,只能发送给交换机,交换机决定要发送到哪个队列,生产者无法决定
- 交换机把消息发送给绑定过的所有队列
- 队列的消费者都能够拿到消息,实现一条消息被多个消费者消费
代码实现
消息生产者
public class Provider {
public static void main(String[] args) throws IOException {
//获取连接对象
Connection connection = RabbitmqUtil.getConnection();
//获取管道
Channel channel = connection.createChannel();
//将管道绑定交换机
channel.exchangeDeclare("logs","fanout");
//发送消息内容
channel.basicPublish("logs","",null,"fanout 广播模式".getBytes());
//关闭连接
RabbitmqUtil.closeConnAndChannel(channel,connection);
}
}
消费者
public class Consumer1 {
public static void main(String[] args) throws IOException {
Connection connection = RabbitmqUtil.getConnection();
Channel channel = connection.createChannel();
//绑定交换机
channel.exchangeDeclare("logs","fanout");
//临时队列
String queue = channel.queueDeclare().getQueue();
//绑定交换机和队列
channel.queueBind(queue,"logs","");
//消费消息
channel.basicConsume(queue,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
throws IOException {
System.out.println("消费者1: "+new String(body));
}
});
}
}
其中一个生产者多个消费者进行消费,广播的效果就是可以将同一条消息发送给所有消费者绑定的队列
第四种模式(Routing)
Routing订阅模式-Direct(直连)
在Fanout模式中,一条消息,会被所有订阅的队列都消费但是在某些场景下,我们希望不同的消息被不同的队列消费。这时候就要用到Direct类型的Exchange。
在Direct下:
1、队列与交换机的绑定不能是任意绑定,而是要指定一个RoutingKey(路由Key)
2、消息的发送方在向交换机发送消息时,需要指定RoutingKey。
3、Exchange不在吧消息交给每一个绑定的队列,而是根据消息的Routing Key进行判断,只有队列的RoutingKey与消息的Routing Key完全一致才会接收到消息。
消息生产者
public class Provider {
public static void main(String[] args) throws IOException {
// 获取连接对象
Connection connection = RabbitmqUtil.getConnection();
// 创建通信管道
Channel channel = connection.createChannel();
// 交换器名称
String exechange = "logs_direct";
channel.exchangeDeclare(exechange,"direct");
// 指定路由key
String router_key = "error";
// 向交换器发送消息
channel.basicPublish(exechange,router_key,
null,("这是基于direct模型发布的 route_key="+router_key+" 发送的消息").getBytes());
RabbitmqUtil.closeConnAndChannel(channel,connection);
}
}
消费者1
public class Consumer1 {
public static void main(String[] args) throws IOException {
Connection connection = RabbitmqUtil.getConnection();
Channel channel = connection.createChannel();
String exechange = "logs_direct";
//创建交换机类型是direct
channel.exchangeDeclare(exechange,"direct");
//创建临时队列
String queue = channel.queueDeclare().getQueue();
//绑定交换机,指明路由id
channel.queueBind(queue,exechange,"info");
channel.queueBind(queue,exechange,"warning");
channel.queueBind(queue,exechange,"error");
//消费消息
channel.basicConsume(queue,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者1: "+new String(body));
}
});
}
}
消费者2
public class Consumer2 {
public static void main(String[] args) throws IOException {
Connection connection = RabbitmqUtil.getConnection();
Channel channel = connection.createChannel();
String exechange = "logs_direct";
//创建交换机类型是direct
channel.exchangeDeclare(exechange,"direct");
//创建临时队列
String queue = channel.queueDeclare().getQueue();
//绑定交换机,指明路由id
channel.queueBind(queue,exechange,"error");
//消费消息
channel.basicConsume(queue,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者2: "+new String(body));
}
});
}
}
第五种模型(Topic)
Topic模型
的Exchange与Direct
相比,是可以根据RoutingKey
的不同把消息路由到不同的队列。只不过Topic类型的Exchange
可以让队列在绑定路由key的时候使用通配符,这种模型的路由key一般都是由一个或多个单词组成,多个单词之间以“.”分割。
# 通配符
* 表示可以匹配一个单词
\# 表示可以匹配一个或多个单词
例如:user.\# 可以代表user.login或者user.logout ,user.register.check等
user.* 表示user.login;user.lgout但是不能表示user.register.check
生产者
public class Provider {
public static void main(String[] args) throws IOException {
// 获取连接对象
Connection connection = RabbitmqUtil.getConnection();
// 创建通信管道
Channel channel = connection.createChannel();
// 交换器名称
String exechange = "topics";
channel.exchangeDeclare(exechange,"topic");
// 指定路由key
String router_key = "response.error";
// 向交换器发送消息
channel.basicPublish(exechange,router_key,
null,("这是基于topic模型发布的 route_key="+router_key+" 发送的消息").getBytes());
RabbitmqUtil.closeConnAndChannel(channel,connection);
}
}
消费者1
public class Consumer1 {
public static void main(String[] args) throws IOException {
Connection connection = RabbitmqUtil.getConnection();
Channel channel = connection.createChannel();
String exechange = "topics";
//创建交换机类型是direct
channel.exchangeDeclare(exechange,"topic");
//创建临时队列
String queue = channel.queueDeclare().getQueue();
//绑定交换机,指明路由id
channel.queueBind(queue,exechange,"response.*");
//消费消息
channel.basicConsume(queue,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者1: "+new String(body));
}
});
}
}
消费者2
public class Consumer2 {
public static void main(String[] args) throws IOException {
Connection connection = RabbitmqUtil.getConnection();
Channel channel = connection.createChannel();
String exechange = "topics";
//创建交换机类型是direct
channel.exchangeDeclare(exechange,"topic");
//创建临时队列
String queue = channel.queueDeclare().getQueue();
//绑定交换机,指明路由id
channel.queueBind(queue,exechange,"response.#");
//消费消息
channel.basicConsume(queue,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者1: "+new String(body));
}
});
}
}
运行结果
路由key改为response.error.code
RabbitMQ常用的几种消息模型的更多相关文章
- RabbitMQ之五种消息模型
首先什么是MQ MQ全称是Message Queue,即消息对列!消息队列是典型的:生产者.消费者模型.生产者不断向消息队列中生产消息,消费者不断的从队列中获取消息.因为消息的生产和消费都是异步的,而 ...
- rabbitmq五种消息模型整理
目录 0. 配置项目 1. 基本消息模型 1.1 生产者发送消息 1.2 消费者获取消息(自动ACK) 1.3 消息确认机制(ACK) 1.4 消费者获取消息(手动ACK) 1.5 自动ACK存在的问 ...
- RabbitMQ,RocketMQ,Kafka 消息模型对比分析
消息模型 消息队列的演进 消息队列模型 发布订阅模型 RabbitMQ的消息模型 交换器的类型 direct topic fanout headers Kafka的消息模型 RocketMQ的消息模型 ...
- JMS两种消息模型
前段时间学习EJB.接触到了JMS(Java消息服务),JMS支持两种消息模型:Point-to-Point(P2P)和Publish/Subscribe(Pub/Sub),即点对点和公布订阅模型. ...
- SpringBoot 整合 RabbitMQ(包含三种消息确认机制以及消费端限流)
目录 说明 生产端 消费端 说明 本文 SpringBoot 与 RabbitMQ 进行整合的时候,包含了三种消息的确认模式,如果查询详细的确认模式设置,请阅读:RabbitMQ的三种消息确认模式 同 ...
- SpringCloud(六) - RabbitMQ安装,三种消息发送模式,消息发送确认,消息消费确认(自动,手动)
1.安装erlang语言环境 1.1 创建 erlang安装目录 mkdir erlang 1.2 上传解压压缩包 上传到: /root/ 解压缩# tar -zxvf otp_src_22.0.ta ...
- RabbitMQ除开RPC的五种消模型----原生API
2.五种消息模型 RabbitMQ提供了6种消息模型,但是第6种其实是RPC,并不是MQ,因此不予学习.那么也就剩下5种. 但是其实3.4.5这三种都属于订阅模型,只不过进行路由的方式不同. 通过一个 ...
- Kafka消息模型
一.消息传递模型 传统的消息队列最少提供两种消息模型,一种P2P,一种PUB/SUB,而Kafka并没有这么做,巧妙的,它提供了一个消费者组的概念,一个消息可以被多个消费者组消费,但是只能被一个消费者 ...
- HTTPD三种工作模型
HTTPD三种工作模型 MPM是apache的多道处理模块,用于定义apache对客户端请求的处理方式.在linux中apache常用的三种MPM模型分别是prefork.worker和event. ...
随机推荐
- 威联通(NAS)搭建个人音乐中心
我为什么要自己搭建音乐服务 曾记得早些年,音乐是可以随便在线听,随便下载的,没有付费这么一说的(背后是音乐平台提供的版权支持).我们听音乐也就可以很随意,但是这几年,音乐的版权开始管理的严禁,音乐没地 ...
- 【NOI2019】弹跳(KDT优化建图)
Description 平面上有 \(n\) 个点,分布在 \(w \times h\) 的网格上.有 \(m\) 个弹跳装置,由一个六元组描述.第 \(i\) 个装置有参数:\((p_i, t_i, ...
- 某宝的微信小程序源码合集
这是我在某宝买的小程序源码合集.能用的我用不到.. 我用的到的有的有没后端.仅供个人参考,严禁侵权或商业用途! 下载地址:https://pan.baidu.com/s/1cQEQ17LdN-7hxD ...
- 你来说一下springboot的启动时的一个自动装配过程吧
前言 继续总结吧,没有面试就继续夯实自己的基础,前阵子的在面试过程中遇到的各种问题陆陆续续都会总结出来分享给大家,这次要说的也是面试中被问到的一个高频的问题,我当时其实没答好,因为很早之前是看到spr ...
- IDEA中flink程序报错找不到类
Idea中运行flink程序,报错找不到类,其中pom文件中一项依赖为: <dependency> <groupId>org.apache.flink</groupId& ...
- Impala的特点
Impala的特点 0. 原理 基于内存的分析框架. 1.为什么会有Impala? hive进行计算太慢了,于是就有了Impala,Impala可以理解为是hive的内存版本. 2.Impala的优点 ...
- 使用基于Vue.js和Hbuilder的混合模式移动开发打造属于自己的移动app
近几年,混合模式移动应用的概念甚嚣尘上,受到了一些中小型企业的青睐,究其原因,混合模式开发可以比传统移动开发节约大量的开发成本和人力成本. Hybrid App(混合模式移动应用)是指介于web-ap ...
- day020|python之面向对象基础2
面向对象基础2 目录 面向对象基础2 7 对象与类型 7.1 类即类型 7.1.1 变量的三个指标 7.1.2 变量类型 7.2 list.append()方法原理 8 对象的高度整合 8.1 通过面 ...
- 给小白整理的一篇Python知识点
1.基本概念 1.1 四种类型 python中数有四种类型:整数.长整数.浮点数和复数. python中数有四种类型:整数.长整数.浮点数和复数. 整数, 如 1 长整数 是比较大的整数 浮点数 如 ...
- MySQL获取上月第一天、上月最后日、本月第一天、本月最后日的方法
直接贴SQL语句了 #上月第一天 SELECT DATE_FORMAT(DATE_SUB(CURDATE(),INTERVAL 1 MONTH),'%Y-%m-01'); #上月最后日 SELECT ...