一、消息中间件产生了消息堆积如何解决
RabbitMQ如果产生了消息堆积如何处理?
产生的背景:如果没有及时的消费者消费消息,生产者一直不断往队列服务器存放消息
会导致消息堆积
。
两种场景:
1.没有消费者消费
的情况下:
1.死信队列
2.设置消息有效期。相当于对我们的消息设置有效期
,在规定的时间内如果没有消费的话,自动过期
,过期的时候会执行客户端回调监听
的方法将消息存放到数据库记录
,后期实现补偿。
2.有一个消费者消费
的情况:
应该提高我们的消费者 消费实现集群
二、RabbitMQ如何保证消息不丢失
RabbitMQ如何彻底保证我们的消息不丢失?
1.MQ服务器端应该消息持久化到硬盘
2.生产者
使用消息确认机制
百分能够将消息投递到MQ成功
3.消费者
使用手动ack机制
确认消息百分百消费成功
如果队列容量满了,在继续投递可能会丢失 .—> 死信队列
死信队列:
称做为备胎队列,消息中间件队列因为某种消费拒绝存放该消息,可以转移到死信队列中存放。
死信队列产生的背景:
1.生产者投递消息到MQ中,消息过期
了;
2.队列
的已经达
到最大长度
(队列存放消息满了)MQ拒绝接受
存放该消息
。
3.消费者多次消费该消息失败
的情况,也会存放死信。(使用场景最多
)
实现我们的死信队列
三、RabbitMQ整合死信队列
2.交换机 队列 绑定队列到交换机
订单:
死信:
@Component
public class DeadLetterMQConfig {
/**
* 订单交换机
*/
@Value("${mayikt.order.exchange}")
private String orderExchange;
/**
* 订单队列
*/
@Value("${mayikt.order.queue}")
private String orderQueue;
/**
* 订单路由key
*/
@Value("${mayikt.order.routingKey}")
private String orderRoutingKey;
/**
* 死信交换机
*/
@Value("${mayikt.dlx.exchange}")
private String dlxExchange;
/**
* 死信队列
*/
@Value("${mayikt.dlx.queue}")
private String dlxQueue;
/**
* 死信路由
*/
@Value("${mayikt.dlx.routingKey}")
private String dlxRoutingKey;
/**
* 声明死信交换机
*
* @return DirectExchange
*/
@Bean
public DirectExchange dlxExchange() {
return new DirectExchange(dlxExchange);
}
/**
* 声明死信队列
*
* @return Queue
*/
@Bean
public Queue dlxQueue() {
return new Queue(dlxQueue);
}
/**
* 声明订单业务交换机
*
* @return DirectExchange
*/
@Bean
public DirectExchange orderExchange() {
return new DirectExchange(orderExchange);
}
/**
* 声明订单队列
*
* @return Queue
*/
@Bean
public Queue orderQueue() {
Map<String, Object> arguments = new HashMap<>(2);
// 绑定我们的死信交换机
arguments.put("x-dead-letter-exchange", dlxExchange);
// 绑定我们的死信路由key
arguments.put("x-dead-letter-routing-key", dlxRoutingKey);
return new Queue(orderQueue, true, false, false, arguments);
}
/**
* 绑定订单队列到订单交换机
*
* @return Binding
*/
@Bean
public Binding orderBinding() {
return BindingBuilder.bind(orderQueue())
.to(orderExchange())
.with(orderRoutingKey);
}
/**
* 绑定死信队列到死信交换机
*
* @return Binding
*/
@Bean
public Binding binding() {
return BindingBuilder.bind(dlxQueue())
.to(dlxExchange())
.with(dlxRoutingKey);
}
}
删掉队列,重新启动:
四、RabbitMQ死信队列效果演示
消息过期存放到死信队列:
@RestController
public class DeadLetterController {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 订单交换机
*/
@Value("${mayikt.order.exchange}")
private String orderExchange;
@Autowired
private OrderMapper orderMapper;
/**
* 订单路由key
*/
@Value("${mayikt.order.routingKey}")
private String orderRoutingKey;
@RequestMapping("/sendOrderMsg")
public String sendOrderMsg() {
// 1.生产订单id
String orderId = System.currentTimeMillis() + "";
String orderName = "好好学习";
OrderEntity orderEntity = new OrderEntity(orderName, orderId);
String msg = JSONObject.toJSONString(orderEntity);
sendMsg(msg);
return orderId;
// 后期客户端主动使用orderId调用服务器接口 查询该订单id是否在数据库中存在数据 消费成功 消费失败
}
@Async
public void sendMsg(String msg) {
rabbitTemplate.convertAndSend(orderExchange, orderRoutingKey, msg,
new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
message.getMessageProperties().setExpiration("10000");
return message;
}
});
// 消息投递失败
}
/**
* 主动查询接口
* 先查询该订单的消息是否投递失败
* 在查询数据库
*/
@RequestMapping("getOrder")
public Object getOrder(String orderId) {
OrderEntity orderEntity = orderMapper.getOrder(orderId);
if (orderEntity == null) {
return "消息正在异步的处理中";
}
return orderEntity;
}
}
@Component
public class OrderDlxConsumer {
/**
* 监听我们的死信队列
*
* @return
*/
@RabbitListener(queues = "mayikt_order_dlx_queue")
public void orderConsumer(String msg) {
System.out.println("死信队列获取消息:" + msg);
}
}
五、死信队列消费者部署流程
死信队列不能够和正常队列存放在同一个服务器中,应该分开服务器存放。
不然就团灭了。
六、RabbitMQ如何获取消费者结果
Rabbitmq异步如何获取消费结果
1.多线程+主动查询
消费的结果根据业务来定
七、主动调用接口查询消费结果
升级:
先生成orderid
分2步:1.异步投递消息 2.反出orderid