/** * Queue consumption snapshot * * @author shijia.wxr<*****@*****.**> * @since 2013-7-24 */ public class ProcessQueue { public static final long RebalanceLockMaxLiveTime = Long.parseLong(System.getProperty("rocketmq.client.rebalance.lockMaxLiveTime", "30000")); public static final long RebalanceLockInterval = Long.parseLong(System.getProperty("rocketmq.client.rebalance.lockInterval", "20000")); private final Logger log = ClientLogger.getLog(); private final ReadWriteLock lockTreeMap = new ReentrantReadWriteLock(); private final TreeMap<Long, MessageExt> msgTreeMap = new TreeMap<Long, MessageExt>(); private volatile long queueOffsetMax = 0L; private final AtomicLong msgCount = new AtomicLong(); private volatile boolean dropped = false; private volatile long lastPullTimestamp = System.currentTimeMillis(); private static final long PullMaxIdleTime = Long.parseLong(System.getProperty("rocketmq.client.pull.pullMaxIdleTime", "120000")); private volatile long lastConsumeTimestamp = System.currentTimeMillis(); private final Lock lockConsume = new ReentrantLock(); private volatile boolean locked = false; private volatile long lastLockTimestamp = System.currentTimeMillis(); private volatile boolean consuming = false; private final TreeMap<Long, MessageExt> msgTreeMapTemp = new TreeMap<Long, MessageExt>(); private final AtomicLong tryUnlockTimes = new AtomicLong(0); private volatile long msgAccCnt = 0; public boolean isLockExpired() { boolean result = (System.currentTimeMillis() - this.lastLockTimestamp) > RebalanceLockMaxLiveTime; return result; } public boolean isPullExpired() { boolean result = (System.currentTimeMillis() - this.lastPullTimestamp) > PullMaxIdleTime; return result; } public boolean putMessage(final List<MessageExt> msgs) { boolean dispatchToConsume = false; try { this.lockTreeMap.writeLock().lockInterruptibly(); try { int validMsgCnt = 0; for (MessageExt msg : msgs) { MessageExt old = msgTreeMap.put(msg.getQueueOffset(), msg); if (null == old) { validMsgCnt++; this.queueOffsetMax = msg.getQueueOffset(); } } msgCount.addAndGet(validMsgCnt); if (!msgTreeMap.isEmpty() && !this.consuming) { dispatchToConsume = true; this.consuming = true; } if (!msgs.isEmpty()) { MessageExt messageExt = msgs.get(msgs.size() - 1); String property = messageExt.getProperty(MessageConst.PROPERTY_MAX_OFFSET); if (property != null) { long accTotal = Long.parseLong(property) - messageExt.getQueueOffset(); if (accTotal > 0) { this.msgAccCnt = accTotal; } } } } finally { this.lockTreeMap.writeLock().unlock(); } } catch (InterruptedException e) { log.error("putMessage exception", e); } return dispatchToConsume; } public long getMaxSpan() { try { this.lockTreeMap.readLock().lockInterruptibly(); try { if (!this.msgTreeMap.isEmpty()) { return this.msgTreeMap.lastKey() - this.msgTreeMap.firstKey(); } } finally { this.lockTreeMap.readLock().unlock(); } } catch (InterruptedException e) { log.error("getMaxSpan exception", e); } return 0; } public long removeMessage(final List<MessageExt> msgs) { long result = -1; final long now = System.currentTimeMillis(); try { this.lockTreeMap.writeLock().lockInterruptibly(); this.lastConsumeTimestamp = now; try { if (!msgTreeMap.isEmpty()) { result = this.queueOffsetMax + 1; int removedCnt = 0; for (MessageExt msg : msgs) { MessageExt prev = msgTreeMap.remove(msg.getQueueOffset()); if (prev != null) { removedCnt--; } } msgCount.addAndGet(removedCnt); if (!msgTreeMap.isEmpty()) { result = msgTreeMap.firstKey(); } } } finally { this.lockTreeMap.writeLock().unlock(); } } catch (Throwable t) { log.error("removeMessage exception", t); } return result; } public TreeMap<Long, MessageExt> getMsgTreeMap() { return msgTreeMap; } public AtomicLong getMsgCount() { return msgCount; } public boolean isDropped() { return dropped; } public void setDropped(boolean dropped) { this.dropped = dropped; } public void setLocked(boolean locked) { this.locked = locked; } public boolean isLocked() { return locked; } public void rollback() { try { this.lockTreeMap.writeLock().lockInterruptibly(); try { this.msgTreeMap.putAll(this.msgTreeMapTemp); this.msgTreeMapTemp.clear(); } finally { this.lockTreeMap.writeLock().unlock(); } } catch (InterruptedException e) { log.error("rollback exception", e); } } public long commit() { try { this.lockTreeMap.writeLock().lockInterruptibly(); try { Long offset = this.msgTreeMapTemp.lastKey(); msgCount.addAndGet(this.msgTreeMapTemp.size() * (-1)); this.msgTreeMapTemp.clear(); if (offset != null) { return offset + 1; } } finally { this.lockTreeMap.writeLock().unlock(); } } catch (InterruptedException e) { log.error("commit exception", e); } return -1; } public void makeMessageToCosumeAgain(List<MessageExt> msgs) { try { this.lockTreeMap.writeLock().lockInterruptibly(); try { for (MessageExt msg : msgs) { this.msgTreeMapTemp.remove(msg.getQueueOffset()); this.msgTreeMap.put(msg.getQueueOffset(), msg); } } finally { this.lockTreeMap.writeLock().unlock(); } } catch (InterruptedException e) { log.error("makeMessageToCosumeAgain exception", e); } } public List<MessageExt> takeMessags(final int batchSize) { List<MessageExt> result = new ArrayList<MessageExt>(batchSize); final long now = System.currentTimeMillis(); try { this.lockTreeMap.writeLock().lockInterruptibly(); this.lastConsumeTimestamp = now; try { if (!this.msgTreeMap.isEmpty()) { for (int i = 0; i < batchSize; i++) { Map.Entry<Long, MessageExt> entry = this.msgTreeMap.pollFirstEntry(); if (entry != null) { result.add(entry.getValue()); msgTreeMapTemp.put(entry.getKey(), entry.getValue()); } else { break; } } } if (result.isEmpty()) { consuming = false; } } finally { this.lockTreeMap.writeLock().unlock(); } } catch (InterruptedException e) { log.error("take Messages exception", e); } return result; } public void clear() { try { this.lockTreeMap.writeLock().lockInterruptibly(); try { this.msgTreeMap.clear(); this.msgTreeMapTemp.clear(); this.msgCount.set(0); this.queueOffsetMax = 0L; } finally { this.lockTreeMap.writeLock().unlock(); } } catch (InterruptedException e) { log.error("rollback exception", e); } } public long getLastLockTimestamp() { return lastLockTimestamp; } public void setLastLockTimestamp(long lastLockTimestamp) { this.lastLockTimestamp = lastLockTimestamp; } public Lock getLockConsume() { return lockConsume; } public long getLastPullTimestamp() { return lastPullTimestamp; } public void setLastPullTimestamp(long lastPullTimestamp) { this.lastPullTimestamp = lastPullTimestamp; } public long getMsgAccCnt() { return msgAccCnt; } public void setMsgAccCnt(long msgAccCnt) { this.msgAccCnt = msgAccCnt; } public long getTryUnlockTimes() { return this.tryUnlockTimes.get(); } public void incTryUnlockTimes() { this.tryUnlockTimes.incrementAndGet(); } public void fillProcessQueueInfo(final ProcessQueueInfo info) { try { this.lockTreeMap.readLock().lockInterruptibly(); if (!this.msgTreeMap.isEmpty()) { info.setCachedMsgMinOffset(this.msgTreeMap.firstKey()); info.setCachedMsgMaxOffset(this.msgTreeMap.lastKey()); info.setCachedMsgCount(this.msgTreeMap.size()); } if (!this.msgTreeMapTemp.isEmpty()) { info.setTransactionMsgMinOffset(this.msgTreeMapTemp.firstKey()); info.setTransactionMsgMaxOffset(this.msgTreeMapTemp.lastKey()); info.setTransactionMsgCount(this.msgTreeMapTemp.size()); } info.setLocked(this.locked); info.setTryUnlockTimes(this.tryUnlockTimes.get()); info.setLastLockTimestamp(this.lastLockTimestamp); info.setDroped(this.dropped); info.setLastPullTimestamp(this.lastPullTimestamp); info.setLastConsumeTimestamp(this.lastConsumeTimestamp); } catch (Exception e) { } finally { this.lockTreeMap.readLock().unlock(); } } public long getLastConsumeTimestamp() { return lastConsumeTimestamp; } public void setLastConsumeTimestamp(long lastConsumeTimestamp) { this.lastConsumeTimestamp = lastConsumeTimestamp; } }
/** * 生产者默认实现 * * @author shijia.wxr<*****@*****.**> * @since 2013-7-24 */ public class DefaultMQProducerImpl implements MQProducerInner { private final Logger log = ClientLogger.getLog(); private final DefaultMQProducer defaultMQProducer; private final ConcurrentHashMap<String /* topic */, TopicPublishInfo> topicPublishInfoTable = new ConcurrentHashMap<String, TopicPublishInfo>(); /** 事务相关 */ protected BlockingQueue<Runnable> checkRequestQueue; protected ExecutorService checkExecutor; private ServiceState serviceState = ServiceState.CREATE_JUST; private MQClientFactory mQClientFactory; /** 发送每条消息会回调 */ private final ArrayList<SendMessageHook> hookList = new ArrayList<SendMessageHook>(); public DefaultMQProducerImpl(final DefaultMQProducer defaultMQProducer) { this.defaultMQProducer = defaultMQProducer; } public void initTransactionEnv() { TransactionMQProducer producer = (TransactionMQProducer) this.defaultMQProducer; this.checkRequestQueue = new LinkedBlockingQueue<Runnable>(producer.getCheckRequestHoldMax()); this.checkExecutor = new ThreadPoolExecutor( // producer.getCheckThreadPoolMinSize(), // producer.getCheckThreadPoolMaxSize(), // 1000 * 60, // TimeUnit.MILLISECONDS, // this.checkRequestQueue); } public void destroyTransactionEnv() { this.checkExecutor.shutdown(); this.checkRequestQueue.clear(); } public boolean hasHook() { return !this.hookList.isEmpty(); } public void registerHook(final SendMessageHook hook) { this.hookList.add(hook); log.info("register sendMessage Hook, {}", hook.hookName()); } public void executeHookBefore(final SendMessageContext context) { if (!this.hookList.isEmpty()) { for (SendMessageHook hook : this.hookList) { try { hook.sendMessageBefore(context); } catch (Throwable e) { } } } } public void executeHookAfter(final SendMessageContext context) { if (!this.hookList.isEmpty()) { for (SendMessageHook hook : this.hookList) { try { hook.sendMessageAfter(context); } catch (Throwable e) { } } } } public void start() throws MQClientException { this.start(true); } public void start(final boolean startFactory) throws MQClientException { switch (this.serviceState) { case CREATE_JUST: this.serviceState = ServiceState.START_FAILED; this.checkConfig(); this.mQClientFactory = MQClientManager.getInstance().getAndCreateMQClientFactory(this.defaultMQProducer); 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", this.defaultMQProducer.getProducerGroup()); 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(); } private void checkConfig() throws MQClientException { // producerGroup 有效性检查 Validators.checkGroup(this.defaultMQProducer.getProducerGroup()); if (null == this.defaultMQProducer.getProducerGroup()) { throw new MQClientException("producerGroup is null", null); } if (this.defaultMQProducer.getProducerGroup().equals(MixAll.DEFAULT_PRODUCER_GROUP)) { throw new MQClientException( "producerGroup can not equal " + MixAll.DEFAULT_PRODUCER_GROUP + ", please specify another one.", null); } } public void shutdown() { this.shutdown(true); } public void shutdown(final boolean shutdownFactory) { switch (this.serviceState) { case CREATE_JUST: break; case RUNNING: this.mQClientFactory.unregisterProducer(this.defaultMQProducer.getProducerGroup()); if (shutdownFactory) { this.mQClientFactory.shutdown(); } log.info("the producer [{}] shutdown OK", this.defaultMQProducer.getProducerGroup()); this.serviceState = ServiceState.SHUTDOWN_ALREADY; break; case SHUTDOWN_ALREADY: break; default: break; } } @Override public Set<String> getPublishTopicList() { Set<String> topicList = new HashSet<String>(); for (String key : this.topicPublishInfoTable.keySet()) { topicList.add(key); } return topicList; } @Override public boolean isPublishTopicNeedUpdate(String topic) { TopicPublishInfo prev = this.topicPublishInfoTable.get(topic); return null == prev || !prev.ok(); } @Override public TransactionCheckListener checkListener() { if (this.defaultMQProducer instanceof TransactionMQProducer) { TransactionMQProducer producer = (TransactionMQProducer) defaultMQProducer; return producer.getTransactionCheckListener(); } return null; } @Override public void checkTransactionState( final String addr, final MessageExt msg, final CheckTransactionStateRequestHeader header) { Runnable request = new Runnable() { private final String brokerAddr = addr; private final MessageExt message = msg; private final CheckTransactionStateRequestHeader checkRequestHeader = header; private final String group = DefaultMQProducerImpl.this.defaultMQProducer.getProducerGroup(); @Override public void run() { TransactionCheckListener transactionCheckListener = DefaultMQProducerImpl.this.checkListener(); if (transactionCheckListener != null) { LocalTransactionState localTransactionState = LocalTransactionState.UNKNOW; Throwable exception = null; try { localTransactionState = transactionCheckListener.checkLocalTransactionState(message); } catch (Throwable e) { log.error( "Broker call checkTransactionState, but checkLocalTransactionState exception", e); exception = e; } this.processTransactionState( // localTransactionState, // group, // exception); } else { log.warn( "checkTransactionState, pick transactionCheckListener by group[{}] failed", group); } } private void processTransactionState( // final LocalTransactionState localTransactionState, // final String producerGroup, // final Throwable exception) { final EndTransactionRequestHeader thisHeader = new EndTransactionRequestHeader(); thisHeader.setCommitLogOffset(checkRequestHeader.getCommitLogOffset()); thisHeader.setProducerGroup(producerGroup); thisHeader.setTranStateTableOffset(checkRequestHeader.getTranStateTableOffset()); thisHeader.setFromTransactionCheck(true); thisHeader.setMsgId(message.getMsgId()); switch (localTransactionState) { case COMMIT_MESSAGE: thisHeader.setCommitOrRollback(MessageSysFlag.TransactionCommitType); break; case ROLLBACK_MESSAGE: thisHeader.setCommitOrRollback(MessageSysFlag.TransactionRollbackType); log.warn("when broker check, client rollback this transaction, {}", thisHeader); break; case UNKNOW: thisHeader.setCommitOrRollback(MessageSysFlag.TransactionNotType); log.warn( "when broker check, client donot know this transaction state, {}", thisHeader); break; default: break; } String remark = null; if (exception != null) { remark = "checkLocalTransactionState Exception: " + RemotingHelper.exceptionSimpleDesc(exception); } try { DefaultMQProducerImpl.this .mQClientFactory .getMQClientAPIImpl() .endTransactionOneway(brokerAddr, thisHeader, remark, 3000); } catch (Exception e) { log.error("endTransactionOneway exception", e); } } }; this.checkExecutor.submit(request); } @Override public void updateTopicPublishInfo(final String topic, final TopicPublishInfo info) { if (info != null && topic != null) { TopicPublishInfo prev = this.topicPublishInfoTable.put(topic, info); if (prev != null) { info.getSendWhichQueue().set(prev.getSendWhichQueue().get()); log.info("updateTopicPublishInfo prev is not null, " + prev.toString()); } } } public void createTopic(String key, String newTopic, int queueNum) throws MQClientException { this.makeSureStateOK(); // topic 有效性检查 Validators.checkTopic(newTopic); this.mQClientFactory.getMQAdminImpl().createTopic(key, newTopic, queueNum); } private void makeSureStateOK() throws MQClientException { if (this.serviceState != ServiceState.RUNNING) { throw new MQClientException( "The producer service state not OK, " // + this.serviceState // + FAQUrl.suggestTodo(FAQUrl.CLIENT_SERVICE_NOT_OK), null); } } public List<MessageQueue> fetchPublishMessageQueues(String topic) throws MQClientException { this.makeSureStateOK(); return this.mQClientFactory.getMQAdminImpl().fetchPublishMessageQueues(topic); } public long searchOffset(MessageQueue mq, long timestamp) throws MQClientException { this.makeSureStateOK(); return this.mQClientFactory.getMQAdminImpl().searchOffset(mq, timestamp); } public long maxOffset(MessageQueue mq) throws MQClientException { this.makeSureStateOK(); return this.mQClientFactory.getMQAdminImpl().maxOffset(mq); } public long minOffset(MessageQueue mq) throws MQClientException { this.makeSureStateOK(); return this.mQClientFactory.getMQAdminImpl().minOffset(mq); } public long earliestMsgStoreTime(MessageQueue mq) throws MQClientException { this.makeSureStateOK(); return this.mQClientFactory.getMQAdminImpl().earliestMsgStoreTime(mq); } public MessageExt viewMessage(String msgId) throws RemotingException, MQBrokerException, InterruptedException, MQClientException { this.makeSureStateOK(); return this.mQClientFactory.getMQAdminImpl().viewMessage(msgId); } public QueryResult queryMessage(String topic, String key, int maxNum, long begin, long end) throws MQClientException, InterruptedException { this.makeSureStateOK(); return this.mQClientFactory.getMQAdminImpl().queryMessage(topic, key, maxNum, begin, end); } /** DEFAULT ASYNC ------------------------------------------------------- */ public void send(Message msg, SendCallback sendCallback) throws MQClientException, RemotingException, InterruptedException { try { this.sendDefaultImpl(msg, CommunicationMode.ASYNC, sendCallback); } catch (MQBrokerException e) { throw new MQClientException("unknow exception", e); } } private SendResult sendDefaultImpl( // Message msg, // final CommunicationMode communicationMode, // final SendCallback sendCallback // ) throws MQClientException, RemotingException, MQBrokerException, InterruptedException { // 有效性检查 this.makeSureStateOK(); Validators.checkMessage(msg, this.defaultMQProducer); final long beginTimestamp = System.currentTimeMillis(); long endTimestamp = beginTimestamp; TopicPublishInfo topicPublishInfo = this.tryToFindTopicPublishInfo(msg.getTopic()); if (topicPublishInfo != null && topicPublishInfo.ok()) { MessageQueue mq = null; Exception exception = null; SendResult sendResult = null; int timesTotal = 1 + this.defaultMQProducer.getRetryTimesWhenSendFailed(); for (int times = 0; times < timesTotal && (endTimestamp - beginTimestamp) < this.defaultMQProducer.getSendMsgTimeout(); times++) { String lastBrokerName = null == mq ? null : mq.getBrokerName(); MessageQueue tmpmq = topicPublishInfo.selectOneMessageQueue(lastBrokerName); if (tmpmq != null) { mq = tmpmq; try { sendResult = this.sendKernelImpl(msg, mq, communicationMode, sendCallback); endTimestamp = System.currentTimeMillis(); switch (communicationMode) { case ASYNC: return null; case ONEWAY: return null; case SYNC: if (sendResult.getSendStatus() != SendStatus.SEND_OK) { if (this.defaultMQProducer.isRetryAnotherBrokerWhenNotStoreOK()) { continue; } } return sendResult; default: break; } } catch (RemotingException e) { log.warn("sendKernelImpl exception", e); log.warn(msg.toString()); exception = e; endTimestamp = System.currentTimeMillis(); continue; } catch (MQClientException e) { log.warn("sendKernelImpl exception", e); log.warn(msg.toString()); exception = e; endTimestamp = System.currentTimeMillis(); continue; } catch (MQBrokerException e) { log.warn("sendKernelImpl exception", e); log.warn(msg.toString()); exception = e; endTimestamp = System.currentTimeMillis(); switch (e.getResponseCode()) { case MQResponseCode.TOPIC_NOT_EXIST_VALUE: case MQResponseCode.SERVICE_NOT_AVAILABLE_VALUE: case ResponseCode.SYSTEM_ERROR_VALUE: case MQResponseCode.NO_PERMISSION_VALUE: continue; default: if (sendResult != null) { return sendResult; } throw e; } } catch (InterruptedException e) { log.warn("sendKernelImpl exception", e); log.warn(msg.toString()); throw e; } } else { break; } } // end of for if (sendResult != null) { return sendResult; } throw new MQClientException("Retry many times, still failed", exception); } List<String> nsList = this.getmQClientFactory().getMQClientAPIImpl().getNameServerAddressList(); if (null == nsList || nsList.isEmpty()) { // 说明没有设置Name Server地址 throw new MQClientException( "No name server address, please set it." + FAQUrl.suggestTodo(FAQUrl.NAME_SERVER_ADDR_NOT_EXIST_URL), null); } throw new MQClientException( "No route info of this topic, " + msg.getTopic() + FAQUrl.suggestTodo(FAQUrl.NO_TOPIC_ROUTE_INFO), null); } /** 尝试寻找Topic路由信息,如果没有则到Name Server上找,再没有,则取默认Topic */ private TopicPublishInfo tryToFindTopicPublishInfo(final String topic) { TopicPublishInfo topicPublishInfo = this.topicPublishInfoTable.get(topic); if (null == topicPublishInfo || !topicPublishInfo.ok()) { this.topicPublishInfoTable.putIfAbsent(topic, new TopicPublishInfo()); this.mQClientFactory.updateTopicRouteInfoFromNameServer(topic); topicPublishInfo = this.topicPublishInfoTable.get(topic); } if (topicPublishInfo != null && topicPublishInfo.ok()) { return topicPublishInfo; } else { this.mQClientFactory.updateTopicRouteInfoFromNameServer(topic, true, this.defaultMQProducer); topicPublishInfo = this.topicPublishInfoTable.get(topic); return topicPublishInfo; } } private SendResult sendKernelImpl( final Message msg, // final MessageQueue mq, // final CommunicationMode communicationMode, // final SendCallback sendCallback // ) throws MQClientException, RemotingException, MQBrokerException, InterruptedException { String brokerAddr = this.mQClientFactory.findBrokerAddressInPublish(mq.getBrokerName()); if (null == brokerAddr) { // TODO 此处可能对Name Server压力过大,需要调优 tryToFindTopicPublishInfo(mq.getTopic()); brokerAddr = this.mQClientFactory.findBrokerAddressInPublish(mq.getBrokerName()); } SendMessageContext context = null; if (brokerAddr != null) { byte[] prevBody = msg.getBody(); try { int sysFlag = 0; if (this.tryToCompressMessage(msg)) { sysFlag |= MessageSysFlag.CompressedFlag; } final String tranMsg = msg.getProperty(MessageConst.PROPERTY_TRANSACTION_PREPARED); if (tranMsg != null && Boolean.parseBoolean(tranMsg)) { sysFlag |= MessageSysFlag.TransactionPreparedType; } // 执行hook if (this.hasHook()) { context = new SendMessageContext(); context.setProducerGroup(this.defaultMQProducer.getProducerGroup()); context.setCommunicationMode(communicationMode); context.setBrokerAddr(brokerAddr); context.setMessage(msg); context.setMq(mq); this.executeHookBefore(context); } SendMessageRequestHeader requestHeader = new SendMessageRequestHeader(); requestHeader.setProducerGroup(this.defaultMQProducer.getProducerGroup()); requestHeader.setTopic(msg.getTopic()); requestHeader.setDefaultTopic(this.defaultMQProducer.getCreateTopicKey()); requestHeader.setDefaultTopicQueueNums(this.defaultMQProducer.getDefaultTopicQueueNums()); requestHeader.setQueueId(mq.getQueueId()); requestHeader.setSysFlag(sysFlag); requestHeader.setBornTimestamp(System.currentTimeMillis()); requestHeader.setFlag(msg.getFlag()); requestHeader.setProperties(MessageDecoder.messageProperties2String(msg.getProperties())); SendResult sendResult = this.mQClientFactory .getMQClientAPIImpl() .sendMessage( // brokerAddr, // 1 mq.getBrokerName(), // 2 msg, // 3 requestHeader, // 4 this.defaultMQProducer.getSendMsgTimeout(), // 5 communicationMode, // 6 sendCallback // 7 ); // 执行hook if (this.hasHook()) { context.setSendResult(sendResult); this.executeHookAfter(context); } return sendResult; } catch (RemotingException e) { // 执行hook if (this.hasHook()) { context.setException(e); this.executeHookAfter(context); } throw e; } catch (MQBrokerException e) { // 执行hook if (this.hasHook()) { context.setException(e); this.executeHookAfter(context); } throw e; } catch (InterruptedException e) { // 执行hook if (this.hasHook()) { context.setException(e); this.executeHookAfter(context); } throw e; } finally { msg.setBody(prevBody); } } throw new MQClientException("The broker[" + mq.getBrokerName() + "] not exist", null); } /** 消息压缩level,默认5 */ private int zipCompressLevel = Integer.parseInt(System.getProperty(MixAll.MESSAGE_COMPRESS_LEVEL, "5")); private boolean tryToCompressMessage(final Message msg) { byte[] body = msg.getBody(); if (body != null) { if (body.length >= this.defaultMQProducer.getCompressMsgBodyOverHowmuch()) { try { byte[] data = UtilAll.compress(body, zipCompressLevel); if (data != null) { msg.setBody(data); return true; } } catch (IOException e) { log.error("tryToCompressMessage exception", e); log.warn(msg.toString()); } } } return false; } /** DEFAULT ONEWAY ------------------------------------------------------- */ public void sendOneway(Message msg) throws MQClientException, RemotingException, InterruptedException { try { this.sendDefaultImpl(msg, CommunicationMode.ONEWAY, null); } catch (MQBrokerException e) { throw new MQClientException("unknow exception", e); } } /** KERNEL SYNC ------------------------------------------------------- */ public SendResult send(Message msg, MessageQueue mq) throws MQClientException, RemotingException, MQBrokerException, InterruptedException { // 有效性检查 this.makeSureStateOK(); Validators.checkMessage(msg, this.defaultMQProducer); if (!msg.getTopic().equals(mq.getTopic())) { throw new MQClientException("message's topic not equal mq's topic", null); } return this.sendKernelImpl(msg, mq, CommunicationMode.SYNC, null); } /** KERNEL ASYNC ------------------------------------------------------- */ public void send(Message msg, MessageQueue mq, SendCallback sendCallback) throws MQClientException, RemotingException, InterruptedException { // 有效性检查 this.makeSureStateOK(); Validators.checkMessage(msg, this.defaultMQProducer); if (!msg.getTopic().equals(mq.getTopic())) { throw new MQClientException("message's topic not equal mq's topic", null); } try { this.sendKernelImpl(msg, mq, CommunicationMode.ASYNC, sendCallback); } catch (MQBrokerException e) { throw new MQClientException("unknow exception", e); } } /** KERNEL ONEWAY ------------------------------------------------------- */ public void sendOneway(Message msg, MessageQueue mq) throws MQClientException, RemotingException, InterruptedException { // 有效性检查 this.makeSureStateOK(); Validators.checkMessage(msg, this.defaultMQProducer); try { this.sendKernelImpl(msg, mq, CommunicationMode.ONEWAY, null); } catch (MQBrokerException e) { throw new MQClientException("unknow exception", e); } } /** SELECT SYNC ------------------------------------------------------- */ public SendResult send(Message msg, MessageQueueSelector selector, Object arg) throws MQClientException, RemotingException, MQBrokerException, InterruptedException { return this.sendSelectImpl(msg, selector, arg, CommunicationMode.SYNC, null); } private SendResult sendSelectImpl( // Message msg, // MessageQueueSelector selector, // Object arg, // final CommunicationMode communicationMode, // final SendCallback sendCallback // ) throws MQClientException, RemotingException, MQBrokerException, InterruptedException { // 有效性检查 this.makeSureStateOK(); Validators.checkMessage(msg, this.defaultMQProducer); TopicPublishInfo topicPublishInfo = this.tryToFindTopicPublishInfo(msg.getTopic()); if (topicPublishInfo != null && topicPublishInfo.ok()) { MessageQueue mq = null; try { mq = selector.select(topicPublishInfo.getMessageQueueList(), msg, arg); } catch (Throwable e) { throw new MQClientException("select message queue throwed exception.", e); } if (mq != null) { return this.sendKernelImpl(msg, mq, communicationMode, sendCallback); } else { throw new MQClientException("select message queue return null.", null); } } throw new MQClientException("No route info for this topic, " + msg.getTopic(), null); } /** SELECT ASYNC ------------------------------------------------------- */ public void send( Message msg, MessageQueueSelector selector, Object arg, SendCallback sendCallback) throws MQClientException, RemotingException, InterruptedException { try { this.sendSelectImpl(msg, selector, arg, CommunicationMode.ASYNC, sendCallback); } catch (MQBrokerException e) { throw new MQClientException("unknow exception", e); } } /** SELECT ONEWAY ------------------------------------------------------- */ public void sendOneway(Message msg, MessageQueueSelector selector, Object arg) throws MQClientException, RemotingException, InterruptedException { try { this.sendSelectImpl(msg, selector, arg, CommunicationMode.ONEWAY, null); } catch (MQBrokerException e) { throw new MQClientException("unknow exception", e); } } public TransactionSendResult sendMessageInTransaction( final Message msg, final LocalTransactionExecuter tranExecuter, final Object arg) throws MQClientException { if (null == tranExecuter) { throw new MQClientException("tranExecuter is null", null); } // 第一步,向Broker发送一条Prepared消息 SendResult sendResult = null; msg.putProperty(MessageConst.PROPERTY_TRANSACTION_PREPARED, "true"); msg.putProperty( MessageConst.PROPERTY_PRODUCER_GROUP, this.defaultMQProducer.getProducerGroup()); try { sendResult = this.send(msg); } catch (Exception e) { throw new MQClientException("send message Exception", e); } // 第二步,回调本地事务 LocalTransactionState localTransactionState = LocalTransactionState.UNKNOW; Throwable localException = null; try { localTransactionState = tranExecuter.executeLocalTransactionBranch(msg, arg); if (null == localTransactionState) { localTransactionState = LocalTransactionState.UNKNOW; } if (localTransactionState != LocalTransactionState.COMMIT_MESSAGE) { log.info("executeLocalTransactionBranch return {}", localTransactionState); log.info(msg.toString()); } } catch (Throwable e) { log.info("executeLocalTransactionBranch exception", e); log.info(msg.toString()); localException = e; } // 第三步,提交或者回滚Broker端消息 try { this.endTransaction(sendResult, localTransactionState, localException); } catch (Exception e) { log.warn( "local transaction execute " + localTransactionState + ", but end broker transaction failed", e); } TransactionSendResult transactionSendResult = new TransactionSendResult(); transactionSendResult.setSendStatus(sendResult.getSendStatus()); transactionSendResult.setMessageQueue(sendResult.getMessageQueue()); transactionSendResult.setMsgId(sendResult.getMsgId()); transactionSendResult.setQueueOffset(sendResult.getQueueOffset()); transactionSendResult.setLocalTransactionState(localTransactionState); return transactionSendResult; } private void endTransaction( // final SendResult sendResult, // final LocalTransactionState localTransactionState, // final Throwable localException) throws RemotingException, MQBrokerException, InterruptedException, UnknownHostException { final MessageId id = MessageDecoder.decodeMessageId(sendResult.getMsgId()); final String addr = RemotingUtil.socketAddress2String(id.getAddress()); EndTransactionRequestHeader requestHeader = new EndTransactionRequestHeader(); requestHeader.setCommitLogOffset(id.getOffset()); switch (localTransactionState) { case COMMIT_MESSAGE: requestHeader.setCommitOrRollback(MessageSysFlag.TransactionCommitType); break; case ROLLBACK_MESSAGE: requestHeader.setCommitOrRollback(MessageSysFlag.TransactionRollbackType); break; case UNKNOW: requestHeader.setCommitOrRollback(MessageSysFlag.TransactionNotType); break; default: break; } requestHeader.setProducerGroup(this.defaultMQProducer.getProducerGroup()); requestHeader.setTranStateTableOffset(sendResult.getQueueOffset()); requestHeader.setMsgId(sendResult.getMsgId()); String remark = localException != null ? ("executeLocalTransactionBranch exception: " + localException.toString()) : null; this.mQClientFactory .getMQClientAPIImpl() .endTransactionOneway( addr, requestHeader, remark, this.defaultMQProducer.getSendMsgTimeout()); } /** DEFAULT SYNC ------------------------------------------------------- */ public SendResult send(Message msg) throws MQClientException, RemotingException, MQBrokerException, InterruptedException { return this.sendDefaultImpl(msg, CommunicationMode.SYNC, null); } public ConcurrentHashMap<String, TopicPublishInfo> getTopicPublishInfoTable() { return topicPublishInfoTable; } public MQClientFactory getmQClientFactory() { return mQClientFactory; } public int getZipCompressLevel() { return zipCompressLevel; } public void setZipCompressLevel(int zipCompressLevel) { this.zipCompressLevel = zipCompressLevel; } }
/** * 客户端Factory类,用来管理Producer与Consumer * * @author shijia.wxr<*****@*****.**> * @since 2013-6-15 */ public class MQClientFactory { private static final long LockTimeoutMillis = 3000; private final Logger log = ClientLogger.getLog(); private final ClientConfig clientConfig; private final int factoryIndex; private final String clientId; private final long bootTimestamp = System.currentTimeMillis(); // Producer对象 private final ConcurrentHashMap<String /* group */, MQProducerInner> producerTable = new ConcurrentHashMap<String, MQProducerInner>(); // Consumer对象 private final ConcurrentHashMap<String /* group */, MQConsumerInner> consumerTable = new ConcurrentHashMap<String, MQConsumerInner>(); // AdminExt对象 private final ConcurrentHashMap<String /* group */, MQAdminExtInner> adminExtTable = new ConcurrentHashMap<String, MQAdminExtInner>(); // Netty客户端配置 private final NettyClientConfig nettyClientConfig; // RPC调用的封装类 private final MQClientAPIImpl mQClientAPIImpl; private final MQAdminImpl mQAdminImpl; // 存储从Name Server拿到的Topic路由信息 private final ConcurrentHashMap<String /* Topic */, TopicRouteData> topicRouteTable = new ConcurrentHashMap<String, TopicRouteData>(); // 调用Name Server获取Topic路由信息时,加锁 private final Lock lockNamesrv = new ReentrantLock(); // 心跳与注销动作加锁 private final Lock lockHeartbeat = new ReentrantLock(); // 存储Broker Name 与Broker Address的对应关系 private final ConcurrentHashMap< String /* Broker Name */, HashMap<Long /* brokerId */, String /* address */>> brokerAddrTable = new ConcurrentHashMap<String, HashMap<Long, String>>(); // 定时线程 private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor( new ThreadFactory() { @Override public Thread newThread(Runnable r) { return new Thread(r, "MQClientFactoryScheduledThread"); } }); // 处理服务器主动发来的请求 private final ClientRemotingProcessor clientRemotingProcessor; // 拉消息服务 private final PullMessageService pullMessageService; // Rebalance服务 private final RebalanceService rebalanceService; // 内置Producer对象 private final DefaultMQProducer defaultMQProducer; private ServiceState serviceState = ServiceState.CREATE_JUST; // 监听一个UDP端口,用来防止同一个Factory启动多份(有可能分布在多个JVM中) private DatagramSocket datagramSocket; public MQClientFactory(ClientConfig clientConfig, int factoryIndex, String clientId) { this.clientConfig = clientConfig; this.factoryIndex = factoryIndex; this.nettyClientConfig = new NettyClientConfig(); this.nettyClientConfig.setClientCallbackExecutorThreads( clientConfig.getClientCallbackExecutorThreads()); this.clientRemotingProcessor = new ClientRemotingProcessor(this); this.mQClientAPIImpl = new MQClientAPIImpl(this.nettyClientConfig, this.clientRemotingProcessor); if (this.clientConfig.getNamesrvAddr() != null) { this.mQClientAPIImpl.updateNameServerAddressList(this.clientConfig.getNamesrvAddr()); log.info("user specfied name server address: {}", this.clientConfig.getNamesrvAddr()); } this.clientId = clientId; this.mQAdminImpl = new MQAdminImpl(this); this.pullMessageService = new PullMessageService(this); this.rebalanceService = new RebalanceService(this); this.defaultMQProducer = new DefaultMQProducer(MixAll.CLIENT_INNER_PRODUCER_GROUP); this.defaultMQProducer.resetClientConfig(clientConfig); log.info( "created a new client fatory, FactoryIndex: {} ClinetID: {} {}", // this.factoryIndex, // this.clientId, // this.clientConfig); } public void start() throws MQClientException { synchronized (this) { switch (this.serviceState) { case CREATE_JUST: this.makesureInstanceNameIsOnly(this.clientConfig.getInstanceName()); this.serviceState = ServiceState.RUNNING; if (null == this.clientConfig.getNamesrvAddr()) { this.clientConfig.setNamesrvAddr(this.mQClientAPIImpl.fetchNameServerAddr()); } this.mQClientAPIImpl.start(); this.startScheduledTask(); this.pullMessageService.start(); this.rebalanceService.start(); this.defaultMQProducer.getDefaultMQProducerImpl().start(false); log.info("the client factory [{}] start OK", this.clientId); break; case RUNNING: break; case SHUTDOWN_ALREADY: break; default: break; } } } private void makesureInstanceNameIsOnly(final String instanceName) throws MQClientException { int udpPort = 33333; int value = instanceName.hashCode(); if (value < 0) { value = Math.abs(value); } udpPort += value % 10000; try { this.datagramSocket = new DatagramSocket(udpPort); } catch (SocketException e) { throw new MQClientException( "instance name is a duplicate one[" + instanceName + "," + udpPort + "], please set a new name" + FAQUrl.suggestTodo(FAQUrl.CLIENT_INSTACNCE_NAME_DUPLICATE_URL), e); } } private void startScheduledTask() { // 定时获取Name Server地址 if (null == this.clientConfig.getNamesrvAddr()) { this.scheduledExecutorService.scheduleAtFixedRate( new Runnable() { @Override public void run() { try { MQClientFactory.this.mQClientAPIImpl.fetchNameServerAddr(); } catch (Exception e) { log.error("ScheduledTask fetchNameServerAddr exception", e); } } }, 1000 * 10, 1000 * 60 * 2, TimeUnit.MILLISECONDS); } // 定时从Name Server获取Topic路由信息 this.scheduledExecutorService.scheduleAtFixedRate( new Runnable() { @Override public void run() { try { MQClientFactory.this.updateTopicRouteInfoFromNameServer(); } catch (Exception e) { log.error("ScheduledTask updateTopicRouteInfoFromNameServer exception", e); } } }, 10, this.clientConfig.getPollNameServerInteval(), TimeUnit.MILLISECONDS); // 定时清理下线的Broker // 向所有Broker发送心跳信息(包含订阅关系等) this.scheduledExecutorService.scheduleAtFixedRate( new Runnable() { @Override public void run() { try { MQClientFactory.this.cleanOfflineBroker(); MQClientFactory.this.sendHeartbeatToAllBrokerWithLock(); } catch (Exception e) { log.error("ScheduledTask sendHeartbeatToAllBroker exception", e); } } }, 1000, this.clientConfig.getHeartbeatBrokerInterval(), TimeUnit.MILLISECONDS); // 定时持久化Consumer消费进度(广播存储到本地,集群存储到Broker) this.scheduledExecutorService.scheduleAtFixedRate( new Runnable() { @Override public void run() { try { MQClientFactory.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() { try { MQClientFactory.this.recordSnapshotPeriodically(); } catch (Exception e) { log.error("ScheduledTask uploadConsumerOffsets exception", e); } } }, 1000 * 10, 1000, TimeUnit.MILLISECONDS); this.scheduledExecutorService.scheduleAtFixedRate( new Runnable() { @Override public void run() { try { MQClientFactory.this.logStatsPeriodically(); } catch (Exception e) { log.error("ScheduledTask uploadConsumerOffsets exception", e); } } }, 1000 * 10, 1000 * 60, TimeUnit.MILLISECONDS); } /** 清理下线的broker */ private void cleanOfflineBroker() { ConcurrentHashMap<String, HashMap<Long, String>> updatedTable = new ConcurrentHashMap<String, HashMap<Long, String>>(); Iterator<Entry<String, HashMap<Long, String>>> itBrokerTable = this.brokerAddrTable.entrySet().iterator(); while (itBrokerTable.hasNext()) { Entry<String, HashMap<Long, String>> entry = itBrokerTable.next(); String brokerName = entry.getKey(); HashMap<Long, String> oneTable = entry.getValue(); HashMap<Long, String> cloneAddrTable = new HashMap<Long, String>(); cloneAddrTable.putAll(oneTable); Iterator<Entry<Long, String>> it = cloneAddrTable.entrySet().iterator(); while (it.hasNext()) { Entry<Long, String> ee = it.next(); String addr = ee.getValue(); if (!this.isBrokerAddrExistInTopicRouteTable(addr)) { it.remove(); log.info("the broker addr[{} {}] is offline, remove it", brokerName, addr); } } if (cloneAddrTable.isEmpty()) { itBrokerTable.remove(); log.info("the broker[{}] name's host is offline, remove it", brokerName); } else { updatedTable.put(brokerName, cloneAddrTable); } } if (!updatedTable.isEmpty()) { this.brokerAddrTable.putAll(updatedTable); } } private boolean isBrokerAddrExistInTopicRouteTable(final String addr) { Iterator<Entry<String, TopicRouteData>> it = this.topicRouteTable.entrySet().iterator(); while (it.hasNext()) { Entry<String, TopicRouteData> entry = it.next(); TopicRouteData topicRouteData = entry.getValue(); List<BrokerData> bds = topicRouteData.getBrokerDatas(); for (BrokerData bd : bds) { if (bd.getBrokerAddrs() != null) { boolean exist = bd.getBrokerAddrs().containsValue(addr); if (exist) return true; } } } return false; } private void recordSnapshotPeriodically() { Iterator<Entry<String, MQConsumerInner>> it = this.consumerTable.entrySet().iterator(); while (it.hasNext()) { Entry<String, MQConsumerInner> entry = it.next(); MQConsumerInner impl = entry.getValue(); if (impl != null) { if (impl instanceof DefaultMQPushConsumerImpl) { DefaultMQPushConsumerImpl consumer = (DefaultMQPushConsumerImpl) impl; consumer.getConsumerStatManager().recordSnapshotPeriodically(); } } } } private void logStatsPeriodically() { Iterator<Entry<String, MQConsumerInner>> it = this.consumerTable.entrySet().iterator(); while (it.hasNext()) { Entry<String, MQConsumerInner> entry = it.next(); MQConsumerInner impl = entry.getValue(); if (impl != null) { if (impl instanceof DefaultMQPushConsumerImpl) { DefaultMQPushConsumerImpl consumer = (DefaultMQPushConsumerImpl) impl; consumer.getConsumerStatManager().logStatsPeriodically(entry.getKey(), this.clientId); } } } } private void persistAllConsumerOffset() { Iterator<Entry<String, MQConsumerInner>> it = this.consumerTable.entrySet().iterator(); while (it.hasNext()) { Entry<String, MQConsumerInner> entry = it.next(); MQConsumerInner impl = entry.getValue(); impl.persistConsumerOffset(); } } public void sendHeartbeatToAllBrokerWithLock() { if (this.lockHeartbeat.tryLock()) { try { this.sendHeartbeatToAllBroker(); } 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 hearbeat, but no consumer and no producer"); return; } 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 (Long id : oneTable.keySet()) { String addr = oneTable.get(id); if (addr != null) { // 说明只有Producer,则不向Slave发心跳 if (consumerEmpty) { if (id != MixAll.MASTER_ID) continue; } try { this.mQClientAPIImpl.sendHearbeat(addr, heartbeatData, 3000); log.info("send heart beat to broker[{} {} {}] success", brokerName, id, addr); log.info(heartbeatData.toString()); } catch (Exception e) { log.error("send heart beat to broker exception", e); } } } } } } private HeartbeatData prepareHeartbeatData() { HeartbeatData heartbeatData = new HeartbeatData(); // clientID heartbeatData.setClientID(this.clientId); // Consumer for (String group : this.consumerTable.keySet()) { MQConsumerInner impl = this.consumerTable.get(group); if (impl != null) { ConsumerData consumerData = new ConsumerData(); consumerData.setGroupName(impl.groupName()); consumerData.setConsumeType(impl.consumeType()); consumerData.setMessageModel(impl.messageModel()); consumerData.setConsumeFromWhere(impl.consumeFromWhere()); consumerData.getSubscriptionDataSet().addAll(impl.subscriptions()); heartbeatData.getConsumerDataSet().add(consumerData); } } // Producer for (String group : this.producerTable.keySet()) { MQProducerInner impl = this.producerTable.get(group); if (impl != null) { ProducerData producerData = new ProducerData(); producerData.setGroupName(group); heartbeatData.getProducerDataSet().add(producerData); } } return heartbeatData; } private void updateTopicRouteInfoFromNameServer() { Set<String> topicList = new HashSet<String>(); // Consumer对象 { Iterator<Entry<String, MQConsumerInner>> it = this.consumerTable.entrySet().iterator(); while (it.hasNext()) { Entry<String, MQConsumerInner> entry = it.next(); MQConsumerInner impl = entry.getValue(); if (impl != null) { Set<SubscriptionData> subList = impl.subscriptions(); if (subList != null) { for (SubscriptionData subData : subList) { topicList.add(subData.getTopic()); } } } } } // Producer { Iterator<Entry<String, MQProducerInner>> it = this.producerTable.entrySet().iterator(); while (it.hasNext()) { Entry<String, MQProducerInner> entry = it.next(); MQProducerInner impl = entry.getValue(); if (impl != null) { Set<String> lst = impl.getPublishTopicList(); topicList.addAll(lst); } } } for (String topic : topicList) { this.updateTopicRouteInfoFromNameServer(topic); } } public boolean updateTopicRouteInfoFromNameServer(final String topic) { return updateTopicRouteInfoFromNameServer(topic, false, null); } /** 调用Name Server接口,根据Topic获取路由信息 */ public boolean updateTopicRouteInfoFromNameServer( final String topic, boolean isDefault, DefaultMQProducer defaultMQProducer) { try { if (this.lockNamesrv.tryLock(LockTimeoutMillis, TimeUnit.MILLISECONDS)) { try { TopicRouteData topicRouteData; if (isDefault) { if (null == defaultMQProducer) return false; topicRouteData = this.mQClientAPIImpl.getTopicRouteInfoFromNameServer( defaultMQProducer.getCreateTopicKey(), 1000 * 3); for (QueueData data : topicRouteData.getQueueDatas()) { // 读写分区个数是一致,故只做一次判断 int queueNums = Math.min(defaultMQProducer.getDefaultTopicQueueNums(), data.getReadQueueNums()); data.setReadQueueNums(queueNums); data.setWriteQueueNums(queueNums); } } else { topicRouteData = this.mQClientAPIImpl.getTopicRouteInfoFromNameServer(topic, 1000 * 3); } if (topicRouteData != null) { TopicRouteData old = this.topicRouteTable.get(topic); boolean changed = topicRouteDataIsChange(old, topicRouteData); if (!changed) { changed = this.isNeedUpdateTopicRouteInfo(topic); } else { log.info( "the topic[{}] route info changed, odl[{}] ,new[{}]", topic, old, topicRouteData); } if (changed) { // 后面排序会影响下次的equal逻辑判断,所以先clone一份 TopicRouteData cloneTopicRouteData = topicRouteData.cloneTopicRouteData(); // 更新Broker地址信息 for (BrokerData bd : topicRouteData.getBrokerDatas()) { this.brokerAddrTable.put(bd.getBrokerName(), bd.getBrokerAddrs()); } // 更新发布队列信息 { TopicPublishInfo publishInfo = topicRouteData2TopicPublishInfo(topic, topicRouteData); Iterator<Entry<String, MQProducerInner>> it = this.producerTable.entrySet().iterator(); while (it.hasNext()) { Entry<String, MQProducerInner> entry = it.next(); MQProducerInner impl = entry.getValue(); if (impl != null) { impl.updateTopicPublishInfo(topic, publishInfo); } } } // 更新订阅队列信息 { Set<MessageQueue> subscribeInfo = topicRouteData2TopicSubscribeInfo(topic, topicRouteData); Iterator<Entry<String, MQConsumerInner>> it = this.consumerTable.entrySet().iterator(); while (it.hasNext()) { Entry<String, MQConsumerInner> entry = it.next(); MQConsumerInner impl = entry.getValue(); if (impl != null) { impl.updateTopicSubscribeInfo(topic, subscribeInfo); } } } log.info("topicRouteTable.put TopicRouteData[{}]", cloneTopicRouteData); this.topicRouteTable.put(topic, cloneTopicRouteData); return true; } } else { log.warn( "updateTopicRouteInfoFromNameServer, getTopicRouteInfoFromNameServer return null, Topic: {}", topic); } } catch (Exception e) { if (!topic.startsWith(MixAll.RETRY_GROUP_TOPIC_PREFIX)) { log.warn("updateTopicRouteInfoFromNameServer Exception", e); } } finally { this.lockNamesrv.unlock(); } } else { log.warn("updateTopicRouteInfoFromNameServer tryLock timeout {}ms", LockTimeoutMillis); } } catch (InterruptedException e) { log.warn("updateTopicRouteInfoFromNameServer Exception", e); } return false; } private boolean topicRouteDataIsChange(TopicRouteData olddata, TopicRouteData nowdata) { if (olddata == null || nowdata == null) return true; TopicRouteData old = olddata.cloneTopicRouteData(); TopicRouteData now = nowdata.cloneTopicRouteData(); Collections.sort(old.getQueueDatas()); Collections.sort(old.getBrokerDatas()); Collections.sort(now.getQueueDatas()); Collections.sort(now.getBrokerDatas()); return !old.equals(now); } public static TopicPublishInfo topicRouteData2TopicPublishInfo( final String topic, final TopicRouteData route) { TopicPublishInfo info = new TopicPublishInfo(); // 顺序消息 if (route.getOrderTopicConf() != null && route.getOrderTopicConf().length() > 0) { String[] brokers = route.getOrderTopicConf().split(";"); for (String broker : brokers) { String[] item = broker.split(":"); int nums = Integer.parseInt(item[1]); for (int i = 0; i < nums; i++) { MessageQueue mq = new MessageQueue(topic, item[0], i); info.getMessageQueueList().add(mq); } } info.setOrderTopic(true); } // 非顺序消息 else { List<QueueData> qds = route.getQueueDatas(); // 排序原因:即使没有配置顺序消息模式,默认队列的顺序同配置的一致。 Collections.sort(qds); for (QueueData qd : qds) { if (PermName.isWriteable(qd.getPerm())) { for (int i = 0; i < qd.getWriteQueueNums(); i++) { MessageQueue mq = new MessageQueue(topic, qd.getBrokerName(), i); info.getMessageQueueList().add(mq); } } } info.setOrderTopic(false); } return info; } public static Set<MessageQueue> topicRouteData2TopicSubscribeInfo( final String topic, final TopicRouteData route) { Set<MessageQueue> mqList = new HashSet<MessageQueue>(); List<QueueData> qds = route.getQueueDatas(); for (QueueData qd : qds) { if (PermName.isReadable(qd.getPerm())) { for (int i = 0; i < qd.getReadQueueNums(); i++) { MessageQueue mq = new MessageQueue(topic, qd.getBrokerName(), i); mqList.add(mq); } } } return mqList; } private boolean isNeedUpdateTopicRouteInfo(final String topic) { boolean result = false; // 查看发布队列是否需要更新 { Iterator<Entry<String, MQProducerInner>> it = this.producerTable.entrySet().iterator(); while (it.hasNext() && !result) { Entry<String, MQProducerInner> entry = it.next(); MQProducerInner impl = entry.getValue(); if (impl != null) { result = impl.isPublishTopicNeedUpdate(topic); } } } // 查看订阅队列是否需要更新 { Iterator<Entry<String, MQConsumerInner>> it = this.consumerTable.entrySet().iterator(); while (it.hasNext() && !result) { Entry<String, MQConsumerInner> entry = it.next(); MQConsumerInner impl = entry.getValue(); if (impl != null) { result = impl.isSubscribeTopicNeedUpdate(topic); } } } return result; } public void shutdown() { // Consumer if (!this.consumerTable.isEmpty()) return; // AdminExt if (!this.adminExtTable.isEmpty()) return; // Producer if (this.producerTable.size() > 1) return; synchronized (this) { switch (this.serviceState) { case CREATE_JUST: break; case RUNNING: this.defaultMQProducer.getDefaultMQProducerImpl().shutdown(false); this.serviceState = ServiceState.SHUTDOWN_ALREADY; this.pullMessageService.shutdown(true); this.scheduledExecutorService.shutdown(); this.mQClientAPIImpl.shutdown(); this.rebalanceService.shutdown(); if (this.datagramSocket != null) { this.datagramSocket.close(); this.datagramSocket = null; } MQClientManager.getInstance().removeClientFactory(this.clientId); log.info("the client factory [{}] shutdown OK", this.clientId); break; case SHUTDOWN_ALREADY: break; default: break; } } } public boolean registerConsumer(final String group, final MQConsumerInner consumer) { if (null == group || null == consumer) { return false; } MQConsumerInner prev = this.consumerTable.putIfAbsent(group, consumer); if (prev != null) { log.warn("the consumer group[" + group + "] exist already."); return false; } return true; } public void unregisterConsumer(final String group) { this.consumerTable.remove(group); this.unregisterClientWithLock(null, group); } private void unregisterClientWithLock(final String producerGroup, final String consumerGroup) { try { if (this.lockHeartbeat.tryLock(LockTimeoutMillis, TimeUnit.MILLISECONDS)) { try { this.unregisterClient(producerGroup, consumerGroup); } catch (Exception e) { log.error("unregisterClient exception", e); } finally { this.lockHeartbeat.unlock(); } } else { log.warn("lock heartBeat, but failed."); } } catch (InterruptedException e) { log.warn("unregisterClientWithLock exception", e); } } private void unregisterClient(final String producerGroup, final String consumerGroup) { 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 (Long id : oneTable.keySet()) { String addr = oneTable.get(id); if (addr != null) { try { this.mQClientAPIImpl.unregisterClient( addr, this.clientId, producerGroup, consumerGroup, 3000); log.info( "unregister client[Producer: {} Consumer: {}] from broker[{} {} {}] success", producerGroup, consumerGroup, brokerName, id, addr); } catch (RemotingException e) { log.error("unregister client exception from broker: " + addr, e); } catch (MQBrokerException e) { log.error("unregister client exception from broker: " + addr, e); } catch (InterruptedException e) { log.error("unregister client exception from broker: " + addr, e); } } } } } } public boolean registerProducer(final String group, final DefaultMQProducerImpl producer) { if (null == group || null == producer) { return false; } MQProducerInner prev = this.producerTable.putIfAbsent(group, producer); if (prev != null) { log.warn("the producer group[{}] exist already.", group); return false; } return true; } public void unregisterProducer(final String group) { this.producerTable.remove(group); this.unregisterClientWithLock(group, null); } public boolean registerAdminExt(final String group, final MQAdminExtInner admin) { if (null == group || null == admin) { return false; } MQAdminExtInner prev = this.adminExtTable.putIfAbsent(group, admin); if (prev != null) { log.warn("the admin group[{}] exist already.", group); return false; } return true; } public void unregisterAdminExt(final String group) { this.adminExtTable.remove(group); } public void rebalanceImmediately() { this.rebalanceService.wakeup(); } public void doRebalance() { for (String group : this.consumerTable.keySet()) { MQConsumerInner impl = this.consumerTable.get(group); if (impl != null) { try { impl.doRebalance(); } catch (Exception e) { log.error("doRebalance exception", e); } } } } public MQProducerInner selectProducer(final String group) { return this.producerTable.get(group); } public MQConsumerInner selectConsumer(final String group) { return this.consumerTable.get(group); } /** * 管理类的接口查询Broker地址,Master优先 * * @param brokerName * @return */ public FindBrokerResult findBrokerAddressInAdmin(final String brokerName) { String brokerAddr = null; boolean slave = false; boolean found = false; HashMap<Long /* brokerId */, String /* address */> map = this.brokerAddrTable.get(brokerName); if (map != null && !map.isEmpty()) { // TODO 如果有多个Slave,可能会每次都选中相同的Slave,这里需要优化 FOR_SEG: for (Map.Entry<Long, String> entry : map.entrySet()) { Long id = entry.getKey(); brokerAddr = entry.getValue(); if (brokerAddr != null) { found = true; if (MixAll.MASTER_ID == id) { slave = false; break FOR_SEG; } else { slave = true; } break; } } // end of for } if (found) { return new FindBrokerResult(brokerAddr, slave); } return null; } /** 发布消息过程中,寻找Broker地址,一定是找Master */ public String findBrokerAddressInPublish(final String brokerName) { HashMap<Long /* brokerId */, String /* address */> map = this.brokerAddrTable.get(brokerName); if (map != null && !map.isEmpty()) { return map.get(MixAll.MASTER_ID); } return null; } /** 订阅消息过程中,寻找Broker地址,取Master还是Slave由参数决定 */ public FindBrokerResult findBrokerAddressInSubscribe( // final String brokerName, // final long brokerId, // final boolean onlyThisBroker // ) { String brokerAddr = null; boolean slave = false; boolean found = false; HashMap<Long /* brokerId */, String /* address */> map = this.brokerAddrTable.get(brokerName); if (map != null && !map.isEmpty()) { brokerAddr = map.get(brokerId); slave = (brokerId != MixAll.MASTER_ID); found = (brokerAddr != null); // 尝试寻找其他Broker if (!found && !onlyThisBroker) { Entry<Long, String> entry = map.entrySet().iterator().next(); brokerAddr = entry.getValue(); slave = (entry.getKey() != MixAll.MASTER_ID); found = true; } } if (found) { return new FindBrokerResult(brokerAddr, slave); } return null; } public List<String> findConsumerIdList(final String topic, final String group) { String brokerAddr = this.findBrokerAddrByTopic(topic); if (null == brokerAddr) { this.updateTopicRouteInfoFromNameServer(topic); brokerAddr = this.findBrokerAddrByTopic(topic); } if (null != brokerAddr) { try { return this.mQClientAPIImpl.getConsumerIdListByGroup(brokerAddr, group, 3000); } catch (Exception e) { log.warn("getConsumerIdListByGroup exception, " + brokerAddr + " " + group, e); } } return null; } public String findBrokerAddrByTopic(final String topic) { TopicRouteData topicRouteData = this.topicRouteTable.get(topic); if (topicRouteData != null) { List<BrokerData> brokers = topicRouteData.getBrokerDatas(); if (!brokers.isEmpty()) { BrokerData bd = brokers.get(0); return bd.selectBrokerAddr(); } } return null; } public TopicRouteData getAnExistTopicRouteData(final String topic) { return this.topicRouteTable.get(topic); } public MQClientAPIImpl getMQClientAPIImpl() { return mQClientAPIImpl; } public MQAdminImpl getMQAdminImpl() { return mQAdminImpl; } public String getClientId() { return clientId; } public long getBootTimestamp() { return bootTimestamp; } public ScheduledExecutorService getScheduledExecutorService() { return scheduledExecutorService; } public PullMessageService getPullMessageService() { return pullMessageService; } public DefaultMQProducer getDefaultMQProducer() { return defaultMQProducer; } }
/** @author shijia.wxr */ public class PullMessageService extends ServiceThread { private final Logger log = ClientLogger.getLog(); private final LinkedBlockingQueue<PullRequest> pullRequestQueue = new LinkedBlockingQueue<PullRequest>(); private final MQClientInstance mQClientFactory; private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor( new ThreadFactory() { @Override public Thread newThread(Runnable r) { return new Thread(r, "PullMessageServiceScheduledThread"); } });; public PullMessageService(MQClientInstance mQClientFactory) { this.mQClientFactory = mQClientFactory; } public void executePullRequestLater(final PullRequest pullRequest, final long timeDelay) { this.scheduledExecutorService.schedule( new Runnable() { @Override public void run() { PullMessageService.this.executePullRequestImmediately(pullRequest); } }, timeDelay, TimeUnit.MILLISECONDS); } public void executePullRequestImmediately(final PullRequest pullRequest) { try { this.pullRequestQueue.put(pullRequest); } catch (InterruptedException e) { log.error("executePullRequestImmediately pullRequestQueue.put", e); } } public void executeTaskLater(final Runnable r, final long timeDelay) { this.scheduledExecutorService.schedule(r, timeDelay, TimeUnit.MILLISECONDS); } public ScheduledExecutorService getScheduledExecutorService() { return scheduledExecutorService; } private void pullMessage(final PullRequest pullRequest) { final MQConsumerInner consumer = this.mQClientFactory.selectConsumer(pullRequest.getConsumerGroup()); if (consumer != null) { DefaultMQPushConsumerImpl impl = (DefaultMQPushConsumerImpl) consumer; impl.pullMessage(pullRequest); } else { log.warn("No matched consumer for the PullRequest {}, drop it", pullRequest); } } @Override public void run() { log.info(this.getServiceName() + " service started"); while (!this.isStoped()) { try { PullRequest pullRequest = this.pullRequestQueue.take(); if (pullRequest != null) { this.pullMessage(pullRequest); } } catch (InterruptedException e) { } catch (Exception e) { log.error("Pull Message Service Run Method exception", e); } } log.info(this.getServiceName() + " service end"); } @Override public String getServiceName() { return PullMessageService.class.getSimpleName(); } }