1:架构图
1.1:NameServer
在RocketMQ中,提供路由注册,发现,剔除等功能,并没有使用zk,redis等作为注册中心,而是使用NameServer作为注册中心,根据官方的话来说是简单,容易实现,并且在RocketMQ中NameServer之间是不存在通信的,也就是所谓的集群不通信,这点或许就是RocketMQ的精髓之一吧,将一些复杂的业务交给别人去实现,RocketMQ始终遵循着可以,但是没有必要的原则(最好的实现方式就是不实现 -- 可以实现,但是没有必要实现)
1.2:Broker
作为RocketMQ最核心的组件之一,掌管着消息配置,消息队列管理,存储等重要的功能,可以说是整个MQ的核心。
1.3:Producer
生产者,用于生产消息,必然会需要一些消息队列相关的配置信息,从而直接先从NameServer上面拉取,根据返回的信息找到对应的Broker,从而实现消息的精准发送到消息队列,这一点和dubbo中很类似,在使用注册中心为zk的情况下,客户端要想调用一个api的时候,会从注册中心拉取到对应api的地址,然后再进行远程调用是一个道理的。
2:源码解析
2.1:NameServer定时扫描,剔除
//定时任务扫描活跃的Broker,每10s一次
//如果刚刚好扫描完,然后Broker下线,此时会有一段事件Broker是不可用的,是怎么处理的,请看下回分解
this.scheduledExecutorService.scheduleAtFixedRate(() -> NamesrvController.this.routeInfoManager.scanNotActiveBroker(), 5, 10, TimeUnit.SECONDS);
public void scanNotActiveBroker() {
Iterator<Entry<String, BrokerLiveInfo>> it = this.brokerLiveTable.entrySet().iterator();
//获取存活的Broker
while (it.hasNext()) {
Entry<String, BrokerLiveInfo> next = it.next();
//获取上一次Broker的时间戳,用来对比是否超过 120s
long last = next.getValue().getLastUpdateTimestamp();
//private final static long BROKER_CHANNEL_EXPIRED_TIME = 1000 * 60 * 2;
if ((last + BROKER_CHANNEL_EXPIRED_TIME) < System.currentTimeMillis()) {
//如果超过了120s,则剔除这个已经过期的Broker
RemotingUtil.closeChannel(next.getValue().getChannel());
it.remove();
log.warn("The broker channel expired, {} {}ms", next.getKey(), BROKER_CHANNEL_EXPIRED_TIME);
this.onChannelDestroy(next.getKey(), next.getValue().getChannel());
}
}
}
2.2:Broker发送心跳包
// 定时向NameServer发动心跳包,报告自己还存活着
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
BrokerController.this.registerBrokerAll(true, false, brokerConfig.isForceRegister());
} catch (Throwable e) {
log.error("registerBrokerAll Exception", e);
}
}
// org.apache.rocketmq.common.BrokerConfig#registerNameServerPeriod
// registerNameServerPeriod = 1000 * 30;
}, 1000 * 10, Math.max(10000, Math.min(brokerConfig.getRegisterNameServerPeriod(), 60000)), TimeUnit.MILLISECONDS);
使用BrokerAPI真正发送心跳包 - 包装请求信息- 使用netty通信
org.apache.rocketmq.broker.BrokerController#registerBrokerAll
org.apache.rocketmq.broker.BrokerController#doRegisterBrokerAll
org.apache.rocketmq.broker.out.BrokerOuterAPI#registerBrokerAll
org.apache.rocketmq.remoting.netty.NettyRemotingClient#invokeOneway
public List<RegisterBrokerResult> registerBrokerAll(
final String clusterName,
final String brokerAddr,
final String brokerName,
final long brokerId,
final String haServerAddr,
final TopicConfigSerializeWrapper topicConfigWrapper,
final List<String> filterServerList,
final boolean oneway,
final int timeoutMills,
final boolean compressed) {
final List<RegisterBrokerResult> registerBrokerResultList = Lists.newArrayList();
List<String> nameServerAddressList = this.remotingClient.getNameServerAddressList();
//获取nameserver的列表
if (nameServerAddressList != null && nameServerAddressList.size() > 0) {
final RegisterBrokerRequestHeader requestHeader = new RegisterBrokerRequestHeader();
requestHeader.setBrokerAddr(brokerAddr);
requestHeader.setBrokerId(brokerId);
requestHeader.setBrokerName(brokerName);
requestHeader.setClusterName(clusterName);
requestHeader.setHaServerAddr(haServerAddr);
requestHeader.setCompressed(compressed);
RegisterBrokerBody requestBody = new RegisterBrokerBody();
requestBody.setTopicConfigSerializeWrapper(topicConfigWrapper);
requestBody.setFilterServerList(filterServerList);
final byte[] body = requestBody.encode(compressed);
final int bodyCrc32 = UtilAll.crc32(body);
requestHeader.setBodyCrc32(bodyCrc32);
final CountDownLatch countDownLatch = new CountDownLatch(nameServerAddressList.size());
//循环列表
for (final String namesrvAddr : nameServerAddressList) {
brokerOuterExecutor.execute(() -> {
try {
//包装请求头信息
/**
* brokerAddr
* brokerId
* brokerName
* clusterName
* haServerAddr
* compressed
*/
RegisterBrokerResult result = registerBroker(namesrvAddr,oneway, timeoutMills,requestHeader,body);
if (result != null) {
registerBrokerResultList.add(result);
}
log.info("register broker[{}]to name server {} OK", brokerId, namesrvAddr);
} catch (Exception e) {
log.warn("registerBroker Exception, {}", namesrvAddr, e);
} finally {
countDownLatch.countDown();
}
});
}
2.3:使用netty发送数据
@Override
public void invokeOneway(String addr, RemotingCommand request, long timeoutMillis) throws InterruptedException,
RemotingConnectException, RemotingTooMuchRequestException, RemotingTimeoutException, RemotingSendRequestException {
final Channel channel = this.getAndCreateChannel(addr);
if (channel != null && channel.isActive()) {
try {
doBeforeRpcHooks(addr, request);
this.invokeOnewayImpl(channel, request, timeoutMillis);
} catch (RemotingSendRequestException e) {
log.warn("invokeOneway: send request exception, so close the channel[{}]", addr);
this.closeChannel(addr, channel);
throw e;
}
} else {
this.closeChannel(addr, channel);
throw new RemotingConnectException(addr);
}
}
NameServer 处理逻辑
使用到了 NameServer的实现类RouteInfoManager
org.apache.rocketmq.namesrv.routeinfo.RouteInfoManager#registerBroker
#获取集群下面的Broker(在发送心跳包的时候,已经将clusterName放人到其中)
//根据集群名称获取当前集群下所有的Broker
Set<String> brokerNames = this.clusterAddrTable.get(clusterName);
if (null == brokerNames) {
brokerNames = new HashSet<String>();
this.clusterAddrTable.put(clusterName, brokerNames);
}
#当前Broker注册表获取不到broker,那么新建一个,并添加到Broker中去
/获取不到broker新来的,需要新增一个
BrokerData brokerData = this.brokerAddrTable.get(brokerName);
if (null == brokerData) {
registerFirst = true;
brokerData = new BrokerData(clusterName, brokerName, new HashMap<Long, String>());
this.brokerAddrTable.put(brokerName, brokerData);
}
#添加Broker注册表, 并进行更新Broker的最新的心跳时间戳
//添加注册表
String oldAddr = brokerData.getBrokerAddrs().put(brokerId, brokerAddr);
registerFirst = registerFirst || (null == oldAddr);
if (null != topicConfigWrapper
&& MixAll.MASTER_ID == brokerId) {
if (this.isBrokerTopicConfigChanged(brokerAddr, topicConfigWrapper.getDataVersion())
|| registerFirst) {
ConcurrentMap<String, TopicConfig> tcTable =
topicConfigWrapper.getTopicConfigTable();
if (tcTable != null) {
//f更新注册Broker的注册时间戳,便于NameServer检测Broker下线
for (Map.Entry<String, TopicConfig> entry : tcTable.entrySet()) {
this.createAndUpdateQueueData(brokerName, entry.getValue());
}
}
}
}
2.3:路由发现
public RemotingCommand getRouteInfoByTopic(ChannelHandlerContext ctx,
RemotingCommand request) throws RemotingCommandException {
final RemotingCommand response = RemotingCommand.createResponseCommand(null);
final GetRouteInfoRequestHeader requestHeader =
(GetRouteInfoRequestHeader) request.decodeCommandCustomHeader(GetRouteInfoRequestHeader.class);
//1.通过nameServer获取路由信息
TopicRouteData topicRouteData = this.namesrvController.getRouteInfoManager().pickupTopicRouteData(requestHeader.getTopic());
if (topicRouteData != null) {
//2.获取是否为顺序消息配置,如果是顺序消息,需要填充 topic头为 NamesrvUtil.NAMESPACE_ORDER_TOPIC_CONFIG,声明为顺序消息
if (this.namesrvController.getNamesrvConfig().isOrderMessageEnable()) {
//3. 如果是顺序消息,需要填充 topic头为 NamesrvUtil.NAMESPACE_ORDER_TOPIC_CONFIG,声明为顺序消息
String orderTopicConf =
this.namesrvController.getKvConfigManager().getKVConfig(NamesrvUtil.NAMESPACE_ORDER_TOPIC_CONFIG,
requestHeader.getTopic());
topicRouteData.setOrderTopicConf(orderTopicConf);
}
byte[] content = topicRouteData.encode();
response.setBody(content);
response.setCode(ResponseCode.SUCCESS);
response.setRemark(null);
return response;
}
获取路由信息
#org.apache.rocketmq.namesrv.routeinfo.RouteInfoManager#pickupTopicRouteData
try {
this.lock.readLock().lockInterruptibly();
//1.通过topic获取到队列信息,里面是携带brokerName的
//org.apache.rocketmq.common.protocol.route.QueueData
/**
private String brokerName;
private int readQueueNums;
private int writeQueueNums;
private int perm;
private int topicSynFlag;
*/
List<QueueData> queueDataList = this.topicQueueTable.get(topic);
if (queueDataList != null) {
topicRouteData.setQueueDatas(queueDataList);
foundQueueData = true;
Iterator<QueueData> it = queueDataList.iterator();
while (it.hasNext()) {
QueueData qd = it.next();
//2.获取BrokerName 的集合
brokerNameSet.add(qd.getBrokerName());
}
for (String brokerName : brokerNameSet) {
//3. 通过brokerName 获取到Broker
BrokerData brokerData = this.brokerAddrTable.get(brokerName);
if (null != brokerData) {
BrokerData brokerDataClone = new BrokerData(brokerData.getCluster(), brokerData.getBrokerName(), (HashMap<Long, String>) brokerData
.getBrokerAddrs().clone());
brokerDataList.add(brokerDataClone);
foundBrokerData = true;
for (final String brokerAddr : brokerDataClone.getBrokerAddrs().values()) {
List<String> filterServerList = this.filterServerTable.get(brokerAddr);
filterServerMap.put(brokerAddr, filterServerList);
}
}
}
}