先看一个官网提供的rockermq发送同步消息的案例代码,以这个发送消息的案例代码为线索来探究rockermq发送消息到brocker的流程
public class SyncProducer {
public static void main(String[] args) throws Exception {
//Instantiate with a producer group name.
DefaultMQProducer producer = new
DefaultMQProducer("please_rename_unique_group_name");
// Specify name server addresses.
producer.setNamesrvAddr("localhost:9876");
//Launch the instance.
producer.start();
for (int i = 0; i < 100; i++) {
//Create a message instance, specifying topic, tag and message body.
Message msg = new Message("TopicTest" /* Topic */,
"TagA" /* Tag */,
("Hello RocketMQ " +
i).getBytes(RemotingHelper.DEFAULT_CHARSET) /* Message body */
);
//Call send message to deliver message to one of brokers.
SendResult sendResult = producer.send(msg);
System.out.printf("%s%n", sendResult);
}
//Shut down once the producer instance is not longer in use.
producer.shutdown();
}
}
一、创建DefaultMQProducer对象,这里由于是发送同步消息,所以调用的是生产者一个参数的构造函数,参数为生成者的group名称,如果是异步则调用带有回调钩子函数的构造函数。构造函数中主要做的事情就是初始化了生产者的具体实现类,并且创建了一个异步发送消息任务的线程池。
生产者的核心属性:
#DefaultMQProducer
/**
* topic的默认队列个数
*/
private volatile int defaultTopicQueueNums = 4;
/**
* 消息发送的超时时间,默认是3秒
*/
private int sendMsgTimeout = 3000;
/**
* 当消息大于4k的时候进行消息压缩
*/
private int compressMsgBodyOverHowmuch = 1024 * 4;
/**
* 同步消息默认消息失败之后的重试次数,所以消息失败的情况下,默认最多发送3次,这会导致消息的重复,需要开发人员进行处理
*/
private int retryTimesWhenSendFailed = 2;
/**
* 异步消息默认失败之后重试次数,也会导致消息重复发送
*/
private int retryTimesWhenSendAsyncFailed = 2;
/**
* Indicate whether to retry another broker on sending failure internally.
*/
private boolean retryAnotherBrokerWhenNotStoreOK = false;
/**
* Maximum allowed message size in bytes.
*/
private int maxMessageSize = 1024 * 1024 * 4; // 4M
二、设置nameserver地址,调用start()方法启动生产者。查看具体的启动代码,初次启动的启动状态为CREATE_JUST
2.1 检查配置,这里主要是检查group名称是否合法
2.2 主要看一下getOrCreateMQClientInstance方法,这里是创建MQClientlnstance 实例,MQClientInstance封装了rocketMq网络处理API,是消息生产者( Producer )、消息消费者
( Consumer )与NameServ町、Broker 打交道的网络通道。
先给MQClientInstance创建一个clientId,先从factoryTable中,如果没有则创建并将实例放入到factoryTable中,这样可以保证启动一个生产者的时候只有一个MQClientInstance。创建完成之后,将MQClientInstance以group为key值放入到producerTable中,如果之前这个group名称已经创建过MQClientInstance实例则抛出错误。
2.3 在topicPublishInfoTable初始化当前topic的路由信息。此时的TopicPublishInfo还没有具体的路由信息。TopicPublishInfo是生产者消息发送到brocker所依赖路由的主要依据。与NameServer的路由信息管理RouteInfoManager是相互同步的。看一下路由表中的主要信息TopicPublishInfo。
TopicRouteData中的详细信息
2.4 启动消息生产者,并且启动定时任务,定时拉取nameserver地址,从broker拉取消息的服务和均衡消息队列服务,负责分配消费者可消费的消息队列,生产者的pullMessageService以及rebalanceService由于启动的是一个deamon线程,并且通过源码查看,任务队列中并没有任务,而且rocketmq的负载均衡是在客户端自己做的,所以这里我不大了解到底是做了什么,待进一步查看,这里就不看了。
三、消息的发送
3.1 创建消息Message,查看消息中的主要内容
3.2 消息的发送,先进行消息的检查,查看消息的topic是否合法,以及消息的内容是否合法(消息长度之类的)最终调用DefaultMQProducerImpl#sendDefaultImpl方法来发送消息。这里查看消息发送的部分主要代码内容
3.2.1 选择消息发送的队列
每次选择的发送队列都是上次选择队列的id加1,然后通过hash算法选择一个队列,这样可以是的消息均匀的分布到每个队列。选择队列之后判断队列所在的brocker是否正常,如果不正常则选择另外一个队列,如果都不正常,则在所有不正常的broker列表中选择最后一个进行发送。
3.2.2 选择broker之后最后通过brokerName在brokerAddrTable获取broker的ip加端口信息,创建请求,然后发送消息请求
总结:生产者主要是通过从nameserver中定时获取路由信息,并将信息维护在topicPublishInfoTable中,然后每次发送消息的时候,从这个路由表中获取具体发送的broker以及队列信息,并且有容错机制以及重试机制。这里只是简单的分析了一下同步消息的发送,至于异步消息,事物消息都没有分析