分布式中间件:RabbitMQ基于DirectExchange的直连消息传输
引言
在分布式系统的开发中,消息队列是一种常用的组件,它能够帮助我们实现系统间的解耦、异步通信以及流量削峰等功能。RabbitMQ 作为一款功能强大的消息队列中间件,提供了多种类型的交换机,其中 Direct Exchange(直连交换机)是一种非常基础且实用的交换机类型。本文将详细介绍如何使用 RabbitMQ 的 Direct Exchange 进行直连消息传输,并结合 Spring Boot 给出具体的代码示例。
Direct Exchange 简介
Direct Exchange 是 RabbitMQ 中最简单的一种交换机类型。它根据消息的路由键(routing key)将消息路由到与之绑定的队列中。每个队列在绑定到 Direct Exchange 时会指定一个绑定键(binding key),当消息发送到 Direct Exchange 时,Exchange 会根据消息的路由键与绑定键进行匹配,如果匹配成功,则将消息发送到对应的队列中。
配置文件
首先,我们需要在配置文件中定义交换机、路由键和队列的名称。以下是 application.yml
文件的示例配置:
basic:
direct:
exchange:
name: testDirectExchange
routing:
key1: testDirectKey1
key2: testDirectKey2
queue:
name1: testDirectQueue1
name2: testDirectQueue2
RabbitMQ 配置类
接下来,我们需要创建一个 RabbitMQ 配置类,用于创建交换机、队列以及它们之间的绑定关系。
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
@Configuration
public class RabbitmqConfig {
private final Environment environment;
public RabbitmqConfig(Environment environment) {
this.environment = environment;
}
// 创建交换机
@Bean("directExchange")
public DirectExchange directExchange() {
return new DirectExchange(environment.getProperty("basic.direct.exchange.name"), true, false);
}
// 创建队列一
@Bean("directQueue1")
public Queue directQueue1() {
return new Queue(environment.getProperty("basic.direct.queue.name1"), true);
}
// 创建队列二
@Bean("directQueue2")
public Queue directQueue2() {
return new Queue(environment.getProperty("basic.direct.queue.name2"), true);
}
// 创建绑定一
@Bean
public Binding directBinding1() {
return BindingBuilder.bind(directQueue1())
.to(directExchange())
.with(environment.getProperty("basic.direct.routing.key1"));
}
// 创建绑定二
@Bean
public Binding directBinding2() {
return BindingBuilder.bind(directQueue2())
.to(directExchange())
.with(environment.getProperty("basic.direct.routing.key2"));
}
}
在上述代码中,我们使用 @Bean
注解创建了一个 DirectExchange
交换机、两个 Queue
队列,并通过 BindingBuilder
将队列与交换机进行绑定,指定了相应的绑定键。
消息生产者
消息生产者负责将消息发送到 RabbitMQ 中。以下是一个简单的消息生产者类:
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.nio.charset.StandardCharsets;
@Component
public class DirectPublisher {
private final Environment environment;
private final ObjectMapper objectMapper;
private final RabbitTemplate rabbitTemplate;
public DirectPublisher(Environment environment, ObjectMapper objectMapper, RabbitTemplate rabbitTemplate) {
this.environment = environment;
this.objectMapper = objectMapper;
this.rabbitTemplate = rabbitTemplate;
}
public void send1(String msg) {
if (!StringUtils.isEmpty(msg)) {
try {
rabbitTemplate.setExchange(environment.getProperty("basic.direct.exchange.name"));
rabbitTemplate.setRoutingKey(environment.getProperty("basic.direct.routing.key1"));
Message message = MessageBuilder.withBody(msg.getBytes(StandardCharsets.UTF_8)).build();
rabbitTemplate.convertAndSend(message);
System.out.println("发送消息:" + msg);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void send2(String msg) {
if (!StringUtils.isEmpty(msg)) {
try {
rabbitTemplate.setExchange(environment.getProperty("basic.direct.exchange.name"));
rabbitTemplate.setRoutingKey(environment.getProperty("basic.direct.routing.key2"));
Message message = MessageBuilder.withBody(msg.getBytes(StandardCharsets.UTF_8)).build();
rabbitTemplate.convertAndSend(message);
System.out.println("发送消息:" + msg);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
在 DirectPublisher
类中,我们定义了两个方法 send1
和 send2
,分别用于发送不同路由键的消息。在发送消息时,我们设置了交换机和路由键,并将消息转换为 Message
对象后发送。
消息消费者
消息消费者负责从 RabbitMQ 中接收消息。以下是一个简单的消息消费者类:
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class DirectConsumer {
private final Environment environment;
private final ObjectMapper objectMapper;
private final RabbitTemplate rabbitTemplate;
public DirectConsumer(Environment environment, ObjectMapper objectMapper, RabbitTemplate rabbitTemplate) {
this.environment = environment;
this.objectMapper = objectMapper;
this.rabbitTemplate = rabbitTemplate;
}
@RabbitListener(queues = "${basic.direct.queue.name1}", containerFactory = "singleListenerContainer")
public void receive1(@Payload JSONObject msg) {
try {
System.out.println("direct1收到消息:" + msg);
} catch (Exception e) {
e.printStackTrace();
}
}
@RabbitListener(queues = "${basic.direct.queue.name2}", containerFactory = "singleListenerContainer")
public void receive2(@Payload JSONObject msg) {
try {
System.out.println("direct2收到消息:" + msg);
} catch (Exception e) {
e.printStackTrace();
}
}
}
在 DirectConsumer
类中,我们使用 @RabbitListener
注解监听两个队列,当队列中有消息时,会调用相应的方法进行处理。
测试代码
最后,我们编写一个测试类来验证消息的发送和接收功能:
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Map;
@SpringBootTest
public class Test3 {
@Autowired
private ObjectMapper objectMapper;
@Autowired
private DirectPublisher directPublisher;
@Test
public void test1() throws Exception {
System.out.println("测试开始");
Map<String, Object> map1 = Map.of("name", "张三", "age", 18, "sex", "男");
Map<String, Object> map2 = Map.of("name", "李四", "age", 18, "sex", "男");
System.out.println(map1);
directPublisher.send1(objectMapper.writeValueAsString(map1));
System.out.println(map2);
directPublisher.send2(objectMapper.writeValueAsString(map2));
System.out.println("测试结束");
}
}
在 Test3
类中,我们创建了两个 Map
对象,并将它们转换为 JSON 字符串后发送到 RabbitMQ 中。运行该测试方法,我们可以在控制台看到消息的发送和接收情况。
结果
测试开始
{age=18, sex=男, name=张三}
发送消息:{"age":18,"sex":"男","name":"张三"}
{age=18, sex=男, name=李四}
发送消息:{"age":18,"sex":"男","name":"李四"}
测试结束
消息发送成功
消息发送成功
2025-03-15 17:08:15.949 INFO 19304 --- [ntContainer#0-2] o.s.a.r.l.SimpleMessageListenerContainer : Waiting for workers to finish.
2025-03-15 17:08:15.951 INFO 19304 --- [ntContainer#1-2] o.s.a.r.l.SimpleMessageListenerContainer : Waiting for workers to finish.
2025-03-15 17:08:15.954 INFO 19304 --- [ntContainer#2-2] o.s.a.r.l.SimpleMessageListenerContainer : Waiting for workers to finish.
2025-03-15 17:08:15.957 INFO 19304 --- [ntContainer#3-2] o.s.a.r.l.SimpleMessageListenerContainer : Waiting for workers to finish.
2025-03-15 17:08:15.960 INFO 19304 --- [ntContainer#4-2] o.s.a.r.l.SimpleMessageListenerContainer : Waiting for workers to finish.
direct1收到消息:{"age":18,"sex":"男","name":"张三"}
direct2收到消息:{"age":18,"sex":"男","name":"李四"}
总结
通过本文的介绍,我们了解了 RabbitMQ 的 Direct Exchange 的基本原理,并结合 Spring Boot 实现了基于 Direct Exchange 的直连消息传输。Direct Exchange 适用于根据消息的路由键进行精确匹配的场景,能够帮助我们实现消息的精准投递。在实际开发中,我们可以根据具体的业务需求选择合适的交换机类型,以满足不同的消息处理需求。