文章目录
一、消息生产者类关系
1、类关系
消息生产者方法由接口MQProducer
定义,默认消息生产者实现类为DefaultMQProducer
。其类关系如下图:
可以看出,MQProducer
实现有两种,除了默认实现DefaultMQProducer
,还有支持事务的实现TransactionMQProducer
2、MQAdmin
MQAdmin核心方法如下
public interface MQAdmin {
//创建消息主题
void createTopic(final String key, final String newTopic, final int queueNum)
throws MQClientException;
//根据时间戳查询消息偏移量
long searchOffset(final MessageQueue mq, final long timestamp) throws MQClientException;
//查找消息队列中最大消息偏移量
long maxOffset(final MessageQueue mq) throws MQClientException;
//查找消息队列中最小消息偏移量
long minOffset(final MessageQueue mq) throws MQClientException;
//消息队列最早存储消息的时间
long earliestMsgStoreTime(final MessageQueue mq) throws MQClientException;
//查找消息id对应消息内容
MessageExt viewMessage(final String offsetMsgId) throws RemotingException, MQBrokerException,
InterruptedException, MQClientException;
//消息主题、key、最大取出消息条数、开始时间、结束时间查找匹配消息
QueryResult queryMessage(final String topic, final String key, final int maxNum, final long begin,
final long end) throws MQClientException, InterruptedException;
//消息主题、消息id查找
MessageExt viewMessage(String topic,
String msgId) throws RemotingException, MQBrokerException, InterruptedException, MQClientException;
}
二、消息生产者启动流程
从消息生产者默认实现类DefaultMQProducer
入手,在创建生产者DefaultMQProducer
实例后,执行start()
方法启动生产者实例。
1、创建DefaultMQProducer实例
DefaultMQProducer构造器代码如下,
/**
* Constructor specifying namespace, producer group, RPC hook, enabled msgTrace flag and customized trace topic
* name.
*
* @param namespace Namespace for this MQ Producer instance.
* @param producerGroup Producer group, see the name-sake field.
* @param rpcHook RPC hook to execute per each remoting command execution.
* @param enableMsgTrace Switch flag instance for message trace.
* @param customizedTraceTopic The name value of message trace topic.If you don't config,you can use the default
* trace topic name.
*/
public DefaultMQProducer(final String namespace, final String producerGroup, RPCHook rpcHook,
boolean enableMsgTrace, final String customizedTraceTopic) {
//生产者命名空间
this.namespace = namespace;
//生产者所属组
this.producerGroup = producerGroup;
//默认生产者实现
defaultMQProducerImpl = new DefaultMQProducerImpl(this, rpcHook);
//if client open the message trace feature
if (enableMsgTrace) {
try {
AsyncTraceDispatcher dispatcher = new AsyncTraceDispatcher(producerGroup, TraceDispatcher.Type.PRODUCE, customizedTraceTopic, rpcHook);
dispatcher.setHostProducer(this.defaultMQProducerImpl);
traceDispatcher = dispatcher;
this.defaultMQProducerImpl.registerSendMessageHook(
new SendMessageTraceHookImpl(traceDispatcher));
this.defaultMQProducerImpl.registerEndTransactionHook(
new EndTransactionTraceHookImpl(traceDispatcher));
} catch (Throwable e) {
log.error("system mqtrace hook init failed ,maybe can't send msg trace data");
}
}
}
参数说明:
- namespace:生产者实例的命名空间
- producerGroup:生产者所属组名
- rpcHook:远程回调钩子
- enableMsgTrace:是否开启消息路径追踪
- customizedTraceTopic:消息路径追踪使用的topic
2、生产者start
创建完生产者DefaultMQProducer
实例后,调用其start()
方法进行实例化
/**
* Start this producer instance. </p>
*
* <strong> Much internal initializing procedures are carried out to make this instance prepared, thus, it's a must
* to invoke this method before sending or querying messages. </strong> </p>
*
* @throws MQClientException if there is any unexpected error.
*/
@Override
public void start() throws MQClientException {
this.setProducerGroup(withNamespace(this.producerGroup));
//启动默认生产者实现
this.defaultMQProducerImpl.start();
if (null != traceDispatcher) {
try {
//启动消息trace追踪实例
traceDispatcher.start(this.getNamesrvAddr(), this.getAccessChannel());
} catch (MQClientException e) {
log.warn("trace dispatcher start failed ", e);
}
}
}
可以看到,生产者实例启动主要调用this.defaultMQProducerImpl.start();
完成生产者的启动。那么,了解defaultMQProducerImpl.start()
即可知道消息生产者是如何启动的
1)defaultMQProducerImpl.start()
defaultMQProducerImpl.start()
代码如下
在该方法中,
- 1、先调用
checkConfig()
方法检查producerGroup是否符合要求,如不符合要求,则报错 - 2、如果producerGroup等于
MixAll.CLIENT_INNER_PRODUCER_GROUP
,则将生产者instanceName属性设置为进程ID - 3、使用
MQClientManager.getInstance()
创建MQClientInstance
实例,并缓存到MQClientManager.factoryTable
中;由于factoryTable
类型为ConcurrentMap<String/* clientId */, MQClientInstance>
[^1]并且全局唯一,所以创建的MQClientInstance
实例在整个JVM实例中只会存在一个。
clientId 生成规则=
客户端IP+ instanceName+ (unitname 可选)+ RequestType.STREAM(enableStreamRequestType=true)
- 4、将当前生产者注册到
MQClientInstance
实例中,方便后续进行远程调用或心跳;启动MQClientInstance
实例 - 5、一切准备就绪,将生产者状态标识为启动中(
this.serviceState = ServiceState.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;
//检查productGroup 是否符合要求
this.checkConfig();
if (!this.defaultMQProducer.getProducerGroup().equals(MixAll.CLIENT_INNER_PRODUCER_GROUP)) {
//并改变生产者的instanceName 为进程ID
this.defaultMQProducer.changeInstanceNameToPID();
}
this.mQClientFactory = MQClientManager.getInstance().getOrCreateMQClientInstance(this.defaultMQProducer, rpcHook);
//将当前mq生产者注册到MQClientInstance中,方便后续远程调用/心跳检测
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());
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;
}
this.mQClientFactory.sendHeartbeatToAllBrokerWithLock();
RequestFutureHolder.getInstance().startScheduledTask(this);
}