5、Producer生产者启动源码

DefaultMQProducer

       DefaultMQProducerImpl

              MQClientInstance

                     brokerAddrTable(ConcurrentMap<String/* Broker Name */, HashMap<Long/* brokerId */, String/* address */>>)

                     topicRouteTable(ConcurrentMap<String/* Topic */, TopicRouteData>)

                     MQConsumerInner(ConcurrentMap<String/* group */, MQConsumerInner>)

                     MQProducerInner(ConcurrentMap<String/* group */, MQProducerInner>)

                            TopicPublishInfo(Map对象,key:topic,value:TopicPublishInfo)

1、创建DefaultMQProducer实例

2、start启动生产者(持有关系DefaultMQProducer-》DefaultMQProducerImpl=》MQClientInstance=》MQProducerInner(Map对象,key:group, value:MQProducerInner)=》TopicPublishInfo(Map对象,key:topic,value:TopicPublishInfo))

       DefaultMQProducer-》DefaultMQProducerImpl(门面模式设计模式)的start启动

              调用checkConfig方法检查生产者的ProducerGroup是否符合规范,如果ProducerGroup为空,或者长度大于255个字符,或者包含非法字符(正常的匹配模式为 ^[%|a-zA-Z0-9_-]+$),或者生产者组名为默认组名DEFAULT_PRODUCER,满足以上任意条件都校验不通过抛出异常。

              调用getOrCreateMQClientInstance方法,然后根据clientId获取或者创建MQClientInstance实例。

                     该方法会先生成clientId,格式为 clientIP@instanceName@unitName。然后从本地缓存factoryTable中,查找该clientId的MQClientInstance实例。如果缓存中没有找到,则创建实例并存入缓存中。

                     MQClientInstance封装了RocketMQ底层网络处理API,Producer、Consumer都会使用到这个类,是Producer、Consumer与NameServer、Broker 打交道的网络通道。因此,同一个clientId对应同一个MQClientInstance实例就可以了,即同一个应用中的多个producer和consumer使用同一个MQClientInstance实例即可。

              将当前生产者注册到MQClientInstance实例的producerTable属性中。

                     ConcurrentMap<String/* group */, MQProducerInner> producerTable

                     key: 生产者组名   value:生产者实例

              添加一个默认topic “TBW102”,将会在isAutoCreateTopicEnable属性开启时在broker上自动创建,RocketMQ会基于该Topic的配置创建新的Topic。

              调用mQClientFactory变量(MQClientInstance)#start方法启动MQClientInstance客户端通信实例,初始化netty服务、各种定时任务、拉取消息服务、rebalanceService服务等等。

                     初始化netty服务:

                            初始化netty客户端(服务请求处理器,处理RemotingCommand消息,即请求和响应的业务处理,并且返回相应的处理结果。)

                            启动定时任务,初始启动3秒后,扫描responseTable,将超时的ResponseFuture直接移除,并且执行这些超时ResponseFuture的回调

                     startScheduledTask启动各种定时任务:

                            如果没有手动指定namesrvAddr,那么每隔2m从nameServer地址服务器拉取最新的nameServer地址并更新。

                            每隔30S尝试从nameServer更新topic路由信息。

                                   从MQClientInstance内部的consumerTable以及producerTable这两个map中获取配置的所有topic集合topicList,包括consumer订阅的topic集合以及producer中topicPublishInfoTable集合中的数据。

                                   从nameSerer拉取到topic路由信息之后,调用topicRouteDataIsChange方法与本地的旧topic路由信息比较看是否更改,比较的数据包括:topic的队列信息queueDatas,topic的broker信息brokerDatas,顺序topic配置orderTopicConf,消费过滤信息filterServerTable。

                                   当判断需要更新的时候,会更新本地的topic缓存,包括:

                                          更新brokerName到brokerAddr的地址的映射关系,即brokerAddrTable;

                                          更新生产者的producerTable集合,更新MQProducerInner的topicPublishInfoTable属性。

                                          更新消费者的consumerTable集合,更新MQConsumerInner的rebalanceImpl.topicSubscribeInfoTable属性。

                                          更新topicRouteTable集合,更新本地topic路由信息。

                                   首先获取一个随机位于nameServer集合长度范围内的整数,作为第一个被选取的nameServer的地址,然后夯实建立长连接,如果建立成功,那么该长连接还会被放入缓存中,以后可以直接使用,这说明,同一个客户端应用中,不同的consumer和produer可以共用同一个nameServer的长连接。

                                   如果没有建立连接成功,那么选择当前位置开始的下一个nameServer地址继续尝试建立长连接,直到最后成功为止。如果所有nameServer都建立长连接失败,那么最终会抛出异常。

                            每隔30S尝试清除无效的broker信息,以及发送心跳信息给所有broker。

                                   遍历并且更新brokerAddrTable这个map集合,该集合类型为ConcurrentMap<String/* Broker Name */, HashMap<Long/* brokerId */, String/* address */>>。

                                   其主要步骤就是获取每一个address,然后去本地路由信息集合topicRouteTable中查找判断broker地址是否存在于topicRouteTable的任意一个topic的路由信息中,如果不存在,则表示该broker已下线,那么清除该broker地址,否则保留。如果brokerAddrTable中的value集合也是空的,那么直接删除键值对。

                                   topicRouteTable的数据是拉取的最新的,而brokerAddrTable里的数据会同步topicRouteTable里的新数据,但是存在的旧数据没有删除,所以要定时清除。

                            每隔5S尝试持久化消费者偏移量,即消费进度。广播消费模式下持久化到本地,集群消费模式下推送到broker端。该定时任务针对消费者,后面学习消费者的时候再学习源码。

                            每隔1m尝试调整push模式的消费线程池的线程数量,该定时任务针对消费者,目前默认没有实现该功能,是一个空方法实现。

              主动调用一次sendHeartbeatToAllBrokerWithLock发送心跳信息给所有broker。

              启动一个定时任务,移除超时的request方法的请求,并执行异常回调,任务间隔1s。

             

总结:

RocketMQ的生产者客户端的源码并不是很难,入口就是DefaultMQProducer的构造器和start方法。

在Producer启动和初始化过程中,会获取或者创建一个非常重要的对象:MQClientInstance。MQClientInstance封装了RocketMQ底层网络处理API,其本身位于通信层,客户端层的Producer、Consumer都会使用到这个类,是Producer、Consumer与NameServer、Broker 打交道的网络通道。因此,同一个clientId对应同一个MQClientInstance实例就可以了,即同一个应用中的多个producer和consumer使用同一个MQClientInstance实例即可。

MQClientInstance的start方法源码也是本次学习的重中之重,但是,在同一个应用中,即使存在多个不同的consume和producer实例,该MQClientInstance实例的start方法中的初始化操作也仅会被调用一次。

MQClientInstance的start方法将会初始化客户端的netty服务、启动各种定时任务(例如定时更新topic信息、定时发送心跳数据等等)、拉取消息服务(后面消费者模块再学习)、rebalanceService负载均衡服务(后面消费者模块再学习)、内部的生产者服务等等服务,这些服务只需要启动一次就好。

由于同一个应用中不同的consume和producer实例共享这个MQClientInstance实例,因此MQClientInstance中的很多方法均需加锁。

生产者的启动源码中使用了一些设计模式,例如:

门面设计模式,DefaultMQProducer是我们使用的生产者,但是其方法几乎均委托给内部的DefaultMQProducerImpl来实现。

单例模式,例如MQClientManager,典型的饿汉单例模式。

状态模式:MQClientInstance和DefaultMQProducerImpl中均有状态字段serviceState,根据不同的状态,其方法的调用会做出不同的行为。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值