概述
![](https://enoc-1304077175.cos.ap-beijing.myqcloud.com/pic/20211017112101.png)
DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name");
producer.start();
try {
/*
* Create a message instance, specifying topic, tag and message body.
*/
Message msg = new Message("TopicTest" /* Topic */,
"TagA" /* Tag */,
("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET) /* Message body */
);
/*
* Call send message to deliver message to one of brokers.
*/
SendResult sendResult = producer.send(msg);
System.out.printf("%s%n", sendResult);
} catch (Exception e) {
e.printStackTrace();
}
/*
* Shut down once the producer instance is not longer in use.
*/
producer.shutdown();
Start
org.apache.rocketmq.client.producer.DefaultMQProducer#start
首先进入 start 方法,可以看出主要的功能实现在于 defaultMQProducerImpl.start(),先忽略细枝末节,接着进去看看
public void start() throws MQClientException {
this.setProducerGroup(withNamespace(this.producerGroup));
this.defaultMQProducerImpl.start();
if (null != traceDispatcher) {
try {
traceDispatcher.start(this.getNamesrvAddr(), this.getAccessChannel());
} catch (MQClientException e) {
log.warn("trace dispatcher start failed ", e);
}
}
}
org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl#start()
然后,我们可以看到会根据当前生产者的状态来进行不同的行为
记得在设计模式里,这叫做"状态模式"
具体的状态有:
- CREATE_JUST
- RUNNING
- START_FAILED
- SHUTDOWN_ALREADY
在进入 start 后状态会变成 START_FAILED
,完成后变成 RUNNING
状态
public void start() throws MQClientException {
this.start(true);
}
public void start(final boolean startFactory) throws MQClientException {
switch (this.serviceState) {
case CREATE_JUST:
this.serviceState = ServiceState.START_FAILED;
// -- 跳过 --
this.serviceState = ServiceState.RUNNING;
break;
case RUNNING:
case START_FAILED:
case SHUTDOWN_ALREADY:
throw new MQClientException("The producer service state not OK, maybe started once, "
+ this.serviceState
+ FAQUrl.suggestTodo(FAQUrl.CLIENT_SERVICE_NOT_OK),
null);
default:
break;
}
}
进入 CREATE_JUST
后开始对元信息进行检查与注册
this.checkConfig();
// 如果自定义了生产者组,则修改PID
if (!this.defaultMQProducer.getProducerGroup().equals(MixAll.CLIENT_INNER_PRODUCER_GROUP)) {
this.defaultMQProducer.changeInstanceNameToPID();
}
// 从MQ工厂获取实例
// MQ工厂保证ClientID唯一
this.mQClientFactory = MQClientManager.getInstance().getOrCreateMQClientInstance(this.defaultMQProducer, rpcHook);
// 注册生产者组
boolean registerOK = mQClientFactory.registerProducer(this.defaultMQProducer.getProducerGroup(), this);
if (!registerOK) {
this.serviceState = ServiceState.CREATE_JUST;
throw new MQClientException("The producer group[" + this.defaultMQProducer.getProducerGroup()
+ "] has been created before, specify another name please." + FAQUrl.suggestTodo(FAQUrl.GROUP_NAME_DUPLICATE_URL),
null);
}
this.topicPublishInfoTable.put(this.defaultMQProducer.getCreateTopicKey(), new TopicPublishInfo());
if (startFactory) {
mQClientFactory.start();
}
log.info("the producer [{}] start OK. sendMessageWithVIPChannel={}", this.defaultMQProducer.getProducerGroup(),
this.defaultMQProducer.isSendMessageWithVIPChannel());
然后具体看 mQClientFactory.start()
方法
org.apache.rocketmq.client.impl.factory.MQClientInstance#start
MQClientInstance 是由一个 JAVA 程序所共用的,其可以从 ClientId 的生成方法看出
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);
}
return sb.toString();
}
由以上代码可以得知,一台机器上的一个 JVM 进程只拥有一个实例,所以以下的初始化方法也是全局的
首先对当前对象加锁来避免多线程带来的问题,然后又进行了一次状态判断来保证状态正确。然后就启动了一堆服务。
synchronized (this) {
switch (this.serviceState) {
case CREATE_JUST:
this.serviceState = ServiceState.START_FAILED;
// NameSrv 地址为空时,尝试通过设定的地址使用HTTP获取NameSrv地址
if (null == this.clientConfig.getNamesrvAddr()) {
this.mQClientAPIImpl.fetchNameServerAddr();
}
// 开启 Netty 的请求响应的 Channel
this.mQClientAPIImpl.start();
// 开启调度任务
this.startScheduledTask();
// 开启拉取服务
this.pullMessageService.start();
// 开启再均衡服务
this.rebalanceService.start();
// 开启push服务
this.defaultMQProducer.getDefaultMQProducerImpl().start(false);
log.info("the client factory [{}] start OK", this.clientId);
this.serviceState = ServiceState.RUNNING;
break;
case START_FAILED:
throw new MQClientException("The Factory object[" + this.getClientId() + "] has been created before, and failed.", null);
default:
break;
}
}
其中的 pullMessageService
和 rebalanceService
等服务都是继承于 ServiceThread
抽象类,这个类被用于在多线程的情况下保证服务启动的正确性。
public void start() {
log.info("Try to start service thread:{} started:{} lastThread:{}", getServiceName(), started.get(), thread);
// 只会被启动一次
if (!started.compareAndSet(false, true)) {
return;
}
stopped = false;
// 启动子类的实现线程,从子类获取服务名
this.thread = new Thread(this, getServiceName());
this.thread.setDaemon(isDaemon);
this.thread.start();
}
其中 RebalanceService
和 PullMessageService
下节再具体分析。
此外,其中的 startScheduledTask()
又开启了一些定时运行的任务
// 从远程服务器不断更新 NameServer 地址
if (null == this.clientConfig.getNamesrvAddr()) {
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public