rocketmq集群监控

1,207 阅读7分钟

为什么要监控?

1.集群可能有节点挂了,需要告警。

2.有大量的积压消息没有被消费,需要告警。

其实说白了,就是两种情况,一种是挂了,一种是执行慢。这个和之前讲的job监控也是一样的,一种是job挂了,一种是job单次执行慢即超时。

监控指标

1.集群是否有节点挂掉?
2.未消费的积压消息数量

如何比较?

即如何读真实数据,然后和阈值比较?

是否挂了?

先从rocketmq官方工具类读真实数据(集群节点信息),然后和阈值比较:
1、如果集群节点数量为0,表示全挂

2、如果集群节点数量比原始数量小,说明至少挂了一组
注:原始数量的值,可以在配置中心配置。

3、如果集群节点数量和原始数量一样,然后再比较主从节点是否有挂掉
1)如果一组主从节点数量不为2,说明主或从已经挂掉
2)根据节点的值是0/1,判断到底是主节点还是从从节点挂了


源码

public void doProcess() {
  LogUtil.LOGGER.info("BrokerClusterJob start");
  //获取集群信息
  Map<String, Object> map = clusterService.list();
  ClusterInfo ClusterInfo = (ClusterInfo) map.get("clusterInfo");
  //获取集群ip信息
  HashMap<String, BrokerData> brokerAddrTable = ClusterInfo.getBrokerAddrTable();
  Config config = ConfigService.getAppConfig();
  long clusterNum = config.getLongProperty("rocketmq.cluster.num", 2L);
  LogUtil.LOGGER.debug("集群监控数据量{}", clusterNum);
  //校验集群ip数量
  if (MapUtils.isEmpty(brokerAddrTable)) { //如果为空,说明集群所有节点全部都已经挂掉
    LogUtil.LOGGER.warn("集群内容为空");
    catMonitor.catBuryPoint("rocketBrokerClusterException", "emptyCluster",
        new RocketBrokerClusterException("emptyCluster"), "集群已经出现问题");
  } else if (brokerAddrTable.size() != clusterNum) { //如果小于集群总数量,说明有一组主从节点已经挂掉
    LogUtil.LOGGER.warn("集群数量不为{}", clusterNum);
    catMonitor.catBuryPoint("rocketBrokerClusterException", "downCluster",
        new RocketBrokerClusterException("downCluster"), "集群中有一组master挂了");
  } else { //集群运行正常
    //校验一组主从节点的主或从是否挂掉
    for (Map.Entry<String, BrokerData> entry : brokerAddrTable.entrySet()) {
      HashMap<Long, String> brokerAddrs = entry.getValue().getBrokerAddrs();
      String brokerName = entry.getValue().getBrokerName();
      if (brokerAddrs.size() != 2) { //如果一组主从节点数量不为2,说明主或从已经挂掉
        //校验是主节点还是从节点挂掉
        String params = entry.getKey().equals(0) ? "主节点" : "从节点"; //TODO 这里的校验有问题
        catMonitor.catBuryPoint("rocketBrokerClusterException", brokerName,
            new RocketBrokerClusterException(brokerName), params);
      }
    }
  }
  LogUtil.LOGGER.info("BrokerClusterJob end");
}

是否有积压?

基于官方提供的工具类,可以读集群/group/topic维度的积压数据数量。

然后,拿这些实时数据和阈值比较,就知道有没有积压,积压了多少消息。


源码

@Override
public void doProcess() {
  LogUtil.LOGGER.info("RocketmqOffsetJob start");
  //获取mq的group集合
  List<GroupConsumeInfo> list = consumerService.queryGroupList();
  //遍历mq的group集合
  for(GroupConsumeInfo groupConsumeInfo : list){
    //获取group的积压消息数量
    long diffTotal = groupConsumeInfo.getDiffTotal();
    long defaultValue = configApp.getLongProperty("group.warn.diffvalue",100l);
    long warnvalue = configApp.getLongProperty("group.warn.diffvalue."+groupConsumeInfo.getGroup(),defaultValue);
    //校验group的积压消息数量是否超过阈值
    if(diffTotal > warnvalue){ //如果超过阈值,则告警
      long total = 0;
      //获取group的topic集合
      List<TopicConsumerInfo> topicConsumerInfoList = consumerService.queryConsumeStatsListByGroupName(groupConsumeInfo.getGroup());
      StringBuffer sb = new StringBuffer("消息组:").append(groupConsumeInfo.getGroup()).
          append("主题积压消息如下:");
      //遍历topic集合,累加得到group的积压消息数量
      for(TopicConsumerInfo topicConsumerInfo : topicConsumerInfoList){
        sb.append("topic ").append(topicConsumerInfo.getTopic().replace("%RETRY%","")).append("积压了")
            .append(topicConsumerInfo.getDiffTotal()).append(",");
        total = total+topicConsumerInfo.getDiffTotal();
      }
      sb.append("总共积压了:").append(total);
      if(obj == null){
        String names = configApp.getProperty(groupNames,"");
        obj  = JSONObject.parseObject(names);
      }
      if(MapUtils.isNotEmpty(obj)){
        Object groupName = obj.get(groupConsumeInfo.getGroup());
        if(groupName != null){
          sb.append(",项目名:").append(groupName);
        }
      }
      LogUtil.LOGGER.warn("group:{}偏移量为{}",groupConsumeInfo.getGroup(),diffTotal);
      //监控指标数据:1.group积压消息数量 2.每个topic积压消息数量
      catMonitor.catBuryPoint("rocketmqDiffException",groupConsumeInfo.getGroup(),new RocketmqDiffException(groupConsumeInfo.getGroup()),sb.toString());
    }
  }
  LogUtil.LOGGER.info("RocketmqOffsetJob end");
}

实现

配置

启动监控类。

package xxx.monitor.config;

import xxx.log.logback.web.LogbackConfigListener;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import xxx.monitor.util.LogUtil;

/**
 * web配置
 */
@Configuration
public class WebConfig implements WebMvcConfigurer {

  @Value("${rocketmq.config.namesrvAddr}")
  private String nameSerAddr;

  @Value("${rocketmq.config.isVIPChannel}")
  private Boolean isVIPChannel;
  @Bean
  public ServletListenerRegistrationBean<LogbackConfigListener> initLogbackConfigListener() {
    ServletListenerRegistrationBean<LogbackConfigListener> result = new ServletListenerRegistrationBean<>();
    result.setListener(new LogbackConfigListener());
    return result;
  }

  /**
   * 启动mq监控
   *
   * @return
   * @throws MQClientException
   */
  @Bean(name = "defaultMQAdminExt")
  public DefaultMQAdminExt initMQAdminInstance() throws MQClientException {
    DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt();
    defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
    LogUtil.LOGGER.info("NamesrvAddr=={},nameSerAddr=={},isVIPChannel={}",defaultMQAdminExt.getNamesrvAddr(),nameSerAddr,isVIPChannel);
    defaultMQAdminExt.setNamesrvAddr(nameSerAddr);
    defaultMQAdminExt.setVipChannelEnabled(isVIPChannel);
    defaultMQAdminExt.start(); //启动mq监控
    return defaultMQAdminExt;
  }

  /**
   * 启动mq监控
   *
   * @return
   * @throws MQClientException
   */
  @Bean(name = "defaultMQAdminExt2")
  public DefaultMQAdminExt initMQAdminInstance2() throws MQClientException {
    DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt();
    defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
    LogUtil.LOGGER.info("NamesrvAddr=={},nameSerAddr=={},isVIPChannel={}",defaultMQAdminExt.getNamesrvAddr(),nameSerAddr,isVIPChannel);
    defaultMQAdminExt.setNamesrvAddr("xxx");
    defaultMQAdminExt.setVipChannelEnabled(isVIPChannel);
    defaultMQAdminExt.start(); //启动mq监控
    return defaultMQAdminExt;
  }
}

启动job

轮询,比如一分钟读取异常集群的运行时数据。

主要有2个job,即每个监控指标,都搞个job。

/**
 * 监控集群节点是否有挂掉
 */
@ElasticJobConf(cron = "0 0/1 * * * ?", name = "BrokerClusterJob", jobParameter = "5", description = "监控rocketmq broker状态", overwrite = true)
public class BrokerClusterJob extends AbstractJob implements SimpleJob {

  @Autowired
  @Qualifier("clusterServiceImpl")
  private ClusterService clusterService; //注入service类
/**
 * 监控积压消息数量
 */
@ElasticJobConf(cron = "0 0/1 * * * ?", name = "RocketmqOffsetJob", jobParameter = "5", description = "监控rocketmq消息偏移量", overwrite = true)
public class RocketmqOffsetJob extends AbstractJob implements SimpleJob, InitializingBean {

  private final String groupNames = "rocket.group.name";

  private JSONObject obj;

  @Autowired
  @Qualifier("consumerServiceImpl")
  private ConsumerService consumerService;

service类

通过调用官方自带的监控类读取运行时数据。

/**
 * 获取集群信息
 */
@Service("clusterServiceImpl")
public class ClusterServiceImpl implements ClusterService {
    private Logger logger = LoggerFactory.getLogger(ClusterServiceImpl.class);

    @Resource
    private MQAdminExtImpl mqAdminExtImpl; //主要是把官方自带的工具类注入进来,然后通过工具类来访问运行时数据

官方自带监控工具类

可以复制到自己的项目。

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package xxx.monitor.service.client;

import com.google.common.base.Throwables;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.apache.rocketmq.client.QueryResult;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.impl.MQAdminImpl;
import org.apache.rocketmq.common.TopicConfig;
import org.apache.rocketmq.common.admin.ConsumeStats;
import org.apache.rocketmq.common.admin.RollbackStats;
import org.apache.rocketmq.common.admin.TopicStatsTable;
import org.apache.rocketmq.common.message.MessageClientIDSetter;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.common.protocol.RequestCode;
import org.apache.rocketmq.common.protocol.ResponseCode;
import org.apache.rocketmq.common.protocol.body.BrokerStatsData;
import org.apache.rocketmq.common.protocol.body.ClusterInfo;
import org.apache.rocketmq.common.protocol.body.ConsumeMessageDirectlyResult;
import org.apache.rocketmq.common.protocol.body.ConsumeStatsList;
import org.apache.rocketmq.common.protocol.body.ConsumerConnection;
import org.apache.rocketmq.common.protocol.body.ConsumerRunningInfo;
import org.apache.rocketmq.common.protocol.body.GroupList;
import org.apache.rocketmq.common.protocol.body.KVTable;
import org.apache.rocketmq.common.protocol.body.ProducerConnection;
import org.apache.rocketmq.common.protocol.body.QueueTimeSpan;
import org.apache.rocketmq.common.protocol.body.SubscriptionGroupWrapper;
import org.apache.rocketmq.common.protocol.body.TopicConfigSerializeWrapper;
import org.apache.rocketmq.common.protocol.body.TopicList;
import org.apache.rocketmq.common.protocol.route.TopicRouteData;
import org.apache.rocketmq.common.subscription.SubscriptionGroupConfig;
import org.apache.rocketmq.remoting.RemotingClient;
import org.apache.rocketmq.remoting.exception.RemotingCommandException;
import org.apache.rocketmq.remoting.exception.RemotingConnectException;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.apache.rocketmq.remoting.exception.RemotingSendRequestException;
import org.apache.rocketmq.remoting.exception.RemotingTimeoutException;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.admin.MQAdminExt;
import org.apache.rocketmq.tools.admin.api.MessageTrack;
import org.joor.Reflect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import xxx.monitor.util.JsonUtil;
import xxx.monitor.util.LogUtil;

import static org.apache.rocketmq.remoting.protocol.RemotingSerializable.decode;

/**
 * 查询监控数据
 */
@Service
public class MQAdminExtImpl implements MQAdminExt {
    private Logger logger = LoggerFactory.getLogger(MQAdminExtImpl.class);

    @Autowired
    @Qualifier("defaultMQAdminExt")
    private DefaultMQAdminExt defaultMQAdminExt;

    @Override
    public void updateBrokerConfig(String brokerAddr, Properties properties)
        throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException,
        UnsupportedEncodingException, InterruptedException, MQBrokerException {
        defaultMQAdminExt.updateBrokerConfig(brokerAddr, properties);
    }

    @Override
    public void createAndUpdateTopicConfig(String addr, TopicConfig config)
        throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
        defaultMQAdminExt.createAndUpdateTopicConfig(addr, config);
    }

    @Override
    public void createAndUpdateSubscriptionGroupConfig(String addr, SubscriptionGroupConfig config)
        throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
        defaultMQAdminExt.createAndUpdateSubscriptionGroupConfig(addr, config);
    }

    @Override
    public SubscriptionGroupConfig examineSubscriptionGroupConfig(String addr, String group) {
        RemotingClient remotingClient = MQAdminInstance.threadLocalRemotingClient();
        RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_ALL_SUBSCRIPTIONGROUP_CONFIG, null);
        RemotingCommand response = null;
        try {
            response = remotingClient.invokeSync(addr, request, 3000);
        }
        catch (Exception err) {
            throw Throwables.propagate(err);
        }
        assert response != null;
        switch (response.getCode()) {
            case ResponseCode.SUCCESS: {
                SubscriptionGroupWrapper subscriptionGroupWrapper = decode(response.getBody(), SubscriptionGroupWrapper.class);
                return subscriptionGroupWrapper.getSubscriptionGroupTable().get(group);
            }
            default:
                throw Throwables.propagate(new MQBrokerException(response.getCode(), response.getRemark()));
        }
    }

    @Override
    public TopicConfig examineTopicConfig(String addr, String topic) {
        RemotingClient remotingClient = MQAdminInstance.threadLocalRemotingClient();
        RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_ALL_TOPIC_CONFIG, null);
        RemotingCommand response = null;
        try {
            response = remotingClient.invokeSync(addr, request, 3000);
        }
        catch (Exception err) {
            throw Throwables.propagate(err);
        }
        switch (response.getCode()) {
            case ResponseCode.SUCCESS: {
                TopicConfigSerializeWrapper topicConfigSerializeWrapper = decode(response.getBody(), TopicConfigSerializeWrapper.class);
                return topicConfigSerializeWrapper.getTopicConfigTable().get(topic);
            }
            default:
                throw Throwables.propagate(new MQBrokerException(response.getCode(), response.getRemark()));
        }
    }

    @Override
    public TopicStatsTable examineTopicStats(String topic)
        throws RemotingException, MQClientException, InterruptedException, MQBrokerException {
        return defaultMQAdminExt.examineTopicStats(topic);
    }

    @Override
    public TopicList fetchAllTopicList() throws RemotingException, MQClientException, InterruptedException {
        TopicList topicList = MQAdminInstance.threadLocalMQAdminExt().fetchAllTopicList();
        logger.debug("op=look={}", JsonUtil.obj2String(topicList.getTopicList()));
        return topicList;
    }

    @Override
    public KVTable fetchBrokerRuntimeStats(String brokerAddr)
        throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException,
        InterruptedException, MQBrokerException {
        return defaultMQAdminExt.fetchBrokerRuntimeStats(brokerAddr);
    }

    @Override
    public ConsumeStats examineConsumeStats(String consumerGroup)
        throws RemotingException, MQClientException, InterruptedException, MQBrokerException {
        return defaultMQAdminExt.examineConsumeStats(consumerGroup);
    }

    @Override
    public ConsumeStats examineConsumeStats(String consumerGroup, String topic)
        throws RemotingException, MQClientException, InterruptedException, MQBrokerException {
        return defaultMQAdminExt.examineConsumeStats(consumerGroup, topic);
    }

    @Override
    public ClusterInfo examineBrokerClusterInfo()
        throws InterruptedException, MQBrokerException, RemotingTimeoutException, RemotingSendRequestException,
        RemotingConnectException {
        return defaultMQAdminExt.examineBrokerClusterInfo();
    }

    @Override
    public TopicRouteData examineTopicRouteInfo(String topic)
        throws RemotingException, MQClientException, InterruptedException {
        return defaultMQAdminExt.examineTopicRouteInfo(topic);
    }

    @Override
    public ConsumerConnection examineConsumerConnectionInfo(String consumerGroup)
        throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException,
        InterruptedException, MQBrokerException, RemotingException, MQClientException {
        return defaultMQAdminExt.examineConsumerConnectionInfo(consumerGroup);
    }

    @Override
    public ProducerConnection examineProducerConnectionInfo(String producerGroup, String topic)
        throws RemotingException, MQClientException, InterruptedException, MQBrokerException {
        return defaultMQAdminExt.examineProducerConnectionInfo(producerGroup, topic);
    }

    @Override
    public List<String> getNameServerAddressList() {
        return defaultMQAdminExt.getNameServerAddressList();
    }

    @Override
    public int wipeWritePermOfBroker(String namesrvAddr, String brokerName)
        throws RemotingCommandException, RemotingConnectException, RemotingSendRequestException,
        RemotingTimeoutException, InterruptedException, MQClientException {
        return defaultMQAdminExt.wipeWritePermOfBroker(namesrvAddr, brokerName);
    }

    @Override
    public void putKVConfig(String namespace, String key, String value) {
        defaultMQAdminExt.putKVConfig(namespace, key, value);
    }

    @Override
    public String getKVConfig(String namespace, String key)
        throws RemotingException, MQClientException, InterruptedException {
        return defaultMQAdminExt.getKVConfig(namespace, key);
    }

    @Override
    public KVTable getKVListByNamespace(String namespace)
        throws RemotingException, MQClientException, InterruptedException {
        return defaultMQAdminExt.getKVListByNamespace(namespace);
    }

    @Override
    public void deleteTopicInBroker(Set<String> addrs, String topic)
        throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
        logger.info("addrs={} topic={}", JsonUtil.obj2String(addrs), topic);
        defaultMQAdminExt.deleteTopicInBroker(addrs, topic);
    }

    @Override
    public void deleteTopicInNameServer(Set<String> addrs, String topic)
        throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
        defaultMQAdminExt.deleteTopicInNameServer(addrs, topic);
    }

    @Override
    public void deleteSubscriptionGroup(String addr, String groupName)
        throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
        defaultMQAdminExt.deleteSubscriptionGroup(addr, groupName);
    }

    @Override
    public void createAndUpdateKvConfig(String namespace, String key, String value)
        throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
        defaultMQAdminExt.createAndUpdateKvConfig(namespace, key, value);
    }

    @Override
    public void deleteKvConfig(String namespace, String key)
        throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
        defaultMQAdminExt.deleteKvConfig(namespace, key);
    }

    @Override
    public List<RollbackStats> resetOffsetByTimestampOld(String consumerGroup, String topic, long timestamp,
        boolean force) throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
        return defaultMQAdminExt.resetOffsetByTimestampOld(consumerGroup, topic, timestamp, force);
    }

    @Override
    public Map<MessageQueue, Long> resetOffsetByTimestamp(String topic, String group, long timestamp,
        boolean isForce) throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
        return defaultMQAdminExt.resetOffsetByTimestamp(topic, group, timestamp, isForce);
    }

    @Override
    public void resetOffsetNew(String consumerGroup, String topic, long timestamp)
        throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
        defaultMQAdminExt.resetOffsetNew(consumerGroup, topic, timestamp);
    }

    @Override
    public Map<String, Map<MessageQueue, Long>> getConsumeStatus(String topic, String group,
        String clientAddr) throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
        return defaultMQAdminExt.getConsumeStatus(topic, group, clientAddr);
    }

    @Override
    public void createOrUpdateOrderConf(String key, String value, boolean isCluster)
        throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
        defaultMQAdminExt.createOrUpdateOrderConf(key, value, isCluster);
    }

    @Override
    public GroupList queryTopicConsumeByWho(String topic)
        throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException,
        InterruptedException, MQBrokerException, RemotingException, MQClientException {
        return defaultMQAdminExt.queryTopicConsumeByWho(topic);
    }

    @Override
    public boolean cleanExpiredConsumerQueue(String cluster)
        throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQClientException,
        InterruptedException {
        return defaultMQAdminExt.cleanExpiredConsumerQueue(cluster);
    }

    @Override
    public boolean cleanExpiredConsumerQueueByAddr(String addr)
        throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQClientException,
        InterruptedException {
        return defaultMQAdminExt.cleanExpiredConsumerQueueByAddr(addr);
    }

    @Override
    public ConsumerRunningInfo getConsumerRunningInfo(String consumerGroup, String clientId, boolean jstack)
        throws RemotingException, MQClientException, InterruptedException {
        return defaultMQAdminExt.getConsumerRunningInfo(consumerGroup, clientId, jstack);
    }

    @Override
    public ConsumeMessageDirectlyResult consumeMessageDirectly(String consumerGroup, String clientId,
        String msgId) throws RemotingException, MQClientException, InterruptedException, MQBrokerException {
        return defaultMQAdminExt.consumeMessageDirectly(consumerGroup, clientId, msgId);
    }

    @Override
    public List<MessageTrack> messageTrackDetail(MessageExt msg)
        throws RemotingException, MQClientException, InterruptedException, MQBrokerException {
        return defaultMQAdminExt.messageTrackDetail(msg);
    }

    @Override
    public void cloneGroupOffset(String srcGroup, String destGroup, String topic, boolean isOffline)
        throws RemotingException, MQClientException, InterruptedException, MQBrokerException {
        defaultMQAdminExt.cloneGroupOffset(srcGroup, destGroup, topic, isOffline);
    }

    @Override
    public void createTopic(String key, String newTopic, int queueNum) throws MQClientException {
        defaultMQAdminExt.createTopic(key, newTopic, queueNum);
    }

    @Override
    public void createTopic(String key, String newTopic, int queueNum, int topicSysFlag)
        throws MQClientException {
        defaultMQAdminExt.createTopic(key, newTopic, queueNum, topicSysFlag);
    }

    @Override
    public long searchOffset(MessageQueue mq, long timestamp) throws MQClientException {
        return defaultMQAdminExt.searchOffset(mq, timestamp);
    }

    @Override
    public long maxOffset(MessageQueue mq) throws MQClientException {
        return defaultMQAdminExt.maxOffset(mq);
    }

    @Override
    public long minOffset(MessageQueue mq) throws MQClientException {
        return defaultMQAdminExt.minOffset(mq);
    }

    @Override
    public long earliestMsgStoreTime(MessageQueue mq) throws MQClientException {
        return defaultMQAdminExt.earliestMsgStoreTime(mq);
    }

    @Override
    public MessageExt viewMessage(String msgId)
        throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
        return defaultMQAdminExt.viewMessage(msgId);
    }

    @Override
    public QueryResult queryMessage(String topic, String key, int maxNum, long begin, long end)
        throws MQClientException, InterruptedException {
        return defaultMQAdminExt.queryMessage(topic, key, maxNum, begin, end);
    }

    /**
     * @deprecated thisMethod is deprecated.use org.apache.rocketmq.console.aspect.admin.MQAdminAspect instead of this
     * @throws MQClientException
     */
    @Override
    @Deprecated
    public void start() throws MQClientException {
        throw new IllegalStateException("thisMethod is deprecated.use org.apache.rocketmq.console.aspect.admin.MQAdminAspect instead of this");
    }

    /**
     * @deprecated thisMethod is deprecated.use org.apache.rocketmq.console.aspect.admin.MQAdminAspect instead of this
     */
    @Override
    @Deprecated
    public void shutdown() {
        throw new IllegalStateException("thisMethod is deprecated.use org.apache.rocketmq.console.aspect.admin.MQAdminAspect instead of this");
    }

    // below is 3.2.6->3.5.8 updated

    @Override
    public List<QueueTimeSpan> queryConsumeTimeSpan(String topic,
        String group) throws InterruptedException, MQBrokerException, RemotingException, MQClientException {
        return defaultMQAdminExt.queryConsumeTimeSpan(topic, group);
    }

    //MessageClientIDSetter.getNearlyTimeFromID has bug,so we subtract half a day
    //next version we will remove it
    //https://issues.apache.org/jira/browse/ROCKETMQ-111
    //https://github.com/apache/incubator-rocketmq/pull/69
    @Override
    public MessageExt viewMessage(String topic,
        String msgId) throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
        logger.info("MessageClientIDSetter.getNearlyTimeFromID(msgId)={} msgId={}", MessageClientIDSetter.getNearlyTimeFromID(msgId), msgId);
        try {
            return viewMessage(msgId);
        }
        catch (Exception e) {
            LogUtil.LOGGER.error("viewMessage error",e);
        }
        MQAdminImpl mqAdminImpl = MQAdminInstance.threadLocalMqClientInstance().getMQAdminImpl();
        QueryResult qr = Reflect.on(mqAdminImpl).call("queryMessage", topic, msgId, 32,
            MessageClientIDSetter.getNearlyTimeFromID(msgId).getTime() - 1000 * 60 * 60 * 13L, Long.MAX_VALUE, true).get();
        if (qr != null && qr.getMessageList() != null && qr.getMessageList().size() > 0) {
            return qr.getMessageList().get(0);
        }
        else {
            return null;
        }
    }

    @Override
    public ConsumeMessageDirectlyResult consumeMessageDirectly(String consumerGroup, String clientId, String topic,
        String msgId) throws RemotingException, MQClientException, InterruptedException, MQBrokerException {
        return defaultMQAdminExt.consumeMessageDirectly(consumerGroup, clientId, topic, msgId);
    }

    @Override
    public Properties getBrokerConfig(
        String brokerAddr) throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, UnsupportedEncodingException, InterruptedException, MQBrokerException {
        return defaultMQAdminExt.getBrokerConfig(brokerAddr);
    }

    @Override
    public TopicList fetchTopicsByCLuster(
        String clusterName) throws RemotingException, MQClientException, InterruptedException {
        return defaultMQAdminExt.fetchTopicsByCLuster(clusterName);
    }

    @Override
    public boolean cleanUnusedTopic(
        String cluster) throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, InterruptedException {
        return defaultMQAdminExt.cleanUnusedTopic(cluster);
    }

    @Override
    public boolean cleanUnusedTopicByAddr(
        String addr) throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, InterruptedException {
        return defaultMQAdminExt.cleanUnusedTopicByAddr(addr);
    }

    @Override
    public BrokerStatsData viewBrokerStatsData(String brokerAddr, String statsName,
        String statsKey) throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, InterruptedException {
        return defaultMQAdminExt.viewBrokerStatsData(brokerAddr, statsName, statsKey);
    }

    @Override
    public Set<String> getClusterList(
        String topic) throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, InterruptedException {
        return defaultMQAdminExt.getClusterList(topic);
    }

    @Override
    public ConsumeStatsList fetchConsumeStatsInBroker(String brokerAddr, boolean isOrder,
        long timeoutMillis) throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, InterruptedException {
        return defaultMQAdminExt.fetchConsumeStatsInBroker(brokerAddr, isOrder, timeoutMillis);
    }

    @Override
    public Set<String> getTopicClusterList(
        String topic) throws InterruptedException, MQBrokerException, MQClientException, RemotingException {
        return defaultMQAdminExt.getTopicClusterList(topic);
    }

    @Override
    public SubscriptionGroupWrapper getAllSubscriptionGroup(String brokerAddr,
        long timeoutMillis) throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQBrokerException {
        return defaultMQAdminExt.getAllSubscriptionGroup(brokerAddr, timeoutMillis);
    }

    @Override
    public TopicConfigSerializeWrapper getAllTopicGroup(String brokerAddr,
        long timeoutMillis) throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQBrokerException {
        return defaultMQAdminExt.getAllTopicGroup(brokerAddr, timeoutMillis);
    }

    @Override
    public void updateConsumeOffset(String brokerAddr, String consumeGroup, MessageQueue mq,
        long offset) throws RemotingException, InterruptedException, MQBrokerException {
        defaultMQAdminExt.updateConsumeOffset(brokerAddr, consumeGroup, mq, offset);
    }

    // 4.0.0 added
    @Override
    public void updateNameServerConfig(Properties properties,
        List<String> list) throws InterruptedException, RemotingConnectException, UnsupportedEncodingException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, MQBrokerException {

    }

    @Override public Map<String, Properties> getNameServerConfig(
        List<String> list) throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQClientException, UnsupportedEncodingException {
        return null;
    }
}

多套集群监控

实现

核心点是上面实现步骤里的每一步,都再来一套。 这里有个关键的问题是,有两套数据源,但是如何区分bean不同的实例?核心是通过bean的名字来区分。

如何区分bean的多个实例?

bean有多个实例

一、通过@bean注解创建bean
@bean注解带上名字表示,
1.@bean注解是创建bean
2.带上名字是可以用来区分不同的实例

@Bean(name = "defaultMQAdminExt")
@Bean(name = "defaultMQAdminExt2")

二、注入bean

@Autowired //注入数据
@Qualifier("defaultMQAdminExt") //@Qualifier注解的作用是指定bean名字,即使用bean的哪个实例
private DefaultMQAdminExt defaultMQAdminExt;

bean接口有多个实现类

一、通过@Service创建bean
同一个接口的实现类1

/**
* 获取集群信息
*/
@Service("clusterServiceImpl")
public class ClusterServiceImpl implements ClusterService {

同一个接口的实现类2

/**
* 获取集群信息
*/
@Service("clusterServiceImpl2") //指定bean名字
public class ClusterServiceImpl2 implements ClusterService {

二、注入bean
如果是单例,类的属性名字clusterService默认就是bean的名字,而且不需要指定名字。但是,如果是多例,就必须要指定名字,才能指定用的是哪个实例,否则会报错。

@Autowired
@Qualifier("clusterServiceImpl")
private ClusterService clusterService;
@Autowired
@Qualifier("clusterServiceImpl2")
private ClusterService clusterService;

参考

www.codenong.com/cs106056231…