/** * Copyright (C) 2010-2013 Alibaba Group Holding Limited * * Licensed 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 com.alibaba.rocketmq.broker.client; import io.netty.channel.Channel; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.rocketmq.common.constant.LoggerName; import com.alibaba.rocketmq.common.consumer.ConsumeFromWhere; import com.alibaba.rocketmq.common.protocol.heartbeat.ConsumeType; import com.alibaba.rocketmq.common.protocol.heartbeat.MessageModel; import com.alibaba.rocketmq.common.protocol.heartbeat.SubscriptionData; /** * 整个Consumer Group信息 * * @author shijia.wxr<vintage.wang@gmail.com> * @since 2013-7-26 */ public class ConsumerGroupInfo { private static final Logger log = LoggerFactory.getLogger(LoggerName.BrokerLoggerName); private final String groupName; private final ConcurrentHashMap<String/* Topic */, SubscriptionData> subscriptionTable = new ConcurrentHashMap<String, SubscriptionData>(); private final ConcurrentHashMap<Channel, ClientChannelInfo> channelInfoTable = new ConcurrentHashMap<Channel, ClientChannelInfo>(16); private volatile ConsumeType consumeType; private volatile MessageModel messageModel; private volatile ConsumeFromWhere consumeFromWhere; private volatile long lastUpdateTimestamp = System.currentTimeMillis(); public ConsumerGroupInfo(String groupName, ConsumeType consumeType, MessageModel messageModel, ConsumeFromWhere consumeFromWhere) { this.groupName = groupName; this.consumeType = consumeType; this.messageModel = messageModel; this.consumeFromWhere = consumeFromWhere; } public ConcurrentHashMap<String, SubscriptionData> getSubscriptionTable() { return subscriptionTable; } public ConcurrentHashMap<Channel, ClientChannelInfo> getChannelInfoTable() { return channelInfoTable; } public List<Channel> getAllChannel() { List<Channel> result = new ArrayList<Channel>(); result.addAll(this.channelInfoTable.keySet()); return result; } public List<String> getAllClientId() { List<String> result = new ArrayList<String>(); Iterator<Entry<Channel, ClientChannelInfo>> it = this.channelInfoTable.entrySet().iterator(); while (it.hasNext()) { Entry<Channel, ClientChannelInfo> entry = it.next(); ClientChannelInfo clientChannelInfo = entry.getValue(); result.add(clientChannelInfo.getClientId()); } return result; } public void unregisterChannel(final ClientChannelInfo clientChannelInfo) { ClientChannelInfo old = this.channelInfoTable.remove(clientChannelInfo.getChannel()); if (old != null) { log.info("unregister a consumer[{}] from consumerGroupInfo {}", this.groupName, old.toString()); } } public void doChannelCloseEvent(final String remoteAddr, final Channel channel) { final ClientChannelInfo info = this.channelInfoTable.remove(channel); if (info != null) { log.warn( "NETTY EVENT: remove not active channel[{}] from ConsumerGroupInfo groupChannelTable, consumer group: {}", info.toString(), groupName); } } /** * 返回值表示是否发生变更 */ public boolean updateChannel(final ClientChannelInfo clientChannelInfo, ConsumeType consumeType, MessageModel messageModel, ConsumeFromWhere consumeFromWhere) { boolean updated = false; this.consumeType = consumeType; this.messageModel = messageModel; this.consumeFromWhere = consumeFromWhere; ClientChannelInfo info = this.channelInfoTable.get(clientChannelInfo.getChannel()); if (null == info) { ClientChannelInfo prev = this.channelInfoTable.put(clientChannelInfo.getChannel(), clientChannelInfo); if (null == prev) { log.info("new consumer connected, group: {} {} {} channel: {}", this.groupName, consumeType, messageModel, clientChannelInfo.toString()); updated = true; } info = clientChannelInfo; } this.lastUpdateTimestamp = System.currentTimeMillis(); info.setLastUpdateTimestamp(this.lastUpdateTimestamp); return updated; } /** * 返回值表示是否发生变更 */ public boolean updateSubscription(final Set<SubscriptionData> subList) { boolean updated = false; // 增加新的订阅关系 for (SubscriptionData sub : subList) { SubscriptionData old = this.subscriptionTable.get(sub.getTopic()); if (old == null) { /* chen.si 增加了新的订阅关系,比如 订阅了新的topic */ SubscriptionData prev = this.subscriptionTable.put(sub.getTopic(), sub); if (null == prev) { updated = true; log.info("subscription changed, add new topic, group: {} {}", this.groupName, sub.toString()); } } else if (sub.getSubVersion() > old.getSubVersion()) { /* chen.si 更新了订阅关系,比如重新subscribe,更新了tag信息 */ if (this.consumeType == ConsumeType.CONSUME_PASSIVELY) { log.info("subscription changed, group: {} OLD: {} NEW: {}", // this.groupName,// old.toString(),// sub.toString()// ); } this.subscriptionTable.put(sub.getTopic(), sub); } } // 删除老的订阅关系 Iterator<Entry<String, SubscriptionData>> it = this.subscriptionTable.entrySet().iterator(); while (it.hasNext()) { Entry<String, SubscriptionData> next = it.next(); String oldTopic = next.getKey(); boolean exist = false; for (SubscriptionData sub : subList) { if (sub.getTopic().equals(oldTopic)) { exist = true; break; } } if (!exist) { log.warn("subscription changed, group[{}] remove topic[{} {}], subList[{}]", // this.groupName,// oldTopic,// next.getValue().toString(),// subList); it.remove(); updated = true; } } this.lastUpdateTimestamp = System.currentTimeMillis(); return updated; } public SubscriptionData findSubscriptionData(final String topic) { return this.subscriptionTable.get(topic); } public ConsumeType getConsumeType() { return consumeType; } public void setConsumeType(ConsumeType consumeType) { this.consumeType = consumeType; } public MessageModel getMessageModel() { return messageModel; } public void setMessageModel(MessageModel messageModel) { this.messageModel = messageModel; } public String getGroupName() { return groupName; } public long getLastUpdateTimestamp() { return lastUpdateTimestamp; } public void setLastUpdateTimestamp(long lastUpdateTimestamp) { this.lastUpdateTimestamp = lastUpdateTimestamp; } public ConsumeFromWhere getConsumeFromWhere() { return consumeFromWhere; } public void setConsumeFromWhere(ConsumeFromWhere consumeFromWhere) { this.consumeFromWhere = consumeFromWhere; } }