直接选 Direct 还是 Topic,取决于你要的是“精准投递”还是“灵活订阅”。Direct 适合路由键完全匹配的场景,Topic 适合需要通配符模糊匹配的场景。
先说结论:Direct 交换机用于精确路由,Topic 交换机用于模式匹配,选型核心看业务是否需要灵活过滤。
- 适合:Direct 用于任务分发、点对点通知;Topic 用于日志收集、分类消息订阅。
- 重点看:Direct 要求 Routing Key 与 Binding Key 完全一致;Topic 支持 * 和 # 通配符。
- 别忽略:Topic 匹配涉及模式树遍历,CPU 消耗略高于 Direct;Direct 无匹配时消息会被丢弃,需配置死信队列。
核心区别与选型
交换机的核心作用是路由,区别在于“匹配规则”不同。Direct 交换机是最基础的模式,它要求消息携带的 Routing Key 必须与队列绑定时指定的 Binding Key 完全相同,消息才会被转发,这保证了精确性。Topic 交换机则引入了通配符机制,Routing Key 被点号分隔成多个单词,Binding Key 可以使用 * 匹配一个单词,或使用 # 匹配零个或多个单词,从而实现一对多的灵活分发。
这种设计差异决定了 Direct 更像“专线”,Topic 更像“分类广播”。如果业务只需要根据固定标识(如订单 ID 类型)路由,Direct 效率更高;如果业务需要根据层级(如日志级别、部门分类)动态订阅,Topic 更合适。
Spring Boot 配置实战
在 Spring Boot 项目中,通常通过配置类声明交换机和队列绑定关系。以下是 Direct 和 Topic 的标准配置示例:
# application.yml spring:rabbitmq:host: localhostport: 5672username: guestpassword: guestlistener:simple:acknowledge-mode: manual # 建议手动 ACK 防止丢失
// RabbitConfig.java
@Configuration
public class RabbitConfig {// Direct 配置@Beanpublic Queue directQueue() {return new Queue("order.direct.queue", true);}@Beanpublic DirectExchange directExchange() {return new DirectExchange("order.direct");}@Beanpublic Binding directBinding(Queue directQueue, DirectExchange directExchange) {return BindingBuilder.bind(directQueue).to(directExchange).with("order.create");}// Topic 配置@Beanpublic Queue topicQueue() {return new Queue("log.topic.queue", true);}@Beanpublic TopicExchange topicExchange() {return new TopicExchange("log.topic");}@Beanpublic Binding topicBinding(Queue topicQueue, TopicExchange topicExchange) {// 匹配所有以 app 开头,第三段为 error 的消息,如 app.sys.errorreturn BindingBuilder.bind(topicQueue).to(topicExchange).with("app.*.error");}
}客户端发送与接收
生产者发送消息时需指定 Routing Key,消费者通过队列监听消息。以下是基于 Spring AMQP 的发送与接收代码:
// 生产者服务
@Service
public class MessageProducer {@Autowiredprivate RabbitTemplate rabbitTemplate;// 发送 Direct 消息public void sendDirect(String msg) {rabbitTemplate.convertAndSend("order.direct", "order.create", msg);}// 发送 Topic 消息public void sendTopic(String level, String msg) {// 动态构建 Routing Key,如 app.sys.errorString routingKey = "app." + level + ".error";rabbitTemplate.convertAndSend("log.topic", routingKey, msg);}
}// 消费者服务
@RabbitListener(queues = "order.direct.queue")
public void consumeDirect(Message message, Channel channel) {// 处理逻辑channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}@RabbitListener(queues = "log.topic.queue")
public void consumeTopic(Message message, Channel channel) {// 处理逻辑channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}命令行验证与排查
配置完成后,主要通过代码配置,也可辅以 rabbitmqadmin 命令行工具进行验证和排查。确保已启用 Management 插件并下载 rabbitmqadmin 脚本。
1. 声明交换机与队列:
./rabbitmqadmin declare exchange name=order.direct type=direct ./rabbitmqadmin declare queue name=order.direct.queue durability=true
2. 绑定关系验证:
# Direct 绑定 ./rabbitmqadmin bind queue=order.direct.queue exchange=order.direct routing_key=order.create# Topic 绑定 ./rabbitmqadmin bind queue=log.topic.queue exchange=log.topic routing_key="app.*.error"
3. 发送测试消息:
# 发送 Direct 消息 ./rabbitmqadmin publish exchange=order.direct routing_key=order.create payload="test_order"# 发送 Topic 消息 (应被接收) ./rabbitmqadmin publish exchange=log.topic routing_key=app.sys.error payload="test_log"# 发送 Topic 消息 (不应被接收) ./rabbitmqadmin publish exchange=log.topic routing_key=app.sys.info payload="test_log_info"
4. 查看队列消息数:
./rabbitmqadmin list queues name messages
常见坑与防护
1. Topic 性能损耗:Topic 交换机的匹配逻辑比 Direct 复杂,涉及模式树遍历,CPU 消耗略高于 Direct。在绑定规则极多或通配符极复杂时,路由效率可能低于 Direct,建议避免过度使用 # 通配符。
2. Direct 消息丢失:Direct 交换机在没有匹配队列时不会报错,消息直接消失。生产环境务必确认绑定关系已建立,或开启持久化和确认机制。建议配置死信队列(DLX)捕获未路由消息:
// 配置死信队列
@Bean
public Queue deadLetterQueue() {return new Queue("order.dlx.queue", true);
}
@Bean
public Queue directQueueWithDLX() {Map args = new HashMap<>();args.put("x-dead-letter-exchange", "order.dlx.exchange");args.put("x-dead-letter-routing-key", "order.dlx.key");return new Queue("order.direct.queue", true, false, false, args);
} 3. 通配符误用:Topic 模式下 # 匹配零个或多个词,* 仅匹配一个词。例如 "#.error" 能匹配 "error",但 "*.error" 不能匹配 "error"(因为前面少了一个词),配置时需仔细测试。
原文链接:https://www.zjcp.cc/ask/11565.html
