9.最佳实践

来源:互联网 发布:kettle 删除表数据 编辑:程序博客网 时间:2024/06/15 06:03
  1. nameserver相关
    1. nameserver如何扩容/下线?
      1. 由于broker,producer,consunmer都涉及到nameserver地址列表,故推荐如下方式配置:

  2. broker相关
    1. broker如何下线?
      1. 先将slave kill掉, 防止kill master后consumer从slave消费
      2. 如果直接kill master,会造成生产者和消费者短暂的异常,而且可能造成部分数据丢失
      3. 可以采用清除broker的写权限的方式,当consumer将剩余的消息从该broker消费完毕后,再将该broker kill掉
    2. broker如何扩容?
      1. 启动一个干净的master<->slave组
      2. 此时broker上没有topic路由,故该broker并不会接受任何读写消息
      3. 需要将现有的topic路由在新的broker上建立起来:
        1. 第一种方法: 一个一个建立
        2. 第二种方法: 新broker启动之前,将老broker的topic.json复制到新broker配置的路径,这样新broker启动时会自动加载这些topic路由
    3. 多个broker部署在同一台服务器上时, 日志路径该如何指定?
      1. 由于broker启动时从固定的目录加载日志配置 : configurator.doConfigure(brokerConfig.getRocketmqHome() + "/conf/logback_broker.xml");
      2. 故只能将rocketmq的项目复制到不同的目录,分别修改配置并分别启动
  3. 消费队列相关
    1. 消费队列应该设置为多少才合适?
      1. 对于默认集群一个broker上建立8个消费队列,共4个broker,所以一共有4*8=32个队列,那么消费者最多有32个.
      2. 32个队列对于一般应用来说足以,不必再手动指定队列数
    2. 消费队列该如何扩容?
      1. 直接在console上修改topic路由配置即可,不会影响消息的发送和消费
    3. 顺序消息队列扩容如何保证消息的顺序性?
      1. 可以将writeQueueNums增大至需要的queue, 此时消费者readQueueNums还是跟以前一样,所以消费者继续从原先的队列中消费, 等待原先的消息消费完了,将readQueueNums改成和writeQueueNums一样,这样可以保证消息的顺序性
  4. 发送消息相关
    1. 所有的调用如果发生异常一定要记录SendResult对象,便于消息的回查
    2. 对于可以丢失的消息 - 采用oneway方式发送, 由于只是一个微秒级系统调用的开销, 参考1.零拷贝原理中的带收集功能的DMA
    3. 对于需要处理返回结果的消息:
      1. 同步调用 - 默认就是此种方式
        1. 只要没有异常,消息即可认为发送成功
        2. 如果消息是不可丢失的,那么需要进一步对消息返回的状态做检查,只要不是SEND_OK(参考7.producer) 就需要重新发送
        3. 对于不可丢失的消息需要做好降级处理
      2. 异步调用
        1. 异步调用与同步调用注意事项类似
    4. 消息的回查
      1. 异常情况下一般会打印消息的msgid, 可以根据此id直接查询具体的消息情况
      2. 正常情况下想查询消息, 需要在发送的时候设置keys字段
  5. 消费消息相关
    1. ConsumerGroupName
      1. 2 一个consumerGroup只对应一个topic
      2. 但是一个topic可以对应多个consumergroup, 不同的consumergroup之间的offset没有关系
    2. 消费消息要做到幂等
    3. 如果做不到幂等, 那么要做到一段时间的消息进行去重:
      1. 发送的时候生成一个唯一的id
      2. 消费端根据该id过滤, 即消费过的不再消费 (可以采用将id作为redis的key或者采用基于基数统计的HyperLogLog实现)
    4. 如何提高消息消费的吞吐量,降低响应时长?  并发消费模式 - 设置合理的并行度
      1. 消息消费的吞吐量和响应时长取决于并行度,  而并行度的设置取决于消息的消费过程
        比如存到数据库, 那么就取决于数据库能承受最多多少个任务进行并行操作, 并行度过大反而变慢, 太小又影响消费进度
      2. 无论是单个jvm还是多个jvm, 都可以增加消者的数量来提高并行度
      3. 单个消费者可以通过修改consumeMessageBatchMaxSize来增加并行度
        比如目前有100条消息, 如果consumeMessageBatchMaxSize为10, 那么会分成10个线程, 每个线程执行10条消息的消费, 那么并行度就为10, 该参数默认为1
      4. 通过修改consumeThreadMin(默认为20), consumeThreadMax(默认为64)来设置线程池的大小
    5. 消息堆积怎么办?
      1. 通过console可以看到每个客户端的消费状况, 从而可以知道消息有没有堆积
      2. 通常来说, 消息堆积只要不超过物理内存大小的80%, 基本都没有问题, 但是一旦消息长时间没有消费, 那么消息通常都已经落地到磁盘了, 如果从头开始消费时会产生大量的IO操作
        此时master为了避免高wio的影响, 会自动将消费的请求转到slave, 由slave提供消费, 一旦检测到消息量已经低于内存的80%, 那么又转向master进行消费
      3. 对于客户端代码来说, 如果是可以丢失的消息, 可以通过如下代码来跳过一批堆积的消息,从而更快的追上生产的进度:

        public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {long offset = msgs.get(0).getQueueOffset();String maxOffset = msgs.get(0).getProperty(MessageConst.PROPERTY_MAX_OFFSET);long diff = Long.parseLong(maxOffset) - offset;if (diff > 100000) {// TODO 消息堆积情况的特殊处理return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;}// TODO 正常消费过程return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;}


      4. 针对某条消息或某批消息消费时间大于1s或500ms的, 记录消息id, 以便定位问题

    6. 消费失败该如何处理?

      1. 对于一次性消费一批消息来说, 假如中间的某个消息消费失败, 可以设置一个下标标识一下, 从而使该消息之后的消息发送到RETRY队列, 进而重新消费:

        public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {for(int i = 0; i < msgs.size(); ++i) {//假设第i个消息消费失败, 设置为i-1, 从而使得第i个及之后的消息重新发回至brokercontext.setAckIndex(i - 1);break;}return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;}


      2. 或者干脆返回ConsumeConcurrentlyStatus.RECONSUME_LATER, 使这批消息全部发送至RETRY队列

0 0