本文章主要参考《RocketMQ技术内幕:RocketMQ架构设计与实现原理》一书,主要是将自己学习的过程进行记录和梳理,方便以后翻阅。
1. 生产者介绍
如图,生产者的默认实现是DefaultMQProducer,在DefaultMQProducer中有一个成员变量DefaultMQProducerImpl,里面实现了生产者的很多方法,可以说是生产者的具体实现类。
在DefaultMQProducer中定义了一些发送消息的核心属性,如下:
/**
* 生产者具体实现
*/
protected final transient DefaultMQProducerImpl defaultMQProducerImpl;
/**
* 生产者组
*/
private String producerGroup;
/**
* 默认的topickey
*/
private String createTopicKey = MixAll.AUTO_CREATE_TOPIC_KEY_TOPIC;
/**
* 每个主题在一个broker中默认的队列数量
*/
private volatile int defaultTopicQueueNums = 4;
/**
* 消息发送超时时间
*/
private int sendMsgTimeout = 3000;
/**
* 当消息体超过该值的时候会使用压缩
*/
private int compressMsgBodyOverHowmuch = 1024 * 4;
/**
* 同步发送失败重试次数
*/
private int retryTimesWhenSendFailed = 2;
/**
* 异步发送失败重试次数
*/
private int retryTimesWhenSendAsyncFailed = 2;
/**
* 消息重试时选择另一个broker时,是否不等待存储结果就返回
*/
private boolean retryAnotherBrokerWhenNotStoreOK = false;
/**
* 允许发送的消息的最大长度
*/
private int maxMessageSize = 1024 * 1024 * 4; // 4M
2. 生产者启动流程
当发送消息的时候,调用DefaultMQProducer的start()方法就开始启动生产者。
#org.apache.rocketmq.client.producer.DefaultMQProducer#start
this.setProducerGroup(withNamespace(this.producerGroup));
this.defaultMQProducerImpl.start();
可以看到,真的的启动其实是在DefaultMQProducerImpl中实现的。
switch (this.serviceState) {
case CREATE_JUST:
this.serviceState = ServiceState.START_FAILED;
this.checkConfig();//1
if (!this.defaultMQProducer.getProducerGroup().equals(MixAll.CLIENT_INNER_PRODUCER_GROUP)) {
//修改instanceName为进程ID
this.defaultMQProducer.changeInstanceNameToPID();//2
}
//获取MQClientInstance
this.mQClientFactory = MQClientManager.getInstance().getOrCreateMQClientInstance(this.defaultMQProducer, rpcHook);//3
boolean registerOK = mQClientFactory.registerProducer(this.defaultMQProducer.getProducerGroup(), this);//4
if (!registerOK) {//已经存在注册的Producer
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());//5
if (startFactory) {//启动MQClientInstance
mQClientFactory.start();//6
}
log.info("the producer [{}] start OK. sendMessageWithVIPChannel={}", this.defaultMQProducer.getProducerGroup(),
this.defaultMQProducer.isSendMessageWithVIPChannel());
this.serviceState = ServiceState.RUNNING;//7
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;
}
//向所有的broker发送心跳检测
this.mQClientFactory.sendHeartbeatToAllBrokerWithLock();//8
1、首先,会对ProducerGroup进行检查。
2、并且如果不是默认的ProducerGroup,将生产者的InstanceName设置为进程ID。
3、接着创建一个MQClientInstance实例:
#org.apache.rocketmq.client.impl.MQClientManager
public MQClientInstance getOrCreateMQClientInstance(final ClientConfig clientConfig, RPCHook rpcHook) {
//IP+instanceName+unitname,
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;
log.warn("Returned Previous MQClientInstance for clientId:[{}]", clientId);
} else {
log.info("Created new MQClientInstance for clientId:[{}]", clientId);
}
}
return instance;
}
首先看一下MQClientManage,MQClientManager是一个单例的,整个JVM中只有一个,其中维护了一个缓存表,负责缓存每个ClientID和MQClientInstance的对应关系。
private static MQClientManager instance = new MQClientManager();
private ConcurrentMap<String/* clientId */, MQClientInstance> factoryTable =
new ConcurrentHashMap<String, MQClientInstance>();
接着看getOrCreateMQClientInstance()方法,先去生成clientId,ClientId主要是由进程IP@InstanceName@unitName组成,其中uniteName是可选的。
# org.apache.rocketmq.client.ClientConfig
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();
}
最后新建一个MQClientInstance实例,并放入缓存中。
4、向MQClientInstance注册当前的生产者,这个注册只是将生产者放入一个缓存列表中,方便后续使用。
# org.apache.rocketmq.client.impl.factory.MQClientInstance
public boolean registerProducer(final String group, final DefaultMQProducerImpl producer) {
if (null == group || null == producer) {
return false;
}
//如果已经存在,则不需要进行注册
MQProducerInner prev = this.producerTable.putIfAbsent(group, producer);
if (prev != null) {
log.warn("the producer group[{}] exist already.", group);
return false;
}
return true;
}
5、维护topic信息列表。 6、启动MQClientInstance。 7、将生产者状态修改为运行中。 8、向所有的Broker发送心跳检测。
到此,生产者启动流程完成。生产者启动最重要的就是创建MQClientInstance实例,并且启动它。
参考资料
- RocketMQ技术内幕:RocketMQ架构设计与实现原理