生产者启动流程



DefaultMQProducer是RocketMQ中默认的生产者实现

核心属性:

namesrvAddr: 继承自 ClientConfig,表示 RocketMQ 集群的Namesrv 地址,如果是多个则用分号分开。比如:127.0.0.1:9876;127.0.0.2:9876。

clientIP: 使用的客户端程序所在机器的 IP地址。支持 IPv4和IPv6,IPv4 排除了本地的环回地址(127.0.xxx.xxx)和私有内网地址(192.168.xxx.xxx)。这里需要注意的是,如果 Client 运行在Docker 容器中,获取的 IP 地址是容器所在的 IP 地址,而非宿主机的IP地址。

instanceName: 实例名,每个实例都需要取唯一的名字,因为有时我们会在同一个机器上部署多个程序进程,如果名字有重复就会导致启动失败。

vipChannelEnabled: 这是一个 boolean 值,表示是否开启 VIP通道。VIP 通道和非VIP通道的区别是:在通信过程中使用的端口号不同。

clientCallbackExecutorThreads: 客户端回调线程数。该参数表示 Netty 通信层回调线程的个数 ,默认值 Runtime.getRuntime().availableProcessors()表示当前CPU的有效个数。

pollNameServerInterval: 获取 Topic 路由信息的间隔时长,单位为 ms,默认为30 000ms。

heartbeatBrokerInterval: 与Broker心跳间隔的时长,单位为 ms,默认为30 000ms。

defaultMQProducerImpl: 默认生产者的实现类,其中封装了Broker的各种API(启动及关闭生产者的接口)。如果你想自己实现一个生产者,可以添加一个新的实现,保持DefaultMQProducer对外接口不变,用户完全没有感知。

producerGroup: 生产者组名,这是一个必须传递的参数。RocketMQ-way表示同一个生产者组中的生产者实例行为需要一致。

sendMsgTimeout: 发送超时时间,单位为ms。

compressMsgBodyOverHowmuch: 消息体的容量上限,超过该上限时消息体会通过ZIP进行压缩,该值默认为4MB。

retryTimesWhenSendFailed: 同步发送失败后重试的次数。默认为2次,也就是说,一共有3次发送机会。

retryTimesWhenSendAsyncFailed: 异步发送失败后重试的次数。默认为2次。异步重试是有条件的重试,并不是每次发送失败后都重试 。 源代码可以查看 org.apache.rocketmq.client.impl.MQClientAPIImpl.sendMessageAsync()方法 。 每次发送失败抛出异常后 , 通过执行onExceptionImpl()方法来决定什么场景进行重试

核心方法

start():这是启动整个生产者实例的入口,主要负责校验生产者的配置参数是否正确,并启动通信通道、各种定时计划任务、Pull服务、Rebalance服务、注册生产者到Broker等操作。

shutdown(): 关闭本地已注册的生产者,关闭已注册到Broker的客户端。

fetchPublishMessageQueues(Topic): 获取一个Topic有哪些Queue。在发送消息、Pull消息时都需要调用。

send(Message msg): 同步发送普通消息。

send(Message msg,long timeout): 同步发送普通消息(超时设置)。

send(Message msg,SendCallback sendCallback): 异步发送普通消息。

send(Message msg , SendCallback sendCallback , long timeout): 异步发送普通消息(超时设置)。

sendOneway(Message msg): 发送单向消息。只负责发送消息,不管发送结果。

send(Message msg,MessageQueue mq): 同步向指定队列发送消息。

send(Message msg,MessageQueue mq,long timeout): 同步向指定队列发送消息(超时设置)。同步向指定队列发送消息时,如果只有一个发送线程,在发送到某个指定队列中时,这个指定队列中的消息是有顺序的,那么就按照发送

时间排序;如果某个Topic的队列都是这种情况,那么我们称该Topic的全部消息是分区有序的。

send(Message msg , MessageQueue mq , SendCallback sendCallback): 异步发送消息到指定队列。

send(Message msg , MessageQueue mq , SendCallback sendCallback,long timeout): 异步发送消息到指定队列(超时设置)。

send(Message msg,MessageQueueSelector selector,Object arg,SendCallback sendCallback): 自定义消息发送到指定队列。通过实现MessageQueueSelector接口来选择将消息发送到哪个队列。

send(Collection<Message>msgs): 批量发送消息。

核心管理接口

createTopic(String key , String newTopic , int queueNum): 创建Topic。

viewMessage(String offsetMsgId): 根据消息id查询消息内容。

启动流程

生产者启动的流程比消费者启动的流程更加简单,一般用户使用DefaultMQProducer的构造函数构造一个生产者实例,并设置各种参数。比如Namesrv地址、生产者组名等,调用start()方法启动生产者实例,start()方法调用了生产者默认实现类的start()方法启动,这里我们主要讲实现类的start()方法内部是怎么实现的,



第一步: 通过 switch-case 判断当前生产者的服务状态,创建时默认状态是CREATE_JUST。设置默认启动状态为启动失败。

第二步: 执行 DefaultMQProducerImpl.checkConfig()方法。校验生产者实例设置的各种参数。比如生产者组名是否为空、是否满足命名规则、长度是否满足等。

第三步: 执行 DefaultMQProducer.changeInstanceNameToPID()方法。校验instancename,如果是默认名字则将其修改为进程id。

第四步: 执行 MQClientManager.getOrCreateMQClientInstance()方法。根据生产者组名获取或者初始化一个

MQClientInstance实例与 clientId是一一对应的,而clientId是由clientIP、instanceName 及 unitName 构成的。因此,为了减少客户端的使用资源,如果将所有的 instanceName和 unitName设置为同样的值,就会只创建一个 MQClientInstance实例

ClientConfig.java

public String buildMQClientId() {
StringBuilder sb = new StringBuilder();
sb.append(this.getClientIP());
sb.append("@");
sb.append(this.getInstanceName());
if (!UtilAll.isBlank(this.unitName)) {
sb.append("@");
sb.append(this.unitName);
}
if (enableStreamRequestType) {
sb.append("@");
sb.append(RequestType.STREAM);
}
return sb.toString();
}

MQClientInstance 实例的功能是管理本实例中全部生产者与消费者的生产和消费行为 。

org.apache.rocketmq.client.impl.factory.MQClientInstance

类的核心属性

public class MQClientInstance {
...
private final ClientConfig clientConfig;
private final int instanceIndex;
private final String clientId;
private final long bootTimestamp = System.currentTimeMillis();
//当前client实例的全部生产者的内部实例。
private final ConcurrentMap<String/* group */, MQProducerInner> producerTable = new ConcurrentHashMap<String, MQProducerInner>();
//当前client实例的全部消费者的内部实例。
private final ConcurrentMap<String/* group */, MQConsumerInner> consumerTable = new ConcurrentHashMap<String, MQConsumerInner>();
//当前client实例的全部管理实例。
private final ConcurrentMap<String/* group */, MQAdminExtInner> adminExtTable = new ConcurrentHashMap<String, MQAdminExtInner>();
private final NettyClientConfig nettyClientConfig;
//其实每个client也是一个Netty Server,也会支持Broker访问,这里实现了全部client支持的接口
private final MQClientAPIImpl mQClientAPIImpl;
//管理接口的本地实现类
private final MQAdminImpl mQAdminImpl;
//当前生产者、消费者中全部Topic的本地缓存路由信息
private final ConcurrentMap<String/* Topic */, TopicRouteData> topicRouteTable = new ConcurrentHashMap<String, TopicRouteData>();
private final Lock lockNamesrv = new ReentrantLock();
private final Lock lockHeartbeat = new ReentrantLock();
private final ConcurrentMap<String/* Broker Name */, HashMap<Long/* brokerId */, String/* address */>> brokerAddrTable =
new ConcurrentHashMap<String, HashMap<Long, String>>();
private final ConcurrentMap<String/* Broker Name */, HashMap<String/* address */, Integer>> brokerVersionTable =
new ConcurrentHashMap<String, HashMap<String, Integer>>();
//本地定时任务,比如定期获取当前 Namesrv 地址、定期同步Namesrv信息、定期更新Topic路由信息、定期发送心跳信息给Broker、定期清理已下线的Broker、
//定期持久化消费位点、定期调整消费线程数(这部分源代码被官方删除了)
private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "MQClientFactoryScheduledThread");
}
});
//请求的处理器,从处理方法 processRequest() 中我们可以知道目前支持哪些功能接口
private final ClientRemotingProcessor clientRemotingProcessor;
//Pull服务
private final PullMessageService pullMessageService;
//重新平衡服务。定期执行重新平衡方法this.mqClientFactory.doRebalance()。
//这里的 mqClientFactory 就是 MQClientInstance 实例,通过依次调用MQClientInstance中保存
//的消费者实例的doRebalance()方法,来感知订阅关系的变化、集群变化等,以达到重新平衡。
private final RebalanceService rebalanceService;
private final DefaultMQProducer defaultMQProducer;
//消费监控 。 比如拉取 RT(Response Time,响应时间)、拉取TPS(Transactions Per Second,每秒处理消息数)、消费RT等都可以统计。
private final ConsumerStatsManager consumerStatsManager;
}

类的核心方法

public class MQClientInstance {
//从多个Namesrv中获取最新Topic路由信息,更新本地缓存
public void updateTopicRouteInfoFromNameServer(){}
//清理已经下线的Broker
private void cleanOfflineBroker(){}
//检查Client是否在Broker中有效
private void checkClientInBroker(){}
//发送客户端的心跳信息给所有的Broker。
public void sendHeartbeatToAllBrokerWithLock() {}
//在本地注册一个消费者。
public synchronized boolean registerConsumer(final String group, final MQConsumerInner consumer) {}
//取消本地注册的消费者。
public synchronized void unregisterConsumer(final String group) {}
//在本地注册一个生产者。
public synchronized boolean registerProducer(final String group, final DefaultMQProducerImpl producer) {}
//取消本地注册的生产者。
public synchronized void unregisterProducer(final String group){}
//注册一个管理实例。
public boolean registerAdminExt(final String group, final MQAdminExtInner admin)
//立即执行一次 Rebalance。该操作是通过 RocketMQ 的一个CountDownLatch2锁来实现的。
public void rebalanceImmediately(){}
//对于所有已经注册的消费者实例 ,执行一次Rebalance。
public void doRebalance(){}
//在本地缓存中查找Slave Broker信息
public FindBrokerResult findBrokerAddressInSubscribe(
final String brokerName,
final long brokerId,
final boolean onlyThisBroker
){}
//在本地缓存中查找Master Broker地址。
public String findBrokerAddressInPublish(final String brokerName){}
//查找消费者id列表。
public List<String> findConsumerIdList(final String topic, final String group) {}
//通过Topic名字查找Broker地址。
public String findBrokerAddrByTopic(final String topic) {}
//重置消费位点。
public synchronized void resetOffset(String topic, String group, Map<MessageQueue, Long> offsetTable){}
//获取一个订阅关系中每个队列的消费进度。
public Map<MessageQueue, Long> getConsumerStatus(String topic, String group){}
//获取本地缓存Topic路由。
public ConcurrentMap<String, TopicRouteData> getTopicRouteTable(){}
//直接将消息发送给指定的消费者消费,和正常投递不同的是,指定了已经订阅的消费者组中的一个,
//而不是全部已经订阅的消费者。一般适用于在消费消息后,某一个消费者组想再消费一次的场景。
public ConsumeMessageDirectlyResult consumeMessageDirectly(final MessageExt msg,
final String consumerGroup,
final String brokerName) {}
//获取消费者的消费统计信息。包含消费RT、消费TPS等。
public ConsumerRunningInfo consumerRunningInfo(final String consumerGroup){}
}

RocketMQ - 生产者启动流程的更多相关文章

  1. RocketMQ生产者消息篇

    系列文章 RocketMQ入门篇 RocketMQ生产者流程篇 RocketMQ生产者消息篇 前言 上文RocketMQ生产者流程篇中详细介绍了生产者发送消息的流程,本文将重点介绍发送消息的通信模式以 ...

  2. RocketMQ之九:RocketMQ消息发送流程解读

    在讨论这个问题之前,我们先看一下Client的整体架构. Producer与Consumer类体系 从下图可以看出以下几点:(1)Producer与Consumer的共同逻辑,封装在MQClientI ...

  3. Flume-ng源码解析之启动流程

    今天我们通过阅读Flume-NG的源码来看看Flume的整个启动流程,废话不多说,翠花,上源码!! 1 主类也是启动类 在这里我贴出Application中跟启动有关的方法,其他你们可以自己看源码,毕 ...

  4. rocketmq生产者代码分析

    rocketmq生产者代码分析 环境安装 参考http://rocketmq.apache.org/docs/quick-start/ ,配置环境变量 export NAMESRV_ADDR=loca ...

  5. Flink 源码解析 —— Standalone Session Cluster 启动流程深度分析之 Job Manager 启动

    Job Manager 启动 https://t.zsxq.com/AurR3rN 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0到1学习 -- Mac ...

  6. Flink 源码解析 —— Standalone session 模式启动流程

    Standalone session 模式启动流程 https://t.zsxq.com/EemAEIi 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0 ...

  7. Flink 源码解析 —— Standalone Session Cluster 启动流程深度分析之 Task Manager 启动

    Task Manager 启动 https://t.zsxq.com/qjEUFau 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0到1学习 -- Ma ...

  8. Netty启动流程剖析

    编者注:Netty是Java领域有名的开源网络库,特点是高性能和高扩展性,因此很多流行的框架都是基于它来构建的,比如我们熟知的Dubbo.Rocketmq.Hadoop等,针对高性能RPC,一般都是基 ...

  9. MyCat源码分析系列之——配置信息和启动流程

    更多MyCat源码分析,请戳MyCat源码分析系列 MyCat配置信息 除了一些默认的配置参数,大多数的MyCat配置信息是通过读取若干.xml/.properties文件获取的,主要包括: 1)se ...

  10. Android进阶系列之源码分析Activity的启动流程

    美女镇楼,辟邪! 源码,是一个程序猿前进路上一个大的而又不得不去翻越障碍,我讨厌源码,看着一大堆.5000多行,要看完得啥时候去了啊.不过做安卓的总有这一天,自从踏上这条不归路,我就认命了.好吧,我慢 ...

随机推荐

  1. c++ *和& 指针,取内容,别名,取地址

    *前面有类型符时为定义指针 &前面有类型符时为定义引用变量(别名) (int ,float,long,double,char等 ) *p:定义xx类型的指针 int *p 整型指针,char ...

  2. 【每日一题】【判断栈是否为空的方法】2022年1月9日-NC76 用两个栈实现队列的出队入队【入队简单】

    描述用两个栈来实现一个队列,使用n个元素来完成 n 次在队列尾部插入整数(push)和n次在队列头部删除整数(pop)的功能. 队列中的元素为int类型.保证操作合法,即保证pop操作时队列内已有元素 ...

  3. VSCode解决Python中空格和制表符混用报错

    Python对缩进的要求非常严格,缩进控制不正确可能会造成代码执行不正确甚至报错. 遇到报错"TabError: inconsistent use of tabs and spaces in ...

  4. 搭建IIS网站后,点击浏览地址,报403错误

    点击左侧的浏览地址,报右侧的错误,可将目录浏览进行启用 双击进去,进行启用即可

  5. 史上最小 x86 Linux 模拟器「GitHub 热点速览 v.22.50」

    本周 GitHub Trending 略显冷清,大概是国内的人们开始在养病,而国外的人们开始过圣诞.元旦双节.热度不减的 ChatGPT 依旧占据了本周大半的 GitHub 热点项目,不过本周的特推和 ...

  6. json提取器和beanshell处理器组合,将提取的所有id以数组返回

    1.添加json提取器 2.添加beanshell处理器,并编写脚本 String str1 = vars.get("buildid_ALL"); log.info(str1); ...

  7. Jmeter之逻辑控制器---while控制器

    while控制器与编程语言中的while语句一样,当条件为真时继续执行,不为真时则跳出while循环体,不再执行. while控制器相对于循环控制器来说多了个条件判断,下面为while控制器使用案例. ...

  8. 从源码层面深度剖析Spring循环依赖

    作者:郭艳红 以下举例皆针对单例模式讨论 图解参考 https://www.processon.com/view/link/60e3b0ae0e3e74200e2478ce 1.Spring 如何创建 ...

  9. Spring IOC官方文档学习笔记(三)之依赖项

    1.依赖注入 (1) 依赖注入(DI)的概念:某个bean的依赖项,由容器来负责注入维护,而非我们自己手动去维护,以此来达到bean之间解耦的目的,如下 //情况一:不使用依赖注入 public cl ...

  10. freeswitch的gateway配置方案

    概述 freeswitch是一款简单好用的VOIP开源软交换平台. 在voip的网络模型中,网关是我们经常会遇到的概念. 在freeswitch中,如何配置gateway,如何使用好gateway的模 ...