SpringBoot2.X–RabbitMQ实战
maven导入mq包:
org.springframework.boot
spring-boot-starter-amqp
mq配置:
spring.rabbitmq.addresses=127.0.0.1:5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/
spring.rabbitmq.connection-timeout=15000
spring.rabbitmq.publisher-confirms=true
spring.rabbitmq.publisher-returns=true
# return 的时候代表消息不可达,设置 broker 不自动删除该消息,
# 而是返回到生产端,让我们进行一些后续的处理
spring.rabbitmq.template.mandatory=true
MQ配置类:
@Configuration
public class RabbitMQConfig {
public static final String QUEUE_SMS = "queue_inform_sms"; //短信队列名
public static final String QUEUE_EMAIL = "queue_inform_email"; //邮件队列名
public static final String ROUTINGKEY_SMS = "routingkey.#.sms.#"; //统配符匹配 短信队列key
public static final String ROUTINGKEY_EMAIL = "routingkey.#.email.#"; //统配符匹配 邮件队列key
public static final String EXCHANGE_ROUTING_INFORM = "exchange_topic_inform"; //交换机
/**
* 加载交换机组件
* @return
*/
@Bean(EXCHANGE_ROUTING_INFORM)
public Exchange getExchange() {
//创建交换机并设置模式
ExchangeBuilder exchangeBuilder = ExchangeBuilder.topicExchange(EXCHANGE_ROUTING_INFORM);
exchangeBuilder.durable(true); //是否持久化 默认为true
Exchange exchange = exchangeBuilder.build();
return exchange;
}
/**
* 加载队列组件
* @return
*/
@Bean(QUEUE_SMS)
public Queue getQueueSMS() {
return new Queue(QUEUE_SMS,true);
}
/**
* 加载队列组件
* @return
*/
@Bean(QUEUE_EMAIL)
public Queue getQueueEMAIL() {
return new Queue(QUEUE_EMAIL,true);
}
/**
* 队列绑定交换机指定routingkey
*/
@Bean
public Binding bindingSMS(@Qualifier(QUEUE_SMS)Queue queue,
@Qualifier(EXCHANGE_ROUTING_INFORM)Exchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(ROUTINGKEY_SMS).noargs();
}
/**
* 队列绑定交换机指定routingkey
*/
@Bean
public Binding bindingEMAIL(@Qualifier(QUEUE_EMAIL)Queue queue,
@Qualifier(EXCHANGE_ROUTING_INFORM)Exchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(ROUTINGKEY_EMAIL).noargs();
}
}
MQ生产者:
@Component
public class RabbitSender {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 监听MQ服务返回的确认请求,消息到达exchange,ack 就返回true
* CorrelationData 获取生产者发送的消息
* ack 判断消息是否到达MQ服务
*/
final RabbitTemplate.ConfirmCallback confirmCallback = new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if(!ack){ //判断消息是否发送成功
String id = correlationData.getId(); //消息ID
Message message = correlationData.getReturnedMessage(); //消息体
// byte[] body = message.getBody(); //消息
System.err.println("消息ID: " + id + " 发送至MQ服务失败...." + message);
}
}
};
/* 简化版
private final RabbitTemplate.ConfirmCallback confirmCallback = (correlationData, ack, cause) -> {
System.out.println("correlationData:" + correlationData);
System.out.println("ack:" + ack);
if (!ack){
System.out.println("补偿处理...");
}
};*/
/**
* 监听exchange 是否将消息转发至对应的队列中(routingKey可能找不到对应的queue)
*/
final RabbitTemplate.ReturnCallback returnCallback = new RabbitTemplate.ReturnCallback() {
@Override
public void returnedMessage(Message message, int replyCode, String replyText,
String exchange, String routingKey) {
if("NO_ROUTE".equals(replyText)) { //replyCode: 312, replyText: NO_ROUTE
System.err.println("交换机转发消息至队列异常:{ Exchange" + exchange + ", RoutingKey: " + routingKey + " }");
}
}
};
/* 简化版
private final RabbitTemplate.ReturnCallback returnCallback = (message, replyCode, replyText,
exchange, routingKey) -> System.out.println("return exchange:" + exchange + ", routingKey:" + routingKey +
", replyText:" + replyText);*/
/**
* Rabbitmq 发送消息
* @param obj 参数
*/
public void sendOrder(Object obj) {
//监听MQ服务回调
rabbitTemplate.setConfirmCallback(confirmCallback);
//监听Exchange回调
rabbitTemplate.setReturnCallback(returnCallback);
//发布者的基类
CorrelationData cd = new CorrelationData();
// 消息唯一标识
String id = UUID.randomUUID().toString();
cd.setId(id);
/**
* String exchange, 交换机
String routingKey, 路由key
Object object, 消息
CorrelationData correlationData
关联发布者的基类确认已发送的消息。使用org.springframework.amqp.rabbit.core。包含其中一个作为参数的RabbitTemplate方法;当收到发布者confirm时,将使用ack/nack返回CorrelationData。
*/
rabbitTemplate.convertAndSend(RabbitMQConfig.EXCHANGE_ROUTING_INFORM, "routingkey.sms", obj, cd);
}
}
MQ消费者:
@Component
public class RabbitConsumer {
/**
* @RabbitListener注解指定目标方法来作为消费消息的方法,通过注解参数指定所监听的队列或者Binding
* @QueueBinding 定义队列并绑定交换机
* @Queue 队列
* @Exchange 交换机
*/
@RabbitListener(bindings =
@QueueBinding(value =@Queue(value = RabbitMQConfig.QUEUE_SMS, durable = "true"),
exchange = @Exchange(value = RabbitMQConfig.EXCHANGE_ROUTING_INFORM,
durable = "true",
type = "topic",
ignoreDeclarationExceptions = "true"),
key = RabbitMQConfig.ROUTINGKEY_SMS)
)
@RabbitHandler
public void onOrderMessage(@Payload Object obj, @Headers Map properties, Channel channel) throws Exception {
System.out.println("消费端 order:" + obj);
// deliveryTag: 确认消息的条数,一般为1
Long deliveryTag = (Long) properties.get(AmqpHeaders.DELIVERY_TAG);
System.out.println("deliveryTag:" + deliveryTag);
// 限流处理:消息体大小不限制,每次限制消费一条,只作用于该Consumer层,不作用于Channel
channel.basicQos(0, 1, false);
// 手工ACK,不批量ack
channel.basicAck(deliveryTag, false);
}
}
测试类:
@SpringBootTest
@RunWith(SpringRunner.class)
public class RabbitMQProduceSend {
@Autowired
public RabbitTemplate rabbitTemplate;
@Autowired
public RabbitSender sender;
@Test
public void sendMsg() {
// String msg = "send msg...";
// rabbitTemplate.convertAndSend(RabbitMQConfig.EXCHANGE_ROUTING_INFORM,"routingkey.sms",msg);
HashMap params = new HashMap<>();
params.put("title","测试");
params.put("body","哈哈哈。。。");
sender.sendOrder(params);
}
}