103 lines
4.0 KiB
Java
103 lines
4.0 KiB
Java
package com.yunhai.userbase.config;
|
|
|
|
import org.springframework.amqp.core.*;
|
|
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
|
|
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
|
|
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
|
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
|
|
import org.springframework.amqp.support.converter.MessageConverter;
|
|
import org.springframework.context.annotation.Bean;
|
|
import org.springframework.context.annotation.Configuration;
|
|
|
|
import com.yunhai.userbase.model.push.PushTaskMessage;
|
|
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
|
|
/**
|
|
* 推送订阅 MQ 配置。<br><br>
|
|
* 拓扑:
|
|
* <pre>
|
|
* push.topic (topic, durable)
|
|
* └─ push.main (durable, concurrency=1, x-dead-letter-exchange=push.dlx) ← 串行消费,可观测
|
|
* push.dlx (fanout, durable)
|
|
* └─ push.dlq (durable) ← 死信归档(重试耗尽)
|
|
* </pre>
|
|
* 连接信息(host/port/user/pwd/vhost)由 Nacos {@code application-infra.yaml} 的 {@code spring.rabbitmq.*}
|
|
* 公共配置注入,本类不写死(也不改 bootstrap.yml / application.yml)。<br>
|
|
* 可靠性:消费端手动 ack,失败由 {@code PushConsumer} 应用层自管重试(共 3 次),耗尽进死信队列。
|
|
*
|
|
* @author chenx
|
|
* @date 2026/8/14
|
|
*/
|
|
@Configuration
|
|
public class PushMqConfig {
|
|
|
|
public static final String EXCHANGE_TOPIC = "push.topic";
|
|
public static final String EXCHANGE_DLX = "push.dlx";
|
|
public static final String QUEUE_MAIN = "push.main";
|
|
public static final String QUEUE_DLQ = "push.dlq";
|
|
|
|
/** 统一 JSON 消息转换器(固定反序列化为 PushTaskMessage,绕过 trusted-packages 版本差异)。 */
|
|
@Bean
|
|
public MessageConverter pushMessageConverter() {
|
|
Jackson2JsonMessageConverter converter = new Jackson2JsonMessageConverter();
|
|
converter.setClassMapper(new PushTaskClassMapper(PushTaskMessage.class));
|
|
return converter;
|
|
}
|
|
|
|
@Bean
|
|
public TopicExchange pushTopicExchange() {
|
|
return new TopicExchange(EXCHANGE_TOPIC, true, false);
|
|
}
|
|
|
|
@Bean
|
|
public FanoutExchange pushDlxExchange() {
|
|
return new FanoutExchange(EXCHANGE_DLX, true, false);
|
|
}
|
|
|
|
/** 主队列:单消费者串行 + 死信路由到 push.dlx。 */
|
|
@Bean
|
|
public Queue pushMainQueue() {
|
|
Map<String, Object> args = new HashMap<>();
|
|
args.put("x-dead-letter-exchange", EXCHANGE_DLX);
|
|
return new Queue(QUEUE_MAIN, true, false, false, args);
|
|
}
|
|
|
|
/** 死信队列:归档重试耗尽的任务。 */
|
|
@Bean
|
|
public Queue pushDlqQueue() {
|
|
return new Queue(QUEUE_DLQ, true, false, false);
|
|
}
|
|
|
|
@Bean
|
|
public Binding pushMainBinding() {
|
|
return BindingBuilder.bind(pushMainQueue()).to(pushTopicExchange()).with("#");
|
|
}
|
|
|
|
@Bean
|
|
public Binding pushDlqBinding() {
|
|
return BindingBuilder.bind(pushDlqQueue()).to(pushDlxExchange());
|
|
}
|
|
|
|
@Bean
|
|
public RabbitTemplate pushRabbitTemplate(ConnectionFactory connectionFactory) {
|
|
RabbitTemplate template = new RabbitTemplate(connectionFactory);
|
|
template.setMessageConverter(pushMessageConverter());
|
|
return template;
|
|
}
|
|
|
|
/** 消费者容器:手动 ack + 单并发 + prefetch=1,保证串行可观测。 */
|
|
@Bean(name = "pushListenerContainerFactory")
|
|
public SimpleRabbitListenerContainerFactory pushListenerContainerFactory(ConnectionFactory connectionFactory) {
|
|
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
|
|
factory.setConnectionFactory(connectionFactory);
|
|
factory.setMessageConverter(pushMessageConverter());
|
|
factory.setAcknowledgeMode(org.springframework.amqp.core.AcknowledgeMode.MANUAL);
|
|
factory.setConcurrentConsumers(1);
|
|
factory.setMaxConcurrentConsumers(1);
|
|
factory.setPrefetchCount(1);
|
|
return factory;
|
|
}
|
|
}
|