本文介绍了DefaultMQPushConsumerImpl消费者,客户端负载均衡相关知识点。本文从DefaultMQPushConsumerImpl启动过程到实现负载均衡,从源代码一步一步分析,共分为6个部分进行介绍,其中第6个部分 rebalanceByTopic 为负载均衡的核心逻辑模块,具体过程运用了图文进行阐述。
 
介绍之前首先抛出几个问题:
1. 要做负载均衡,首先要解决的一个问题是什么?
2. 负载均衡是Client端处理还是Broker端处理?
个人理解:
1. 要做负载均衡,首先要做的就是信号收集。
所谓信号收集,就是得知道每一个consumerGroup有哪些consumer,对应的topic是谁。信号收集分为Client端信号收集与Broker端信号收集两个部分。
2. 负载均衡放在Client端处理。
具体做法是:消费者客户端在启动时完善rebalanceImpl实例,同时拷贝订阅信息存放rebalanceImpl实例对象中,另外也是很重要的一个步骤 -- 通过心跳消息,不停的上报自己到所有Broker,注册RegisterConsumer,等待上述过程准备好之后在Client端不断执行的负载均衡服务线程从Broker端获取一份全局信息(该consumerGroup下所有的消费Client),然后分配这些全局信息,获取当前客户端分配到的消费队列。
 
本文具体的内容:
I. copySubscription
Client端信号收集,拷贝订阅信息。

在DefaultMQPushConsumerImpl.start()时,会将消费者的topic订阅关系设置到rebalanceImpl的SubscriptionInner的map中用于负载:

private void copySubscription() throws MQClientException {
try {
//注:一个consumer对象可以订阅多个topic
Map<String, String> sub = this.defaultMQPushConsumer.getSubscription();
if (sub != null) {
for (final Map.Entry<String, String> entry : sub.entrySet()) {
final String topic = entry.getKey();
final String subString = entry.getValue();
SubscriptionData subscriptionData =
FilterAPI.buildSubscriptionData(this.defaultMQPushConsumer.getConsumerGroup(),//
topic, subString);
this.rebalanceImpl.getSubscriptionInner().put(topic, subscriptionData);
}
} if (null == this.messageListenerInner) {
this.messageListenerInner = this.defaultMQPushConsumer.getMessageListener();
} switch (this.defaultMQPushConsumer.getMessageModel()) {
case BROADCASTING:
break;
case CLUSTERING:
final String retryTopic = MixAll.getRetryTopic(this.defaultMQPushConsumer.getConsumerGroup());
SubscriptionData subscriptionData =
FilterAPI.buildSubscriptionData(this.defaultMQPushConsumer.getConsumerGroup(),//
retryTopic, SubscriptionData.SUB_ALL);
this.rebalanceImpl.getSubscriptionInner().put(retryTopic, subscriptionData);
break;
default:
break;
}
}
catch (Exception e) {
throw new MQClientException("subscription exception", e);
}
}
FilterAPI.buildSubscriptionData接口将订阅关系转换为SubscriptionData 数据,其中subString包含订阅tag等信息。另外,如果该消费者的消费模式为集群消费,则会将retry的topic一并放到。
 
II. 完善rebalanceImpl实例
Client继续收集信息:
 this.rebalanceImpl.setConsumerGroup(this.defaultMQPushConsumer.getConsumerGroup());
this.rebalanceImpl.setMessageModel(this.defaultMQPushConsumer.getMessageModel());
this.rebalanceImpl.setAllocateMessageQueueStrategy(this.defaultMQPushConsumer
.getAllocateMessageQueueStrategy());
this.rebalanceImpl.setmQClientFactory(this.mQClientFactory);
本文以DefaultMQPushConsumerImpl为例,因此this对象类型为DefaultMQPushConsumerImp。
 
III. this.rebalanceService.start()
开启负载均衡服务。this.rebalanceService是一个RebalanceService实例对象,它继承与ServiceThread,是一个线程类。 this.rebalanceService.start()执行时,也即执行RebalanceService线程体:
   @Override
public void run() {
log.info(this.getServiceName() + " service started"); while (!this.isStoped()) {
this.waitForRunning(WaitInterval);
this.mqClientFactory.doRebalance();
} log.info(this.getServiceName() + " service end");
}
IV. this.mqClientFactory.doRebalance
客户端遍历消费组table,对该客户端上所有消费者独立进行负载均衡,分发消费队列:
 public void doRebalance() {
for (String group : this.consumerTable.keySet()) {
MQConsumerInner impl = this.consumerTable.get(group);
if (impl != null) {
try {
impl.doRebalance();
} catch (Exception e) {
log.error("doRebalance exception", e);
}
}
}
}
V. MQConsumerInner.doRebalance
由于本文以DefaultMQPushConsumerImpl消费过程为例,即DefaultMQPushConsumerImpl.doRebalance:
@Override
public void doRebalance() {
if (this.rebalanceImpl != null) {
this.rebalanceImpl.doRebalance();
}
}
步骤II 中完善了rebalanceImpl实例,为调用rebalanceImpl.doRebalance()提供了初始数据。
rebalanceImpl.doRebalance()过程如下:
public void doRebalance() {
     // 前文copySubscription中初始化了SubscriptionInner
Map<String, SubscriptionData> subTable = this.getSubscriptionInner();
if (subTable != null) {
for (final Map.Entry<String, SubscriptionData> entry : subTable.entrySet()) {
final String topic = entry.getKey();
try {
this.rebalanceByTopic(topic);
} catch (Exception e) {
if (!topic.startsWith(MixAll.RETRY_GROUP_TOPIC_PREFIX)) {
log.warn("rebalanceByTopic Exception", e);
}
}
}
} this.truncateMessageQueueNotMyTopic();
}

VI. rebalanceByTopic -- 核心步骤之一
rebalanceByTopic方法中根据消费者的消费类型为BROADCASTING或CLUSTERING做不同的逻辑处理。CLUSTERING逻辑包括BROADCASTING逻辑,本部分只介绍集群消费负载均衡的逻辑。
集群消费负载均衡逻辑主要代码如下(省略了log等代码):

//1.从topicSubscribeInfoTable列表中获取与该topic相关的所有消息队列
Set<MessageQueue> mqSet = this.topicSubscribeInfoTable.get(topic);
//2. 从broker端获取消费该消费组的所有客户端clientId
List<String> cidAll = this.mQClientFactory.findConsumerIdList(topic, consumerGroup);
f (null == mqSet) { ... }
if (null == cidAll) { ... }
if (mqSet != null && cidAll != null) {
List<MessageQueue> mqAll = new ArrayList<MessageQueue>();
mqAll.addAll(mqSet);
Collections.sort(mqAll);
Collections.sort(cidAll);      // 3.创建DefaultMQPushConsumer对象时默认设置为AllocateMessageQueueAveragely
AllocateMessageQueueStrategy strategy = this.allocateMessageQueueStrategy; List<MessageQueue> allocateResult = null;
try {
         // 4.调用AllocateMessageQueueAveragely.allocate方法,获取当前client分配消费队列
allocateResult = strategy.allocate(
this.consumerGroup,
this.mQClientFactory.getClientId(),
mqAll,
cidAll);
} catch (Throwable e) {
return;
}
    // 5. 将分配得到的allocateResult 中的队列放入allocateResultSet 集合
Set<MessageQueue> allocateResultSet = new HashSet<MessageQueue>();
if (allocateResult != null) {
allocateResultSet.addAll(allocateResult);
}

     //6. 更新updateProcessQueue
boolean changed = this.updateProcessQueueTableInRebalance(topic, allocateResultSet);
if (changed) {
this.messageQueueChanged(topic, mqSet, allocateResultSet);
}
}
注:BROADCASTING逻辑只包含上述的1、6。
集群消费负载均衡逻辑中的1、2、4这三个点相关知识为其核心过程,各个点相关知识如下:
 
第1点:从topicSubscribeInfoTable列表中获取与该topic相关的所有消息队列

第2点: 从broker端获取消费该消费组的所有客户端clientId
首先,消费者对象不断地向所有broker发送心跳包,上报自己,注册并更新订阅关系以及客户端ChannelInfoTable;之后,客户端在做消费负载均衡时获取那些消费客户端,对这些客户端进行负载均衡,分发消费的队列。具体过程如下图所示:

第4点:调用AllocateMessageQueueAveragely.allocate方法,获取当前client分配消费队列
 

注:上图中cId1、cId2、...、cIdN通过 getConsumerIdListByGroup 获取,它们在这个ConsumerGroup下所有在线客户端列表中。
当前消费对进行负载均衡策略后获取对应的消息消费队列。具体的算法很简单,可以看源码。
 
 
 

rocketmq消费负载均衡--push消费为例的更多相关文章

  1. RocketMQ-2.RocketMQ的负载均衡

    目录 RocketMQ的负载均衡 producer对MessageQueue的负载均衡 producer负载均衡 系统计算路由MessageQueue 自定义路由MessageQueue Consum ...

  2. Nginx 简单的负载均衡配置演示样例

    近期在做开放查询应用的时候,因为数据两天特别多,两千多万条呢,用户訪问需求也比較大,所以就用nginx做了 负载均衡,以下是改动之后的相关内容. http://www.cnblogs.com/xiao ...

  3. RocketMQ(消息重发、重复消费、事务、消息模式)

    分布式开放消息系统(RocketMQ)的原理与实践 RocketMQ基础:https://github.com/apache/rocketmq/tree/rocketmq-all-4.5.1/docs ...

  4. 深入剖析 RocketMQ 源码 - 负载均衡机制

    RocketMQ作为一款流行的消息中间件在各大互联网应用广泛,本文主要分析RocketMq在消息生产和消费过程中的负载均衡机制,并创新提出消费端负载均衡策略的改写以实现固定IP消费的可能.

  5. 通过http、https域名访问静态网页、nginx配置负载均衡(nginx配置)

    很多场景下需要可以通过浏览器访问静态网页,不想把服务器ip地址直接暴露出来,通过nginx可以解决这个问题. 实现http域名访问静态网页 1.域名解析配置(本文都是以阿里云为例,其他平台,操作步骤类 ...

  6. ③SpringCloud 实战:使用 Ribbon 客户端负载均衡

    这是SpringCloud实战系列中第三篇文章,了解前面第两篇文章更有助于更好理解本文内容: ①SpringCloud 实战:引入Eureka组件,完善服务治理 ②SpringCloud 实战:引入F ...

  7. 玩转Spring Cloud之服务注册发现(eureka)及负载均衡消费(ribbon、feign)

    如果说用Spring Boot+Spring MVC是开发单体应用(或单体服务)的利器,那么Spring Boot+Spring MVC+Spring Cloud将是开发分布式应用(快速构建微服务)的 ...

  8. spring cloud(服务消费者(利用ribbon实现服务消费及负载均衡)——初学二)

    Ribbon是一个基于HTTP和TCP客户端的负载均衡器,利用ribbon实现服务消费,并实现客户端的负载均衡. 一.准备工作(利用上一节的内容) 启动服务注册中心 启动computer-servic ...

  9. Spring Cloud ---- 服务消费与负载均衡(feign)

    feign是一个声明式的伪客户端,只需要创建一个接口并且注解,它具有可插拔的特性.feign集合了Ribbon,再与Eurake结合实现服务的注册发现与负载均衡.结合Hystrix,具有熔断功能. 1 ...

随机推荐

  1. flex 伸缩布局

    伸缩布局 布局的传统解决方案,基于盒状模型,依赖 display属性 + position属性 + float属性.它对于那些特殊布局非常不方便.CSS3在布局方面做了非常大的改进,使得我们对块级元素 ...

  2. asp.net远程调用WebService的两种方法(转载)

    一,静态方法在“解决方案‘项目名’” -> 相应的文件夹,如“Web References” ->右键“添加WEB引用”->在URL里写入地址.二,动态方法在“解决方案‘项目名’” ...

  3. jest js 测试框架-简单方便人性化

    1. 安装 yarn global add jest-cli or npm install -g jest-cli 备注:可以安装为依赖不用全局安装 2. 项目代码 a. 项目初始化 yarn ini ...

  4. Go语言学习笔记 package

    加 Golang学习 QQ群共同学习进步成家立业工作 ^-^ 群号:96933959 strings import "strings" strings包实现了用于操作字符的简单函数 ...

  5. 利用OsCache实现后端轮循

    轮循随处可见,最常用的是APP首页的一些促销活动,一两秒切换一张图片,让前端实现起来也不难.这里说下后端的轮循,实现原理是数组+缓存.将数组放入缓存,指定缓存失效时间,如果是在失效前从缓存中取数据,那 ...

  6. Python中dir()与help()的使用

    python内置了很多内置函数.类方法属性及各种模块.当我们想要当我们想要了解某种类型有哪些属性方法以及每种方法该怎么使用时,我们可以使用dir()函数和help()函数在python ide交互式模 ...

  7. JAVA-Unit04: SQL(高级查询)

    Unit04: SQL(高级查询) 查看SMITH的上司在那个城市工作? SELECT e.ename,m.ename,d.loc FROM emp e,emp m,dept d WHERE e.mg ...

  8. JAVASE02-Unit011: TCP通信(小程序)

    TCP通信(小程序) server端: package chat; import java.io.BufferedReader; import java.io.IOException; import ...

  9. 用 Linux blkid 命令查找块设备详情

    今天我们将会向你展示如何使用 lsblk 和 blkid 工具来查找关于块设备的信息,我们使用的是一台安装了 CentOS 7.0 的机器. lsblk lsblk 是一个 Linux 工具,它会显示 ...

  10. linux命令ls -l的total是怎么计算出来的?

    自己手写实现ls -l命令纠结了很久的地方,刚才总算搞明白了.直接上代码重要部分 long nTotalBlocks = 0; DIR* dp = opendir(dirPathName); stru ...