【源码解析5】生产者启动源码解析

64 阅读4分钟

代码位置

下面我们来看一下一个简单的生产者是怎么执行程序的

我们来到方法org.apache.rocketmq.example.simple.Producer#main中

public class Producer {
    public static void main(String[] args) throws MQClientException, InterruptedException {
        // 将分组名命名为ProducerGroupName,并通过DefaultMQProducer的构造方法创建生产者
        DefaultMQProducer producer = new DefaultMQProducer("ProducerGroupName");
        // 指定注册中心的ip和port
        producer.setNamesrvAddr("127.0.0.1:9876");
        // 启动生产者,期间会开启和broker、namesrv的TCP连接,并开启心跳的定时任务
        producer.start();
        // 开始准备发送消息
        try {
            // 将消息包装为Message的对象
            Message msg = new Message("TopicTest",
                                      "TagA",
                                      "OrderID188",
                                      "Hello world".getBytes(RemotingHelper.DEFAULT_CHARSET));
            // 通过生产者发送被包装好的Message对象
            SendResult sendResult = producer.send(msg);
            System.out.printf("%s%n", sendResult);
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 关闭生产者
        producer.shutdown();
}

new DefaultMQProducer("xx")的过程

构造方法

进入构造函数,发现有很多构造函数的重载方法,该函数调用了另外一个构造函数

namespace很好理解,一般用来做一级业务划分。

producerGroup自然是用来做二级业务划分。

 public DefaultMQProducer(final String producerGroup) {
     // 参数分别为namespace、producerGroup、rpcHook
 	this(null, producerGroup, null);
 }

调用勾子,给请求和响应追加代码片段,每当请求执行时,都会运行

public interface RPCHook {
    void doBeforeRequest(final String remoteAddr, final RemotingCommand request);

    void doAfterResponse(final String remoteAddr, final RemotingCommand request,
        final RemotingCommand response);
}

重载后的构造方法

public DefaultMQProducer(final String namespace, final String producerGroup, RPCHook rpcHook) {
    this.namespace = namespace;
    this.producerGroup = producerGroup;
    this.defaultMQProducerImpl = new DefaultMQProducerImpl(this, rpcHook);
}

初始化生产者执行器

 public DefaultMQProducerImpl(final DefaultMQProducer defaultMQProducer, RPCHook rpcHook) {
     this.defaultMQProducer = defaultMQProducer;
     this.rpcHook = rpcHook;

     this.asyncSenderThreadPoolQueue = new LinkedBlockingQueue<Runnable>(50000);
     this.defaultAsyncSenderExecutor = new ThreadPoolExecutor(
         Runtime.getRuntime().availableProcessors(),
         Runtime.getRuntime().availableProcessors(),
         1000 * 60,
         TimeUnit.MILLISECONDS,
         this.asyncSenderThreadPoolQueue,
         new ThreadFactory() {
             private AtomicInteger threadIndex = new AtomicInteger(0);

             @Override
             public Thread newThread(Runnable r) {
                 return new Thread(r, "AsyncSenderExecutor_" + this.threadIndex.incrementAndGet());
             }
         });
 }

producer.start()的过程

判断serviceState的状态

整体流程

我们可以看到运行start之后,首先会根据当前的serviceState类型,走不一样的逻辑分支

serviceState是类DefaultMQProducerImpl的一个属性,它的默认值是ServiceState.CREATE_JUST

public void start(final boolean startFactory) throws MQClientException {
	switch (this.serviceState) {
		case CREATE_JUST:
		case RUNNING:
		case START_FAILED:
		case SHUTDOWN_ALREADY:
		default:
			break;
    }
}

ServiceState枚举

ServiceState是生产者服务运行状态的枚举

public enum ServiceState {
    /**
     * Service just created,not start
     */
    CREATE_JUST,
    /**
     * Service Running
     */
    RUNNING,
    /**
     * Service shutdown
     */
    SHUTDOWN_ALREADY,
    /**
     * Service Start failure
     */
    START_FAILED;
}

过程解析

RUNNING、START_FAILED、SHUTDOWN_ALREADY

根据由简入深的流程,我们先来看看RUNNING、START_FAILED、SHUTDOWN_ALREADY等状态会做些什么。

如果处于RUNNING和START_FAILED状态,则直接跳过该逻辑

而面对SHUTDOWN_ALREADY的场景则会直接抛出异常,并提示客户当前的生产者服务异常,需要重新start一次

image-20230619144503919.png

CREATE_JUST

CREATE_JUST做的事情,我这边就直接写注释注明一下了

switch (this.serviceState) {
    case CREATE_JUST:
        // 先给serviceState来个默认失败哈哈
        this.serviceState = ServiceState.START_FAILED;
        // 用来校验分组名是否符合规范
        this.checkConfig();
        // 这段逻辑的目的是让内部生产者的instanceName全局唯一,为了保证全局唯一还拼了一个纳米级别的时间
        // 内部生产者的默认分组会是CLIENT_INNER_PRODUCER_GROUP,默认instanceName是DEFAULT,会在这里被改为全局唯一的时间
        if (!this.defaultMQProducer.getProducerGroup().equals(MixAll.CLIENT_INNER_PRODUCER_GROUP)) {
            this.defaultMQProducer.changeInstan,ceNameToPID();
        }
        // 这段代码是为了MQClientInstance的实例,放入我们的默认生产者和rpc勾子,rpc勾子是用来在处理请求数据和响应数据的勾子对象
        // 获取MQClientInstance的实例时,会优先尝试在MQClientManager.factoryTable中去取MQClientInstance,如果取不到则会根据客户端的配置创建一个新的MQClientInstance实例
        this.mQClientFactory = MQClientManager.getInstance().getOrCreateMQClientInstance(this.defaultMQProducer, rpcHook);
        // MQClientInstance有一个ConcurrentMap<String, MQConsumerInner>类型的属性producerTable
        // 这段代码的含义是将当前生产者放入线程安全的Map“producerTable”中,如果分组和生产者有一个为null或者该生产者在Map中已存在则返回false,然后就会抛出异常。
        // 由此可知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);
        }
        // 将主题推送信息在topicPublishInfoTable Map中管理起来
        this.topicPublishInfoTable.put(this.defaultMQProducer.getCreateTopicKey(), new TopicPublishInfo());
        // 通过MQClientInstance启动生产者
        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;
}
// 发送心跳给所有的broker
this.mQClientFactory.sendHeartbeatToAllBrokerWithLock();

// 开启定时任务
 RequestFutureHolder.getInstance().startScheduledTask(this);

定时任务解析

ScheduledExecutorService是一个可以执行定时任务、周期性任务和延迟任务的线程池

这里创建的线程池是

 public synchronized void startScheduledTask(DefaultMQProducerImpl producer) {
        this.producerSet.add(producer);
        if (null == scheduledExecutorService) {
            this.scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl("RequestHouseKeepingService"));

            this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
                @Override
                public void run() {
                    try {
                        RequestFutureHolder.getInstance().scanExpiredRequest();
                    } catch (Throwable e) {
                        log.error("scan RequestFutureTable exception", e);
                    }
                }
            }, 1000 * 3, 1000, TimeUnit.MILLISECONDS);

        }
    }

scheduleAtFixedRate的解析如下

    /**
     * @param command the task to execute
     * @param initialDelay the time to delay first execution // 首次执行的延迟时间
     * @param period the period between successive executions // 连续执行的间隔时间
     * @param unit the time unit of the initialDelay and period parameters // 时间的单位
     */
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit);