MQ消息的可靠性

MQ消息的可靠性

消息的可靠性即避免消息丢失。
代码以RabbitMQ为例。

Channel:

void basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body) throws IOException;
String basicConsume(String queue, boolean autoAck, Consumer callback) throws IOException;

消息从Producer到Broker

如果发送成功,Broker返回Ack。

事务模式

Channel:

Tx.SelectOk txSelect() throws IOException; // 开启事务模式
Tx.CommitOk txCommit() throws IOException; // 提交
Tx.RollbackOk txRollback() throws IOException; // 回滚

确认模式

Channel:

Confirm.SelectOk confirmSelect() throws IOException; // 开启确认模式
// 同步确认
boolean waitForConfirms() throws InterruptedException; // 是否所有消息被确认
boolean waitForConfirms(long timeout) throws InterruptedException, TimeoutException; // 带超时时间,是否所有消息被确认
void waitForConfirmsOrDie() throws IOException, InterruptedException; // 确认所有消息,如果有有未确认抛IOException
void waitForConfirmsOrDie(long timeout) throws IOException, InterruptedException, TimeoutException; // 带超时时间,确认所有消息,如果有未确认抛IOException
// 异步确认
void addConfirmListener(ConfirmListener listener);
public interface ConfirmListener {
    // multiple为true,即确认多个消息时,deliveryTag为最后一个确认的消息序号(消息序号从1开始)
    // multiple为false,即确认单个消息时,deliveryTag为确认的消息序号(消息序号从1开始)
    void handleAck(long deliveryTag, boolean multiple)
        throws IOException;

    void handleNack(long deliveryTag, boolean multiple)
        throws IOException;
}

消息从Exchange到Message Queue

失败的可能原因和解决方案

1.交换机未持久化

解决方案:

Channel:

    Exchange.DeclareOk exchangeDeclare(String exchange,
                                              String type,
                                              boolean durable,
                                              boolean autoDelete,
                                              boolean internal,
                                              Map<String, Object> arguments) throws IOException;

durable设为true

2.队列未持久化

解决方案:

Channel:

    Queue.DeclareOk queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments) throws IOException;

durable设为true

3.路由键和绑定键不匹配
解决方案:
指定交换机的备份交换机,到达交换机的消息没有对应的队列,将消息发送给备份交换机。

        Map<String,Object> arguments = new HashMap<String,Object>();
        arguments.put("alternate-exchange","TEST_ALTERNATE_EXCHANGE");
        channel.exchangeDeclare("TEST_EXCHANGE","topic", false, false, false, arguments);

或者回发消息,告知生产者。

消息在队列中

丢失的可能原因和解决方案

1.消息未持久化

解决方案:

        AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
                .deliveryMode(2); // 2代表持久化

Channel:

void basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body) throws IOException;

消息从Message Queue到Consumer

Channel:

String basicConsume(String queue, boolean autoAck, Consumer callback) throws IOException;
GetResponse basicGet(String queue, boolean autoAck) throws IOException;

注:basicConsume是监听器将队列中的消息推送给消费者;basicGet是消费者从队列中拉取。

自动Ack:autoAck为true,消费者接收到就返回Ack。
手动Ack:autoAck为为false;调用basicAck,消费者接收到消息,手动返回Ack,如处理完消息后确认:

Channel:

void basicAck(long deliveryTag, boolean multiple) throws IOException; // 确认
void basicNack(long deliveryTag, boolean multiple, boolean requeue)  throws IOException; // 拒绝一个或多个
void basicReject(long deliveryTag, boolean requeue) throws IOException;// 拒绝一个

requeue可以控制是否将消息重新入队。

如果消息持久化了,没有确认,或者拒绝后重新入队还没有被消费,重启RabbitMQ后会造成消息重复消费。

如果队列只对应一个消费者,每次都拒绝消息然后重新入队,会造成死循环。

注意:Ack不会到达生产者。

生产者知道消费者消费了消息

消费者回调

消息者执行生产者预定义的API(Http或TCP接口)回复消息。

补偿机制

方案一:生产者定时检查消费者回调超时的消息,重发消息。

方案二:消费者本地消息表记录超时未确认的消息,发送给生产者,生产者重发消息。

衰减机制

重发消息后又回调超时可以采取衰减机制,重发间隔越来越大,如:1分钟重发、2分钟重发、5分钟重发、10分钟重发…
注意控制重发次数,不能无限次重发。

消息幂等性

重发消息需保证消息不会被重复消费。

消息需有唯一标识,如转账消息对应有流水号(转账双方的ID + 转账时间戳)。

关于转账时间戳,可以从毫秒级推进至微秒或纳秒级,或者在同一毫秒内不允许再次支付:支付过于频繁,请稍后重试。

微信在转账双方相同,转账时间戳很接近时,会有提示:你已在当前商户支付过一笔相同金额的订单,请确认是否继续支付。防止用户转账误操作。

最终一致

可以引入对账机制防止最终的消息丢失。如银行和支付宝、银联的夜间对账(以银行的核心系统为准)。(支付宝在各大银行创建账户。支付宝用户跨行转账,会成为同一银行内用户和支付宝账户的转账)

消息的顺序性

一个队列对应多个消费者,即使消息被顺序取出,但消费者消费消息的速度不同,不能保证消息被顺序消费。如果消息有顺序性的需求(如发布微博,发表评论),可以一个队列只对应一个消费者进行消费。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风铃峰顶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值