本文已参与「新人创作礼」活动,一起开启掘金创作之路。
RabbitMQ 的结构
RabbitMQ 有几个个概念:
- RoutingKey 路由键,在发布订阅模型中根据 RoutingKey 可以找到具体队列。
- Publisher 生产者,生产消息发送给 Exchange(如果没有 Exchange 就直接发送发到 Queue)。
- Exchange 交换机,根据与 Queue 绑定的 BindingKey 将消息发送到 Queue(BindingKey 与 RoutingKey 概念相似)。
- Queue 队列,Exchange 将消息发送到 Queue 后,消息暂存在 Queue 内,等待 消费者消费。
- Consumer 消费者,监听具体队列,从队列中获取消息后消费消息。
总结:生产者面向 Exchange 和 Queue 编程。消费者面向Queue 编程。
RabbitMQ 的六种模式
RabbitMQ 有六种模式:
- 简单模式:无交换机,一个队列只能连接一个消费者。
- 工作模式:无交换机,一条队列能有连接多个消费者。
- 广播模式:交换机将接收到的消息路由到每一个跟其绑定的队列。
- 路由模式:交换机将接收的消息根据规则路由到指定的队列。
- 主题模式:交换机将接收到的消息,根据匹配路由到队列。路由键必须是多个单词的列表,使用 ‘.’ 分割。
- RPC 模式:调用远程方法需要等待结果时可用。
发布订阅模式包含了广播模式、路由模式和主题模式。都是多个生产者多个消费者模式,通过 RoutingKey 来进行路由。
SpringAMQP
SpringAMQP 是在 Spring 框架中整合了 AMQP 规则,可以进行队列配置操作。
声明队列和交换机,并将两者进行绑定。有两种方式,第一种 @Bean 进行类配置,第二种 @RabbitListener 进行注解配置。
类配置方式:
// 声明 FanoutExchange 交换机
@Bean
public FanoutExchange fanoutExchange() {
return new FanoutExchange("itcast.fanout");
}
// 声明队列
@Bean
public Queue fanoutQueue() {
return new Queue("fanout.queue");
}
// 绑定队列和交换机
@Bean
public Binding bindingQueue1(Queue fanoutQueue1, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(fanoutQueue).to(fanoutExchange);
}
注解方式:
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "direct.queue2"), // 监听队列
exchange = @Exchange(name = "itcast.direct", type = ExchangeTypes.DIRECT), // 交换器名称和交换器的类型
key = {"red", "yellow"} // 路由键
))
Fanout Exchange 广播模式
广播模式,声明交换机和队列,将交换器与队列绑定。将消息发送到所有与之绑定的队列中。
// 声明 FanoutExchange 交换机
@Bean
public FanoutExchange fanoutExchange() {
return new FanoutExchange("itcast.fanout");
}
// 声明第一个队列
@Bean
public Queue fanoutQueue1() {
return new Queue("fanout.queue1");
}
// 声明第二个队列
@Bean
public Queue fanoutQueue2() {
return new Queue("fanout.queue2");
}
// 绑定队列1和交换机
@Bean
public Binding bindingQueue1(Queue fanoutQueue1, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(fanoutQueue1).to(fanoutExchange);
}
// 绑定队列2和交换机
@Bean
public Binding bindingQueue2(Queue fanoutQueue2, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(fanoutQueue2).to(fanoutExchange);
}
// consumer 声明监听队列
@RabbitListener(queues = "fanout.queue1")
public void listenFanoutQueue1(String message) {
System.out.println("消费者接收到fanout.queue1消息:【" + message + "】");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 测试给 itcast.fanout 队列发送消息。
@Test
public void testSendFanoutExchange() {
// 交换机名称
String exchangeName = "itcast.fanout";
// 消息
String msg = "hello, everyone";
rabbitTemplate.convertAndSend(exchangeName, "", msg);
}
Direct Exchange 路由模式
路由模式,声明交换机和队列,将交换器与队列绑定。将消息发送到与之绑定的队列中,一般只绑定一个队列,实现精准匹配。Direct Exchange 也可以模拟 Fanout Exchange。Exchange 与 Queue 的 BindingKey 相同即可(RoutingKey 相同)。
// consumer
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "direct.queue1"), // 监听队列
exchange = @Exchange(name = "itcast.direct", type = ExchangeTypes.DIRECT), // 交换器
key = {"red", "blue"} // 路由键
))
public void listenDirectQueue1(String message) {
System.out.println("消费者接收到direct.queue1消息:【" + message + "】");
}
// publisher
@Test
public void testSendDirectExchange() {
// 交换机名称
String exchangeName = "itcast.direct";
// 消息
String msg = "hello, blue";
rabbitTemplate.convertAndSend(exchangeName, "blue", msg);
}
Topic Exchange 主题模式
通过 RoutingKey 进行主题通配符匹配,Exchange 与 Queue 的 BindingKey 若匹配上消息的 Routingkey,则可以接收。
#指代0或多个单词 *指代一个单词
// consumer
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "topic.queue1"), // 监听队列
exchange = @Exchange(name = "itcast.topic", type = ExchangeTypes.TOPIC), // 交换器
key = "china.#" // 路由键
))
public void listenTopicQueue1(String message) {
System.out.println("消费者接收到topic.queue1消息:【" + message + "】");
}
// publisher
@Test
public void testSendTopicExchange() {
// 交换机名称
String exchangeName = "itcast.topic";
// 消息
String msg = "不涉及他国领土问题!";
rabbitTemplate.convertAndSend(exchangeName, "china.news", msg);
}
消息转换器
使用 RabbitTemplate 发送消息时,消息体都是 Object 类型,默认采用的是 Serializable 序列化。使用 Serializable 序列化十分笨重,我们可以采用自定义的消息转换器。
// 导入依赖
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.11.2</version>
</dependency>
// Consumer 和 Publisher: 覆盖MessageConverter 。
@Bean
public MessageConverter messageConverter() {
return new Jackson2JsonMessageConverter();
}