RocketMq-Consumer

producer源码结构如下:

我们通常使用mq接受消息,实例化consumer的方式就是:

DefaultMQPushConsumer consumer =  new  DefaultMQPushConsumer( "MyTopic-Consume-Single" );
                                                        //实际调用了
public  DefaultMQPushConsumer(String consumerGroup) {
         this (consumerGroup, (RPCHook) null new  AllocateMessageQueueAveragely());     //注意此处默认创建了一个消费负载均衡策略
}


所以我们就从DefaultMQPushConsumer开始说起吧。

DefaultMQPushConsumer继承了ClientConfig并且实现了MQPushConsumer接口。与producer类似,同时注入一个重要的属性

protected final transient DefaultMQPushConsumerImpl defaultMQPushConsumerImpl;

public  class  DefaultMQPushConsumer  extends  ClientConfig  implements  MQPushConsumer {
 
     protected  final  transient  DefaultMQPushConsumerImpl defaultMQPushConsumerImpl;


MQPushConsumer接口定义了一些最基本的方法,例如:

void  registerMessageListener( final  MessageListenerConcurrently messageListener);             //消费者注册监听器。
void  subscribe( final  String topic,  final  String subExpression)  throws  MQClientException;          //设置订阅的topic以及tag的方法。


ClientConfig在producer里已经介绍过了,就不重复说了。

而消费消息大致流程如下,我们具体看看各个步骤都做了什么。

consumer.setNamesrvAddr( "10.3.254.52:9876" );                                //设置namesrv,实际是调用ClientConfig.setNamesrvAddr(String namesrvAddr);
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);   //设置消费起始位置。
consumer.setConsumeMessageBatchMaxSize( 5 );                                  //设置每次消费消息数量。
consumer.subscribe( "TopicTest" "*" );                                       //设置订阅的topic和tag。
consumer.registerMessageListener( new  StockListener() );                     //注册消费端的监听器,分为普通消费监听器和顺序消费监听器。其实就是把StockListener赋值给DefaultMQPushConsumer的MessageListener属性。
consumer.start();                                                           //启动消费者。

重点看一下,启动消费者都干了些什么?

   consumer.start();该方法实际调用了DefaultMQPushConsumerImpl .start()     【以下环节都是针对普通消费顺序】

  1、首先进行状态检查,如果是CREATE_JUST才进行以下操作:

  2、将状态置为START_FAILED。

  3、this.checkConfig();进行参数检查,检查范围比较多,大致如下:

       3.1)consumerGroup不能为空,不能含有非法字符,长度不能超过255。

       3.2) 不能为默认的消费者组名"DEFAULT_CONSUMER"。

       3.3)消费模型不能为空,即必须是集群消费或者广播消费的一种【默认是集群消费】

       3.4)起始消费位置不能为空。【只对初次消费有效】

       3.5)消费时间戳不能为空 -- (消息回溯,默认默认值是半小时以前)【只对初次消费有效】

       3.6)消费负载均衡策略不能为空,默认是AllocateMessageQueueAveragely。下面我们看看,这个策略是怎么做到消费负载均衡的。

               public List<MessageQueue> allocate(String consumerGroup, String currentCID, List<MessageQueue> mqAll, List<String> cidAll) ;这个方法就是做消费负载均衡的。他的参数currentCID代表这个消费者组的其中一个消费者                                 id,List<MessageQueue> mqAll代表所有的订阅的topic下所有的messageQueue, List<String> cidAll代表消费者组的消费者集合。该方法最终会为每个消费者分配对应的messageQueue。具体算法查看源码。例如,一个8个                                     messageQueue,消费者组内有三个消费者。

               最终分配情况大致如下:

                            sonsumer0          consumer1          consumer2

                              queue0                queue3                 queue6

                              queue1                queue4                 queue7

                              queue2                queue5

        3.7)存储订阅关系的subscription不能为空,private Map<String /* topic */, String /* sub expression */subscription new HashMap<String, String>();

        3.8)消费端注册的监听器不能为空,并检查是普通消费还是顺序消费,并且必须是这二者其一。

        3.9)检查消费者默认线程池最小和最大是否是在1~1000且最小值不能大于最大值。【默认最小20,最大64】每1分钟调整一次线程池,这也是针对消费者来说的,具体为如果消息堆积超过10W条,则调大线程池,最多64个线程;如果消      息堆积少于8W条,则调小线程池。

        3.10)检查单队列并行消费最大跨度consumeConcurrentlyMaxSpan,不能小于1不能大于65535。consumeConcurrentlyMaxSpan这个值默认是2000,当RocketMQ发现本地缓存的消息的最大值-最小值差距大于这个值(2000)的时候,会  触发流控——也就是说如果头尾都卡住了部分消息,达到了这个阈值就不再拉取消息。

        3.11)检查拉消息本地队列缓存消息最大数pullThresholdForQueue,不能小于1,大于65535。【默认是1000】。含义是:消费者不间断的从broker拉取消息,消息拉取到本地队列,然后本地消费线程消费本地消息队列,只是一个异步过    程,拉取线程不会等待本地消费线程,这种模式实时性非常高(本地消息队列达到解耦的效果,响应时间减少)。对消费者对本地队列有一个保护,因此本地消息队列不能无限大,否则可能会占用大量内存。ps:还记得broker启动至少需要4G的磁盘吗?还记得每条消息的最大值默认是4M吗?那这里设置的1000是巧合呢还是有意为之?

        3.12)检查pullThresholdForTopic值是否为默认的-1,如果不是则必须在1~65535之间。【表示每个topic在本地缓存最多的消息条数】

        3.13)检查消息缓存值pullThresholdSizeForQueue,不能小于1M,不能大于1024M。【默认是100M】

        3.14)检查每次批量消费规模consumeMessageBatchMaxSize,不能小于1条,不能大于1024条。【默认是1条】

        3.15)检查每次从broker批量拉取消息数量pullBatchSize,不能小于1条,不能大于1024条。【默认32条】

   4、调用this.copySubscription();Client端信号收集,拷贝订阅信息,将消费者的topic订阅关系设置到rebalanceImpl的SubscriptionInner的map中用于负载。

   5、如果是集群消费模式,则将客户端实例名由"DEFAULT"变成客户端实例的进程号。

   6、调用MQClientManager.getInstance().getAndCreateMQClientInstance(this.defaultMQPushConsumerthis.rpcHook);方法,以当前consumer作为参数实例化一个消费端实例。

   7、接着完善rebalanceImpl实例,给他设置消费者组,消费模型,消费端负载均衡策略,以及消费端实例。

   8、构建PullAPIWrapper对象,该对象封装了具体拉取消息的逻辑,PULL,PUSH模式最终都会调用PullAPIWrapper类的方法从Broker拉取消息。

   9、this.pullAPIWrapper.registerFilterMessageHook(filterMessageHookList);又是什么钩子,感觉可有可无,因为跟进去发现接口定义的方法没被实现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值