前言:
安装rabbitmq,以及插件地址:自行百度
RabbitMQ超详细安装教程(Linux)_rabbitmq安装_Baret-H的博客-优快云博客
Rabbitmq延迟队列插件安装_rabbitmq安装x-delayed_最菜Java开发实习生的博客-优快云博客
面试突击
1.消息丢失怎么处理
1.设置手动ACK,
2.设置发布者确认 ConfirmCallback,就是消息到达exchange了返回ack
3.设置到达确认,ReturnCallback--已经过期方法,就是消息到达Queue了返回ack
4.消息持久化 durable = "true"
1.引入rabbitmq依赖
<!--amqp-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2.配置文件
spring:
rabbitmq:
addresses: 127.0.0.1:5672
username: admin
password: admin
virtual-host: /
connection-timeout: 15000
#发布者确认,启动消息确认模式,correlated值是发布消息成功到交换器后会触发回调方法
#开启消息确认机制 confirm 异步
publisher-confirm-type: correlated
#生产者确认机制,确保消息会正确发送,如果发送失败会有错误回执,从而触发重试
#之前的旧版本 开启消息确认机制的方式
#publisher-confirms: true
#发布者到达确认(消息不能正确路由),设置return消息模式,注意要和mandatory一起配合使用
publisher-returns: true
template:
#如果消息没有匹配上队列,则记录;第二种是在webui设置一个默认交换器进行收集处理
mandatory: true
#失败重试
retry:
#开启失败重试
enabled: true
#initial-interval: 10000ms第一次重试的间隔时长
#max-interval: 300000ms最长重试间隔,超过这个间隔将不再重试
#multiplier: 2下次重试间隔的倍数,此处是2即下次重试间隔是上次的2倍
listener:
simple:
#表示消费者消费成功消息以后需要手工的进行验收(ACK),默认auto,simple关闭自动ack,手动ack
acknowledge-mode: manual
#线程数,消费端的监听个数(即@RabbitListener开启几个线程去处理数据)
#concurrency: 5
#批量消息消费
#prefetch: 1
#最大线程数
#max-concurrency: 10
#direct:
#acknowledge-mode: manual
retry:
enabled: true
3.延迟消息

3.1.生产者
TODO:rabbitTemplate.setConfirmCallback 报错exception only one 则需要配置RabbitTemplate,后文【5】会说明
@Resource
private RabbitTemplate rabbitTemplate;
/**
* 订单支付超时监听
* @param orderTimeOutMessage
*/
public void orderTimeOutSendMessage(OrderTimeOutMessage orderTimeOutMessage) {
log.info("[发送MQ消息-订单30分钟未支付]成功 {}", JSON.toJSONString(orderTimeOutMessage));
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if(ack){
log.info("[ConfirmCallback] [ack = true] cause=" + cause);
}else{
log.error("[ConfirmCallback] [ack = false] cause=" + cause);
}
}
});
rabbitTemplate.convertAndSend("order.timeout.delayed", "",orderTimeOutMessage, new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
//设置消息的持久化
message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
//设置延迟的时间
//message.getMessageProperties().setDelay(threeTimeOut);
message.getMessageProperties().setDelay(30 * 60 * 1000);
return message;
}
});
}
@Data
public class OrderTimeOutMessage implements Serializable {
/**
* 枚举类型
*/
@NotNull
private String allTypeValue;
/**
* 订单ID
*/
@NotNull
private Long orderId;
/**
* 订单号
*/
@NotNull
private String orderNo;
3.2.消费者
@Slf4j
@Component
@RequiredArgsConstructor
public class RabbitQueueOrderTimeOutDelayedListener {
private static final String QUEUE = "order.timeout.delayed.queue";
private static final String EXCHANGE = "order.timeout.delayed";
@Resource
private Service service;
@RabbitListener(
bindings = @QueueBinding(
value = @Queue(value = QUEUE, durable = "true"),
//exchange = @Exchange(value = EXCHANGE, type = ExchangeTypes.FANOUT, ignoreDeclarationExceptions = "true"),
exchange = @Exchange(value = EXCHANGE, type = "x-delayed-message", arguments = @Argument(name = "x-delayed-type",value ="fanout"), ignoreDeclarationExceptions = "true"),
key = "")
)
@RabbitHandler
public void onMessage(OrderTimeOutMessage orderTimeOutMessage,Message message, Channel channel){
long deliveryTag = message.getMessageProperties().getDeliveryTag();
String body = new String(message.getBody(), StandardCharsets.UTF_8);
log.info("订单30分钟未支付- consumer RabbitMq消息内容 {}", JSON.toJSONString(orderTimeOutMessage));
try {
AllTypeEnum allTypeEnum = AllTypeEnum.valueOf(orderTimeOutMessage.getAllTypeValue());
switch (allTypeEnum){
case a: service.a(orderTimeOutMessage);return;
case b: service.b(orderTimeOutMessage);return;
default:log.error("订单类型 {} 不存在", orderTimeOutMessage.getAllTypeValue());
}
//rabbitmq 的ack机制,第二个参数返回true,表示需要将这条消息投递给其他消费者重新消费
channel.basicAck(deliveryTag, false);
}catch (Exception e){
e.printStackTrace();
try {
//第三个参数true,表示这个消费会重新进入队列
channel.basicNack(deliveryTag, false, true);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
}
4.普通消息
4.1生产者
本文只讲解ExchangeTypes.FANOUT类型,其他类型修改ExchangeTypes类型,在生产者设置routingKey即可,消费端设置key = {"",""}即可,没什么可说的,就是模糊匹配
routingKey简单说就是标签,设置模糊匹配的规则,类似rocketmq的tag
public void orderSendMessage(OrderTimeOutMessage orderTimeOutMessage) {
rabbitTemplate.setConfirmCallback(....同上);
rabbitTemplate.convertAndSend("test.exchange", "",orderTimeOutMessage);
}
4.2消费者
@Slf4j
@Component
@RequiredArgsConstructor
public class RabbitQueueTimeOutListener {
private static final String QUEUE = "test.exchange.queue";
private static final String EXCHANGE = "test.exchange";
@RabbitListener(
bindings = @QueueBinding(
value = @Queue(value = QUEUE, durable = "true"),
exchange = @Exchange(value = EXCHANGE, type = ExchangeTypes.FANOUT, ignoreDeclarationExceptions = "true"),
key = "")
)
@RabbitHandler
public void onMessage(OrderTimeOutMessage orderTimeOutMessage,Message message, Channel channel){
long deliveryTag = message.getMessageProperties().getDeliveryTag();
String body = new String(message.getBody(), StandardCharsets.UTF_8);
log.info("RabbitMq消息内容 {}", body);
try {
//todo
//rabbitmq 的ack机制,第二个参数返回true,表示需要将这条消息投递给其他消费者重新消费
channel.basicAck(deliveryTag, false);
}catch (Exception e){
e.printStackTrace();
try {
//第三个参数true,表示这个消费会重新进入队列
channel.basicNack(deliveryTag, false, true);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
}
5.针对出现Only one ConfirmCallback is supported by each RabbitTemplate

5.1解决方法
@Configuration
@Slf4j
public class RabbitmqConfig implements RabbitTemplate.ConfirmCallback {
@Bean
@Scope("prototype")//作用域-多例,避免only one ConfirmCallback
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMandatory(true);
rabbitTemplate.setConfirmCallback(this);//这里是全局配置监听,也可以在每次发送的时候监听,二者取其一
return rabbitTemplate;
}
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
//log.info("[ConfirmCallback] [ack = true] correlationData=" + correlationData.toString());
if(ack){
log.info("[ConfirmCallback] [ack = true] cause=" + cause);
}else{
log.error("[ConfirmCallback] [ack = false] cause=" + cause);
}
}
}
本文介绍了如何在SpringBoot应用中集成RabbitMQ,包括安装RabbitMQ和延迟队列插件,以及处理消息丢失的方法。在延迟队列部分,讨论了生产者和消费者的配置,而普通消息部分则关注了FANOUT类型的交换机和路由键的使用。此外,还解决了RabbitTemplate中ConfirmCallback的限制,并提到了惰性队列的使用。

21万+

被折叠的 条评论
为什么被折叠?



