PageRenderTime 94ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/rocketmq-client/src/main/java/com/alibaba/rocketmq/client/impl/factory/MQClientInstance.java

https://gitlab.com/xialeizhou/RocketMQ
Java | 1227 lines | 993 code | 195 blank | 39 comment | 207 complexity | ca473ba5265d09c76e48e461bf157df5 MD5 | raw file
  1. /**
  2. * Copyright (C) 2010-2013 Alibaba Group Holding Limited
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.alibaba.rocketmq.client.impl.factory;
  17. import java.io.UnsupportedEncodingException;
  18. import java.net.DatagramSocket;
  19. import java.util.Collections;
  20. import java.util.HashMap;
  21. import java.util.HashSet;
  22. import java.util.Iterator;
  23. import java.util.List;
  24. import java.util.Map;
  25. import java.util.Map.Entry;
  26. import java.util.Set;
  27. import java.util.concurrent.ConcurrentHashMap;
  28. import java.util.concurrent.Executors;
  29. import java.util.concurrent.ScheduledExecutorService;
  30. import java.util.concurrent.ThreadFactory;
  31. import java.util.concurrent.TimeUnit;
  32. import java.util.concurrent.locks.Lock;
  33. import java.util.concurrent.locks.ReentrantLock;
  34. import org.slf4j.Logger;
  35. import com.alibaba.rocketmq.client.ClientConfig;
  36. import com.alibaba.rocketmq.client.admin.MQAdminExtInner;
  37. import com.alibaba.rocketmq.client.exception.MQBrokerException;
  38. import com.alibaba.rocketmq.client.exception.MQClientException;
  39. import com.alibaba.rocketmq.client.impl.ClientRemotingProcessor;
  40. import com.alibaba.rocketmq.client.impl.FindBrokerResult;
  41. import com.alibaba.rocketmq.client.impl.MQAdminImpl;
  42. import com.alibaba.rocketmq.client.impl.MQClientAPIImpl;
  43. import com.alibaba.rocketmq.client.impl.MQClientManager;
  44. import com.alibaba.rocketmq.client.impl.consumer.DefaultMQPullConsumerImpl;
  45. import com.alibaba.rocketmq.client.impl.consumer.DefaultMQPushConsumerImpl;
  46. import com.alibaba.rocketmq.client.impl.consumer.MQConsumerInner;
  47. import com.alibaba.rocketmq.client.impl.consumer.ProcessQueue;
  48. import com.alibaba.rocketmq.client.impl.consumer.PullMessageService;
  49. import com.alibaba.rocketmq.client.impl.consumer.RebalanceService;
  50. import com.alibaba.rocketmq.client.impl.producer.DefaultMQProducerImpl;
  51. import com.alibaba.rocketmq.client.impl.producer.MQProducerInner;
  52. import com.alibaba.rocketmq.client.impl.producer.TopicPublishInfo;
  53. import com.alibaba.rocketmq.client.log.ClientLogger;
  54. import com.alibaba.rocketmq.client.producer.DefaultMQProducer;
  55. import com.alibaba.rocketmq.client.stat.ConsumerStatsManager;
  56. import com.alibaba.rocketmq.common.MQVersion;
  57. import com.alibaba.rocketmq.common.MixAll;
  58. import com.alibaba.rocketmq.common.ServiceState;
  59. import com.alibaba.rocketmq.common.UtilAll;
  60. import com.alibaba.rocketmq.common.conflict.PackageConflictDetect;
  61. import com.alibaba.rocketmq.common.constant.PermName;
  62. import com.alibaba.rocketmq.common.message.MessageExt;
  63. import com.alibaba.rocketmq.common.message.MessageQueue;
  64. import com.alibaba.rocketmq.common.protocol.body.ConsumeMessageDirectlyResult;
  65. import com.alibaba.rocketmq.common.protocol.body.ConsumerRunningInfo;
  66. import com.alibaba.rocketmq.common.protocol.heartbeat.ConsumeType;
  67. import com.alibaba.rocketmq.common.protocol.heartbeat.ConsumerData;
  68. import com.alibaba.rocketmq.common.protocol.heartbeat.HeartbeatData;
  69. import com.alibaba.rocketmq.common.protocol.heartbeat.ProducerData;
  70. import com.alibaba.rocketmq.common.protocol.heartbeat.SubscriptionData;
  71. import com.alibaba.rocketmq.common.protocol.route.BrokerData;
  72. import com.alibaba.rocketmq.common.protocol.route.QueueData;
  73. import com.alibaba.rocketmq.common.protocol.route.TopicRouteData;
  74. import com.alibaba.rocketmq.remoting.RPCHook;
  75. import com.alibaba.rocketmq.remoting.common.RemotingHelper;
  76. import com.alibaba.rocketmq.remoting.exception.RemotingException;
  77. import com.alibaba.rocketmq.remoting.netty.NettyClientConfig;
  78. /**
  79. * @author shijia.wxr<vintage.wang@gmail.com>
  80. * @since 2013-6-15
  81. */
  82. public class MQClientInstance {
  83. private final static long LockTimeoutMillis = 3000;
  84. private final Logger log = ClientLogger.getLog();
  85. private final ClientConfig clientConfig;
  86. private final int instanceIndex;
  87. private final String clientId;
  88. private final long bootTimestamp = System.currentTimeMillis();
  89. private final ConcurrentHashMap<String/* group */, MQProducerInner> producerTable =
  90. new ConcurrentHashMap<String, MQProducerInner>();
  91. private final ConcurrentHashMap<String/* group */, MQConsumerInner> consumerTable =
  92. new ConcurrentHashMap<String, MQConsumerInner>();
  93. private final ConcurrentHashMap<String/* group */, MQAdminExtInner> adminExtTable =
  94. new ConcurrentHashMap<String, MQAdminExtInner>();
  95. private final NettyClientConfig nettyClientConfig;
  96. private final MQClientAPIImpl mQClientAPIImpl;
  97. private final MQAdminImpl mQAdminImpl;
  98. private final ConcurrentHashMap<String/* Topic */, TopicRouteData> topicRouteTable =
  99. new ConcurrentHashMap<String, TopicRouteData>();
  100. private final Lock lockNamesrv = new ReentrantLock();
  101. private final Lock lockHeartbeat = new ReentrantLock();
  102. private final ConcurrentHashMap<String/* Broker Name */, HashMap<Long/* brokerId */, String/* address */>> brokerAddrTable =
  103. new ConcurrentHashMap<String, HashMap<Long, String>>();
  104. private final ScheduledExecutorService scheduledExecutorService = Executors
  105. .newSingleThreadScheduledExecutor(new ThreadFactory() {
  106. @Override
  107. public Thread newThread(Runnable r) {
  108. return new Thread(r, "MQClientFactoryScheduledThread");
  109. }
  110. });
  111. private final ClientRemotingProcessor clientRemotingProcessor;
  112. private final PullMessageService pullMessageService;
  113. private final RebalanceService rebalanceService;
  114. private final DefaultMQProducer defaultMQProducer;
  115. private ServiceState serviceState = ServiceState.CREATE_JUST;
  116. private DatagramSocket datagramSocket;
  117. private final ConsumerStatsManager consumerStatsManager;
  118. public MQClientInstance(ClientConfig clientConfig, int instanceIndex, String clientId, RPCHook rpcHook) {
  119. this.clientConfig = clientConfig;
  120. this.instanceIndex = instanceIndex;
  121. this.nettyClientConfig = new NettyClientConfig();
  122. this.nettyClientConfig.setClientCallbackExecutorThreads(clientConfig
  123. .getClientCallbackExecutorThreads());
  124. this.clientRemotingProcessor = new ClientRemotingProcessor(this);
  125. this.mQClientAPIImpl =
  126. new MQClientAPIImpl(this.nettyClientConfig, this.clientRemotingProcessor, rpcHook);
  127. if (this.clientConfig.getNamesrvAddr() != null) {
  128. this.mQClientAPIImpl.updateNameServerAddressList(this.clientConfig.getNamesrvAddr());
  129. log.info("user specified name server address: {}", this.clientConfig.getNamesrvAddr());
  130. }
  131. this.clientId = clientId;
  132. this.mQAdminImpl = new MQAdminImpl(this);
  133. this.pullMessageService = new PullMessageService(this);
  134. this.rebalanceService = new RebalanceService(this);
  135. this.defaultMQProducer = new DefaultMQProducer(MixAll.CLIENT_INNER_PRODUCER_GROUP);
  136. this.defaultMQProducer.resetClientConfig(clientConfig);
  137. this.consumerStatsManager = new ConsumerStatsManager(this.scheduledExecutorService);
  138. log.info("created a new client Instance, FactoryIndex: {} ClinetID: {} {} {}",//
  139. this.instanceIndex, //
  140. this.clientId, //
  141. this.clientConfig, //
  142. MQVersion.getVersionDesc(MQVersion.CurrentVersion));
  143. }
  144. public MQClientInstance(ClientConfig clientConfig, int instanceIndex, String clientId) {
  145. this(clientConfig, instanceIndex, clientId, null);
  146. }
  147. public void start() throws MQClientException {
  148. PackageConflictDetect.detectFastjson();
  149. synchronized (this) {
  150. switch (this.serviceState) {
  151. case CREATE_JUST:
  152. this.serviceState = ServiceState.START_FAILED;
  153. //If not specified,looking address from name server
  154. if (null == this.clientConfig.getNamesrvAddr()) {
  155. this.clientConfig.setNamesrvAddr(this.mQClientAPIImpl.fetchNameServerAddr());
  156. }
  157. //Start request-response channel
  158. this.mQClientAPIImpl.start();
  159. //Start various schedule tasks
  160. this.startScheduledTask();
  161. //Start pull service
  162. this.pullMessageService.start();
  163. //Start rebalance service
  164. this.rebalanceService.start();
  165. //Start push service
  166. this.defaultMQProducer.getDefaultMQProducerImpl().start(false);
  167. log.info("the client factory [{}] start OK", this.clientId);
  168. this.serviceState = ServiceState.RUNNING;
  169. break;
  170. case RUNNING:
  171. break;
  172. case SHUTDOWN_ALREADY:
  173. break;
  174. case START_FAILED:
  175. throw new MQClientException("The Factory object[" + this.getClientId()
  176. + "] has been created before, and failed.", null);
  177. default:
  178. break;
  179. }
  180. }
  181. }
  182. private void startScheduledTask() {
  183. if (null == this.clientConfig.getNamesrvAddr()) {
  184. this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
  185. @Override
  186. public void run() {
  187. try {
  188. MQClientInstance.this.mQClientAPIImpl.fetchNameServerAddr();
  189. } catch (Exception e) {
  190. log.error("ScheduledTask fetchNameServerAddr exception", e);
  191. }
  192. }
  193. }, 1000 * 10, 1000 * 60 * 2, TimeUnit.MILLISECONDS);
  194. }
  195. this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
  196. @Override
  197. public void run() {
  198. try {
  199. MQClientInstance.this.updateTopicRouteInfoFromNameServer();
  200. } catch (Exception e) {
  201. log.error("ScheduledTask updateTopicRouteInfoFromNameServer exception", e);
  202. }
  203. }
  204. }, 10, this.clientConfig.getPollNameServerInteval(), TimeUnit.MILLISECONDS);
  205. this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
  206. @Override
  207. public void run() {
  208. try {
  209. MQClientInstance.this.cleanOfflineBroker();
  210. MQClientInstance.this.sendHeartbeatToAllBrokerWithLock();
  211. } catch (Exception e) {
  212. log.error("ScheduledTask sendHeartbeatToAllBroker exception", e);
  213. }
  214. }
  215. }, 1000, this.clientConfig.getHeartbeatBrokerInterval(), TimeUnit.MILLISECONDS);
  216. this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
  217. @Override
  218. public void run() {
  219. try {
  220. MQClientInstance.this.persistAllConsumerOffset();
  221. } catch (Exception e) {
  222. log.error("ScheduledTask persistAllConsumerOffset exception", e);
  223. }
  224. }
  225. }, 1000 * 10, this.clientConfig.getPersistConsumerOffsetInterval(), TimeUnit.MILLISECONDS);
  226. this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
  227. @Override
  228. public void run() {
  229. try {
  230. MQClientInstance.this.adjustThreadPool();
  231. } catch (Exception e) {
  232. log.error("ScheduledTask adjustThreadPool exception", e);
  233. }
  234. }
  235. }, 1, 1, TimeUnit.MINUTES);
  236. }
  237. /**
  238. * Remove offline broker
  239. */
  240. private void cleanOfflineBroker() {
  241. try {
  242. if (this.lockNamesrv.tryLock(LockTimeoutMillis, TimeUnit.MILLISECONDS))
  243. try {
  244. ConcurrentHashMap<String, HashMap<Long, String>> updatedTable =
  245. new ConcurrentHashMap<String, HashMap<Long, String>>();
  246. Iterator<Entry<String, HashMap<Long, String>>> itBrokerTable =
  247. this.brokerAddrTable.entrySet().iterator();
  248. while (itBrokerTable.hasNext()) {
  249. Entry<String, HashMap<Long, String>> entry = itBrokerTable.next();
  250. String brokerName = entry.getKey();
  251. HashMap<Long, String> oneTable = entry.getValue();
  252. HashMap<Long, String> cloneAddrTable = new HashMap<Long, String>();
  253. cloneAddrTable.putAll(oneTable);
  254. Iterator<Entry<Long, String>> it = cloneAddrTable.entrySet().iterator();
  255. while (it.hasNext()) {
  256. Entry<Long, String> ee = it.next();
  257. String addr = ee.getValue();
  258. if (!this.isBrokerAddrExistInTopicRouteTable(addr)) {
  259. it.remove();
  260. log.info("the broker addr[{} {}] is offline, remove it", brokerName, addr);
  261. }
  262. }
  263. if (cloneAddrTable.isEmpty()) {
  264. itBrokerTable.remove();
  265. log.info("the broker[{}] name's host is offline, remove it", brokerName);
  266. } else {
  267. updatedTable.put(brokerName, cloneAddrTable);
  268. }
  269. }
  270. if (!updatedTable.isEmpty()) {
  271. this.brokerAddrTable.putAll(updatedTable);
  272. }
  273. } finally {
  274. this.lockNamesrv.unlock();
  275. }
  276. } catch (InterruptedException e) {
  277. log.warn("cleanOfflineBroker Exception", e);
  278. }
  279. }
  280. private boolean isBrokerAddrExistInTopicRouteTable(final String addr) {
  281. Iterator<Entry<String, TopicRouteData>> it = this.topicRouteTable.entrySet().iterator();
  282. while (it.hasNext()) {
  283. Entry<String, TopicRouteData> entry = it.next();
  284. TopicRouteData topicRouteData = entry.getValue();
  285. List<BrokerData> bds = topicRouteData.getBrokerDatas();
  286. for (BrokerData bd : bds) {
  287. if (bd.getBrokerAddrs() != null) {
  288. boolean exist = bd.getBrokerAddrs().containsValue(addr);
  289. if (exist)
  290. return true;
  291. }
  292. }
  293. }
  294. return false;
  295. }
  296. private void persistAllConsumerOffset() {
  297. Iterator<Entry<String, MQConsumerInner>> it = this.consumerTable.entrySet().iterator();
  298. while (it.hasNext()) {
  299. Entry<String, MQConsumerInner> entry = it.next();
  300. MQConsumerInner impl = entry.getValue();
  301. impl.persistConsumerOffset();
  302. }
  303. }
  304. public void sendHeartbeatToAllBrokerWithLock() {
  305. if (this.lockHeartbeat.tryLock()) {
  306. try {
  307. this.sendHeartbeatToAllBroker();
  308. this.uploadFilterClassSource();
  309. } catch (final Exception e) {
  310. log.error("sendHeartbeatToAllBroker exception", e);
  311. } finally {
  312. this.lockHeartbeat.unlock();
  313. }
  314. } else {
  315. log.warn("lock heartBeat, but failed.");
  316. }
  317. }
  318. private void uploadFilterClassToAllFilterServer(final String consumerGroup, final String fullClassName,
  319. final String topic, final String filterClassSource) throws UnsupportedEncodingException {
  320. byte[] classBody = null;
  321. int classCRC = 0;
  322. try {
  323. classBody = filterClassSource.getBytes(MixAll.DEFAULT_CHARSET);
  324. classCRC = UtilAll.crc32(classBody);
  325. } catch (Exception e1) {
  326. log.warn("uploadFilterClassToAllFilterServer Exception, ClassName: {} {}", //
  327. fullClassName,//
  328. RemotingHelper.exceptionSimpleDesc(e1));
  329. }
  330. TopicRouteData topicRouteData = this.topicRouteTable.get(topic);
  331. if (topicRouteData != null //
  332. && topicRouteData.getFilterServerTable() != null
  333. && !topicRouteData.getFilterServerTable().isEmpty()) {
  334. Iterator<Entry<String, List<String>>> it =
  335. topicRouteData.getFilterServerTable().entrySet().iterator();
  336. while (it.hasNext()) {
  337. Entry<String, List<String>> next = it.next();
  338. List<String> value = next.getValue();
  339. for (final String fsAddr : value) {
  340. try {
  341. this.mQClientAPIImpl.registerMessageFilterClass(fsAddr, consumerGroup, topic,
  342. fullClassName, classCRC, classBody, 5000);
  343. log.info(
  344. "register message class filter to {} OK, ConsumerGroup: {} Topic: {} ClassName: {}",
  345. fsAddr, consumerGroup, topic, fullClassName);
  346. } catch (Exception e) {
  347. log.error("uploadFilterClassToAllFilterServer Exception", e);
  348. }
  349. }
  350. }
  351. } else {
  352. log.warn(
  353. "register message class filter failed, because no filter server, ConsumerGroup: {} Topic: {} ClassName: {}",
  354. consumerGroup, topic, fullClassName);
  355. }
  356. }
  357. private void uploadFilterClassSource() {
  358. Iterator<Entry<String, MQConsumerInner>> it = this.consumerTable.entrySet().iterator();
  359. while (it.hasNext()) {
  360. Entry<String, MQConsumerInner> next = it.next();
  361. MQConsumerInner consumer = next.getValue();
  362. if (ConsumeType.CONSUME_PASSIVELY == consumer.consumeType()) {
  363. Set<SubscriptionData> subscriptions = consumer.subscriptions();
  364. for (SubscriptionData sub : subscriptions) {
  365. if (sub.isClassFilterMode() && sub.getFilterClassSource() != null) {
  366. final String consumerGroup = consumer.groupName();
  367. final String className = sub.getSubString();
  368. final String topic = sub.getTopic();
  369. final String filterClassSource = sub.getFilterClassSource();
  370. try {
  371. this.uploadFilterClassToAllFilterServer(consumerGroup, className, topic,
  372. filterClassSource);
  373. } catch (Exception e) {
  374. log.error("uploadFilterClassToAllFilterServer Exception", e);
  375. }
  376. }
  377. }
  378. }
  379. }
  380. }
  381. private void sendHeartbeatToAllBroker() {
  382. final HeartbeatData heartbeatData = this.prepareHeartbeatData();
  383. final boolean producerEmpty = heartbeatData.getProducerDataSet().isEmpty();
  384. final boolean consumerEmpty = heartbeatData.getConsumerDataSet().isEmpty();
  385. if (producerEmpty && consumerEmpty) {
  386. log.warn("sending hearbeat, but no consumer and no producer");
  387. return;
  388. }
  389. Iterator<Entry<String, HashMap<Long, String>>> it = this.brokerAddrTable.entrySet().iterator();
  390. while (it.hasNext()) {
  391. Entry<String, HashMap<Long, String>> entry = it.next();
  392. String brokerName = entry.getKey();
  393. HashMap<Long, String> oneTable = entry.getValue();
  394. if (oneTable != null) {
  395. for (Long id : oneTable.keySet()) {
  396. String addr = oneTable.get(id);
  397. if (addr != null) {
  398. if (consumerEmpty) {
  399. if (id != MixAll.MASTER_ID)
  400. continue;
  401. }
  402. try {
  403. this.mQClientAPIImpl.sendHearbeat(addr, heartbeatData, 3000);
  404. log.info("send heart beat to broker[{} {} {}] success", brokerName, id, addr);
  405. log.info(heartbeatData.toString());
  406. } catch (Exception e) {
  407. log.error("send heart beat to broker exception", e);
  408. }
  409. }
  410. }
  411. }
  412. }
  413. }
  414. private HeartbeatData prepareHeartbeatData() {
  415. HeartbeatData heartbeatData = new HeartbeatData();
  416. // clientID
  417. heartbeatData.setClientID(this.clientId);
  418. // Consumer
  419. for (String group : this.consumerTable.keySet()) {
  420. MQConsumerInner impl = this.consumerTable.get(group);
  421. if (impl != null) {
  422. ConsumerData consumerData = new ConsumerData();
  423. consumerData.setGroupName(impl.groupName());
  424. consumerData.setConsumeType(impl.consumeType());
  425. consumerData.setMessageModel(impl.messageModel());
  426. consumerData.setConsumeFromWhere(impl.consumeFromWhere());
  427. consumerData.getSubscriptionDataSet().addAll(impl.subscriptions());
  428. consumerData.setUnitMode(impl.isUnitMode());
  429. heartbeatData.getConsumerDataSet().add(consumerData);
  430. }
  431. }
  432. // Producer
  433. for (String group : this.producerTable.keySet()) {
  434. MQProducerInner impl = this.producerTable.get(group);
  435. if (impl != null) {
  436. ProducerData producerData = new ProducerData();
  437. producerData.setGroupName(group);
  438. heartbeatData.getProducerDataSet().add(producerData);
  439. }
  440. }
  441. return heartbeatData;
  442. }
  443. public void updateTopicRouteInfoFromNameServer() {
  444. Set<String> topicList = new HashSet<String>();
  445. // Consumer
  446. {
  447. Iterator<Entry<String, MQConsumerInner>> it = this.consumerTable.entrySet().iterator();
  448. while (it.hasNext()) {
  449. Entry<String, MQConsumerInner> entry = it.next();
  450. MQConsumerInner impl = entry.getValue();
  451. if (impl != null) {
  452. Set<SubscriptionData> subList = impl.subscriptions();
  453. if (subList != null) {
  454. for (SubscriptionData subData : subList) {
  455. topicList.add(subData.getTopic());
  456. }
  457. }
  458. }
  459. }
  460. }
  461. // Producer
  462. {
  463. Iterator<Entry<String, MQProducerInner>> it = this.producerTable.entrySet().iterator();
  464. while (it.hasNext()) {
  465. Entry<String, MQProducerInner> entry = it.next();
  466. MQProducerInner impl = entry.getValue();
  467. if (impl != null) {
  468. Set<String> lst = impl.getPublishTopicList();
  469. topicList.addAll(lst);
  470. }
  471. }
  472. }
  473. for (String topic : topicList) {
  474. this.updateTopicRouteInfoFromNameServer(topic);
  475. }
  476. }
  477. public boolean updateTopicRouteInfoFromNameServer(final String topic) {
  478. return updateTopicRouteInfoFromNameServer(topic, false, null);
  479. }
  480. public boolean updateTopicRouteInfoFromNameServer(final String topic, boolean isDefault,
  481. DefaultMQProducer defaultMQProducer) {
  482. try {
  483. if (this.lockNamesrv.tryLock(LockTimeoutMillis, TimeUnit.MILLISECONDS)) {
  484. try {
  485. TopicRouteData topicRouteData;
  486. if (isDefault && defaultMQProducer != null) {
  487. topicRouteData =
  488. this.mQClientAPIImpl.getDefaultTopicRouteInfoFromNameServer(
  489. defaultMQProducer.getCreateTopicKey(), 1000 * 3);
  490. if (topicRouteData != null) {
  491. for (QueueData data : topicRouteData.getQueueDatas()) {
  492. int queueNums =
  493. Math.min(defaultMQProducer.getDefaultTopicQueueNums(),
  494. data.getReadQueueNums());
  495. data.setReadQueueNums(queueNums);
  496. data.setWriteQueueNums(queueNums);
  497. }
  498. }
  499. } else {
  500. topicRouteData =
  501. this.mQClientAPIImpl.getTopicRouteInfoFromNameServer(topic, 1000 * 3);
  502. }
  503. if (topicRouteData != null) {
  504. TopicRouteData old = this.topicRouteTable.get(topic);
  505. boolean changed = topicRouteDataIsChange(old, topicRouteData);
  506. if (!changed) {
  507. changed = this.isNeedUpdateTopicRouteInfo(topic);
  508. } else {
  509. log.info("the topic[{}] route info changed, odl[{}] ,new[{}]", topic, old,
  510. topicRouteData);
  511. }
  512. if (changed) {
  513. TopicRouteData cloneTopicRouteData = topicRouteData.cloneTopicRouteData();
  514. for (BrokerData bd : topicRouteData.getBrokerDatas()) {
  515. this.brokerAddrTable.put(bd.getBrokerName(), bd.getBrokerAddrs());
  516. }
  517. // Update Pub info
  518. {
  519. TopicPublishInfo publishInfo =
  520. topicRouteData2TopicPublishInfo(topic, topicRouteData);
  521. publishInfo.setHaveTopicRouterInfo(true);
  522. Iterator<Entry<String, MQProducerInner>> it =
  523. this.producerTable.entrySet().iterator();
  524. while (it.hasNext()) {
  525. Entry<String, MQProducerInner> entry = it.next();
  526. MQProducerInner impl = entry.getValue();
  527. if (impl != null) {
  528. impl.updateTopicPublishInfo(topic, publishInfo);
  529. }
  530. }
  531. }
  532. //Update sub info
  533. {
  534. Set<MessageQueue> subscribeInfo =
  535. topicRouteData2TopicSubscribeInfo(topic, topicRouteData);
  536. Iterator<Entry<String, MQConsumerInner>> it =
  537. this.consumerTable.entrySet().iterator();
  538. while (it.hasNext()) {
  539. Entry<String, MQConsumerInner> entry = it.next();
  540. MQConsumerInner impl = entry.getValue();
  541. if (impl != null) {
  542. impl.updateTopicSubscribeInfo(topic, subscribeInfo);
  543. }
  544. }
  545. }
  546. log.info("topicRouteTable.put TopicRouteData[{}]", cloneTopicRouteData);
  547. this.topicRouteTable.put(topic, cloneTopicRouteData);
  548. return true;
  549. }
  550. } else {
  551. log.warn(
  552. "updateTopicRouteInfoFromNameServer, getTopicRouteInfoFromNameServer return null, Topic: {}",
  553. topic);
  554. }
  555. } catch (Exception e) {
  556. if (!topic.startsWith(MixAll.RETRY_GROUP_TOPIC_PREFIX)
  557. && !topic.equals(MixAll.DEFAULT_TOPIC)) {
  558. log.warn("updateTopicRouteInfoFromNameServer Exception", e);
  559. }
  560. } finally {
  561. this.lockNamesrv.unlock();
  562. }
  563. } else {
  564. log.warn("updateTopicRouteInfoFromNameServer tryLock timeout {}ms", LockTimeoutMillis);
  565. }
  566. } catch (InterruptedException e) {
  567. log.warn("updateTopicRouteInfoFromNameServer Exception", e);
  568. }
  569. return false;
  570. }
  571. private boolean topicRouteDataIsChange(TopicRouteData olddata, TopicRouteData nowdata) {
  572. if (olddata == null || nowdata == null)
  573. return true;
  574. TopicRouteData old = olddata.cloneTopicRouteData();
  575. TopicRouteData now = nowdata.cloneTopicRouteData();
  576. Collections.sort(old.getQueueDatas());
  577. Collections.sort(old.getBrokerDatas());
  578. Collections.sort(now.getQueueDatas());
  579. Collections.sort(now.getBrokerDatas());
  580. return !old.equals(now);
  581. }
  582. public static TopicPublishInfo topicRouteData2TopicPublishInfo(final String topic,
  583. final TopicRouteData route) {
  584. TopicPublishInfo info = new TopicPublishInfo();
  585. if (route.getOrderTopicConf() != null && route.getOrderTopicConf().length() > 0) {
  586. String[] brokers = route.getOrderTopicConf().split(";");
  587. for (String broker : brokers) {
  588. String[] item = broker.split(":");
  589. int nums = Integer.parseInt(item[1]);
  590. for (int i = 0; i < nums; i++) {
  591. MessageQueue mq = new MessageQueue(topic, item[0], i);
  592. info.getMessageQueueList().add(mq);
  593. }
  594. }
  595. info.setOrderTopic(true);
  596. }
  597. else {
  598. List<QueueData> qds = route.getQueueDatas();
  599. Collections.sort(qds);
  600. for (QueueData qd : qds) {
  601. if (PermName.isWriteable(qd.getPerm())) {
  602. BrokerData brokerData = null;
  603. for (BrokerData bd : route.getBrokerDatas()) {
  604. if (bd.getBrokerName().equals(qd.getBrokerName())) {
  605. brokerData = bd;
  606. break;
  607. }
  608. }
  609. if (null == brokerData) {
  610. continue;
  611. }
  612. if (!brokerData.getBrokerAddrs().containsKey(MixAll.MASTER_ID)) {
  613. continue;
  614. }
  615. for (int i = 0; i < qd.getWriteQueueNums(); i++) {
  616. MessageQueue mq = new MessageQueue(topic, qd.getBrokerName(), i);
  617. info.getMessageQueueList().add(mq);
  618. }
  619. }
  620. }
  621. info.setOrderTopic(false);
  622. }
  623. return info;
  624. }
  625. public static Set<MessageQueue> topicRouteData2TopicSubscribeInfo(final String topic,
  626. final TopicRouteData route) {
  627. Set<MessageQueue> mqList = new HashSet<MessageQueue>();
  628. List<QueueData> qds = route.getQueueDatas();
  629. for (QueueData qd : qds) {
  630. if (PermName.isReadable(qd.getPerm())) {
  631. for (int i = 0; i < qd.getReadQueueNums(); i++) {
  632. MessageQueue mq = new MessageQueue(topic, qd.getBrokerName(), i);
  633. mqList.add(mq);
  634. }
  635. }
  636. }
  637. return mqList;
  638. }
  639. private boolean isNeedUpdateTopicRouteInfo(final String topic) {
  640. boolean result = false;
  641. {
  642. Iterator<Entry<String, MQProducerInner>> it = this.producerTable.entrySet().iterator();
  643. while (it.hasNext() && !result) {
  644. Entry<String, MQProducerInner> entry = it.next();
  645. MQProducerInner impl = entry.getValue();
  646. if (impl != null) {
  647. result = impl.isPublishTopicNeedUpdate(topic);
  648. }
  649. }
  650. }
  651. {
  652. Iterator<Entry<String, MQConsumerInner>> it = this.consumerTable.entrySet().iterator();
  653. while (it.hasNext() && !result) {
  654. Entry<String, MQConsumerInner> entry = it.next();
  655. MQConsumerInner impl = entry.getValue();
  656. if (impl != null) {
  657. result = impl.isSubscribeTopicNeedUpdate(topic);
  658. }
  659. }
  660. }
  661. return result;
  662. }
  663. public void shutdown() {
  664. // Consumer
  665. if (!this.consumerTable.isEmpty())
  666. return;
  667. // AdminExt
  668. if (!this.adminExtTable.isEmpty())
  669. return;
  670. // Producer
  671. if (this.producerTable.size() > 1)
  672. return;
  673. synchronized (this) {
  674. switch (this.serviceState) {
  675. case CREATE_JUST:
  676. break;
  677. case RUNNING:
  678. this.defaultMQProducer.getDefaultMQProducerImpl().shutdown(false);
  679. this.serviceState = ServiceState.SHUTDOWN_ALREADY;
  680. this.pullMessageService.shutdown(true);
  681. this.scheduledExecutorService.shutdown();
  682. this.mQClientAPIImpl.shutdown();
  683. this.rebalanceService.shutdown();
  684. if (this.datagramSocket != null) {
  685. this.datagramSocket.close();
  686. this.datagramSocket = null;
  687. }
  688. MQClientManager.getInstance().removeClientFactory(this.clientId);
  689. log.info("the client factory [{}] shutdown OK", this.clientId);
  690. break;
  691. case SHUTDOWN_ALREADY:
  692. break;
  693. default:
  694. break;
  695. }
  696. }
  697. }
  698. public boolean registerConsumer(final String group, final MQConsumerInner consumer) {
  699. if (null == group || null == consumer) {
  700. return false;
  701. }
  702. MQConsumerInner prev = this.consumerTable.putIfAbsent(group, consumer);
  703. if (prev != null) {
  704. log.warn("the consumer group[" + group + "] exist already.");
  705. return false;
  706. }
  707. return true;
  708. }
  709. public void unregisterConsumer(final String group) {
  710. this.consumerTable.remove(group);
  711. this.unregisterClientWithLock(null, group);
  712. }
  713. private void unregisterClientWithLock(final String producerGroup, final String consumerGroup) {
  714. try {
  715. if (this.lockHeartbeat.tryLock(LockTimeoutMillis, TimeUnit.MILLISECONDS)) {
  716. try {
  717. this.unregisterClient(producerGroup, consumerGroup);
  718. } catch (Exception e) {
  719. log.error("unregisterClient exception", e);
  720. } finally {
  721. this.lockHeartbeat.unlock();
  722. }
  723. } else {
  724. log.warn("lock heartBeat, but failed.");
  725. }
  726. } catch (InterruptedException e) {
  727. log.warn("unregisterClientWithLock exception", e);
  728. }
  729. }
  730. private void unregisterClient(final String producerGroup, final String consumerGroup) {
  731. Iterator<Entry<String, HashMap<Long, String>>> it = this.brokerAddrTable.entrySet().iterator();
  732. while (it.hasNext()) {
  733. Entry<String, HashMap<Long, String>> entry = it.next();
  734. String brokerName = entry.getKey();
  735. HashMap<Long, String> oneTable = entry.getValue();
  736. if (oneTable != null) {
  737. for (Long id : oneTable.keySet()) {
  738. String addr = oneTable.get(id);
  739. if (addr != null) {
  740. try {
  741. this.mQClientAPIImpl.unregisterClient(addr, this.clientId, producerGroup,
  742. consumerGroup, 3000);
  743. log.info(
  744. "unregister client[Producer: {} Consumer: {}] from broker[{} {} {}] success",
  745. producerGroup, consumerGroup, brokerName, id, addr);
  746. } catch (RemotingException e) {
  747. log.error("unregister client exception from broker: " + addr, e);
  748. } catch (MQBrokerException e) {
  749. log.error("unregister client exception from broker: " + addr, e);
  750. } catch (InterruptedException e) {
  751. log.error("unregister client exception from broker: " + addr, e);
  752. }
  753. }
  754. }
  755. }
  756. }
  757. }
  758. public boolean registerProducer(final String group, final DefaultMQProducerImpl producer) {
  759. if (null == group || null == producer) {
  760. return false;
  761. }
  762. MQProducerInner prev = this.producerTable.putIfAbsent(group, producer);
  763. if (prev != null) {
  764. log.warn("the producer group[{}] exist already.", group);
  765. return false;
  766. }
  767. return true;
  768. }
  769. public void unregisterProducer(final String group) {
  770. this.producerTable.remove(group);
  771. this.unregisterClientWithLock(group, null);
  772. }
  773. public boolean registerAdminExt(final String group, final MQAdminExtInner admin) {
  774. if (null == group || null == admin) {
  775. return false;
  776. }
  777. MQAdminExtInner prev = this.adminExtTable.putIfAbsent(group, admin);
  778. if (prev != null) {
  779. log.warn("the admin group[{}] exist already.", group);
  780. return false;
  781. }
  782. return true;
  783. }
  784. public void unregisterAdminExt(final String group) {
  785. this.adminExtTable.remove(group);
  786. }
  787. public void rebalanceImmediately() {
  788. this.rebalanceService.wakeup();
  789. }
  790. public void doRebalance() {
  791. for (String group : this.consumerTable.keySet()) {
  792. MQConsumerInner impl = this.consumerTable.get(group);
  793. if (impl != null) {
  794. try {
  795. impl.doRebalance();
  796. } catch (Exception e) {
  797. log.error("doRebalance exception", e);
  798. }
  799. }
  800. }
  801. }
  802. public MQProducerInner selectProducer(final String group) {
  803. return this.producerTable.get(group);
  804. }
  805. public MQConsumerInner selectConsumer(final String group) {
  806. return this.consumerTable.get(group);
  807. }
  808. public FindBrokerResult findBrokerAddressInAdmin(final String brokerName) {
  809. String brokerAddr = null;
  810. boolean slave = false;
  811. boolean found = false;
  812. HashMap<Long/* brokerId */, String/* address */> map = this.brokerAddrTable.get(brokerName);
  813. if (map != null && !map.isEmpty()) {
  814. FOR_SEG:
  815. for (Map.Entry<Long, String> entry : map.entrySet()) {
  816. Long id = entry.getKey();
  817. brokerAddr = entry.getValue();
  818. if (brokerAddr != null) {
  819. found = true;
  820. if (MixAll.MASTER_ID == id) {
  821. slave = false;
  822. break FOR_SEG;
  823. } else {
  824. slave = true;
  825. }
  826. break;
  827. }
  828. } // end of for
  829. }
  830. if (found) {
  831. return new FindBrokerResult(brokerAddr, slave);
  832. }
  833. return null;
  834. }
  835. public String findBrokerAddressInPublish(final String brokerName) {
  836. HashMap<Long/* brokerId */, String/* address */> map = this.brokerAddrTable.get(brokerName);
  837. if (map != null && !map.isEmpty()) {
  838. return map.get(MixAll.MASTER_ID);
  839. }
  840. return null;
  841. }
  842. public FindBrokerResult findBrokerAddressInSubscribe(//
  843. final String brokerName,//
  844. final long brokerId,//
  845. final boolean onlyThisBroker//
  846. ) {
  847. String brokerAddr = null;
  848. boolean slave = false;
  849. boolean found = false;
  850. HashMap<Long/* brokerId */, String/* address */> map = this.brokerAddrTable.get(brokerName);
  851. if (map != null && !map.isEmpty()) {
  852. brokerAddr = map.get(brokerId);
  853. slave = (brokerId != MixAll.MASTER_ID);
  854. found = (brokerAddr != null);
  855. if (!found && !onlyThisBroker) {
  856. Entry<Long, String> entry = map.entrySet().iterator().next();
  857. brokerAddr = entry.getValue();
  858. slave = (entry.getKey() != MixAll.MASTER_ID);
  859. found = true;
  860. }
  861. }
  862. if (found) {
  863. return new FindBrokerResult(brokerAddr, slave);
  864. }
  865. return null;
  866. }
  867. public List<String> findConsumerIdList(final String topic, final String group) {
  868. String brokerAddr = this.findBrokerAddrByTopic(topic);
  869. if (null == brokerAddr) {
  870. this.updateTopicRouteInfoFromNameServer(topic);
  871. brokerAddr = this.findBrokerAddrByTopic(topic);
  872. }
  873. if (null != brokerAddr) {
  874. try {
  875. return this.mQClientAPIImpl.getConsumerIdListByGroup(brokerAddr, group, 3000);
  876. } catch (Exception e) {
  877. log.warn("getConsumerIdListByGroup exception, " + brokerAddr + " " + group, e);
  878. }
  879. }
  880. return null;
  881. }
  882. public String findBrokerAddrByTopic(final String topic) {
  883. TopicRouteData topicRouteData = this.topicRouteTable.get(topic);
  884. if (topicRouteData != null) {
  885. List<BrokerData> brokers = topicRouteData.getBrokerDatas();
  886. if (!brokers.isEmpty()) {
  887. BrokerData bd = brokers.get(0);
  888. return bd.selectBrokerAddr();
  889. }
  890. }
  891. return null;
  892. }
  893. public void resetOffset(String topic, String group, Map<MessageQueue, Long> offsetTable) {
  894. DefaultMQPushConsumerImpl consumer = null;
  895. try {
  896. MQConsumerInner impl = this.consumerTable.get(group);
  897. if (impl != null && impl instanceof DefaultMQPushConsumerImpl) {
  898. consumer = (DefaultMQPushConsumerImpl) impl;
  899. } else {
  900. log.info("[reset-offset] consumer dose not exist. group={}", group);
  901. return;
  902. }
  903. ConcurrentHashMap<MessageQueue, ProcessQueue> processQueueTable =
  904. consumer.getRebalanceImpl().getProcessQueueTable();
  905. Iterator<MessageQueue> itr = processQueueTable.keySet().iterator();
  906. while (itr.hasNext()) {
  907. MessageQueue mq = itr.next();
  908. if (topic.equals(mq.getTopic())) {
  909. ProcessQueue pq = processQueueTable.get(mq);
  910. pq.setDropped(true);
  911. pq.clear();
  912. }
  913. }
  914. Iterator<MessageQueue> iterator = offsetTable.keySet().iterator();
  915. while (iterator.hasNext()) {
  916. MessageQueue mq = iterator.next();
  917. consumer.updateConsumeOffset(mq, offsetTable.get(mq));
  918. log.info("[reset-offset] reset offsetTable. topic={}, group={}, mq={}, offset={}",
  919. new Object[]{topic, group, mq, offsetTable.get(mq)});
  920. }
  921. consumer.getOffsetStore().persistAll(offsetTable.keySet());
  922. try {
  923. TimeUnit.SECONDS.sleep(10);
  924. } catch (InterruptedException e) {
  925. //
  926. }
  927. iterator = offsetTable.keySet().iterator();
  928. while (iterator.hasNext()) {
  929. MessageQueue mq = iterator.next();
  930. consumer.updateConsumeOffset(mq, offsetTable.get(mq));
  931. log.info("[reset-offset] reset offsetTable. topic={}, group={}, mq={}, offset={}",
  932. new Object[]{topic, group, mq, offsetTable.get(mq)});
  933. }
  934. consumer.getOffsetStore().persistAll(offsetTable.keySet());
  935. iterator = offsetTable.keySet().iterator();
  936. processQueueTable = consumer.getRebalanceImpl().getProcessQueueTable();
  937. while (iterator.hasNext()) {
  938. MessageQueue mq = iterator.next();
  939. processQueueTable.remove(mq);
  940. }
  941. } finally {
  942. consumer.getRebalanceImpl().doRebalance();
  943. }
  944. }
  945. public Map<MessageQueue, Long> getConsumerStatus(String topic, String group) {
  946. MQConsumerInner impl = this.consumerTable.get(group);
  947. if (impl != null && impl instanceof DefaultMQPushConsumerImpl) {
  948. DefaultMQPushConsumerImpl consumer = (DefaultMQPushConsumerImpl) impl;
  949. return consumer.getOffsetStore().cloneOffsetTable(topic);
  950. } else if (impl != null && impl instanceof DefaultMQPullConsumerImpl) {
  951. DefaultMQPullConsumerImpl consumer = (DefaultMQPullConsumerImpl) impl;
  952. return consumer.getOffsetStore().cloneOffsetTable(topic);
  953. } else {
  954. return Collections.EMPTY_MAP;
  955. }
  956. }
  957. public TopicRouteData getAnExistTopicRouteData(final String topic) {
  958. return this.topicRouteTable.get(topic);
  959. }
  960. public MQClientAPIImpl getMQClientAPIImpl() {
  961. return mQClientAPIImpl;
  962. }
  963. public MQAdminImpl getMQAdminImpl() {
  964. return mQAdminImpl;
  965. }
  966. public String getClientId() {
  967. return clientId;
  968. }
  969. public long getBootTimestamp() {
  970. return bootTimestamp;
  971. }
  972. public ScheduledExecutorService getScheduledExecutorService() {
  973. return scheduledExecutorService;
  974. }
  975. public PullMessageService getPullMessageService() {
  976. return pullMessageService;
  977. }
  978. public DefaultMQProducer getDefaultMQProducer() {
  979. return defaultMQProducer;
  980. }
  981. public ConcurrentHashMap<String, TopicRouteData> getTopicRouteTable() {
  982. return topicRouteTable;
  983. }
  984. public void adjustThreadPool() {
  985. Iterator<Entry<String, MQConsumerInner>> it = this.consumerTable.entrySet().iterator();
  986. while (it.hasNext()) {
  987. Entry<String, MQConsumerInner> entry = it.next();
  988. MQConsumerInner impl = entry.getValue();
  989. if (impl != null) {
  990. try {
  991. if (impl instanceof DefaultMQPushConsumerImpl) {
  992. DefaultMQPushConsumerImpl dmq = (DefaultMQPushConsumerImpl) impl;
  993. dmq.adjustThreadPool();
  994. }
  995. } catch (Exception e) {
  996. }
  997. }
  998. }
  999. }
  1000. public ConsumeMessageDirectlyResult consumeMessageDirectly(final MessageExt msg, //
  1001. final String consumerGroup, //
  1002. final String brokerName) {
  1003. MQConsumerInner mqConsumerInner = this.consumerTable.get(consumerGroup);
  1004. if (null != mqConsumerInner) {
  1005. DefaultMQPushConsumerImpl consumer = (DefaultMQPushConsumerImpl) mqConsumerInner;
  1006. ConsumeMessageDirectlyResult result =
  1007. consumer.getConsumeMessageService().consumeMessageDirectly(msg, brokerName);
  1008. return result;
  1009. }
  1010. return null;
  1011. }
  1012. public ConsumerRunningInfo consumerRunningInfo(final String consumerGroup) {
  1013. MQConsumerInner mqConsumerInner = this.consumerTable.get(consumerGroup);
  1014. ConsumerRunningInfo consumerRunningInfo = mqConsumerInner.consumerRunningInfo();
  1015. List<String> nsList = this.mQClientAPIImpl.getRemotingClient().getNameServerAddressList();
  1016. String nsAddr = "";
  1017. if (nsList != null) {
  1018. for (String addr : nsList) {
  1019. nsAddr = nsAddr + addr + ";";
  1020. }
  1021. }
  1022. consumerRunningInfo.getProperties().put(ConsumerRunningInfo.PROP_NAMESERVER_ADDR, nsAddr);
  1023. consumerRunningInfo.getProperties().put(ConsumerRunningInfo.PROP_CONSUME_TYPE,
  1024. mqConsumerInner.consumeType());
  1025. consumerRunningInfo.getProperties().put(ConsumerRunningInfo.PROP_CLIENT_VERSION,
  1026. MQVersion.getVersionDesc(MQVersion.CurrentVersion));
  1027. return consumerRunningInfo;
  1028. }
  1029. public ConsumerStatsManager getConsumerStatsManager() {
  1030. return consumerStatsManager;
  1031. }
  1032. }