RocketMq源码分析(五)--消息生产者启动流程

一、消息生产者类关系

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);

    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值