最近看了下阿里Rocketmq,里面有很多的东西值得我们学习.
创建Producer
DefaultMQProducer producer = new DefaultMQProducer("cluster");
producer.setNamesrvAddr(MyUtils.getNamesrvAddr());
producer.start();
一个producer最简单的创建流程为以上几步
- new DefaultMQProducer()对象并设置其groupName
- 设置namesrvAddr
- 启动
public class DefaultMQProducer extends ClientConfig implements MQProducer
查看DefaultMQProducer源码不难发现该类是继承ClientConfig ,并去实现了MQProducer接口.
ClientConfig
private String namesrvAddr = System.getProperty(MixAll.NAMESRV_ADDR_PROPERTY,
System.getenv(MixAll.NAMESRV_ADDR_ENV));//namesrvAddr 地址
private String clientIP = RemotingUtil.getLocalAddress();//当前ip
private String instanceName = System.getProperty("rocketmq.client.name", "DEFAULT");//instanceName
DefaultMQProducer
private String createTopicKey = MixAll.DEFAULT_TOPIC;//默认topic名称
private volatile int defaultTopicQueueNums = 4;//默认topic对了数量
private int sendMsgTimeout = 3000;//发送超时
private int compressMsgBodyOverHowmuch = 1024 * 4;//压缩
private int retryTimesWhenSendFailed = 2;//发送重试次数
private boolean retryAnotherBrokerWhenNotStoreOK = false;//换别的broker
private int maxMessageSize = 1024 * 128;//最大msgBody长度
当创建一个DefaultMQProducer对象时候会去初始化ClientConfig 与DefaultMQProducer中一系列的参数比如从环境变量去拿namesrvAddr地址,当前客户端的ip等.
Producer启动
下图为一个producer启动整个时序图.
代码解读
this.checkConfig();
改方法主要是针对producerGroup是否为空,是否占用默认的groupName等一些列的验证.
this.mQClientFactory=MQClientManager.getInstance().getAndCreateMQClientInstance(this.defaultMQProducer,rpcHook);
此处调用了getAndCreateMQClientInstance()方法,并把当前的producer对象,以及rpcHook作为参数传入
public MQClientInstance getAndCreateMQClientInstance(final ClientConfig clientConfig, RPCHook rpcHook) {
String clientId = clientConfig.buildMQClientId();
MQClientInstance instance = this.factoryTable.get(clientId);
if (null == instance) {
instance =
new MQClientInstance(clientConfig.cloneClientConfig(),
this.factoryIndexGenerator.getAndIncrement(), clientId, rpcHook);
MQClientInstance prev = this.factoryTable.putIfAbsent(clientId, instance);
if (prev != null) {
instance = prev;
} else {
// TODO log
}
}
return instance;
}
- 创建一个当前producer的clientId格式为ip@pid
- 如果当前客户端不在mq客户端实例集合中,则创建一个实例并加入
boolean registerOK=mQClientFactory.registerProducer(this.defaultMQProducer.getProducerGroup(), this);
把当前返回客户端实例注册到producer集合中
this.topicPublishInfoTable.put(this.defaultMQProducer.getCreateTopicKey(), new TopicPublishInfo());
将topic放入topic集合中.createTopicKey其实是在程序配置的一个默认的topic名字.
为什么会传入一个默认的topic名字呢?
应为在创建每一个topic时,程序会去这个默认的topic作为模板,改topic以及所有创建的topic会保存在当前用户目录下的store/config/topics.json文件中.具体创建过程在后面的章节会详细说明.
mQClientFactory.start();
MQClientInstance.start()方法主要分为以下几个部分
- 获取nameService地址
- 启动client端远程通信
- 启动各种定时
- 更新nameService地址
- 更新从nameService更新topic路由信息
- 清理挂掉的broker,向broker发送心跳信息
- 持久化consumerOffset
- 调整消费线程池
- 启动拉取消息服务
- 启动消费端负载均衡服务
- 再次调用DefaultMQProducerImpl.start()
- 改变serviceState状态
this.mQClientFactory.sendHeartbeatToAllBrokerWithLock();
心跳服务
疑点
1. MQClientInstance.start()方法中最后this.defaultMQProducer.getDefaultMQProducerImpl().start(false);
反过来又去调用一次DefaultMQProducerImpl.start()方法这个我比较费解.感觉什么都没做,就调用了一次心跳服务.
2. 在rocketmq中不管是producer,还是consumer都抽象成一个MQClientInstance,启动时都会去调用MQClientInstance.start().该方法启动的server大部分都是和consumer相关,而在producer启动也在调用.
以上两个问题我看代码中比较有疑惑的,请大神们来指点