目录
[3.Virtual host](#3.Virtual host)
[2.Work Queue(工作队列)](#2.Work Queue(工作队列))
[7.Publisher Confirms(发布确认)](#7.Publisher Confirms(发布确认))
一.核心概念
1.Producer和Consumer
Producer:生产者,是RabbitMQ Server的客户端,向RabbitMQ发送消息;
Consumer:消费者,是RabbitMQ Server的客户端,从RabbitMQ接收消息;
Broker:是RabbitMQ Server,主要是接收和收发消息;
2.Connection和Channel
Connection:连接,是客户端和RabbitMQ服务器之间的⼀个TCP连接。这个连接是建立消息传递的基础, 它负责传输客户端和服务器之间的所有数据和控制信息;
Channel:信道, Channel是在Connection之上的⼀个抽象层。在 RabbitMQ 中,⼀个TCP连接可以有多个Channel,每个Channel 都是独立的虚拟连接。消息的发送和接收都是基于 Channel的。通道的主要作用是将消息的读写操作复用到同⼀个TCP连接上,这样可以减少建立和关闭连接的开销,提高性能。
3.Virtual host
虚拟主机。这是⼀个虚拟概念。它为消息队列提供了⼀种逻辑上的隔离机制。对于RabbitMQ而言,⼀个 BrokerServer 上可以存在多个 Virtual Host。当多个不同的用户使用同⼀个RabbitMQ Server 提供的服务时,可以虚拟划分出多个 vhost,每个用户在自己的 vhost 创建exchange/queue 等
4.Queue
队列,是RabbitMQ的内部对象,用于存储消息
5.Exchange
交换机。生产者将消息发送到Exchange,由交换机将消息按⼀定规则路由到⼀个或多个队列中。交换机只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与Exchange绑定,或者没有符合路由规则的队列,那么消息就会丢失。
RabbitMQ交换机有四种类型:Fanout、Direct、topic、Headers,不同类型有着不同的路由策略。
1)Fanout:广播,将消息交给所有绑定到交换机的队列;
2)Direct:定向,把消息交给符合指定routing key的队列;
3)Topic:通配符,把消息交给符合routing pattern(路由模式)的队列;
4)Headers:交换器不依赖于路由键的匹配规则来路由消息,而是根据发送的消息内容中的headers属性进行匹配。Headers类型的交换器性能会很差,而且也不使用,基本上不会看到它的存在。
RoutingKey:路由键。生产者将消息发给交换器时,指定的⼀个字符串,用来告诉交换机应该如何处理这个消息;
Binding Key:绑定。RabbitMQ中通过Binding(绑定)将交换器与队列关联起来,在绑定的时候⼀般会指定⼀个Binding Key,这样RabbitMQ就知道如何正确地将消息路由到队列了。
二.工作模式
一共有7中工作模式,想了解更多请看官网:RabbitMQ Tutorials | RabbitMQ
1.Simple(简单模式)

P:生产者,发送消息的程序
C:消费者,消费的接收者
Queue:消息队列,生产者可以向其发送信息,消费者可以从中取出消息
Producer:
java
//1.创建连接
ConnectionFactory connectionFactory=new ConnectionFactory();
connectionFactory.setHost(Constants.HOST);
connectionFactory.setPort(Constants.PORT);
connectionFactory.setUsername(Constants.USER_NAME);
connectionFactory.setPassword(Constants.PASSWORD);
connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
Connection connection=connectionFactory.newConnection();
//2.开启信道
Channel channel=connection.createChannel();
//3.声明交换机
//4.声明队列
channel.queueDeclare("hello",true,false,false,null);
//5.发送消息
String msg="hello rabbitmq";
channel.basicPublish("","hello",null,msg.getBytes());
//6.释放连接
channel.close();
connection.close();
Consumer:
java
//1.创建连接
ConnectionFactory connectionFactory=new ConnectionFactory();
connectionFactory.setHost(Constants.HOST);
connectionFactory.setPort(Constants.PORT);
connectionFactory.setUsername(Constants.USER_NAME);
connectionFactory.setPassword(Constants.PASSWORD);
connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
Connection connection=connectionFactory.newConnection();
//2.开启信道
Channel channel=connection.createChannel();
//3.声明交换机
//4.声明队列
channel.queueDeclare("hello",true,false,false,null);
//5.消费消息
DefaultConsumer consumer=new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("接收到消息:"+new String(body));
}
};
channel.basicConsume("hello",true,consumer);
//6.释放连接
channel.close();
connection.close();
2.Work Queue(工作队列)

一个生产者P,多个消费者C1、C2,Queue将信息分给不同消费者,消息不会重复,分配给不同的消费者。适用于集群环境中做异步处理。
Producer:
java
//1.创建连接
ConnectionFactory connectionFactory=new ConnectionFactory();
connectionFactory.setHost(Constants.HOST);
connectionFactory.setPort(Constants.PORT);
connectionFactory.setUsername(Constants.USER_NAME);
connectionFactory.setPassword(Constants.PASSWORD);
connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
Connection connection=connectionFactory.newConnection();
//2.开启信道
Channel channel=connection.createChannel();
//3.声明交换机,声明队列
channel.queueDeclare(Constants.WORK_QUEUE,true,false,false,null);
//4.发送消息
String msg="hello work queue";
channel.basicPublish("",Constants.WORK_QUEUE,null,msg.getBytes());
//5.释放连接
channel.close();
connection.close();
Consumer:
java
//1.创建连接
ConnectionFactory connectionFactory=new ConnectionFactory();
connectionFactory.setHost(Constants.HOST);
connectionFactory.setPort(Constants.PORT);
connectionFactory.setUsername(Constants.USER_NAME);
connectionFactory.setPassword(Constants.PASSWORD);
connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
Connection connection=connectionFactory.newConnection();
//2.开启信道
Channel channel=connection.createChannel();
//3.声明交换机,声明队列
channel.queueDeclare(Constants.WORK_QUEUE,true,false,false,null);
//4.消费消息
DefaultConsumer consumer=new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("接收到消息:"+new String(body));
}
};
channel.basicConsume(Constants.WORK_QUEUE,true,consumer);
//5.释放连接
channel.close();
connection.close();
3.Publish/Subscribe(发布/订阅模式)

X:交换机,是Fanout类型的
交换机将消息复制多份,每个消费者C都接收相同的消息。适用于消息需要被多个消费者同时接收的场景。
Producer:
java
//1.创建连接
ConnectionFactory connectionFactory=new ConnectionFactory();
connectionFactory.setHost(Constants.HOST);
connectionFactory.setPort(Constants.PORT);
connectionFactory.setUsername(Constants.USER_NAME);
connectionFactory.setPassword(Constants.PASSWORD);
connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
Connection connection=connectionFactory.newConnection();
//2.开启信道
Channel channel=connection.createChannel();
//3.声明交换机
channel.exchangeDeclare(Constants.FANOUT_EXCHANGE, BuiltinExchangeType.FANOUT,true);
//4.声明队列
channel.queueDeclare(Constants.FANOUT_QUEUE1,true,false,false,null);
channel.queueDeclare(Constants.FANOUT_QUEUE2,true,false,false,null);
//5.绑定交换机和队列
channel.queueBind(Constants.FANOUT_QUEUE1,Constants.FANOUT_EXCHANGE,"");
channel.queueBind(Constants.FANOUT_QUEUE2,Constants.FANOUT_EXCHANGE,"");
//6.发布消息
String msg="hello fanout";
channel.basicPublish(Constants.FANOUT_EXCHANGE,"",null,msg.getBytes());
//7.释放连接
channel.close();
connection.close();
Consumer:
java
// 有两个消费者,这里只给出一个
//1.创建连接
ConnectionFactory connectionFactory=new ConnectionFactory();
connectionFactory.setHost(Constants.HOST);
connectionFactory.setPort(Constants.PORT);
connectionFactory.setUsername(Constants.USER_NAME);
connectionFactory.setPassword(Constants.PASSWORD);
connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
Connection connection=connectionFactory.newConnection();
//2.开启信道
Channel channel=connection.createChannel();
//3.声明队列
channel.queueDeclare(Constants.FANOUT_QUEUE1,true,false,false,null);
//5.消费消息
DefaultConsumer consumer=new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("接收到消息:"+new String(body));
}
};
channel.basicConsume(Constants.FANOUT_QUEUE1,true,consumer);
//6.释放连接
channel.close();
connection.close();
4.Routing(路由模式)

X:交换机,是Direct类型的
交换机根据RoutingKey分发给对应的消费者队列,就如上图的abc。适用于需要特定规则分发消息的场景。
Producer:
java
//1.创建连接
ConnectionFactory connectionFactory=new ConnectionFactory();
connectionFactory.setHost(Constants.HOST);
connectionFactory.setPort(Constants.PORT);
connectionFactory.setUsername(Constants.USER_NAME);
connectionFactory.setPassword(Constants.PASSWORD);
connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
Connection connection=connectionFactory.newConnection();
//2.开启信道
Channel channel=connection.createChannel();
//3.声明交换机
channel.exchangeDeclare(Constants.DIRECT_EXCHANGE, BuiltinExchangeType.DIRECT,true);
//4.声明队列
channel.queueDeclare(Constants.DIRECT_QUEUE1,true,false,false,null);
channel.queueDeclare(Constants.DIRECT_QUEUE2,true,false,false,null);
//5.绑定交换机和队列
channel.queueBind(Constants.DIRECT_QUEUE1,Constants.DIRECT_EXCHANGE,"a");
channel.queueBind(Constants.DIRECT_QUEUE2,Constants.DIRECT_EXCHANGE,"a");
channel.queueBind(Constants.DIRECT_QUEUE2,Constants.DIRECT_EXCHANGE,"b");
channel.queueBind(Constants.DIRECT_QUEUE2,Constants.DIRECT_EXCHANGE,"c");
//6.发布消息
String msg_a="hello routingKey is a";
channel.basicPublish(Constants.DIRECT_EXCHANGE,"a",null,msg_a.getBytes());
String msg_b="hello routingKey is b";
channel.basicPublish(Constants.DIRECT_EXCHANGE,"b",null,msg_b.getBytes());
String msg_c="hello routingKey is c";
channel.basicPublish(Constants.DIRECT_EXCHANGE,"c",null,msg_c.getBytes());
//7.释放连接
channel.close();
connection.close();
Consumer:
java
//1.创建连接
ConnectionFactory connectionFactory=new ConnectionFactory();
connectionFactory.setHost(Constants.HOST);
connectionFactory.setPort(Constants.PORT);
connectionFactory.setUsername(Constants.USER_NAME);
connectionFactory.setPassword(Constants.PASSWORD);
connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
Connection connection=connectionFactory.newConnection();
//2.开启信道
Channel channel=connection.createChannel();
//3.声明队列
channel.queueDeclare(Constants.DIRECT_QUEUE1,true,false,false,null);
//5.消费消息
DefaultConsumer consumer=new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("接收到消息:"+new String(body));
}
};
channel.basicConsume(Constants.DIRECT_QUEUE1,true,consumer);
//6.释放连接
channel.close();
connection.close();
5.Topics(通配符模式)

路由模式的升级版,路由的RoutingKey可以通过正则表达式的方式来定义,匹配更加灵活。
Producer:
java
//1.创建连接
ConnectionFactory connectionFactory=new ConnectionFactory();
connectionFactory.setHost(Constants.HOST);
connectionFactory.setPort(Constants.PORT);
connectionFactory.setUsername(Constants.USER_NAME);
connectionFactory.setPassword(Constants.PASSWORD);
connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
Connection connection=connectionFactory.newConnection();
//2.开启信道
Channel channel=connection.createChannel();
//3.声明交换机
channel.exchangeDeclare(Constants.TOPIC_EXCHANGE, BuiltinExchangeType.TOPIC,true);
//4.声明队列
channel.queueDeclare(Constants.TOPIC_QUEUE1,true,false,false,null);
channel.queueDeclare(Constants.TOPIC_QUEUE2,true,false,false,null);
//5.绑定交换机和队列
channel.queueBind(Constants.TOPIC_QUEUE1,Constants.TOPIC_EXCHANGE,"*.a.*");
channel.queueBind(Constants.TOPIC_QUEUE2,Constants.TOPIC_EXCHANGE,"*.*.b");
channel.queueBind(Constants.TOPIC_QUEUE2,Constants.TOPIC_EXCHANGE,"c.#");
//6.发布消息
String msg_a="hello topic,routingKey is r.a.few";
channel.basicPublish(Constants.TOPIC_EXCHANGE,"er.a.few",null,msg_a.getBytes());
String msg_b="hello topic,routingKey is erg.a.b";
channel.basicPublish(Constants.TOPIC_EXCHANGE,"erg.a.b",null,msg_b.getBytes());
String msg_c="hello topic,routingKey is c";
channel.basicPublish(Constants.TOPIC_EXCHANGE,"c.wfew",null,msg_c.getBytes());
//7.释放连接
channel.close();
connection.close();
Consumer:
java
//1.创建连接
ConnectionFactory connectionFactory=new ConnectionFactory();
connectionFactory.setHost(Constants.HOST);
connectionFactory.setPort(Constants.PORT);
connectionFactory.setUsername(Constants.USER_NAME);
connectionFactory.setPassword(Constants.PASSWORD);
connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
Connection connection=connectionFactory.newConnection();
//2.开启信道
Channel channel=connection.createChannel();
//3.声明队列
channel.queueDeclare(Constants.TOPIC_QUEUE1,true,false,false,null);
//5.消费消息
DefaultConsumer consumer=new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("接收到消息:"+new String(body));
}
};
channel.basicConsume(Constants.TOPIC_QUEUE1,true,consumer);
//6.释放连接
channel.close();
connection.close();
6.RPC(RPC通信)
在RPC通信中,没有消费者和生产者,通过两个队列来实现一个可回调的过程。
1)客户端发送消息到⼀个指定的队列,并在消息属性中设置replyTo字段,这个字段指定了⼀个回调队列, 用于接收服务端的响应;
2)服务端接收到请求后,处理请求并发送响应消息到replyTo指定的回调队列;
3)客户端在回调队列上等待响应消息。⼀旦收到响应,客户端会检查消息的correlationId属性,以确保它是所期望的响应。
Client(客户端):
java
//1.创建连接
ConnectionFactory connectionFactory=new ConnectionFactory();
connectionFactory.setHost(Constants.HOST);
connectionFactory.setPort(Constants.PORT);
connectionFactory.setUsername(Constants.USER_NAME);
connectionFactory.setPassword(Constants.PASSWORD);
connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
Connection connection=connectionFactory.newConnection();
//2.开启信道
Channel channel=connection.createChannel();
//3.声明队列
channel.queueDeclare(Constants.RPC_REQUEST_QUEUE,true,false,false,null);
channel.queueDeclare(Constants.RPC_RESPONSE_QUEUE,true,false,false,null);
//4.发送请求
//消息内容
String msg="hello rpc";
//设置请求的唯一标识
String correlationId= UUID.randomUUID().toString();
//设置相关属性
AMQP.BasicProperties pros=new AMQP.BasicProperties().builder()
.correlationId(correlationId)
.replyTo(Constants.RPC_RESPONSE_QUEUE)
.build();
channel.basicPublish("",Constants.RPC_REQUEST_QUEUE,pros,msg.getBytes());
//5.接收响应
final BlockingQueue<String> response=new ArrayBlockingQueue<>(1);
DefaultConsumer consumer=new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String respMsg=new String(body);
System.out.println("接收到消息");
if(correlationId.equals(properties.getCorrelationId())){
response.offer(respMsg);
}
}
};
channel.basicConsume(Constants.RPC_RESPONSE_QUEUE,true,consumer);
String result=response.take();
System.out.println("rpc接收到的信息是:"+result);
Server(服务端):
java
//1.创建连接
ConnectionFactory connectionFactory=new ConnectionFactory();
connectionFactory.setHost(Constants.HOST);
connectionFactory.setPort(Constants.PORT);
connectionFactory.setUsername(Constants.USER_NAME);
connectionFactory.setPassword(Constants.PASSWORD);
connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
Connection connection=connectionFactory.newConnection();
//2.开启信道
Channel channel=connection.createChannel();
//3.接收请求
channel.basicQos(1); //设置每次只能有一个请求
DefaultConsumer consumer=new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String request=new String(body,"UTF-8");
System.out.println("接收到请求消息");
String response="针对请求:"+request+",请求成功";
AMQP.BasicProperties pros=new AMQP.BasicProperties().builder()
.correlationId(properties.getCorrelationId())
.build();
channel.basicPublish("",Constants.RPC_RESPONSE_QUEUE,pros,response.getBytes());
channel.basicAck(envelope.getDeliveryTag(),false);
}
};
channel.basicConsume(Constants.RPC_REQUEST_QUEUE,false,consumer);
7.Publisher Confirms(发布确认)
Publisher Confirms模式是RabbitMQ提供的⼀种确保消息可靠发送到RabbitMQ服务器的机制。在这种模式下,生产者可以等待RabbitMQ服务器的确认,以确保消息已经被服务器接收并处理。通过Publisher Confirms模式,生产者可以确保消息被RabbitMQ服务器成功接收, 从而避免消息丢失
的问题。
具体过程:
1)生产者将Channel设置为confirm模式(通过调用channel.confirmSelect()完成)后, 发布的每一条消息都会获得⼀个唯⼀的ID,生产者可以将这些序列号与消息关联起来,以便跟踪消息的状态;
2)当消息被RabbitMQ服务器接收并处理后,服务器会异步地向生产者发送⼀个确认(ACK)给生产者(包含消息的唯⼀ID),表明消息已经送达。
java
public class PublisherConfirms {
private static final Integer MESSAGE_COUNT = 10000;
static Connection createConnection() throws Exception {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost(Constants.HOST);
connectionFactory.setPort(Constants.PORT); //需要提前开放端口号
connectionFactory.setUsername(Constants.USER_NAME);//账号
connectionFactory.setPassword(Constants.PASSWORD); //密码
connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST); //虚拟主机
return connectionFactory.newConnection();
}
public static void main(String[] args) throws Exception {
//单独确认
publishingMessagesIndividually();
//批量确认
publishingMessagesInBatches();
//异步确认
//handlingPublisherConfirmsAsynchronously();
}
/**
* 异步确认
*/
private static void handlingPublisherConfirmsAsynchronously() throws Exception{
try (Connection connection = createConnection()){
//1. 开启信道
Channel channel = connection.createChannel();
//2. 设置信道为confirm模式
channel.confirmSelect();
//3. 声明队列
channel.queueDeclare(Constants.PUBLISHER_CONFIRMS_QUEUE3, true, false, false, null);
//4. 监听confirm
//集合中存储的是未确认的消息ID
long start = System.currentTimeMillis();
SortedSet<Long> confirmSeqNo = Collections.synchronizedSortedSet(new TreeSet<>());
channel.addConfirmListener(new ConfirmListener() {
@Override
public void handleAck(long deliveryTag, boolean multiple) throws IOException {
if (multiple){
confirmSeqNo.headSet(deliveryTag+1).clear();
}else {
confirmSeqNo.remove(deliveryTag);
}
}
@Override
public void handleNack(long deliveryTag, boolean multiple) throws IOException {
if (multiple){
confirmSeqNo.headSet(deliveryTag+1).clear();
}else {
confirmSeqNo.remove(deliveryTag);
}
//业务需要根据实际场景进行处理, 比如重发, 此处代码省略
}
});
//5. 发送消息
for (int i = 0; i < MESSAGE_COUNT; i++) {
String msg = "hello publisher confirms"+i;
long seqNo = channel.getNextPublishSeqNo();
channel.basicPublish("",Constants.PUBLISHER_CONFIRMS_QUEUE3, null, msg.getBytes());
confirmSeqNo.add(seqNo);
}
while (!confirmSeqNo.isEmpty()){
Thread.sleep(10);
}
long end = System.currentTimeMillis();
System.out.printf("异步确认策略, 消息条数: %d, 耗时: %d ms \n",MESSAGE_COUNT, end-start);
}
}
/**
* 批量确认
*/
private static void publishingMessagesInBatches() throws Exception{
try(Connection connection = createConnection()) {
//1. 开启信道
Channel channel = connection.createChannel();
//2. 设置信道为confirm模式
channel.confirmSelect();
//3. 声明队列
channel.queueDeclare(Constants.PUBLISHER_CONFIRMS_QUEUE2, true, false, false, null);
//4. 发送消息, 并进行确认
long start = System.currentTimeMillis();
int batchSize = 100;
int outstandingMessageCount = 0;
for (int i = 0; i < MESSAGE_COUNT; i++) {
String msg = "hello publisher confirms"+i;
channel.basicPublish("",Constants.PUBLISHER_CONFIRMS_QUEUE2, null, msg.getBytes());
outstandingMessageCount++;
if (outstandingMessageCount==batchSize){
channel.waitForConfirmsOrDie(5000);
outstandingMessageCount = 0;
}
}
if (outstandingMessageCount>0){
channel.waitForConfirmsOrDie(5000);
}
long end = System.currentTimeMillis();
System.out.printf("批量确认策略, 消息条数: %d, 耗时: %d ms \n",MESSAGE_COUNT, end-start);
}
}
/**
* 单独确认
*/
private static void publishingMessagesIndividually() throws Exception{
try(Connection connection = createConnection()) {
//1. 开启信道
Channel channel = connection.createChannel();
//2. 设置信道为confirm模式
channel.confirmSelect();
//3. 声明队列
channel.queueDeclare(Constants.PUBLISHER_CONFIRMS_QUEUE1, true, false, false, null);
//4. 发送消息, 并等待确认
long start = System.currentTimeMillis();
for (int i = 0; i < MESSAGE_COUNT; i++) {
String msg = "hello publisher confirms"+i;
channel.basicPublish("",Constants.PUBLISHER_CONFIRMS_QUEUE1, null, msg.getBytes());
//等待确认
channel.waitForConfirmsOrDie(5000);
}
long end = System.currentTimeMillis();
System.out.printf("单独确认策略, 消息条数: %d, 耗时: %d ms \n",MESSAGE_COUNT, end-start);
}
}
}