RocketMQ源码解析——Producer部分之Producer启动过程DefaultMQProducer(1)


 前面分析Broker的启动过程,接下来分析Producer的启动过程。

从官网说明开始

 先来看看官网的发送消息额实例代码

public class Producer {
    public static void main(String[] args) throws MQClientException {
        // 创建指定分组名的生产者
        DefaultMQProducer producer = new DefaultMQProducer("ProducerGroupName");

        // 启动生产者
        producer.start();

        for (int i = 0; i < 128; i++)
            try {
            	// 构建消息
                Message msg = new Message("TopicTest",
                        "TagA",
                        "OrderID188",
                        "Hello world".getBytes(RemotingHelper.DEFAULT_CHARSET));

                // 同步发送
                SendResult sendResult = producer.send(msg);

                // 打印发送结果
                System.out.printf("%s%n", sendResult);
            } catch (Exception e) {
                e.printStackTrace();
            }

        producer.shutdown();
    }
}

 整个发送消息的步骤主要分为以下几步:

  1. 创建DefaultMQProducer
  2. 启动生产者实例
  3. 构建消息
  4. 调用DefaultMQProducersend方法发送消息

DefaultMQProducer该类是线程安全的。在配置并启动完成后可在多个线程间安全共享)类是应用用来投递消息的入口,开箱即用,可通过无参构造方法快速创建一个生产者。主要负责消息的发送,支持同步/异步/oneway的发送方式,这些发送方式均支持批量发送。可以通过该类提供的getter/setter方法,调整发送者的参数。DefaultMQProducer提供了多个send方法,每个send方法略有不同。

字段摘要
类型字段名称描述
DefaultMQProducerImpldefaultMQProducerImpl生产者的内部默认实现
StringproducerGroup生产者分组
StringcreateTopicKey在发送消息时,自动创建服务器不存在的topic
intdefaultTopicQueueNums创建topic时默认的队列数量
intsendMsgTimeout发送消息的超时时间
intcompressMsgBodyOverHowmuch压缩消息体的阈值
intretryTimesWhenSendFailed同步模式下内部尝试发送消息的最大次数
intretryTimesWhenSendAsyncFailed异步模式下内部尝试发送消息的最大次数
booleanretryAnotherBrokerWhenNotStoreOK是否在内部发送失败时重试另一个broker
intmaxMessageSize消息的最大长度
TraceDispatchertraceDispatcher消息追踪器。使用rcpHook来追踪消息
构造方法摘要
方法名称方法描述
DefaultMQProducer()由默认参数值创建一个生产者
DefaultMQProducer(final String producerGroup)使用指定的分组名创建一个生产者
DefaultMQProducer(final String producerGroup, boolean enableMsgTrace)使用指定的分组名创建一个生产者,并设置是否开启消息追踪
DefaultMQProducer(final String producerGroup, boolean enableMsgTrace, final String customizedTraceTopic)使用指定的分组名创建一个生产者,并设置是否开启消息追踪及追踪topic的名称
DefaultMQProducer(RPCHook rpcHook)使用指定的hook创建一个生产者
DefaultMQProducer(final String producerGroup, RPCHook rpcHook)使用指定的分组名及自定义hook创建一个生产者
DefaultMQProducer(final String producerGroup, RPCHook rpcHook, boolean enableMsgTrace,final String customizedTraceTopic)使用指定的分组名及自定义hook创建一个生产者,并设置是否开启消息追踪及追踪topic的名称
使用方法摘要
返回值方法名称方法描述
voidcreateTopic(String key, String newTopic, int queueNum)在broker上创建指定的topic
voidcreateTopic(String key, String newTopic, int queueNum, int topicSysFlag)在broker上创建指定的topic
longearliestMsgStoreTime(MessageQueue mq)查询最早的消息存储时间
ListfetchPublishMessageQueues(String topic)获取topic的消息队列
longmaxOffset(MessageQueue mq)查询给定消息队列的最大offset
longminOffset(MessageQueue mq)查询给定消息队列的最小offset
QueryResultqueryMessage(String topic, String key, int maxNum, long begin, long end)按关键字查询消息
longsearchOffset(MessageQueue mq, long timestamp)查找指定时间的消息队列的物理offset
SendResultsend(Collection msgs)同步批量发送消息
SendResultsend(Collection msgs, long timeout)同步批量发送消息
SendResultsend(Collection msgs, MessageQueue messageQueue)向指定的消息队列同步批量发送消息
SendResultsend(Collection msgs, MessageQueue messageQueue, long timeout)向指定的消息队列同步批量发送消息,并指定超时时间
SendResultsend(Message msg)同步单条发送消息
SendResultsend(Message msg, long timeout)同步发送单条消息,并指定超时时间
SendResultsend(Message msg, MessageQueue mq)向指定的消息队列同步发送单条消息
SendResultsend(Message msg, MessageQueue mq, long timeout)向指定的消息队列同步单条发送消息,并指定超时时间
voidsend(Message msg, MessageQueue mq, SendCallback sendCallback)向指定的消息队列异步单条发送消息,并指定回调方法
voidsend(Message msg, MessageQueue mq, SendCallback sendCallback, long timeout)向指定的消息队列异步单条发送消息,并指定回调方法和超时时间
SendResultsend(Message msg, MessageQueueSelector selector, Object arg)向消息队列同步单条发送消息,并指定发送队列选择器
SendResultsend(Message msg, MessageQueueSelector selector, Object arg, long timeout)向消息队列同步单条发送消息,并指定发送队列选择器与超时时间
voidsend(Message msg, MessageQueueSelector selector, Object arg, SendCallback sendCallback)向指定的消息队列异步单条发送消息
voidsend(Message msg, MessageQueueSelector selector, Object arg, SendCallback sendCallback, long timeout)向指定的消息队列异步单条发送消息,并指定超时时间
voidsend(Message msg, SendCallback sendCallback)异步发送消息
voidsend(Message msg, SendCallback sendCallback, long timeout)异步发送消息,并指定回调方法和超时时间
TransactionSendResultsendMessageInTransaction(Message msg, LocalTransactionExecuter tranExecuter, final Object arg)发送事务消息,并指定本地执行事务实例
TransactionSendResultsendMessageInTransaction(Message msg, Object arg)发送事务消息
voidsendOneway(Message msg)单向发送消息,不等待broker响应
voidsendOneway(Message msg, MessageQueue mq)单向发送消息到指定队列,不等待broker响应
voidsendOneway(Message msg, MessageQueueSelector selector, Object arg)单向发送消息到队列选择器的选中的队列,不等待broker响应
voidshutdown()关闭当前生产者实例并释放相关资源
voidstart()启动生产者
MessageExtviewMessage(String offsetMsgId)根据给定的msgId查询消息
MessageExtpublic MessageExt viewMessage(String topic, String msgId)根据给定的msgId查询消息,并指定topic

代码分析

DefaultMQProducer的构造方法

 直接分析最复杂的一个构造方法

public DefaultMQProducer(final String producerGroup, RPCHook rpcHook, boolean enableMsgTrace,
        final String customizedTraceTopic) {
		//消息组
        this.producerGroup = producerGroup;
        //消息发送实现类
        defaultMQProducerImpl = new DefaultMQProducerImpl(this, rpcHook);
        //if client open the message trace feature
		//如果开启了消息轨迹功能,增加对应的RPC钩子方法来追踪消息
        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");
            }
        }
    }

 这里主要就是创建一个生产者内部的默认实现类DefaultMQProducerImpl以及消息轨迹相关的钩子方法的注册。进一步的分析对应的默认实现类的构造方法

public DefaultMQProducerImpl(final DefaultMQProducer defaultMQProducer, RPCHook rpcHook) {
        this.defaultMQProducer = defaultMQProducer;
		//RPC钩子
        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());
                }
            });
    }

 逻辑也是比较简单的。继续下面的分析

生产者实例的启动start方法
    public void start() throws MQClientException {
    	//设置生产者的producerGroup,也就是分组名,如果没有设置,会按照一定的规律生成一个
        this.setProducerGroup(withNamespace(this.producerGroup));
		//调用生产者默认实现DefaultMQProducerImpl的启动方法
        this.defaultMQProducerImpl.start();
        //如果消息轨迹相关的消息分发类不是null,说明启用来消息轨迹分析,这里需要进行启动。不对消息轨迹的逻辑进行分析
        if (null != traceDispatcher) {
            try {
                traceDispatcher.start(this.getNamesrvAddr(), this.getAccessChannel());
            } catch (MQClientException e) {
                log.warn("trace dispatcher start failed ", e);
            }
        }
    }

 生成者的启动实际逻辑也是在DefaultMQProducerImpl中。进入DefaultMQProducerImpl看其start方法实现逻辑

 public void start(final boolean startFactory) throws MQClientException {
		//根据服务的状态进行不同的逻辑处理
        switch (this.serviceState) {
			//刚创建,还没启动
            case CREATE_JUST:
            	//预先设置为启动失败
                this.serviceState = ServiceState.START_FAILED;
                //检查分组配置
                this.checkConfig();
                //如果生产者分组不是内部消息的分组,则修改对应的实例名(instanceName)
                if (!this.defaultMQProducer.getProducerGroup().equals(MixAll.CLIENT_INNER_PRODUCER_GROUP)) {
                    this.defaultMQProducer.changeInstanceNameToPID();
                }
                //创建客户端
                this.mQClientFactory = MQClientManager.getInstance().getOrCreateMQClientInstance(this.defaultMQProducer, rpcHook);
                //注册生产者,这个会把对应的生产者加入到对应的分组集合中(group, producer),然后在向broker发送心跳包的时候带过去
                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);
                }
                //消息topic发布的集合缓存
                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;
        }
        //加锁的方式发送心跳给所有的broker
        this.mQClientFactory.sendHeartbeatToAllBrokerWithLock();
        //定期清除超时请求的任务
        this.timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                try {
                    RequestFutureTable.scanExpiredRequest();
                } catch (Throwable e) {
                    log.error("scan RequestFutureTable exception", e);
                }
            }
        }, 1000 * 3, 1000);
    }

start主要逻辑还是先根据服务启动的状态,进行不同的处理逻辑,其中只有刚创建,还没启动的逻辑是复杂的。服务启动之后会向所有的broker发送心跳,然后启动定时清除超时的发送消息请求的任务。

 启动的服务的逻辑如下:

  1. 检查生产者的分组配置
  2. 创建客户端
  3. 吧对应的客户端和对应的分组保存到一个集合中(这个集合会通过心跳发送broker)
  4. 检查是不是添加是不是成功,然后启动服务,更新服务状态

 这几个步骤中比较复杂的逻辑是客户端的创建。因为这个客户端的创建不仅仅是消息的生产者调用,还是消息的消费者也会调用的类。客户端的创建会通过MQClientManager包装一次,然后创建MQClientInstance。包装的作用是,对生产者客户端进行缓存,避免重复创建,因为DefaultMQProducer是可以多线程调用的。

 public MQClientInstance getOrCreateMQClientInstance(final ClientConfig clientConfig, RPCHook rpcHook) {
        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;
    }

 可以看到DefaultMQProducerImplstart方法的主要做的还是创建MQClientInstance对象,然后调用他的start方法。对于MQClientInstance下一篇文章进行分析。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值