All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alibaba.rocketmq.broker.offset.ConsumerOffsetManager Maven / Gradle / Ivy

The newest version!
/**
 * 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.offset;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
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.broker.BrokerController;
import com.alibaba.rocketmq.broker.BrokerPathConfigHelper;
import com.alibaba.rocketmq.common.ConfigManager;
import com.alibaba.rocketmq.common.UtilAll;
import com.alibaba.rocketmq.common.constant.LoggerName;
import com.alibaba.rocketmq.remoting.protocol.RemotingSerializable;


/**
 * Consumer消费进度管理
 * 
 * @author shijia.wxr
 * @since 2013-8-11
 */
public class ConsumerOffsetManager extends ConfigManager {
    private static final Logger log = LoggerFactory.getLogger(LoggerName.BrokerLoggerName);
    private static final String TOPIC_GROUP_SEPARATOR = "@";

    private ConcurrentHashMap> offsetTable =
            new ConcurrentHashMap>(512);

    private transient BrokerController brokerController;


    public ConsumerOffsetManager() {
    }


    public ConsumerOffsetManager(BrokerController brokerController) {
        this.brokerController = brokerController;
    }


    /**
     * 扫描数据被删除了的topic,offset记录也对应删除
     */
    public void scanUnsubscribedTopic() {
        Iterator>> it = this.offsetTable.entrySet().iterator();
        while (it.hasNext()) {
            Entry> next = it.next();
            String topicAtGroup = next.getKey();
            String[] arrays = topicAtGroup.split(TOPIC_GROUP_SEPARATOR);
            if (arrays != null && arrays.length == 2) {
                String topic = arrays[0];
                String group = arrays[1];
                // 当前订阅关系里面没有group-topic订阅关系(消费端当前是停机的状态)并且offset落后很多,则删除消费进度
                if (null == brokerController.getConsumerManager().findSubscriptionData(group, topic)
                        && this.offsetBehindMuchThanData(topic, next.getValue())) {
                    it.remove();
                    log.warn("remove topic offset, {}", topicAtGroup);
                }
            }
        }
    }


    private boolean offsetBehindMuchThanData(final String topic, ConcurrentHashMap table) {
        Iterator> it = table.entrySet().iterator();
        boolean result = !table.isEmpty();

        while (it.hasNext() && result) {
            Entry next = it.next();
            long minOffsetInStore =
                    this.brokerController.getMessageStore().getMinOffsetInQuque(topic, next.getKey());
            long offsetInPersist = next.getValue();
            if (offsetInPersist > minOffsetInStore) {
                result = false;
            }
        }

        return result;
    }


    public Set whichTopicByConsumer(final String group) {
        Set topics = new HashSet();

        Iterator>> it = this.offsetTable.entrySet().iterator();
        while (it.hasNext()) {
            Entry> next = it.next();
            String topicAtGroup = next.getKey();
            String[] arrays = topicAtGroup.split(TOPIC_GROUP_SEPARATOR);
            if (arrays != null && arrays.length == 2) {
                if (group.equals(arrays[1])) {
                    topics.add(arrays[0]);
                }
            }
        }

        return topics;
    }


    public Set whichGroupByTopic(final String topic) {
        Set groups = new HashSet();

        Iterator>> it = this.offsetTable.entrySet().iterator();
        while (it.hasNext()) {
            Entry> next = it.next();
            String topicAtGroup = next.getKey();
            String[] arrays = topicAtGroup.split(TOPIC_GROUP_SEPARATOR);
            if (arrays != null && arrays.length == 2) {
                if (topic.equals(arrays[0])) {
                    groups.add(arrays[1]);
                }
            }
        }

        return groups;
    }


    public void commitOffset(final String group, final String topic, final int queueId, final long offset) {
        // topic@group
        String key = topic + TOPIC_GROUP_SEPARATOR + group;
        this.commitOffset(key, queueId, offset);
    }


    public long queryOffset(final String group, final String topic, final int queueId) {
        // topic@group
        String key = topic + TOPIC_GROUP_SEPARATOR + group;
        ConcurrentHashMap map = this.offsetTable.get(key);
        if (null != map) {
            Long offset = map.get(queueId);
            if (offset != null)
                return offset;
        }

        return -1;
    }


    private void commitOffset(final String key, final int queueId, final long offset) {
        ConcurrentHashMap map = this.offsetTable.get(key);
        if (null == map) {
            map = new ConcurrentHashMap(32);
            map.put(queueId, offset);
            this.offsetTable.put(key, map);
        }
        else {
            map.put(queueId, offset);
        }
    }


    public String encode() {
        return this.encode(false);
    }


    public String encode(final boolean prettyFormat) {
        return RemotingSerializable.toJson(this, prettyFormat);
    }


    @Override
    public void decode(String jsonString) {
        if (jsonString != null) {
            ConsumerOffsetManager obj =
                    RemotingSerializable.fromJson(jsonString, ConsumerOffsetManager.class);
            if (obj != null) {
                this.offsetTable = obj.offsetTable;
            }
        }
    }


    @Override
    public String configFilePath() {
        return BrokerPathConfigHelper.getConsumerOffsetPath(this.brokerController.getMessageStoreConfig()
            .getStorePathRootDir());
    }


    public ConcurrentHashMap> getOffsetTable() {
        return offsetTable;
    }


    public void setOffsetTable(ConcurrentHashMap> offsetTable) {
        this.offsetTable = offsetTable;
    }


    public Map queryMinOffsetInAllGroup(final String topic, final String filterGroups) {

        Map queueMinOffset = new HashMap();
        Set topicGroups = this.offsetTable.keySet();
        if (!UtilAll.isBlank(filterGroups)) {
            for (String group : filterGroups.split(",")) {
                Iterator it = topicGroups.iterator();
                while (it.hasNext()) {
                    if (group.equals(it.next().split(TOPIC_GROUP_SEPARATOR)[1])) {
                        it.remove();
                    }
                }
            }
        }
        for (String topicGroup : topicGroups) {
            String[] topicGroupArr = topicGroup.split(TOPIC_GROUP_SEPARATOR);
            if (topic.equals(topicGroupArr[0])) {
                for (Entry entry : this.offsetTable.get(topicGroup).entrySet()) {
                    long minOffset =
                            this.brokerController.getMessageStore()
                                .getMinOffsetInQuque(topic, entry.getKey());
                    if (entry.getValue() >= minOffset) {
                        Long offset = queueMinOffset.get(entry.getKey());
                        if (offset == null) {
                            queueMinOffset.put(entry.getKey(), Math.min(Long.MAX_VALUE, entry.getValue()));
                        }
                        else {
                            queueMinOffset.put(entry.getKey(), Math.min(entry.getValue(), offset));
                        }
                    }
                }
            }
        }
        return queueMinOffset;
    }


    public Map queryOffset(final String group, final String topic) {
        // topic@group
        String key = topic + TOPIC_GROUP_SEPARATOR + group;
        return this.offsetTable.get(key);
    }


    public void cloneOffset(final String srcGroup, final String destGroup, final String topic) {
        ConcurrentHashMap offsets =
                this.offsetTable.get(topic + TOPIC_GROUP_SEPARATOR + srcGroup);
        if (offsets != null) {
            this.offsetTable.put(topic + TOPIC_GROUP_SEPARATOR + destGroup, offsets);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy