前言
本章主要讲RabbitMQ的简单模式以及三种交换机的使用方法,核心概念请转到:RabbitMQ核心概念
编程环境:
JDK:1.8 Maven:3.6.0 Amqp:3.6.5
简单模式 :
一个生产者对应一个消费者。
消息生产者
/**
*
* @ClassName Producter
* @Description 消息生产者
* @Date 2019年12月9日上午11:01:40
*/
public class Producter {
public static void main(String[] args) throws IOException, TimeoutException {
//创建链接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置IP,端口号,虚拟主机
factory.setHost("127.0.0.1");
factory.setPort(5672);
factory.setVirtualHost("testhost");
factory.setUsername("admin");
factory.setPassword("admin");
//通过链接工厂创建链接
Connection connection = factory.newConnection();
//通过链接创建信道
Channel channel = connection.createChannel();
//通过信道发布消息
String msg = "hello rabbitmq";
/**
* 参数1:交换机名称--当没有指定时,会将消息投递到默认交换机(AMQPDefault)
* 参数2:routingkey 路由键 --交换机会通过routingKey将消息发送到同名的消息队列里
* 参数3:消息的额外属性
* 参数4:消息
*/
for (int i = 0; i < 5; i++) {
channel.basicPublish("", "test001", null, msg.getBytes());
}
//释放资源
channel.close();
connection.close();
}
}
消息消费者
/**
*
* @ClassName Consumer
* @Description 消费者
* @Date 2019年12月9日上午11:10:46
*/
public class Consumer {
public static void main(String[] args) throws IOException, TimeoutException, ShutdownSignalException, ConsumerCancelledException, InterruptedException {
//创建链接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置IP,端口号,虚拟主机
factory.setHost("127.0.0.1");
factory.setPort(5672);
//我在控制台新建了账号和虚拟主机,也可以使用默认的guest账号
factory.setVirtualHost("testhost");
factory.setUsername("admin");
factory.setPassword("admin");
//通过链接工厂创建链接
Connection connection = factory.newConnection();
//通过链接创建信道
Channel channel = connection.createChannel();
//创建消息队列
/**
* 参数1:消息队列名称
* 参数2:消息是否持久化--当服务重启后是否可以拿到没有消费的消息
* 参数3:消息队列是否被channel独占--false表示此消息队列可以被其他channel占有
* 参数4:消息队列是否自动删除--消息队列没有绑定交换机时是否自动删除
* 参数5:消息队列扩展参数
*/
String queueName = "test001";
channel.queueDeclare(queueName,true,false,false,null);
//创建消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
//通过channel将消息队列和消费者绑定
/**
* 参数2:是否自动签收--当消息队列中的消息被消费者处理后是否自动通知服务器消已被处理
*/
channel.basicConsume(queueName, true,consumer);
//获取消息
while(true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println("消费端获取到消息:"+msg);
}
}
}
POM依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>3.6.5</version>
</dependency>
</dependencies>
运行测试
运行消息生产者后查看控制台
运行消息消费者后查看控制台
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KYJ0xbax-1575887037576)(http://q0s3dp691.bkt.clouddn.com/blog/imageCB0B04B3-C44A-4a08-B345-731A91B663F9.png)]
消费者接收到的消息
Direct模式
消息生产者
/**
*
* @ClassName ProducterForDirect
* @Description 消息生产者-Direct
* @Date 2019年12月9日上午11:01:40
*/
public class ProducterForDirect {
public static void main(String[] args) throws IOException, TimeoutException {
//创建链接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置IP,端口号,虚拟主机
factory.setHost("127.0.0.1");
factory.setPort(5672);
factory.setVirtualHost("testhost");
factory.setUsername("admin");
factory.setPassword("admin");
//通过链接工厂创建链接
Connection connection = factory.newConnection();
//通过链接创建信道
Channel channel = connection.createChannel();
//通过信道发布消息
/**
* 参数1:交换机名称--当没有指定时,会将消息投递到默认交换机(AMQPDefault)
* 参数2:routingkey 路由键 --交换机会通过routingKey将消息发送到同名的消息队列里
* 参数3:消息的额外属性
* 参数4:消息
*/
//消息
String msg = "hello rabbitmq for direct exchange";
//交换机名称
String exchangeName = "test-direct";
//routingKey 路由键
String routingKey = "test.direct";
for (int i = 0; i < 5; i++) {
channel.basicPublish(exchangeName, routingKey, null, msg.getBytes());
}
//释放资源
channel.close();
connection.close();
}
}
消息消费者
/**
*
* @ClassName ConsumerForDirect
* @Description 消费者-direct
* @Date 2019年12月9日上午11:10:46
*/
public class ConsumerForDirect {
public static void main(String[] args) throws IOException, TimeoutException, ShutdownSignalException, ConsumerCancelledException, InterruptedException {
//创建链接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置IP,端口号,虚拟主机
factory.setHost("127.0.0.1");
factory.setPort(5672);
factory.setVirtualHost("testhost");
factory.setUsername("admin");
factory.setPassword("admin");
//通过链接工厂创建链接
Connection connection = factory.newConnection();
//通过链接创建信道
Channel channel = connection.createChannel();
//创建消息队列
/**
* 参数1:消息队列名称
* 参数2:消息是否持久化--当服务重启后是否可以拿到没有消费的消息
* 参数3:消息队列是否被channel独占--false表示此消息队列可以被其他channel占有
* 参数4:消息队列是否自动删除--消息队列没有绑定交换机时是否自动删除
* 参数5:消息队列扩展参数
*/
String queueName = "test-queue";
channel.queueDeclare(queueName,true,false,false,null);
//声明交换机
/**
* 声明交换机参数详解:
* exchange:交换机名称
* type:交换机类型-direct,topic,fanout,headers
* durable:是否需要持久化,true为持久化
* autoDelete:当最后一个绑定到交换机上的队列删除后,自动删除该交换机
* internal:当前交换机是否用于rabbitMQ内部使用,默认为false
* arguments:可扩展参数,用户自定义交换机时用到的参数
*/
String exchangeName = "test-direct";
String exchangeType = "direct";
channel.exchangeDeclare(exchangeName, exchangeType, true, false, false, null);
//将交换机和消息队列进行绑定
//routingKey---必须要和消息生产者中的routingKey一致
String routingKey = "test.direct";
channel.queueBind(queueName, exchangeName, routingKey);
//创建消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
//通过channel将消息队列和消费者绑定
/**
* 参数2:是否自动签收--当消息队列中的消息被消费者处理后是否自动通知服务器消已被处理
*/
channel.basicConsume(queueName, true,consumer);
//获取消息
while(true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println("消费端获取到消息:"+msg);
}
}
}
运行测试
运行消息生产者和消费者后查看控制台
Topic模式
消息生产者
/**
*
* @ClassName ProducterForTopic
* @Description 消息生产者-Topic
* @Date 2019年12月9日上午11:01:40
*/
public class ProducterForTopic {
public static void main(String[] args) throws IOException, TimeoutException {
//创建链接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置IP,端口号,虚拟主机
factory.setHost("127.0.0.1");
factory.setPort(5672);
factory.setVirtualHost("testhost");
factory.setUsername("admin");
factory.setPassword("admin");
//通过链接工厂创建链接
Connection connection = factory.newConnection();
//通过链接创建信道
Channel channel = connection.createChannel();
//通过信道发布消息
/**
* 参数1:交换机名称--当没有指定时,会将消息投递到默认交换机(AMQPDefault)
* 参数2:routingkey 路由键 --交换机会通过routingKey将消息发送到同名的消息队列里
* 参数3:消息的额外属性
* 参数4:消息
*/
//消息
String msg = "hello rabbitmq for topic exchange";
//交换机名称
String exchangeName = "test-topic";
//routingKey 路由键
String routingKey = "test.topic";
for (int i = 0; i < 5; i++) {
channel.basicPublish(exchangeName, routingKey, null, msg.getBytes());
}
//释放资源
channel.close();
connection.close();
}
}
消息消费者
/**
*
* @ClassName ConsumerForTopic
* @Description 消费者-Topic
* @Date 2019年12月9日上午11:10:46
*/
public class ConsumerForTopic {
public static void main(String[] args) throws IOException, TimeoutException, ShutdownSignalException, ConsumerCancelledException, InterruptedException {
//创建链接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置IP,端口号,虚拟主机
factory.setHost("127.0.0.1");
factory.setPort(5672);
factory.setVirtualHost("testhost");
factory.setUsername("admin");
factory.setPassword("admin");
//通过链接工厂创建链接
Connection connection = factory.newConnection();
//通过链接创建信道
Channel channel = connection.createChannel();
//创建消息队列
/**
* 参数1:消息队列名称
* 参数2:消息是否持久化--当服务重启后是否可以拿到没有消费的消息
* 参数3:消息队列是否被channel独占--false表示此消息队列可以被其他channel占有
* 参数4:消息队列是否自动删除--消息队列没有绑定交换机时是否自动删除
* 参数5:消息队列扩展参数
*/
String queueName = "test-queue";
channel.queueDeclare(queueName,true,false,false,null);
//声明交换机
/**
* 声明交换机参数详解:
* exchange:交换机名称
* type:交换机类型-direct,topic,fanout,headers
* durable:是否需要持久化,true为持久化
* autoDelete:当最后一个绑定到交换机上的队列删除后,自动删除该交换机
* internal:当前交换机是否用于rabbitMQ内部使用,默认为false
* arguments:可扩展参数,用户自定义交换机时用到的参数
*/
String exchangeName = "test-topic";
String exchangeType = "topic";
channel.exchangeDeclare(exchangeName, exchangeType, true, false, false, null);
//将交换机和消息队列进行绑定
//routingKey---必须要和消息生产者中的routingKey一致
String routingKey = "test.#";
channel.queueBind(queueName, exchangeName, routingKey);
//创建消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
//通过channel将消息队列和消费者绑定
/**
* 参数2:是否自动签收--当消息队列中的消息被消费者处理后是否自动通知服务器消已被处理
*/
channel.basicConsume(queueName, true,consumer);
//获取消息
while(true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println("消费端获取到消息:"+msg);
}
}
}
运行测试
Topic模式只需要更改交换机的类型即可。请自行测试
Fanout模式
消息生产者
/**
*
* @ClassName ProducterForTopic
* @Description 消息生产者-Topic
* @Date 2019年12月9日上午11:01:40
*/
public class ProducterForFanout {
public static void main(String[] args) throws IOException, TimeoutException {
//创建链接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置IP,端口号,虚拟主机
factory.setHost("127.0.0.1");
factory.setPort(5672);
factory.setVirtualHost("testhost");
factory.setUsername("admin");
factory.setPassword("admin");
//通过链接工厂创建链接
Connection connection = factory.newConnection();
//通过链接创建信道
Channel channel = connection.createChannel();
//通过信道发布消息
/**
* 参数1:交换机名称--当没有指定时,会将消息投递到默认交换机(AMQPDefault)
* 参数2:routingkey 路由键 --交换机会通过routingKey将消息发送到同名的消息队列里
* 参数3:消息的额外属性
* 参数4:消息
*/
//消息
String msg = "hello rabbitmq for fanout exchange";
//交换机名称
String exchangeName = "test-fanout";
//routingKey 路由键
String routingKey = "";
for (int i = 0; i < 5; i++) {
channel.basicPublish(exchangeName, routingKey, null, msg.getBytes());
}
//释放资源
channel.close();
connection.close();
}
}
消息消费者
/**
*
* @ClassName ConsumerForTopic
* @Description 消费者-Topic
* @Date 2019年12月9日上午11:10:46
*/
public class ConsumerForFanout {
public static void main(String[] args) throws IOException, TimeoutException, ShutdownSignalException, ConsumerCancelledException, InterruptedException {
//创建链接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置IP,端口号,虚拟主机
factory.setHost("127.0.0.1");
factory.setPort(5672);
factory.setVirtualHost("testhost");
factory.setUsername("admin");
factory.setPassword("admin");
//通过链接工厂创建链接
Connection connection = factory.newConnection();
//通过链接创建信道
Channel channel = connection.createChannel();
//创建消息队列
/**
* 参数1:消息队列名称
* 参数2:消息是否持久化--当服务重启后是否可以拿到没有消费的消息
* 参数3:消息队列是否被channel独占--false表示此消息队列可以被其他channel占有
* 参数4:消息队列是否自动删除--消息队列没有绑定交换机时是否自动删除
* 参数5:消息队列扩展参数
*/
String queueName = "test-queue";
channel.queueDeclare(queueName,true,false,false,null);
//声明交换机
/**
* 声明交换机参数详解:
* exchange:交换机名称
* type:交换机类型-direct,topic,fanout,headers
* durable:是否需要持久化,true为持久化
* autoDelete:当最后一个绑定到交换机上的队列删除后,自动删除该交换机
* internal:当前交换机是否用于rabbitMQ内部使用,默认为false
* arguments:可扩展参数,用户自定义交换机时用到的参数
*/
String exchangeName = "test-fanout";
String exchangeType = "fanout";
channel.exchangeDeclare(exchangeName, exchangeType, true, false, false, null);
//将交换机和消息队列进行绑定
// 由于fanout交换机不会根据routingKey投递消息,所以routingKey是不起作用的。有没有都无所谓
String routingKey = "1111";
channel.queueBind(queueName, exchangeName, routingKey);
//创建消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
//通过channel将消息队列和消费者绑定
/**
* 参数2:是否自动签收--当消息队列中的消息被消费者处理后是否自动通知服务器消已被处理
*/
channel.basicConsume(queueName, true,consumer);
//获取消息
while(true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println("消费端获取到消息:"+msg);
}
}
}
运行测试
测试方法同前两种模式一样
总结:
Direct: 1:1 direct的routingKey是固定的,不能使用通配符,当routingKey和消息队列名相同时所匹配。
Topic : 1:N topic和direct不同点在于,它的routingKey是可以使用通配符的。也就是说topic交换机有可能将消息投递到多个消息队列中
Fanout:N:1 fanout交换机是没有routingKey的,它会将接收到的消息转发到所有与该交换机绑定的队列上。因为不需要验证routingKey,所以fanout交换机的转发速度是最快的。