示例#1
0
/**
 * 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;
  }
}
示例#3
0
/**
 * 客户端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();
  }
}