一、概述
在mq消息队列中主要分广播式消息、持久式消息、死信式消息。
**广播式(Fanout,如 hos_notice_task):**不看路由键、无脑复制,一条消息发给所有绑定的队列各一份------"公告群发,人人有份"(通知前端专用,发时路由键传空串)
**持久式/业务式(Topic/Direct,如 hos_topic → 各业务队列):**按"路由键=队列名"精确匹配投递,消息持久化躺在队列里等消费,一条消息只被处理一次------"一件活只干一遍,消息落地为安"
**死信(TTL + DLX 延时,如 hos_delay_exchange):**消息在源队列睡到 TTL 到期变死信,再按死信路由键转投实发队列、到点消费一次------"设个闹钟,到点才响"
一句话记忆:广播=群发复制,业务=点对点持久,死信=到点触发------三种模式对应三种诉求:"人人都要知道"、"一件活干一次"、"到时候再看结果"。
二、广播式
实例化示例如下:
java
public static final String NOTICE_FANOUT_NAME = "hos_notice_task";
@Bean
public FanoutExchange noticeFanoutExchange() {
return new FanoutExchange(NOTICE_FANOUT_NAME, false, false);
}
三、持久式/业务式
实例化示例如下:
java
public static final String TOPICS_NAME = "hos_topic";
@Bean
public TopicExchange hosTopicExchange() {
return new TopicExchange(TOPICS_NAME, true, false);
}
四、死信
实例化示例如下:
java
public static final String DELAY_EXCHANGE = "hos_delay_exchange";
@Bean(DELAY_EXCHANGE)
public Exchange delayExchange() {
return ExchangeBuilder.topicExchange(DELAY_EXCHANGE).durable(true).build();
}
五、队列(Queue)
实例化示例如下:
java
public static final String TEST_SAVE_QUEUE_NAME = "hos_test_save";
@Bean
public Queue testSaveQueue() {
return new Queue(TEST_SAVE_QUEUE_NAME, true);
}
队列(queue)的四种初始化函数
java
new Queue(String name)
new Queue(String name, boolean durable)
new Queue(String name, boolean durable, boolean exclusive, boolean autoDelete)
new Queue(String name, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
| 参数 | 含义 | 用法 |
|---|---|---|
| `name` | 队列名,全局唯一 | `hos_test`、`hos_test_save`... |
| `durable` | 队列定义**持久化**:broker 重启队列还在 | 全部 `true` |
| `exclusive` | 排他:只归声明它的连接用,连接断开即删 | 全部 `false`(业务队列要共享) |
| `autoDelete` | 最后一个消费者退出后自动删队列 | 全部 `false`(断连不删,消息要留存) |
| `arguments` | 扩展参数 Map:延时 / 死信 / 优先级都在这 | 优先级队列、死信源队列使用 |
注意:durable=true 只保**队列定义**,不保**消息**。消息要持久必须发布时标 deliveryMode=2;new Queue(name, true) 等价于 durable=true、exclusive=false、autoDelete=false。
六、绑定(Binding)
注意:绑定与队列成对出现,一个绑定对应一个队列
实例化示例如下:
java
@Bean
public Binding bindingTestSaveQueue() {
return BindingBuilder.bind(testSaveQueue()).to(hosTopicExchange()).with(TEST_SAVE_QUEUE_NAME);
}
new Binding(destination, destinationType, exchange, routingKey, arguments)
BindingBuilder.bind(队列).to(交换机).with(绑定键).noargs()
| 要素 | 含义 | 用法 |
|---|---|---|
| `destination` | 消息去哪 | `bind(队列)` |
| `destinationType` | 目标是队列还是交换机(支持交换机→交换机) | `QUEUE`(默认) |
| `exchange` | 消息从哪个交换机来 | `.to(hosTopicExchange())` / `.to(delayExchange())` |
| `key` | 绑定键:消息路由键与它相等/通配才投递 | 恒等于队列名(项目约定) |
| `arguments` | 绑定参数(headers 交换机的 `x-match` 等) | 无(`.noargs()`) |
七、arguments
实例化示例如下:
java
Map<String, Object> arguments = new HashMap<>();
arguments.put("x-max-priority", 10);
arguments.put("x-dead-letter-exchange", DELAY_EXCHANGE);
arguments.put("x-dead-letter-routing-key", ACT_FIVE_MINUTE_NOTICE_QUEUE);
arguments.put("x-message-ttl", 60000 * 5);
return new Queue(TEST_QUEUE_NAME, true, false, false, arguments);
|---------------------------|------------------------------|
| 参数 | 含义 |
| x-message-ttl | 消息存活时间(**毫秒**),到期变死信/被丢弃 |
| x-dead-letter-exchange | 死信交换机:过期/被拒/超长后转投给谁 |
| x-dead-letter-routing-key | 死信路由键:转投时消息用什么键匹配 |
| x-max-priority | 优先级上限,值越大越先被消 |
八、发送端
发送消息统一用RabbitTemplate 的 convertAndSend 方法
实例化示例如下:
java
//普通 JSON 消息(ProcessAutoSender)
rabbitTemplate.convertAndSend(
RabbitConfig.TOPICS_NAME, // 交换机 hos_topic
RabbitConfig.PREPROCESS_AUTO_QUEUE_NAME, // 路由键 = 队列名
jsonStr); // 消息体:JSON 字符串
//带优先级(testSender,队列声明 x-max-priority=10)
rabbitTemplate.convertAndSend(
RabbitConfig.TOPICS_NAME,
RabbitConfig.TEST_QUEUE_NAME,
jsonStr,
msg -> {
msg.getMessageProperties().setPriority(priority);
return msg;
});
//Fanout 广播(NoticeSender)------路由键必须传空串
rabbitTemplate.convertAndSend(RabbitConfig.NOTICE_FANOUT_NAME, "", JsonUtils.toJsonStr(notice));
九、接收端
接收:@RabbitListener + @RabbitHandler
@RabbitListener(queues=xxxx) 声明监听哪个队列
@RabbitHandler 标记处理方法
实例化示例如下:
java
// 按 Map 解析
@RabbitListener(queues = RabbitConfig.TEST_SAVE_QUEUE_NAME)
@RabbitHandler
public void testSave(String message, Channel channel) {
Map msg = JsonUtils.parseObject(message, Map.class);
String test= (String) msg.get("test");
...
}
// 按 DTO 解析
@RabbitListener(queues = RabbitConfig.TEST_RESULT_QUEUE_NAME)
@RabbitHandler
public void cephResult(String message, Channel channel) {
DTO resultDTO = JsonUtils.parseObject(message, DTO.class);
Long taskId = resultDTO.getTaskId();
...
}
// Fanout 接收用匿名队列绑定(websocket NoticeConsumer):
@RabbitListener(bindings = @QueueBinding(
value = @Queue, // 匿名队列,自动生成
exchange = @Exchange(value = RabbitConfig.NOTICE_FANOUT_NAME,
durable = "false", type = ExchangeTypes.FANOUT))) // 没有 key
@RabbitHandler
public void noticeResult(String message, Channel channel) throws Exception {
Map<String, String> notice = new HashMap<>();
notice.put("type", "notice");
notice.put("content", message);
webSocketSessionManager.broadcast(JsonUtils.toJsonStr(notice));
}
十、结语
消息体统一 JSON 字符串,收发双方字段对齐,跨模块联调基本零歧义。如果你也在做微服务拆解、任务异步化,这套"交换机-队列-绑定-死信"的落地姿势可以直接抄作业。有任何细节欢迎评论区交流。