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. ...
随机推荐
- Panda Global 要点聚焦,区块链在数字医疗的落地应
据Panda Global,随着区块链技术影响力的不断扩大,其应用性已涉及更加广泛的领域,不断更新着人们的认知.在区块链技术未介入之前,关于医疗行业和数字经济结合早已不是什么新鲜话题,相关研究不少 但 ...
- 题解-Quantifier Question
Quantifier Question 有长度为 \(n\) 的序列 \(x\{n\}\),有 \(m\) 个条件 \((j_i,k_i)\).有 \(n\) 个待定的条件符 \(Q_i\in\{\f ...
- 配置 Spring Batch 批处理失败重试机制
1. 引言 默认情况下,Spring批处理作业在执行过程中出现任何错误都会失败.然而有些时候,为了提高应用程序的弹性,我们就需要处理这类间歇性的故障. 在这篇短文中,我们就来一起探讨 如何在Sprin ...
- MySQL事务提交流程
有binlog的CR方式(重点核心!!): 有binlog情况下,commit动作开始时,会有一个Redo XID 的动作记录写到redo,然后写data到binlog,binlog写成功后,会将bi ...
- redis 常用基本命令
redis 常用基本命令 redis-cli 启动set 键 值 # 存储 单条数据 # set 'zsj' 'bab' get 键 # 通过键获取值 # get ...
- ES6、ES7、ES8、ES9、ES10新特性
ES6新特性(2015) ES6的特性比较多,在 ES5 发布近 6 年(2009-11 至 2015-6)之后才将其标准化.两个发布版本之间时间跨度很大,所以ES6中的特性比较多. 在这里列举几个常 ...
- vue第十二单元(vue中过渡效果的实现)
第十二单元(vue中过渡效果的实现) #课程目标 熟练掌握transition组件的用法 熟练使用transition组件做过渡特效 熟练使用transition组件做动画特效 了解使用transit ...
- Spark性能调优九之常用算子调优
1.使用mapPartitions算子提高性能 mapPartition的优点:使用普通的map操作,假设一个partition中有1万条数据,那么function就要被执行1万次,但是使用mapPa ...
- Maven之继承
这里我还是将通过一个例子来了解一下Maven继承的初步使用配置.还是使用三个工程项目Project-Parent.Project-C和Project-D来进行说明,三个项目关系如下: <?xml ...
- OC 大数组分割成由小数组重新组合的新数组
NSLog(@"++++%@",[self seprateBigArrBySize:3 BigArr:@[@"1",@"2",@" ...