RocketMq源码解读(三)Producer启动

本章主要介绍RocketMq的消息格式和消息的发送

 

一、RocketMQ的消息格式

在RocketMq中,每一个消息被封装为Message,有属性topic,flag,扩展字段和消息内容。

public class Message implements Serializable{
    private String topic;
    private int flag;
    // 扩展属性字段
    private Map<String, String> properties;
    private byte[] body;
...
}

二、Producer的启动

消息的发送和接收都在client包下。

1、接口MQAdmin提供了基本的MQ管理操作,

public interface MQAdmin{
    // 创建topic
    void createTopic(final String key, final String newTopic, final int queueNum) throws MQClientException;
    // 增加topic的系统标识
    void createTopic(String key, String newTopic, int queueNum, int topicSysFlag) throws MQClientException;
    // 根据时间戳获取消息队列的offset
    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;
    // 根据messageId查询消息
    MessageExt viewMessage(final String offsetMsgId) throws RemotingException, MQBrokerException, InterruptedException, MQClientException;
    MessageExt viewMessage(String topic, String msgId) throws RemotingException, MQBrokerException, InterruptedException, MQClientException;
    QueryResult queryMessage(final String topic, final String key, final int maxNum, final long begin, final long end) throws MQClientException, InterruptedException;
}

2、MQProducer继承自MQAdmin,增加了消息的发送方式

public interface MQProducer extends MQAdmin{
    // 根据topic获取消息队列
    List<MessageQueue> fetchPublishMessageQueues(final String topic) throws MQClientException;
    SendResult send(final Message msg) throws MQClientException, RemotingException, MQBrokerException,InterruptedException;
    // 发送消息后,会回调
    void send(final Message msg, final SendCallback sendCallback, final long timeout)
        throws MQClientException, RemotingException, InterruptedException;
    SendResult send(final Message msg, final MessageQueue mq, final long timeout)
        throws MQClientException, RemotingException, MQBrokerException, InterruptedException;
    // 通过策略发送消息
    void send(final Message msg, final MessageQueueSelector selector, final Object arg,
        final SendCallback sendCallback, final long timeout) throws MQClientException, RemotingException, InterruptedException;
    // 发送事务消息
    TransactionSendResult sendMessageInTransaction(final Message msg,
        final LocalTransactionExecuter tranExecuter, final Object arg) throws MQClientException;
}

基本实现类是DefaultMqProducer, 继承了client的依稀额基本配置clientConfig

public class ClientConfig{
    // VIP通道
    public static final String SEND_MESSAGE_WITH_VIP_CHANNEL_PROPERTY = "com.rocketmq.sendMessageWithVIPChannel";
    // 获取NameSrv的地址
    private String namesrvAddr = System.getProperty(MixAll.NAMESRV_ADDR_PROPERTY, System.getenv(MixAll.NAMESRV_ADDR_ENV));
    // 对外暴露地址
    private String clientIP = RemotingUtil.getLocalAddress();
    // 实例名字
    private String instanceName = System.getProperty("rocketmq.client.name", "DEFAULT");
    // 回调线程初始化大小
    private int clientCallbackExecutorThreads = Runtime.getRuntime().availableProcessors();
    // 和namsrv30秒交换一次信息
    private int pollNameServerInteval = 1000 * 30;
    // 和broker30秒心跳一次
    private int heartbeatBrokerInterval = 1000 * 30;
    // 5秒持久化一次consumer的消费offset
    private int persistConsumerOffsetInterval = 1000 * 5;
}
public class DefaultMQProducer extends ClientConfig implements MQProducer{
    // 所有的实际操作委托给这个实现
    protected final transient DefaultMQProducerImpl defaultMQProducerImpl;
    // 默认topic队列数
    private volatile int defaultTopicQueueNums = 4;
    private int sendMsgTimeout = 3000;
    private int compressMsgBodyOverHowmuch = 1024 * 4;
    private int retryTimesWhenSendFailed = 2;
    private int retryTimesWhenSendAsyncFailed = 2;
    private boolean retryAnotherBrokerWhenNotStoreOK = false;
    private int maxMessageSize = 1024 * 1024 * 4;
    // 组合模式,委托到具体的实现类
    protected final transient DefaultMQProducerImpl defaultMQProducerImpl;
    ...
    public DefaultMQProducer() {
        this(MixAll.DEFAULT_PRODUCER_GROUP, null);
    }
    public DefaultMQProducer(final String producerGroup, RPCHook rpcHook) {
        this.producerGroup = producerGroup;
        defaultMQProducerImpl = new DefaultMQProducerImpl(this, rpcHook);
    }
    public DefaultMQProducer(final String producerGroup) {
        this(producerGroup, null);
    }
    public DefaultMQProducer(RPCHook rpcHook) {
        this(MixAll.DEFAULT_PRODUCER_GROUP, rpcHook);
    }
    ...
}

继续查看DefaultMQProducerImpl类

3、启动producer,调用DefaultMQProducer.start()方法,实际调用到DefaultMQProducerImpl.start()方法

public void start() throws MQClientException {
     this.start(true);
}
public void start(final boolean startFactory) throws MQClientException {
    // 默认为启动的时候,serviceState是CREATE_JUST
    switch (this.serviceState) {
        case CREATE_JUST:
        // 标记初始化失败,这个技巧不错。
        this.serviceState = ServiceState.START_FAILED;
        // 检查producer的group信息
        this.checkConfig();
        if (!this.defaultMQProducer.getProducerGroup().equals(MixAll.CLIENT_INNER_PRODUCER_GROUP)) {
            this.defaultMQProducer.changeInstanceNameToPID();
        }
        // 创建或获取MQClient对象,并且存储到内存factoryTable中
        this.mQClientFactory = MQClientManager.getInstance().getAndCreateMQClientInstance(this.defaultMQProducer, rpcHook);
        // 注册Producer,只是保存了group和producer的一个关系map
        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和topic的路由信息保存到内存中
        this.topicPublishInfoTable.put(this.defaultMQProducer.getCreateTopicKey(), new TopicPublishInfo());
        // 启动MQClient对象
        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();
}
getAndCreateMQClientInstance方法会根据clientId查看内存中是否有MQClientInstance,这个包含了与底层交互的netty client,注册netty的事件处理processor。
//初始化客户端请求实例
public MQClientInstance(ClientConfig clientConfig, int instanceIndex, String clientId, RPCHook rpcHook) {
    //mq的核心配置信息
    this.clientConfig = clientConfig;
    //当前进程内的唯一标识,升序数值
    this.instanceIndex = instanceIndex;
    //netty通信的客户端配置
    this.nettyClientConfig = new NettyClientConfig();
this.nettyClientConfig.setClientCallbackExecutorThreads(clientConfig.getClientCallbackExecutorThreads());
    this.nettyClientConfig.setUseTLS(clientConfig.isUseTLS());

    //解析客户端请求,封装的事件处理
    this.clientRemotingProcessor = new ClientRemotingProcessor(this);
    //客户端实例的实际实现,网络通信的核心,只是初始化了通信框架,具体的链接后面根据不同的地址再进行链接操作
    this.mQClientAPIImpl = new MQClientAPIImpl(this.nettyClientConfig, this.clientRemotingProcessor, rpcHook, clientConfig);
    //设置核心的nameserv地址
    if (this.clientConfig.getNamesrvAddr() != null) {
        this.mQClientAPIImpl.updateNameServerAddressList(this.clientConfig.getNamesrvAddr());
        log.info("user specified name server address: {}", this.clientConfig.getNamesrvAddr());
    }
    this.clientId = clientId;
    //mq管理
    this.mQAdminImpl = new MQAdminImpl(this);

    //拉取消息的实现
    this.pullMessageService = new PullMessageService(this);

    //负载均衡的实现,可能有相关的机器增加删除,需要定期的进行重负载操作
    this.rebalanceService = new RebalanceService(this);

    //消息发送者的包装,发送者的发送者,这个逻辑有点乱,并且在构造方法中重新初始化的
    //producer -> DefaultMQProducer -> DefaultMQProducerImpl -> MQClientInstance -> DefaultMQProducer
    this.defaultMQProducer = new DefaultMQProducer(MixAll.CLIENT_INNER_PRODUCER_GROUP);
    this.defaultMQProducer.resetClientConfig(clientConfig);
    //消费客户端的状态管理
    this.consumerStatsManager = new ConsumerStatsManager(this.scheduledExecutorService);
    log.info("Created a new client Instance, InstanceIndex:{}, ClientID:{}, ClientConfig:{}, ClientVersion:{}, SerializerType:{}",
        this.instanceIndex,
        this.clientId,
        this.clientConfig,
        MQVersion.getVersionDesc(MQVersion.CURRENT_VERSION), RemotingCommand.getSerializeTypeConfigInThisServer());
}

网络通信MQClientAPIImpl的构造

public MQClientAPIImpl(final NettyClientConfig nettyClientConfig,
    final ClientRemotingProcessor clientRemotingProcessor,
    RPCHook rpcHook, final ClientConfig clientConfig) {
    //核心的配置
    this.clientConfig = clientConfig;
    //该功能主要是如果namesrv为空,从约定的服务上去拉取
    topAddressing = new TopAddressing(MixAll.getWSAddr(), clientConfig.getUnitName());
    //通信客户端的核心实现,底层基于netty的链接
    this.remotingClient = new NettyRemotingClient(nettyClientConfig, null);
    //请求事件封装处理
    this.clientRemotingProcessor = clientRemotingProcessor;
    this.remotingClient.registerRPCHook(rpcHook);
    //将事件处理绑定到上层传递过来的事件处理封装类上
    this.remotingClient.registerProcessor(RequestCode.CHECK_TRANSACTION_STATE, this.clientRemotingProcessor, null);
    this.remotingClient.registerProcessor(RequestCode.NOTIFY_CONSUMER_IDS_CHANGED, this.clientRemotingProcessor, null);
    this.remotingClient.registerProcessor(RequestCode.RESET_CONSUMER_CLIENT_OFFSET, this.clientRemotingProcessor, null);
    this.remotingClient.registerProcessor(RequestCode.GET_CONSUMER_STATUS_FROM_CLIENT, this.clientRemotingProcessor, null);
    this.remotingClient.registerProcessor(RequestCode.GET_CONSUMER_RUNNING_INFO, this.clientRemotingProcessor, null);
    this.remotingClient.registerProcessor(RequestCode.CONSUME_MESSAGE_DIRECTLY, this.clientRemotingProcessor, null);
}

registerProducer只是在MQClient的Map中保存一个信息,ConcurrentHashMap<String/* group */, MQProducerInner> producerTable。mQClientFactory.start()方法会启动netty服务,启动定时线程任务,从namsesrv获取broker信息和topic信息。 

public class MQClientInstance {
    ...
    // 启动producer
    public void start() throws MQClientException {
        synchronized (this) {
            switch (this.serviceState) {
                case CREATE_JUST:
                    this.serviceState = ServiceState.START_FAILED;
                    // If not specified,looking address from name server
                    if (null == this.clientConfig.getNamesrvAddr()) {
                        this.mQClientAPIImpl.fetchNameServerAddr(); // TODO 待读:获取namesrv,从url
                    }
                    // Start request-response channel
                    this.mQClientAPIImpl.start();
                    // Start various schedule tasks
                    this.startScheduledTask();
                    // Start pull service
                    this.pullMessageService.start(); // TODO 疑问:producer调用这个干啥
                    // Start rebalance service
                    this.rebalanceService.start(); // TODO 疑问:producer调用这个干啥
                    // Start push service
                    this.defaultMQProducer.getDefaultMQProducerImpl().start(false); // TODO 疑问:为什么这里要调用
                    log.info("the client factory [{}] start OK", this.clientId);
                    this.serviceState = ServiceState.RUNNING;
                    break;
                case RUNNING:
                    break;
                case SHUTDOWN_ALREADY:
                    break;
                case START_FAILED:
                    throw new MQClientException("The Factory object[" + this.getClientId() + "] has been created before, and failed.", null);
                default:
                    break;
            }
        }
    }

 private void startScheduledTask() {
        if (null == this.clientConfig.getNamesrvAddr()) { //获取namesrv,从url
            this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
                @Override
                public void run() {
                    try {
                        MQClientInstance.this.mQClientAPIImpl.fetchNameServerAddr();
                    } catch (Exception e) {
                        log.error("ScheduledTask fetchNameServerAddr exception", e);
                    }
                }
            }, 1000 * 10, 1000 * 60 * 2, TimeUnit.MILLISECONDS);
        }

        // 定时拉取 Topic路由配置
        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    MQClientInstance.this.updateTopicRouteInfoFromNameServer();
                } catch (Exception e) {
                    log.error("ScheduledTask updateTopicRouteInfoFromNameServer exception", e);
                }
            }
        }, 10, this.clientConfig.getPollNameServerInteval(), TimeUnit.MILLISECONDS);

        // 定时同步消费进度
        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    MQClientInstance.this.cleanOfflineBroker();
                    MQClientInstance.this.sendHeartbeatToAllBrokerWithLock();
                } catch (Exception e) {
                    log.error("ScheduledTask sendHeartbeatToAllBroker exception", e);
                }
            }
        }, 1000, this.clientConfig.getHeartbeatBrokerInterval(), TimeUnit.MILLISECONDS);

        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() { // TODO 待读:ALL
                try {
                    MQClientInstance.this.persistAllConsumerOffset();
                } catch (Exception e) {
                    log.error("ScheduledTask persistAllConsumerOffset exception", e);
                }
            }
        }, 1000 * 10, this.clientConfig.getPersistConsumerOffsetInterval(), TimeUnit.MILLISECONDS);

        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() { // TODO 待读:ALL
                try {
                    MQClientInstance.this.adjustThreadPool();
                } catch (Exception e) {
                    log.error("ScheduledTask adjustThreadPool exception", e);
                }
            }
        }, 1, 1, TimeUnit.MINUTES);
    }
}

4、通过心跳的方式,向所有的broker注册Producer,并且将对应的groupName注册到broker。

this.mQClientFactory.sendHeartbeatToAllBrokerWithLock();

public void sendHeartbeatToAllBrokerWithLock() {
        if (this.lockHeartbeat.tryLock()) {
            try {
                this.sendHeartbeatToAllBroker();
                this.uploadFilterClassSource();
            } catch (final Exception e) {
                log.error("sendHeartbeatToAllBroker exception", e);
            } finally {
                this.lockHeartbeat.unlock();
            }
        } else {
            log.warn("lock heartBeat, but failed.");
        }
}

private void sendHeartbeatToAllBroker() {
        // 组装心跳数据
        final HeartbeatData heartbeatData = this.prepareHeartbeatData();
        final boolean producerEmpty = heartbeatData.getProducerDataSet().isEmpty();
        final boolean consumerEmpty = heartbeatData.getConsumerDataSet().isEmpty();
        if (producerEmpty && consumerEmpty) {
            log.warn("sending heartbeat, but no consumer and no producer");
            return;
        }
        
        long times = this.storeTimesTotal.getAndIncrement();
        // 遍历所有的broker,开始发送心跳包
        Iterator<Entry<String, HashMap<Long, String>>> it = this.brokerAddrTable.entrySet().iterator();
        while (it.hasNext()) {
            Entry<String, HashMap<Long, String>> entry = it.next();
            String brokerName = entry.getKey();
            HashMap<Long, String> oneTable = entry.getValue();
            if (oneTable != null) {
                for (Map.Entry<Long, String> entry1 : oneTable.entrySet()) {
                    Long id = entry1.getKey();
                    String addr = entry1.getValue();
                    if (addr != null) {
                        if (consumerEmpty) {
                            if (id != MixAll.MASTER_ID)
                                continue;
                        }

                        try {
                            this.mQClientAPIImpl.sendHearbeat(addr, heartbeatData, 3000);
                            if (times % 20 == 0) {
                                log.info("send heart beat to broker[{} {} {}] success", brokerName, id, addr);
                                log.info(heartbeatData.toString());
                            }
                        } catch (Exception e) {
                            if (this.isBrokerInNameServer(addr)) {
                                log.error("send heart beat to broker exception", e);
                            } else {
                                log.info("send heart beat to broker[{} {} {}] exception, because the broker not up, forget it", brokerName,
                                    id, addr);
                            }
                        }
                    }
                }
            }
        }
    }

到此,producer整体服务启动完成,状态变成RUNNING,可以创建topic和发送消息。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值