/**
* 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;
}
}