RocketMQ常见面试问题

  1. 如何避免消息重复消费?
    RocketMQ不保证消息不重复,如果需要保证严格的不重复消费,需要自己在业务端去重,接口幂等性保障,消费端处理业务消息需要要吃幂等性,可以用Redis、关系数据库等来配合验证消息是否被消费。
  2. RocketMQ如何保证消息的可靠性传输
    produce端: 不采用oneway发送,使用同步或则异步方式发送,做好重试,但是重试的Message key必须唯一。投递的日志需要保存,关键字段,投递时间,投递状态,重试次数,请求体,响应体
    broker端: 双主双从架构,NamerServer需要多节点,同步双写、异步刷盘(同步刷盘则可靠性更高,但是性能差点,根据业务选择)
    consumer端: 消息消费务必保留日志,即消息的元数据和消息体,做好幂等性处理
  3. 消息发生大量堆积应该怎么处理(大量堆积在broker里面)
    线上故障了,怎么处理?
    消息堆积了10小时,有几千万条消息待处理,现在怎么办?
    修复consumer,然后慢慢消费?也需要几小时才可以消费完成,新的消息怎么办?
    正确的姿势:
    临时topic队列扩容,并提高消费能力,但是如果增加Consumer数量,堆积的topic里面的message queue 数量固定,过多的
    consumer不能分配到message queue。编写临时的处理分发程序,从旧topic快速读取到临时的新topic中,新topic的queue数量扩容多倍,然后再启动更多的consumer进行再临时的新的topic里消费

    消息刷盘

  4. 同步刷盘:
    如上图所示,只有在消息真正持久化至磁盘后RocketMQ的Broker端才会真正返回Producer 端一个成功的ACK响应。同步刷盘对MQ消息可靠性来说是一种不错的保障, 但是性能上 会有较大影响,一般适用于金融业务应用该模式较多。

  5. 异步刷盘:
    能够充分利用OS的PageCache的优势,只要消息写入PageCache即可将成功的ACK返回给 Producer端。消息刷盘采用后台异步线程提交的方式进行,降低了读写延迟,提高了MQ的 性能和吞吐量。

    消息重试

    Consumer消费消息失败后,要提供一种重试机制,令消息再消费一次。Consumer消费消息 失败通常可以认为有以下几种情况:
    由于消息本身的原因,例如反序列化失败,消息数据本身无法处理(例如话费充值,当前消 息的手机号被注销,无法充值)等。这种错误通常需要跳过这条消息,再消费其它消息,而 这条失败的消息即使立刻重试消费,99%也不成功,所以最好提供一种定时重试机制,即过 10秒后再重试。
    由于依赖的下游应用服务不可用,例如db连接不可用,外系统网络不可达等。遇到这种错 误,即使跳过当前失败的消息,消费其他消息同样也会报错。这种情况建议应用sleep 30s, 再消费下一条消息,这样可以减轻Broker重试消息的压力。
    RocketMQ会为每个消费组都设置一个Topic名称为“%RETRY%+consumerGroup”的重试队列 (这里需要注意的是,这个Topic的重试队列是针对消费组,而不是针对每个Topic设置的), 用于暂时保存因为各种异常而导致Consumer端无法消费的消息。考虑到异常恢复起来需要 一些时间,会为重试队列设置多个重试级别,每个重试级别都有与之对应的重新投递延时, 重试次数越多投递延时就越大。RocketMQ对于重试消息的处理是先保存至Topic名称为 “SCHEDULE_TOPIC_XXXX”的延迟队列中,后台定时任务按照对应的时间进行Delay后重 新保存至“%RETRY%+consumerGroup”的重试队列中。

    消息从投

    生产者在发送消息时,同步消息失败会重投,异步消息有重试,oneway没有任何保证。消息 重投保证消息尽可能发送成功、不丢失,但可能会造成消息重复,消息重复在RocketMQ中 是无法避免的问题。消息重复在一般情况下不会发生,当出现消息量大、网络抖动,消息重 复就会是大概率事件。另外,生产者主动重发、consumer负载变化也会导致重复消息。如下 方法可以设置消息重试策略:
    retryTimesWhenSendFailed:同步发送失败重投次数,默认为2,因此生产者会最多尝试发送 retryTimesWhenSendFailed + 1次。不会选择上次失败的broker,尝试向其他broker发送,最 大程度保证消息不丢。超过重投次数,抛出异常,由客户端保证消息不丢。当出现 RemotingException、MQClientException和部分MQBrokerException时会重投。
    retryTimesWhenSendAsyncFailed:异步发送失败重试次数,异步重试不会选择其他broker,仅 在同一个broker上做重试,不保证消息不丢。
    retryAnotherBrokerWhenNotStoreOK:消息刷盘(主或备)超时或slave不可用(返回状态非 SEND_OK),是否尝试发送到其他broker,默认false。十分重要消息可以开启。

    死信队列

    死信队列用于处理无法被正常消费的消息。当一条消息初次消费失败,消息队列会自动进行消息重试;达到最大重试次数后,若消费依然失败,则表明消费者在正常情况下无法正确地消费该消息,此时,消息队列 不会立刻将消息丢弃,而是将其发送到该消费者对应的特殊队列中。
    RocketMQ将这种正常情况下无法被消费的消息称为死信消息(Dead-Letter Message),将存 储死信消息的特殊队列称为死信队列(Dead-Letter Queue)。在RocketMQ中,可以通过使 用console控制台对死信队列中的消息进行重发来使得消费者实例再次进行消费。

    开启ACL机制

    在broker.conf里面加上: aclEnable = true
    在plain_acl.yml里面进行账号权限设置
    如果找不到包,需要把jdk的ext绝对路径添加在tools.sh里面的配置上,如下:
    JAVA_OPT=”${JAVA_OPT} -Djava.ext.dirs=${BASE_DIR}/lib:${JAVA_HOME}/jre/lib/ext:${JAVA_HOME}/lib/ext:/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.292.b10-0.el8_3.x86_64/jre/lib/ext
    如不行,需要把ext里面sunjce_provider.jar拷贝到lib目录下