1. 如何实现消费者和生产者
1.1. 引入依赖包
<!-- https://mvnrepository.com/artifact/com.rabbitmq/amqp-client -->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.20.0</version>
</dependency>
1.2. 创建生产者
package com.lz;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 生产者
*/
public class Producer {
public static void main(String[] args) throws Exception {
String exchangeName = "lz_exchange";
String queueName = "lz_queue";
// 创建一个连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("127.0.0.1");
factory.setPort(5672);
factory.setUsername("guest");
factory.setPassword("guest");
// 创建连接
Connection connection = factory.newConnection();
// 创建通道
Channel channel = connection.createChannel();
/**
* 创建交换机
* 1.交换机名称
* 2.交换机类型,direct,topic,fanout和headers
* 3.指定交换机是否需要持久化,如果设置为true,那么交换机的元数据要持久化
* 4.指定交换机在没有队列绑定时,是否需要删除,设置false表示不删除
* 5.Map<String,Object>类型,用来指定我们交换机其他的一些结构化参数
*/
channel.exchangeDeclare(exchangeName, BuiltinExchangeType.DIRECT, true, false, null);
/**
* 创建队列
* 1.队列的名称
* 2.是否持久化,如果设置为true,那么队列名称等这些元数据会持久化,而不是队列中消息数据的持久化
* 3.是否排外,如果设置为true,那么只有当前连接的消费者才能消费队列中的消息,其他连接的消费者无法消费队列中的消息
* 4.是否自动删除,如果设置为true,那么当队列中没有绑定的消费者时,队列会自动删除
* 5.Map<String,Object>类型,用来指定我们队列的其他一些结构化参数,如死信队列等
*/
channel.queueDeclare(queueName, true, false, false, null);
/**
* 绑定队列和交换机
* 1.队列名称
* 2.交换机名称
* 3.路由键,在直连模式下,可以为我们的队列名称
*/
channel.queueBind(queueName, exchangeName, queueName);
// 发送消息
String message = "hello rabbitmq";
/**
* 发送消息
* 1.发送到哪个交换机
* 2.队列名称
* 3.其他参数信息
* 4.消息主体
*/
channel.basicPublish(exchangeName, queueName, null, message.getBytes());
channel.close();
connection.close();
}
}
1.3. 创建消费者
package com.lz;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.TreeMap;
import java.util.concurrent.TimeoutException;
public class Consumer {
public static void main(String[] args) throws Exception {
String exchangeName = "lz_exchange";
String queueName = "lz_queue";
// 创建一个连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("127.0.0.1");
factory.setPort(5672);
factory.setUsername("guest");
factory.setPassword("guest");
// 创建连接
Connection connection = factory.newConnection();
// 创建通道
Channel channel = connection.createChannel();
// 接受消息的回调
DeliverCallback deliverCallback = (consumerTage, message) -> {
System.out.println("接收到的消息:" + new String(message.getBody()));
};
// 取消消息的回调
CancelCallback cancelCallback = (consumerTag) -> {
System.out.println("消息消费被中断");
};
/**
* 消费消息
* 1.消费哪个队列
* 2.消费成功之后是否需要自动应答,true 表示自动应答,false 表示手动应答
* 3.接受消息的回调
* 4.取消消息的回调
*/
channel.basicConsume(queueName, true, deliverCallback,cancelCallback);
}
}
2. RabbitMQ交换机类型
在RabbitMQ中,生产者的消息都是通过交换器来接收,然后再由交换器分发到不同的队列中去,在分发的过程中交换器类型会影响分发的逻辑。
在RabbitMQ中交换机类型主要由四种:
直连交换机:Direct exchange
扇形交换机:Fanout exchange
主题交换机:Topic exchange
首部交换机:Headers exchange
2.1. Direct(直连交换机)
直连交换机就是,路由键与队列名完全匹配的交换机。通过RoutingKey路由键将交换机和队列进行绑定,消息被发送到exchange时,需要根据消息的RoutingKey,来进行匹配,只将消息发送到完全匹配到此RoutingKey的列。
比如:如果一个队列绑定到交换机要求路由键为“lz_key”,则只转发RoutingKey标记为“lz_key”的消息,不会转发"key1",也不会转发“key.1”等等。它是完全匹配、单播的模式。
同一个key可以绑定多个queue队列,当匹配到key1时,queue1和queue2都会收到消息
public class ProducerDirect {
public static void main(String[] args) throws Exception {
String exchangeName = "lz_exchange";
String queueName1 = "lz_queue_direct_1";
String queueName2 = "lz_queue_direct_2";
String queueName3 = "lz_queue_direct_3";
String queueName4 = "lz_queue_direct_4";
String routingKey1 = "key1";
String routingKey2 = "key2";
String routingKey3 = "key3";
String routingKey4 = "key4";
Channel channel = RabbitMQConnector.createConnectionAndChannel();
/**
* 创建交换机
*/
channel.exchangeDeclare(exchangeName, BuiltinExchangeType.DIRECT, true, false, null);
/**
* 创建队列
*/
channel.queueDeclare(queueName1, true, false, false, null);
channel.queueDeclare(queueName2, true, false, false, null);
channel.queueDeclare(queueName3, true, false, false, null);
channel.queueDeclare(queueName4, true, false, false, null);
/**
* 绑定队列和交换机
* 1.队列名称
* 2.交换机名称
* 3.路由键,在直连模式下,可以为我们的队列名称
*/
channel.queueBind(queueName1, exchangeName, routingKey1);
channel.queueBind(queueName2, exchangeName, routingKey1);
channel.queueBind(queueName3, exchangeName, routingKey3);
channel.queueBind(queueName4, exchangeName, routingKey4);
/**
* 发送消息
* 1.发送到哪个交换机
* 2.路由键 routingKey
* 3.其他参数信息
* 4.消息主体
*/
channel.basicPublish(exchangeName, routingKey1, null, "key1 message".getBytes());
channel.basicPublish(exchangeName, routingKey2, null, "key2 message".getBytes());
channel.basicPublish(exchangeName, routingKey3, null, "key3 message".getBytes());
channel.basicPublish(exchangeName, routingKey4, null, "key4 message".getBytes());
RabbitMQConnector.closeResources();
}
}
public class ConsumerDirect {
public static void main(String[] args) throws Exception {
String exchangeName = "lz_exchange";
String queueName1 = "lz_queue_direct_1";
String queueName2 = "lz_queue_direct_2";
String queueName3 = "lz_queue_direct_3";
String queueName4 = "lz_queue_direct_4";
Channel channel = RabbitMQConnector.createConnectionAndChannel();
// 接受消息的回调
DeliverCallback deliverCallback = (consumerTage, message) -> {
System.out.println(message.getEnvelope());
System.out.println("接收到的消息:" + new String(message.getBody()));
};
// 取消消息的回调
CancelCallback cancelCallback = (consumerTag) -> {
System.out.println("消息消费被中断");
};
/**
* 消费消息
* 1.消费哪个队列
* 2.消费成功之后是否需要自动应答,true 表示自动应答,false 表示手动应答
* 3.接受消息的回调
* 4.取消消息的回调
*/
channel.basicConsume(queueName1, true, deliverCallback,cancelCallback);
channel.basicConsume(queueName2, true, deliverCallback,cancelCallback);
channel.basicConsume(queueName3, true, deliverCallback,cancelCallback);
channel.basicConsume(queueName4, true, deliverCallback,cancelCallback);
}
}
queue1和queue2绑定的都是key1,所以接受的消息都是key1 message
2.2. Fanout(扇形交换机)
Fanout,扇出类型交换机,此种交换机,会将消息分发给所有绑定了此交换机的队列(就是广播消息),此时RoutingKey参数无效。fanout类型交换机下发送消息一条,无论RoutingKey是什么,queue1,queue2,queue3,queue4都可以收到消息。
public class ProducerFanout {
public static void main(String[] args) throws Exception {
String exchangeName = "lz_exchange_fanout";
String queueName1 = "lz_queue_fanout_1";
String queueName2 = "lz_queue_fanout_2";
String queueName3 = "lz_queue_fanout_3";
String queueName4 = "lz_queue_fanout_4";
String routingKey1 = "key1";
String routingKey2 = "key2";
String routingKey3 = "key3";
String routingKey4 = "key4";
Channel channel = RabbitMQConnector.createConnectionAndChannel();
/**
* 创建交换机
*/
channel.exchangeDeclare(exchangeName, BuiltinExchangeType.FANOUT, true, false, null);
/**
* 创建队列
*/
channel.queueDeclare(queueName1, true, false, false, null);
channel.queueDeclare(queueName2, true, false, false, null);
channel.queueDeclare(queueName3, true, false, false, null);
channel.queueDeclare(queueName4, true, false, false, null);
/**
* 绑定队列和交换机
* 1.队列名称
* 2.交换机名称
* 3.路由键,在直连模式下,可以为我们的队列名称
*/
channel.queueBind(queueName1, exchangeName, routingKey1);
channel.queueBind(queueName2, exchangeName, routingKey1);
channel.queueBind(queueName3, exchangeName, routingKey3);
channel.queueBind(queueName4, exchangeName, routingKey4);
/**
* 发送消息
* 1.发送到哪个交换机
* 2.路由键 routingKey
* 3.其他参数信息
* 4.消息主体
*/
channel.basicPublish(exchangeName, routingKey1, null, "fanout message".getBytes());
System.out.println("发送消息成功");
RabbitMQConnector.closeResources();
}
}
public class ConsumerFanout {
public static void main(String[] args) throws Exception {
String exchangeName = "lz_exchange_fanout";
String queueName1 = "lz_queue_fanout_1";
String queueName2 = "lz_queue_fanout_2";
String queueName3 = "lz_queue_fanout_3";
String queueName4 = "lz_queue_fanout_4";
Channel channel = RabbitMQConnector.createConnectionAndChannel();
// 接受消息的回调
DeliverCallback deliverCallback = (consumerTage, message) -> {
System.out.println("接收到的消息:" + new String(message.getBody()));
};
// 取消消息的回调
CancelCallback cancelCallback = (consumerTag) -> {
System.out.println("消息消费被中断");
};
/**
* 消费消息
* 1.消费哪个队列
* 2.消费成功之后是否需要自动应答,true 表示自动应答,false 表示手动应答
* 3.接受消息的回调
* 4.取消消息的回调
*/
channel.basicConsume(queueName1, true, deliverCallback,cancelCallback);
channel.basicConsume(queueName2, true, deliverCallback,cancelCallback);
channel.basicConsume(queueName3, true, deliverCallback,cancelCallback);
channel.basicConsume(queueName4, true, deliverCallback,cancelCallback);
}
}
只给交换机发送消息,与该交换机绑定的队列都会收到消息
2.3. Topic(主题交换机)
Direct能做的 Topic也能做
Topic,主题类型交换机,此种交换机与Direct类似,也是需要通过RoutingKey
路由键进行匹配分发,区别在于Topic可以进行模糊匹配,Direct是完全匹配。
- Topic中,将routingkey通过"."来分为多个部分
- "*":代表一个部分
- "#":代表0个或多个部分(如果绑定的路由键为“#”时,则接受所有消息,因为路由键所有都匹配)
例如,RoutingKey
为 user.*
的消息可以匹配 user.create
或 user.update
,而 user.#
可以匹配 user.create.profile
或 user.update
.
当路由键:key1.key2,只有2和4匹配上
当路由键:key1.key2.key3,1、3、4都匹配上
public class ProducerTopic {
public static void main(String[] args) throws Exception {
String exchangeName = "lz_exchange_topic";
String queueName1 = "lz_queue_topic_1";
String queueName2 = "lz_queue_topic_2";
String queueName3 = "lz_queue_topic_3";
String queueName4 = "lz_queue_topic_4";
String routingKey1 = "key1.key2.*";
String routingKey2 = "key1.*";
String routingKey3 = "*.key2.*";
String routingKey4 = "#";
Channel channel = RabbitMQConnector.createConnectionAndChannel();
/**
* 创建交换机
*/
channel.exchangeDeclare(exchangeName, BuiltinExchangeType.TOPIC, true, false, null);
/**
* 创建队列
*/
channel.queueDeclare(queueName1, true, false, false, null);
channel.queueDeclare(queueName2, true, false, false, null);
channel.queueDeclare(queueName3, true, false, false, null);
channel.queueDeclare(queueName4, true, false, false, null);
/**
* 绑定队列和交换机
* 1.队列名称
* 2.交换机名称
* 3.路由键,在直连模式下,可以为我们的队列名称
*/
channel.queueBind(queueName1, exchangeName, routingKey1);
channel.queueBind(queueName2, exchangeName, routingKey2);
channel.queueBind(queueName3, exchangeName, routingKey3);
channel.queueBind(queueName4, exchangeName, routingKey4);
/**
* 发送消息
* 1.发送到哪个交换机
* 2.路由键 routingKey
* 3.其他参数信息
* 4.消息主体
*/
channel.basicPublish(exchangeName, "key1.key2.key3", null, "topic message".getBytes());
System.out.println("发送消息成功");
RabbitMQConnector.closeResources();
}
}
public class ConsumerTopic {
public static void main(String[] args) throws Exception {
String exchangeName = "lz_exchange_topic";
String queueName1 = "lz_queue_topic_1";
String queueName2 = "lz_queue_topic_2";
String queueName3 = "lz_queue_topic_3";
String queueName4 = "lz_queue_topic_4";
Channel channel = RabbitMQConnector.createConnectionAndChannel();
// 接受消息的回调
DeliverCallback deliverCallback = (consumerTage, message) -> {
System.out.println("接收到的消息:" + new String(message.getBody()));
};
// 取消消息的回调
CancelCallback cancelCallback = (consumerTag) -> {
System.out.println("消息消费被中断");
};
/**
* 消费消息
* 1.消费哪个队列
* 2.消费成功之后是否需要自动应答,true 表示自动应答,false 表示手动应答
* 3.接受消息的回调
* 4.取消消息的回调
*/
channel.basicConsume(queueName1, true, deliverCallback,cancelCallback);
channel.basicConsume(queueName2, true, deliverCallback,cancelCallback);
channel.basicConsume(queueName3, true, deliverCallback,cancelCallback);
channel.basicConsume(queueName4, true, deliverCallback,cancelCallback);
}
}
2.4. Headers(首部交换机)
Headers 交换机不使用 RoutingKey
,而是通过消息头中的属性来路由消息。队列绑定时指定的头部属性必须与消息头部属性完全匹配,消息才会被路由到该队列。
消费方指定的headers中必须包含一个“x-match"的键。
键"x-match"的值有2个
- x-match=all:表示所有的键值对都匹配才能接受到消息
- x-match=any:表示只要有键值对匹配就能接受到消息
当值为{ "name":"lizheng","sex":"男"}时,全部匹配和任一匹配的两个队列都接收到消息
当值为{ "age":24,"sex":"女"}时,只有任一匹配队列符合,所以queue2接收到消息
public class ProducerHeaders {
public static void main(String[] args) throws Exception {
String exchangeName = "lz_exchange_headers";
String queueName1 = "lz_queue_headers_1";
String queueName2 = "lz_queue_headers_2";
Channel channel = RabbitMQConnector.createConnectionAndChannel();
/**
* 创建交换机
*/
channel.exchangeDeclare(exchangeName, BuiltinExchangeType.HEADERS, true, false, null);
/**
* 创建队列
*/
channel.queueDeclare(queueName1, true, false, false, null);
channel.queueDeclare(queueName2, true, false, false, null);
// 创建一个HashMap实例,用于存储绑定队列时的额外属性
Map<String, Object> allMap = new HashMap<>();
// 设置匹配规则,"all"表示所有条件必须都满足
allMap.put("x-match", "all");
allMap.put("name","lizheng");
allMap.put("sex","男");
// 绑定队列到交换机,使用空的路由键和之前设置的额外属性
channel.queueBind(queueName1, exchangeName, "", allMap);
Map<String, Object> anyMap = new HashMap<>();
anyMap.put("x-match", "any");
anyMap.put("name","lizheng");
anyMap.put("age","24");
channel.queueBind(queueName2, exchangeName, "", anyMap);
// 创建一个HashMap实例,用于存储消息的属性
Map<String, Object> hashMap1 = new HashMap<>();
hashMap1.put("name","lizheng");
hashMap1.put("sex","男");
// 使用hashMap1中的数据构建AMQP.BasicProperties对象
AMQP.BasicProperties.Builder Properties1 = new AMQP.BasicProperties().builder().headers(hashMap1);
Map<String, Object> hashMap2 = new HashMap<>();
hashMap2.put("sex","女");
hashMap2.put("age","24");
AMQP.BasicProperties.Builder Properties2 = new AMQP.BasicProperties().builder().headers(hashMap2);
/**
* 发送消息
* 1.发送到哪个交换机
* 2.路由键 routingKey
* 3.其他参数信息
* 4.消息主体
*/
channel.basicPublish(exchangeName, "", Properties1.build(), "all message".getBytes());
channel.basicPublish(exchangeName, "", Properties2.build(), "any message".getBytes());
System.out.println("发送消息成功");
RabbitMQConnector.closeResources();
}
}
public class ConsumerHeaders {
public static void main(String[] args) throws Exception {
String queueName1 = "lz_queue_headers_1";
String queueName2 = "lz_queue_headers_2";
Channel channel = RabbitMQConnector.createConnectionAndChannel();
// 接受消息的回调
DeliverCallback deliverCallback = (consumerTage, message) -> {
System.out.println("接收到的消息:" + new String(message.getBody()));
};
// 取消消息的回调
CancelCallback cancelCallback = (consumerTag) -> {
System.out.println("消息消费被中断");
};
/**
* 消费消息
* 1.消费哪个队列
* 2.消费成功之后是否需要自动应答,true 表示自动应答,false 表示手动应答
* 3.接受消息的回调
* 4.取消消息的回调
*/
channel.basicConsume(queueName1, true, deliverCallback,cancelCallback);
channel.basicConsume(queueName2, true, deliverCallback,cancelCallback);
}
}