文章目录
- [1. Publish / Subscribe(发布 / 订阅)](#1. Publish / Subscribe(发布 / 订阅))
- 2、引入依赖
- 3、生产者代码编写
-
- [3.1 创建交换机](#3.1 创建交换机)
- [3.2 声明两个队列](#3.2 声明两个队列)
- [3.3 绑定队列和交换机](#3.3 绑定队列和交换机)
- [3.4 发送消息](#3.4 发送消息)
- [3.5 完整代码](#3.5 完整代码)
- 4、消费者代码编写
-
- [4.1 消费者一](#4.1 消费者一)
- [4.1 消费者二](#4.1 消费者二)
- [5. 观察结果](#5. 观察结果)
1. Publish / Subscribe(发布 / 订阅)
在发布 / 订阅模型中,多了一个 Exchange 角色。
Exchange 常见有三种类型,分别代表不同的路由规则:
- Fanout:广播,将消息交给所有绑定到交换机的队列(Publish / Subscribe 模式)
- Direct:定向,把消息交给符合指定 routing key 的队列(Routing 模式)
- Topic:通配符,把消息交给符合 routing pattern(路由模式)的队列(Topics 模式)
它们分别对应不同的工作模式,我们来看看 Publish / Subscribe 模式。

步骤:
- 1、引入依赖
- 2、编写生产者代码
- 3、编写消费者代码
2、引入依赖
先引入 rabbitmq 的依赖
xml
<!-- Source: https://mvnrepository.com/artifact/com.rabbitmq/amqp-client -->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.20.0</version>
<scope>compile</scope>
</dependency>
3、生产者代码编写
和前面两个的区别是:需要创建交换机,并且绑定队列和交换机。
3.1 创建交换机
需要用到下面的函数来创建
java
exchangeDeclare(String exchange, BuiltinExchangeType type, boolean durable, boolean autoDelete, boolean internal, Map<String, Object> arguments)
参数:
- exchange:交换机名称
- type:交换机类型
- DIRECT("direct"):定向、直连、routing
- FANOUT("fanout"):扇形(广播),每个队列都能收到消息
- TOPIC("topic"):通配符
- HEADERS("headers"):参数匹配(工作用的较少)
- durable:是否持久化
- true - 持久化,false - 非持久化
- 持久化可以将交换器存盘,在服务器重启的时候不会丢失相关信息
- autoDelete:自动删除
- 自动删除的前提是:至少有一个队列或者交换器与这个交换器绑定,之后所有与这个交换器绑定的队列或者交换器都与此解绑。
- 而不是这种理解:当与此交换器连接的客户端都断开时,RabbitMQ 会自动删除本交换器。
- internal:内部使用,一般为 false
- 如果设置为 true,表示内部使用
- 客户端程序无法直接发送消息到这个交换器中,只能通过交换器路由到交换器这种方式。
- arguments:参数
先在 Constants 函数中定义一个交换机和两个队列:
java
// 发布订阅模式
public static final String FANOUT_EXCHANGE = "fanout.exchange"; // 声明交换机
public static final String FANOUT_QUEUE1 = "fanout.queue1"; // 声明队列
public static final String FANOUT_QUEUE2 = "fanout.queue2"; // 声明队列
接着在生产者中编写代码
java
// 3. 声明交换机(使用内置的交换机即可)
channel.exchangeDeclare(Constants.FANOUT_EXCHANGE, BuiltinExchangeType.FANOUT, true);
3.2 声明两个队列
后面验证是否两个队列都能收到消息。
java
// 4. 声明队列
// 如果没有一个这样的队列,会自动创建,如果有,则不创建
channel.queueDeclare(Constants.FANOUT_QUEUE1, true, false, false, null);
channel.queueDeclare(Constants.FANOUT_QUEUE2, true, false, false, null);
3.3 绑定队列和交换机
需要用到的函数如下:
java
queueBind(String queue, String exchange, String routingKey)
参数如下:
- queue:队列名称
- exchange:交换机名称
- routingKey:路由key,路由规则
- 如果交换机类型为 fanout,且 routingkey 设置为
"",表示每个消费者都可以收到全部信息。
- 如果交换机类型为 fanout,且 routingkey 设置为
代码如下所示:
java
// 5. 绑定队列和交换机
channel.queueBind(Constants.FANOUT_QUEUE1, Constants.FANOUT_EXCHANGE, "");
channel.queueBind(Constants.FANOUT_QUEUE2, Constants.FANOUT_EXCHANGE, "");
3.4 发送消息
需要用到下面的函数:
java
basicPublish(String exchange, String routingKey, AMQP.BasicProperties props, byte[] body)
参数说明:
- Exchange:交换机名称
- routingKey:如果交换机类型为 fanout,且 routingkey 设置为
"",表示每个消费者都可以收到全部信息。
代码如下所示:
java
// 6. 发送消息
String msg = "Hello fanout... ";
channel.basicPublish(Constants.FANOUT_EXCHANGE, "",null, msg.getBytes());
System.out.println("消息发送成功");
3.5 完整代码
如下所示:
java
package fanout;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import constant.Constants;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
// 1. 建立连接
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(Constants.HOST); // MQ所在的服务器地址
factory.setPort(Constants.PORT); // 端口号
factory.setUsername(Constants.USERNAME); // 账号
factory.setPassword(Constants.PASSWORD); // 密码
factory.setVirtualHost(Constants.VIRTUAL_HOST); // 虚拟主机
Connection connection = factory.newConnection();
// 2. 开启 channel 通道
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());
System.out.println("消息发送成功");
// 7. 资源释放
channel.close();
connection.close();
}
}
4、消费者代码编写
交换机和队列的绑定关系及声明已经在生产方写完,所以消费者不需要再写了。这里只需要去掉声明队列的代码就可以了。
1、创建Channel
2、接收消息,并处理
4.1 消费者一
代码如下所示:
java
package fanout;
import com.rabbitmq.client.*;
import constant.Constants;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Consumer1 {
public static void main(String[] args) throws IOException, TimeoutException {
// 1. 建立连接
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(Constants.HOST); // MQ所在的服务器地址
factory.setPort(Constants.PORT); // 端口号
factory.setUsername(Constants.USERNAME); // 账号
factory.setPassword(Constants.PASSWORD); // 密码
factory.setVirtualHost(Constants.VIRTUAL_HOST); // 虚拟主机
Connection connection = factory.newConnection();
// 2. 开启 channel 通道
Channel channel = connection.createChannel();
// 3. 声明队列
channel.queueDeclare(Constants.FANOUT_QUEUE1, 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.FANOUT_QUEUE1, true, consumer);
// 5. 不需要释放资源
}
}
4.1 消费者二
代码如下所示:
java
package fanout;
import com.rabbitmq.client.*;
import constant.Constants;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Consumer2 {
public static void main(String[] args) throws IOException, TimeoutException {
// 1. 建立连接
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(Constants.HOST); // MQ所在的服务器地址
factory.setPort(Constants.PORT); // 端口号
factory.setUsername(Constants.USERNAME); // 账号
factory.setPassword(Constants.PASSWORD); // 密码
factory.setVirtualHost(Constants.VIRTUAL_HOST); // 虚拟主机
Connection connection = factory.newConnection();
// 2. 开启 channel 通道
Channel channel = connection.createChannel();
// 3. 声明队列
channel.queueDeclare(Constants.FANOUT_QUEUE2, 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.FANOUT_QUEUE2, true, consumer);
// 5. 不需要释放资源
}
}
5. 观察结果
先运行生产者,可以看到两个队列分别有了一条消息。

然后在 Exchange 中找到咱们自己创建的交换机 fanout.exchange

可以看到 fanout.exchange 中多了队列绑定关系。

然后运行 Consumer 1 代码:

然后运行 Consumer 2 代码:

可以看到,生产者只发送了 1 次消息,而两个消费者都收到了该消息。