RabbitMQ和kafka对于消费失败处理总结

一、kafka

1、kafka消息模型总结

发送消息到topic,每个topic可以分成多个Partition,每个Partition对用一个消费者消费,属于无状态消息,Partition每个消息对应唯一的offset,通过zk保存信息,消费端维护offset,持久化属于日志型持久话默认七天删除消息。


RabbitMQ和kafka对于消费失败处理总结_第1张图片
kafka
2、消费失败处理方案(个人思考)

业务处理异常时,暂不提交offset,利用数据库(关系型或非关系型)保存失败的消息记录,根据失败策略处理相应消息。保存好记录之后可以提交offset。
失败处理可以隔五分钟再往对应的消息队列发送该消息(发送成功就次数+1,将消息id也传入消息队列,方便记录失败次数)复杂情况可能需要记录消息失败的次数,到达一定次数后,改为手工处理

3、保证不丢失消息处理

参考:https://www.jianshu.com/p/7a6deaba34d2

一般是要求起码设置如下4个参数:
1、给topic设置replication.factor参数:
这个值必须大于1,要求每个partition必须有至少2个副本在kafka服务端
2、设置min.insync.replicas参数:
这个值必须大于1,这个是要求一个leader至少感知到有至少一个follower还跟自己保持联系,没掉队,这样才能确保leader挂了还有一个follower吧在producer端
3、设置acks=all:
这个是要求每条数据,必须是写入所有replica之后,才能认为是写成功了在producer端
4、设置retries=MAX
(很大很大很大的一个值,无限次重试的意思):这个是要求一旦写入失败,就无限重试,卡在这里了我们生产环境就是按照上述要求配置的,这样配置之后,至少在kafka broker端就可以保证在leader所在broker发生故障,进行leader切换时,数据不会丢失

二、RabbitMQ

1、消费失败处理方案
(1)相关配置
spring:
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest
    listener:
      simple:
        retry:                    #抛异常会按retry策略重发(建议不在程序内抛异常,记录失败消息,然后确认)
          enabled: true           #允许重发
          max-attempts: 5         #重发次数
          initial-interval: 30000 #重发间隔时间
        acknowledge-mode: manual  #不确认宕机重启时会重新消费,确认失败会一直重发
    publisher-confirms: true      # 如果消息没有到exchange,则confirm回调,ack=false,
                                  # 如果消息到达exchange,则confirm回调,ack=true
    publisher-returns: true       #exchange到queue成功,则不回调return
                                  #exchange到queue失败,则回调return(需设置mandatory=true,否则不回回调,消息就丢了)

消息手动确认模式的几点说明:
1、监听的方法内部必须使用channel进行消息确认,包括消费成功或消费失败
2、如果不手动确认,也不抛出异常,消息不会自动重新推送(包括其他消费者),因为对于rabbitmq来说始终没有接收到消息消费是否成功的确认,并且Channel是在消费端有缓存的,没有断开连接
3、如果rabbitmq断开,连接后会自动重新推送(不管是网络问题还是宕机)
4、如果消费端应用重启,消息会自动重新推送
5、如果消费端处理消息的时候宕机,消息会自动推给其他的消费者
6、如果监听消息的方法抛出异常,消息会按照listener.retry的配置进行重发,但是重发次数完了之后还抛出异常的话,消息不会重发(也不会重发到其他消费者),只有应用重启后会重新推送。因为retry是消费端内部处理的,包括异常也是内部处理,对于rabbitmq是不知道的(此场景解决方案后面有)
7、spring.rabbitmq.listener.retry配置的重发是在消费端应用内处理的,不是rabbitqq重发

(2)方案描述

参考:https://my.oschina.net/dengfuwei/blog/1595047

消费确认机制改为manual手动确认,在消费方法中try catch中,记录消费失败的消息,然后basicAck确认,通过自定义重试策略取出失败的消息重新消费,失败达到一定次数手动处理
需要注意的 basicAck 方法需要传递两个参数:
(1)deliveryTag(唯一标识 ID):当一个消费者向 RabbitMQ 注册后,会建立起一个 Channel ,RabbitMQ 会用 basic.deliver 方法向消费者推送消息,这个方法携带了一个 delivery tag, 它代表了 RabbitMQ 向该 Channel 投递的这条消息的唯一标识 ID,是一个单调递增的正整数,delivery tag 的范围仅限于 Channel
(2)multiple:为了减少网络流量,手动确认可以被批处理,当该参数为 true 时,则可以一次性确认 delivery_tag 小于等于传入值的所有消息

2、保证消息不丢失

失败重发参考:https://www.cnblogs.com/xujishou/p/6288623.html

1、设置消息持久化
2、利用confirm模式,发送失败重新放送
(很多帖子说,confirm模式但是confirm回调测试没有消息数据无法重发,建议:https://www.cnblogs.com/xujishou/p/6288623.html)
3、return exchange到队列失败回调,可以获取到消息相关消息可重发

confirm模式 重发消息,生成CorrelationData,重新发送

private CorrelationData getCorrelationData(String exchange, String routeKey, byte[] body) {
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setReceivedExchange(exchange);
        messageProperties.setReceivedRoutingKey(routeKey);
        Message message = new Message(body, messageProperties);
        CorrelationData correlationData = new CorrelationData();
        correlationData.setReturnedMessage(message);
        return correlationData;
    }

@Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        if (ack) {
            System.out.println("confirm消息发送成功:" + cause);
        } else {
            String msg = new String(correlationData.getReturnedMessage().getBody());
            System.out.println("confirm消息发送失败:" + msg);
            MessageProperties messageProperties = correlationData.getReturnedMessage().getMessageProperties();
            rabbitTemplate.convertAndSend(messageProperties.getReceivedExchange(),
                    messageProperties.getReceivedRoutingKey(),
                    correlationData.getReturnedMessage(),
                    correlationData);
        }
    }
publisher-confirms: true      # 如果消息没有到exchange,则confirm回调,ack=false,
                                  # 如果消息到达exchange,则confirm回调,ack=true
publisher-returns: true       #exchange到queue成功,则不回调return
                                  #exchange到queue失败,则回调return(需设置mandatory=true,否则不回回调,消息就丢了)

三、疑问

发送消息时,消息发送成功,业务失败。业务成功消息发送失败?
保证业务处理成功后发送消息,发送失败一直重试发送消息。

四、demo

kafka:https://github.com/huangxiongbiao12/kafka.git
rabbitmq:https://github.com/huangxiongbiao12/rabbitmq-demo.git

你可能感兴趣的:(RabbitMQ和kafka对于消费失败处理总结)