1. 消息的可靠投递
- 在使用 RabbitMQ 的时候,作为消息发送方希望杜绝任何消息丢失或者投递失败场景;RabbitMQ 为我们提供了两种方式用来控制消息的投递可靠性模式。
- confirm 确认模式
- return 退回模式
RabbitMQ消息投递的路径(一般是):
producer—>rabbitmq broker—>exchange—>queue—>consumer
消息从 producer 到 exchange 则会返回一个 confirmCallback 。
消息从 exchange–>queue 投递失败则会返回一个 returnCallback 。
用代码展示具体的使用:
confirm确认模式:
// 定义回调
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
/*
correlationData 相关配置信息
ack: exchange交换机是否成功收到了消息 true 成功 false失败
cause:失败的原因
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if (ack) {
// 就收成功
} else {
// 接受失败
// 做一些后续的处理
}
}
});
// 发送消息
rabbitTemplate.convertAndSend("交换机", "路由", "发送的消息");
return 退回模式:
// 回退模式:当消息发送给 exchange后,exchange路由到Queue失败时才会执行ReturnCallBack
// 设置交换机处理失败消息的模式
rabbitTemplate.setMandatory(true); // 将消息返给生产者
rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
/*
message 消息对象
replyCode 错误码
replyText 错误信息
exchange 交换机
routingKey 路由键
*/
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange,
String routingKey) {
// 进行进一步处理
}
});
// 发送消息
rabbitTemplate.convertAndSend("交换机", "路由", "发送到消息");
总结:在使用这个是需要在yml文件里面配置相关属性
#RabbitMQ的其他配置(地址,端口,账号,密码...)
#开启确认模式
publisher-confirms: true
# 开启回退模式
publisher-returns: true
在RabbitMQ中也提供了事务机制,但是性能较差,一般不使用,了解即可
txSelect(), 用于将当前channel设置成transaction模式
txCommit(),用于提交事务
txRollback(),用于回滚事务
Connection connection = null;
Channel channel = null;
try {
ConnectionFactory connectionFactory = new ConnectionFactory();
connection = connectionFactory.newConnection();
channel = connection.createChannel();
// 发送到消息
String s = "发送的消息";
// 绑定交换机
channel.exchangeDeclare("交换机名称", "交换机类型", true); // 最后一个参数是说是否持久化
// 声明队列
channel.queueDeclare("队列名", true, true, false, null); // 第一个Boolean 是否持久化
//第二个Boolean 是否排外的
//第三个Boolean 是否自动删除
// 第四个Map 队列中的消息什么时候会自动被删除?
// 绑定队列
channel.queueBind("队列名", "交换机名称", "路由键");
channel.txSelect(); // 用于将当前channel设置成transaction模式
channel.basicPublish("交换机名称",
"路由键",
true,
com.rabbitmq.client.MessageProperties.MINIMAL_BASIC,
s.getBytes());
channel.txCommit(); // 用于提交事务
} catch (Exception e) {
channel.txRollback(); // 用于回滚事务
} finally {
// 关闭连接 关闭信道
}
2. 消费者 Acknowledge
Acknowledge表示消费者收到消息后的确认方式。一共有三种:
自动确认:acknowledge=“none”
手动确认:acknowledge=“manual”
根据异常情况确认:acknowledge=“auto”(了解)
自动确认方式是指,当消息一旦被Consumer接收到,则自动确认收到,并将相应 message 从 RabbitMQ 的消息缓存中移除。但是在实际业务处理中,很可能消息接收到,业务处理出现异常,那么该消息就会丢失。
手动确认方式,则需要在业务处理成功后,调用channel.basicAck(),手动签收,如果出现异常,则调用channel.basicNack()方法,让其自动重新发送消息。
在使用这个是需要在yml文件里面配置相关属性
listener:
simple:
acknowledge-mode: manual # manual为手动确认 none为自动确认 auto根据异常情况确认
@RabbitListener(queuesToDeclare = @Queue("${队列全路径名}"))
public void receiveMessage(Message message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag)
throws Exception {
try {
// 就收message消息
String s = new String(message.getBody());
// 处理逻辑
channel.basicAck(tag, true); // Boolean类型multiple 如果设置为true,一条消息应答了,那么之前的全部消息将被应答。
// 比如目前channel中有delivery tags为5,6,7,8的消息,那么一旦8被应答,那么5,6,7将都被应答;
// 如果设置为false,那么5,6,7将不会被应答。
} catch (Exception e) {
channel.basicNack(tag,true,true); // 第二个Boolean 如果为true表示重新回到队列,反之
//channel.basicReject(tag,true);
}
}
3. 消息可靠性总结
持久化( exchange要持久化 queue要持久化 message要持久化 )
生产方确认Confirm
消费方确认Ack
Broker高可用
4. 消费端限流
配置如下就可以对读取MQ里面的数据进行限流
listener:
simple:
#限流
prefetch: 1 #配置每次接受条数即可
5. TTL
TTL 全称 Time To Live(存活时间/过期时间)。
当消息到达存活时间后,还没有被消费,会被自动清除。
RabbitMQ可以对消息设置过期时间,也可以对整个队列(Queue)设置过期时间。
在发送消息的时候,对消息设置如下代码:
//1.设置message的信息
message.getMessageProperties().setExpiration("5000");//消息的过期时间 5s过期
6. 死信队列
死信队列,英文缩写:DLX 。
Dead Letter Exchange(死信交换机),当消息成为Dead message后,可以被重新发送到另一个交换机,这个交换机就是DLX。
一条消息成为死信一共有三种情况:
- 队列里面的消息达到队列长度极限;
- 消费者拒绝接受消息,并且不会到原来队列;(BasicNack/basicReject 方法的queue参数为flase);
- 原队列当中设置了消息过期时间,消息已经过期。
将原队列与死信交换机进行绑定:
具体代码如下:
/**
* 为队列绑定死信队列
* @return
*/
@Bean
public Queue queueA() {
Map<String, Object> args = new HashMap<>(3);
//声明死信交换器
args.put("x-dead-letter-exchange", "dlx.exchange");
//声明死信路由键
args.put("x-dead-letter-routing-key", ROUTINGKEY_A );
//声明队列消息过期时间 30分钟
args.put("x-message-ttl", 1800000);
// args.put("x-message-ttl", 10000);
return new Queue(QUEUE_A, true, false, false, args);//队列持久
}
7. 延迟队列
概念:即消息进入队列后不会立即被消费,只有到达指定时间后,才会被消费。
使用场景:
- 下单后,30分钟未支付,取消订单,回滚库存。
- 新用户注册成功7天后,发送短信问候。
但是MQ并没有提供延迟队列这种类型,我们需要通过死信队列+TTL来实现。
@Bean
public Queue payQueue(){
Map<String,Object> params = new HashMap<>();
//设置队列的过期时间
params.put(ttl,10000);
//声明当前队列绑定的死信交换机
params.put(dle,dlTopicExchange);
//声明当前队列的死信路由键
params.put(dlk,dlRoutingKey);
return QueueBuilder.durable(queueName).withArguments(params).build();
}
8.日志与监控
通过UI界面可以看到每一个队列的具体信息;此处不详细解说。