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

com.taobao.drc.clusterclient.impl.DefaultPartitionManager Maven / Gradle / Ivy

There is a newer version: 5.0.0.1.beta
Show newest version
package com.taobao.drc.clusterclient.impl;

import com.alibaba.fastjson.JSON;
import com.aliyun.drc.client.Listener;
import com.taobao.drc.clusterclient.*;
import com.taobao.drc.clusterclient.clustermanager.*;
import com.taobao.drc.clusterclient.coordinator.Coordinator;
import com.taobao.drc.clusterclient.coordinator.CoordinatorManager;
import com.taobao.drc.clusterclient.partition.IPartition;
import com.taobao.drc.clusterclient.partition.PartitionRef;
import com.taobao.drc.clusterclient.partition.PartitionState;
import com.taobao.drc.clusterclient.partition.PartitionStateChangeListener;
import com.taobao.drc.clusterclient.util.Futures;
import com.taobao.drc.clusterclient.util.Time;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author yangyang
 * @since 17/6/20
 */
public class DefaultPartitionManager implements PartitionManager {
    static final int CODE_NO_ERROR = 0;
    static final int CODE_WRITE_DB_FAILURE = 440;
    static final int CODE_INVALID_GENERATION = 511;
    static final long ACTION_ID_NOOP = -1;
    static final long METRICS_SUBMISSION_PERIOD = 55 * 1000;

    private static final Logger logger = LoggerFactory.getLogger(DefaultPartitionManager.class);

    private final CoordinatorManager coordinatorManager;
    private final Coordinator coordinator;
    private final PartitionClientFactory partitionClientFactory;
    private final C context;
    private final String version;
    private String ip;
    // package-level access for test
    final Map activePartitionMap = new HashMap();
    private final List notifiers;
    private final List partitionStateChangeListeners;
    private final Time time;
    private int nextListenerIdx = 0;
    private long actionId = 0;
    private boolean submittingMetrics = false;
    private long lastMetricsSubmitMs = 0;
    private ConsumerState state = ConsumerState.STARTING;
    private volatile String seq;

    //新增
    private volatile Listener clientCtrListener = null;

    private final BlockingQueue futuresOnStopped = new LinkedBlockingDeque();

    public DefaultPartitionManager(CoordinatorManager coordinatorManager,
                                   PartitionClientFactory partitionClientFactory, C context, String version, String ip,
                                   List notifiers, List partitionStateChangeListeners,Listener clientCtrListener) {
        this(coordinatorManager, partitionClientFactory, context, version, ip, notifiers, partitionStateChangeListeners,
                clientCtrListener,
                Time.SYSTEM);
    }

    public DefaultPartitionManager(CoordinatorManager coordinatorManager,
                                   PartitionClientFactory partitionClientFactory, C context, String version, String ip,
                                   List notifiers, List partitionStateChangeListeners,
                                   Listener clientCtrListener,Time time) {
        this.coordinatorManager = coordinatorManager;
        this.coordinator = coordinatorManager.acquireCoordinator(context.getClusterUrl());
        this.partitionClientFactory = partitionClientFactory;
        this.context = context;
        this.version = version;
        this.ip = ip;
        this.notifiers = notifiers;
        this.partitionStateChangeListeners = partitionStateChangeListeners;
        this.time = time;

        this.clientCtrListener =clientCtrListener;
        logger.info("DefaultPartitionManager:version:[{}]", version);
    }

    @Override
    public ConsumerState getState() {
        return state;
    }

    public void start() throws ExecutionException, InterruptedException {
        this.coordinator.register(this).get();
    }

    public void stop() {
        // stop all clients asynchronously
        futuresOnStopped.add(coordinator.runOnEventThread(new Runnable() {
            @Override
            public void run() {
                logger.info("To stop partition manager [{}][{}]", context.getAppGuid(), context.getAppGroup());
                state = ConsumerState.STOPPING;
                for (ActivePartition activePartition : activePartitionMap.values()) {
                    switch (activePartition.getState()) {
                        case RUNNING:
                            Future future = maybeStopPartitionClient(activePartition);
                            if (future != null) {
                                futuresOnStopped.add(future);
                            }
                        default:
                            activePartition.setState(PartitionState.STOPPING);
                            break;
                    }
                }
            }
        }));
        futuresOnStopped.add(this.coordinator.deregister(this));
        futuresOnStopped.add(this.coordinatorManager.releaseCoordinator(this.coordinator));
    }

    public boolean isStopping() {
        return state == ConsumerState.STOPPING;
    }

    public void waitForStop(long timeLimitInSec) throws InterruptedException {
        long now = time.millis();
        long due = now + TimeUnit.SECONDS.toMillis(timeLimitInSec);
        Iterator iterator = futuresOnStopped.iterator();
        boolean fullyStopped = true;
        while (iterator.hasNext()) {
            if (due <= now) {
                fullyStopped = false;
                break;
            }
            try {
                iterator.next().get(due - now, TimeUnit.MILLISECONDS);
                iterator.remove();
            } catch (TimeoutException e) {
                fullyStopped = false;
                break;
            } catch (Exception e) {
                logger.error("Caught exception on stopping partition manager [{}][{}]", context.getAppGuid(),
                        context.getAppGroup(), e);
                iterator.remove();
            }
            now = time.millis();
        }
        if (!fullyStopped) {
            logger.warn("The partition manager [{}][{}][{}] has not been stopped completely within [{}] seconds",
                    context.getAppGuid(), context.getAppGroup(), seq, timeLimitInSec);
        } else {
            logger.info("The partition manager [{}][{}][{}] has been stopped completely", context.getAppGuid(),
                    context.getAppGroup(), seq);
        }
    }

    @Override
    public C getContext() {
        return context;
    }

    @Override
    public T getNextListener() {
        synchronized (this) {
            nextListenerIdx = nextListenerIdx % notifiers.size();
            return notifiers.get(nextListenerIdx++);
        }
    }

    private long getSessionTimeoutMs() {
        return coordinator.getSessionTimeoutMs();
    }

    private long getNextActionId() {
        return actionId++;
    }

    private ActivePartition getActivePartition(String partition) {
        return activePartitionMap.get(partition);
    }

    private void startClient(final String partition, final PartitionClient client, final long actionId) {
        coordinator.runOnIOPool(new Runnable() {
            @Override
            public void run() {
                try {
                    client.start();
                    coordinator.runOnEventThread(new Runnable() {
                        @Override
                        public void run() {
                            onClientStarted(partition, actionId, client, null);
                        }
                    });
                } catch (final Exception e) {
                    coordinator.runOnEventThread(new Runnable() {
                        @Override
                        public void run() {
                            logger.warn("startClinet error:" + e.getMessage());
                            for (T notifier : notifiers) {
                                logger.warn("startClinet error:notifier.onExcepion " + e.getMessage());
                                notifier.onExcepion(e);
                            }
                            onClientStarted(partition, actionId, client, e);
                        }
                    });
                }
            }
        });
    }

    private Future stopClient(final String partition, final PartitionClient client, final long actionId) {
        return coordinator.runOnIOPool(new Runnable() {
            @Override
            public void run() {
                try {
                    client.close();
                    coordinator.runOnEventThread(new Runnable() {
                        @Override
                        public void run() {
                            onClientStopped(partition, actionId, null);
                        }
                    });
                } catch (final Exception e) {
                    coordinator.runOnEventThread(new Runnable() {
                        @Override
                        public void run() {
                            onClientStopped(partition, actionId, e);
                        }
                    });
                }
            }
        });
    }

    Future maybeStopPartitionClient(final ActivePartition activePartition) {
        if (activePartition == null) {
            return Futures.success(null);
        }

        logger.info("maybeStopPartitionClient:activePartition.getState:"+activePartition.getState());
        switch (activePartition.getState()) {
            case RUNNING:
                // stop the notification
                activePartition.getClient().getNotifyController().close();
                // stop the client
                activePartition.setState(PartitionState.STOPPING);
                activePartition.setCurrentActionId(getNextActionId());

                return stopClient(activePartition.getPartition(), activePartition.getClient(),
                        activePartition.getCurrentActionId());
            case INIT:
                activePartition.setState(PartitionState.STOPPED);
                break;
            case STARTING:
                // the client will not be able to notify messages, so just rest the
                // action id
                // the newly started client will be stopped immediately
                activePartition.setCurrentActionId(ACTION_ID_NOOP);
                activePartition.setState(PartitionState.STOPPED);
                break;
            case STOPPING:
                break;
            case STOPPED:
                break;
            default:
        }
        return Futures.success(null);
    }

    private boolean isStateTimeout(ActivePartition activePartition) {
        return time.millis() - activePartition.getStateStartTime() > getSessionTimeoutMs();
    }

    public String getMessage(boolean withMetrics) {
        Map map = new TreeMap();
        List> metrics = new ArrayList>();
        for (T notifier : notifiers) {
            metrics.add(notifier.getMetrics());
        }
        logger.info("Notifier metrics for [{}][{}]: {}", context.getAppGuid(), context.getAppGroup(), metrics);
        if (withMetrics) {
            map.put("metrics", metrics);
            logger.info("DefaultPartitionManager::getMessage: [{}][{}] return :[{}]", context.getAppGuid(),
                    context.getAppGroup(), JSON.toJSONString(map));
        }
        return JSON.toJSONString(map);
    }

    @Override
    public LocalConsumerStatus getLocalConsumerStatus() {
        if (state == ConsumerState.RUNNING) {
            submittingMetrics = time.millis() - lastMetricsSubmitMs > METRICS_SUBMISSION_PERIOD;
        } else if (state == ConsumerState.STOPPING) {
            submittingMetrics = true;
        }

        LocalConsumerStatus consumerStatus = new LocalConsumerStatus(context.getAppGuid(), context.getAppGroup(),
                context.getAppGroupUserName(), context.getAppGroupPassword(), version, ip, context.getMaxConns(),
                state.toString(), getMessage(submittingMetrics));

        // partition info
        List toRemove = new ArrayList();
        for (ActivePartition activePartition : activePartitionMap.values()) {
            switch (activePartition.getState()) {
                case STOPPED:
                    toRemove.add(activePartition);
                    continue;
                case RUNNING:
                    if (!activePartition.getClient().isActive()) {
                        logger.warn("The client for {} is not active", activePartition);
                        maybeStopPartitionClient(activePartition);
                    } else if (activePartition.getClient().getNotifyController().isClosed()) {
                        logger.warn("The notify controller for {} is closed", activePartition);
                        maybeStopPartitionClient(activePartition);
                    }
                    break;
                default:
                    if (isStateTimeout(activePartition)) {
                        logger.error("DefaultPartitionManager State timeout for partition {}: action started at [{}]",
                                activePartition, activePartition.getStateStartTime());
                        activePartition.setState(PartitionState.STOPPED);
                    }
                    break;
            }
            String offset = activePartition.offset();
            if (offset == null) {
                logger.info("Ignore partition {} the offset of which is still null", activePartition);
                continue;
            }

            consumerStatus.addPartition(
                    new LocalPartitionStatus(activePartition.getPartition(), activePartition.getGeneration(), offset,
                            activePartition.getState().name(), activePartition.getMessage(submittingMetrics)));
        }
        for (ActivePartition activePartition : toRemove) {
            logger.info("Partition {} has been stopped completely, remove it from active list", activePartition);
            activePartitionMap.remove(activePartition.getPartition());
        }
        logger.info(
                "DefaultPartitionManager::getLocalConsumerStatus:state:[{}],AppGuid:[{}],AppGroup:[{}],AppGroupUserName:[{}],version:[{}],ip:[{}],activePartitionMap size:[{}]",
                state, context.getAppGuid(), context.getAppGroup(), context.getAppGroupUserName(), version, ip,
                activePartitionMap.size());
        for (ActivePartition activePartition : activePartitionMap.values()) {
            logger.info("DefaultPartitionManager::getLocalConsumerStatus: activePartition:[{}]", activePartition);
        }

        return consumerStatus;
    }

    private boolean isUpgradeRequired(String expectedVersionStr, String currentVersionStr) {
        if (expectedVersionStr == null || currentVersionStr == null) {
            return false;
        }
        try {
            Version expected = new Version(expectedVersionStr);
            Version current = new Version(currentVersionStr);
            if (expected.compareTo(current) > 0) {
                return true;
            } else {
                return false;
            }
        } catch (Exception e) {
            logger.warn("Invalid version, current [{}], expected [{}]", currentVersionStr, expectedVersionStr);
            return false;
        }
    }

    private int getActivePartitionNum() {
        return activePartitionMap.size();
    }

    @Override
    public Map onCommitComplete(ExpectedConsumerStatus expectedConsumerStatus,
                                                    Throwable throwable) {
        if (isStopping()) {
            return Collections.emptyMap();
        }
        long now = time.millis();
        if (throwable == null) {//无异常抛出
            logger.info(
                    "DefaultPartitionManager::onCommitComplete enter:expectedConsumerStatus.getExpectedPartitionStatusList:size:[{}]",
                    expectedConsumerStatus.getExpectedPartitionStatusList().size());
            if (state == ConsumerState.STARTING && expectedConsumerStatus.isNewSeqAllocated()) {//首次启动
                state = ConsumerState.RUNNING;
                this.seq = expectedConsumerStatus.getAllocatedSeq();
            }
            if (expectedConsumerStatus.getErrCode() != CODE_NO_ERROR) {
                logger.error("Failed to commit for [{}][{}], error code [{}], msg: [{}]", context.getAppGuid(),
                        context.getAppGroup(), expectedConsumerStatus.getErrCode(), expectedConsumerStatus.getErrMsg());
                return Collections.emptyMap();
            }
            if (submittingMetrics) {
                lastMetricsSubmitMs = time.millis();
            }
            if (isUpgradeRequired(expectedConsumerStatus.getVersion(), version)) {
                logger.warn("Please update to the version [{}], current version is [{}]",
                        expectedConsumerStatus.getVersion(), version);
            }
            if (expectedConsumerStatus.getIp() != null && !StringUtils.equals(expectedConsumerStatus.getIp(), ip)) {
                logger.info("Changed ip of [{}] from [{}] to [{}] according to response",
                        expectedConsumerStatus.getSeq(), ip, expectedConsumerStatus.getIp());
                this.ip = expectedConsumerStatus.getIp();
            }

            Map newPartitions = new HashMap();
            //Partition信息更新
            for (ExpectedPartitionStatus expectedPartitionStatus : expectedConsumerStatus
                    .getExpectedPartitionStatusList()) {
                ActivePartition activePartition = getActivePartition(expectedPartitionStatus.getPartition());

                // handle invalid partition
                switch (expectedPartitionStatus.getErrCode()) {
                    case CODE_NO_ERROR:
                        break;
                    case CODE_WRITE_DB_FAILURE:
                        logger.warn("Cluster manager failed to commit checkpoint of [{}][{}][{}] on [{}] to database",
                                context.getAppGuid(), context.getAppGroup(), expectedPartitionStatus.getPartition(),
                                expectedConsumerStatus.getSeq());
                        break;
                    case CODE_INVALID_GENERATION:
                        if (activePartition != null) {
                            handleInvalidGeneration(activePartition, expectedPartitionStatus);
                            newPartitions.put(activePartition.getRef(), expectedPartitionStatus.getGeneration());
                        }
                        break;
                    default:
                        logger.warn("Got error for partition [{}][{}][{}][{}]: [{}][{}]", context.getAppGuid(),
                                context.getAppGroup(), expectedPartitionStatus.getPartition(),
                                expectedPartitionStatus.getGeneration(), expectedPartitionStatus.getErrCode(),
                                expectedPartitionStatus.getErrMsg());

                        maybeStopPartitionClient(activePartition);
                        continue;
                }

                if (activePartition == null) {//如果还无Active Partition
                    if (getActivePartitionNum() >= context.getMaxConns()) {
                        logger.warn(
                                "DefaultPartitionManager Active partition number has reached [{}], do not start partition [{}][{}][{}][{}]",
                                context.getMaxConns(), context.getAppGuid(), context.getAppGroup(),
                                expectedPartitionStatus.getPartition(), expectedPartitionStatus.getGeneration());
                        continue;
                    }
                    // start new partition if possible
                    logger.warn(
                            "onCommitComplete:Got new partition [{}][{}][{}] on [{}]: generation [{}], offset: [{}]",
                            context.getAppGuid(), context.getAppGroup(), expectedPartitionStatus.getPartition(), seq,
                            expectedPartitionStatus.getGeneration(), expectedPartitionStatus.getOffset());
                    activePartition = new ActivePartition(this, expectedPartitionStatus.getPartition(), time,
                            partitionStateChangeListeners);
                    activePartition.setGeneration(expectedPartitionStatus.getGeneration());
                    activePartition.setInitOffset(expectedPartitionStatus.getOffset());
                    activePartitionMap.put(expectedPartitionStatus.getPartition(), activePartition);

                    newPartitions.put(activePartition.getRef(), expectedPartitionStatus.getGeneration());
                } else {//存在Active的Partition
                    // partition is still valid, extend the lease
                    switch (activePartition.getState()) {
                        case RUNNING:
                            activePartition.extendLease(getSessionTimeoutMs());
                            break;
                        default:
                            // nothing to do,此处可能有问题
                            logger.warn("need check activePartition:{}", activePartition);
                    }
                }
                activePartition.setLastSuccessCommitAt(now);
            }
            return newPartitions;
        } else {
            // commit failed, the notify lease should be checked and extended
            for (ActivePartition activePartition : activePartitionMap.values()) {
                if (activePartition.getState() != PartitionState.RUNNING) {
                    logger.info("Failed to commit for partition {}, while the state of the partition is [{}]",
                            activePartition, activePartition.getState());
                    continue;
                }
                // check for out-dated partitions
                if (!activePartition.getClient().getNotifyController().isValid()) {
                    logger.warn(
                            "The running partition {} has not committed successfully since [{}], exceeded session timeout [{}] for [{}] ms",
                            activePartition, activePartition.getLastSuccessCommitAt(), getSessionTimeoutMs(),
                            now - activePartition.getLastSuccessCommitAt() - getSessionTimeoutMs());
                    // check notification status
                    NotifyController notifyController = activePartition.getClient().getNotifyController();
                    if (!notifyController.isClosed() && (notifyController.isNotifying()
                            || (now - notifyController.getLastNotifiedMs() < getSessionTimeoutMs()))) {
                        long targetMs = now + getSessionTimeoutMs();
                        logger.info(
                                "The client for partition {} is still notifying messages, last notified at [{}], extends the lease to [{}]",
                                activePartition, notifyController.getLastNotifiedMs(), targetMs);
                        notifyController.extendLeaseTo(targetMs);
                    } else {
                        logger.warn(
                                "The partition {} is not notifying message, and no message has arrived since [{}], stop the client",
                                activePartition, notifyController.getLastNotifiedMs());
                        maybeStopPartitionClient(activePartition);
                    }
                } else {
                    logger.info("Failed to commit for partition {}, while the partition is still valid for consuming",
                            activePartition);
                    activePartition.getClient().getNotifyController().close();
                }
            }
            return Collections.emptyMap();
        }
    }

    private void handleInvalidGeneration(ActivePartition activePartition,
                                         ExpectedPartitionStatus expectedPartitionStatus) {
        logger.warn("Generation of [{}][{}][{}] changed: [{}] -> [{}], offset: [{}]", context.getAppGuid(),
                context.getAppGroup(), expectedPartitionStatus.getPartition(), activePartition.getGeneration(),
                expectedPartitionStatus.getGeneration(), expectedPartitionStatus.getOffset());

        switch (activePartition.getState()) {
            case RUNNING:
                // generation changed, stop the client
                maybeStopPartitionClient(activePartition);

                activePartition.setPartitionInfo(null);
                activePartition.setClient(null);
                break;
            default:
                // reset the action id
                activePartition.setCurrentActionId(ACTION_ID_NOOP);
                break;
        }

        activePartition.setGeneration(expectedPartitionStatus.getGeneration());
        activePartition.setInitOffset(expectedPartitionStatus.getOffset());
        activePartition.setState(PartitionState.INIT);
    }

    @Override
    public void onGetPartitionInfoComplete(Map partitionInfoMap,
                                           Map generationMap, Throwable throwable) {
        if (isStopping()) {
            return;
        }
        if (throwable == null) {
            for (Map.Entry entry : partitionInfoMap.entrySet()) {
                Long generation = generationMap.get(entry.getKey());
                PartitionInfo partitionInfo = entry.getValue();

                ActivePartition activePartition = getActivePartition(partitionInfo.getPartition());
                if (activePartition == null) {
                    logger.warn("Got info of partition [{}][{}] while active partition not exists",
                            partitionInfo.getPartition(), generation);
                    continue;
                }
                if (generation == null || activePartition.getGeneration() != generation) {
                    logger.warn(
                            "Got info of partition [{}] of generation [{}], while current generation of the partition is [{}]",
                            partitionInfo.getPartition(), generation, activePartition.getGeneration());
                    continue;
                }
                if (activePartition.getPartitionInfo() == null) {
                    if (partitionInfo.getErrCode() != CODE_NO_ERROR) {
                        logger.error("Got invalid partition info [{}][{}] for [{}][{}]", partitionInfo.getErrCode(),
                                partitionInfo.getErrMsg(), partitionInfo.getPartition(), generation);
                        // remove the partition from active partition map
                        activePartitionMap.remove(activePartition.getPartition());
                        continue;
                    }
                    if (!StringUtils.equals(partitionInfo.getGuid(), context.getAppGuid())) {
                        logger.error("Got invalid guid from the partition info [{}], expected [{}], group [{}]",
                                partitionInfo, context.getAppGuid(), context.getAppGroup());
                        activePartitionMap.remove(activePartition.getPartition());
                        continue;
                    }
                    activePartition.setPartitionInfo(partitionInfo);
                    try {
                        activePartition.setClient(partitionClientFactory.create(context, partitionInfo,
                                getNextListener(), activePartition.getInitOffset(),clientCtrListener));
                    } catch (Exception e) {
                        // cause error:has no tables?
                        // reason:partitionInfo 变量: tables 为空
                        logger.error("Failed to create client for partition [{}], mark the partition as stopped",
                                activePartition, e);
                        activePartition.setState(PartitionState.STOPPED);
                        continue;
                    }
                    activePartition.setState(PartitionState.STARTING);
                    activePartition.setCurrentActionId(getNextActionId());
                    startClient(activePartition.getPartition(), activePartition.getClient(),
                            activePartition.getCurrentActionId());
                } else {
                    logger.warn("Partition info has already been set for {}", activePartition);
                }
            }
        } else {
            logger.error("Failed to get partition info for {}", generationMap, throwable);
            // remove corresponding partitions that is at INIT state
            for (Map.Entry entry : generationMap.entrySet()) {
                ActivePartition activePartition = getActivePartition(entry.getKey().getPartition());
                if (activePartition.getGeneration() == entry.getValue()
                        && activePartition.getState() == PartitionState.INIT) {
                    activePartitionMap.remove(activePartition.getPartition());
                    logger.error("Removed partition [{}][{}] due to getting partition info failure",
                            activePartition.getPartition(), activePartition.getGeneration(), throwable);
                }
            }
        }
    }

    void onClientStarted(String partition, long actionId, PartitionClient client, Throwable throwable) {
        logger.info("onClientStarted");
        if (throwable == null) {
            logger.info("Client for partition [{}][{}][{}] started, action id [{}]", context.getAppGuid(),
                    context.getAppGroup(), partition, actionId);
        } else {
            logger.warn("Failed to start client for partition [{}][{}][{}], action id [{}]", context.getAppGuid(),
                    context.getAppGroup(), partition, actionId, throwable);
        }

        // if not running, stop the successfully started client
        if (isStopping()) {
            if (throwable == null) {
                logger.warn(
                        "Client for partition [{}][{}][{}], action id [{}] started while the partition manager is not running",
                        context.getAppGuid(), context.getAppGroup(), partition, actionId);
                stopClient(partition, client, ACTION_ID_NOOP);
                return;
            } else {
                return;
            }
        }

        // if expected partition not exists, stop the successfully started
        // client
        ActivePartition activePartition = getActivePartition(partition);
        if (activePartition == null) {
            if (throwable == null) {
                logger.warn("Client started while no active partition [{}][{}][{}] exists, action id [{}]",
                        context.getAppGuid(), context.getAppGroup(), partition, actionId);
                stopClient(partition, client, ACTION_ID_NOOP);
                return;
            } else {
                logger.warn("Client started failed while no active partition [{}][{}][{}] exists, action id [{}]",
                        context.getAppGuid(), context.getAppGroup(), partition, actionId, throwable);
                return;
            }
        }

        // if the client is started successfully, advance the partition state to
        // RUNNING if and only if
        // 1. the action id of started client matches the expected action id of
        // the current active partition
        // 2. the state of the current active partition is STARTING
        // otherwise, stop the client and set the state of the partition to
        // STOPPED
        if (throwable == null) {
            switch (activePartition.getState()) {
                case STARTING:
                    if (activePartition.getCurrentActionId() == actionId) {
                        // generation matches
                        activePartition.setState(PartitionState.RUNNING);
                        activePartition.setClient(client);
                        activePartition.setCurrentActionId(ACTION_ID_NOOP);

                        // extend the lease to receive message from the started
                        // client
                        activePartition.extendLease(getSessionTimeoutMs());
                        break;
                    } else {
                        // action id does not match, just stop the client
                        logger.error("Client started for partition {}, but the action id does not match [{}]",
                                activePartition, actionId);
                        stopClient(partition, client, ACTION_ID_NOOP);
                        break;
                    }
                default:
                    logger.error("Client started for partition {}, but the partition is in an invalid state [{}]",
                            activePartition, activePartition.getState());
                    stopClient(partition, client, actionId);
                    activePartition.setState(PartitionState.STOPPING);
                    break;
            }
        } else {
            if (activePartition.getCurrentActionId() == actionId) {
                logger.error("Failed to start client for partition {}", activePartition, throwable);
                activePartition.setCurrentActionId(ACTION_ID_NOOP);
                activePartition.setState(PartitionState.STOPPED);
            } else {
                logger.error(
                        "Failed to start client for [{}][{}][{}], action id [{}], while the current action id is [{}], ignore",
                        context.getAppGuid(), context.getAppGroup(), partition, actionId,
                        activePartition.getCurrentActionId());
            }
        }
    }

    void onClientStopped(String partition, long actionId, Throwable throwable) {
        if (throwable == null) {
            logger.info("Client for partition [{}][{}][{}] stopped, action id [{}]", context.getAppGuid(),
                    context.getAppGroup(), partition, actionId);
            //调整状态(世辉)
            state =ConsumerState.STOPPING;
        } else {
            logger.warn("Failed to stop client for partition [{}][{}][{}], action id [{}]", context.getAppGuid(),
                    context.getAppGroup(), partition, actionId, throwable);
        }

        if (isStopping()) {
            logger.info("isStopping");
            return;
        }

        ActivePartition activePartition = getActivePartition(partition);
        if (activePartition == null) {
            logger.warn("No active partition for [{}] exists, action id [{}]", partition, actionId);
            return;
        }

        if (activePartition.getState() == PartitionState.STOPPING && activePartition.getCurrentActionId() == actionId) {
            activePartition.setState(PartitionState.STOPPED);
            activePartition.setCurrentActionId(ACTION_ID_NOOP);
            activePartition.setClient(null);


        } else {
            logger.error("Partition [{}] is not in expected state, action id [{}]", activePartition, actionId);
        }
    }

    static class Version implements Comparable {
        private static final Pattern VERSION_PATTERN = Pattern.compile("^(\\d+)\\.(\\d+)\\.(\\d+).*");

        private final int majorVersion;
        private final int minorVersion;
        private final int revisionNumber;

        public Version(String versionStr) {
            Matcher matcher = VERSION_PATTERN.matcher(versionStr);
            if (matcher.matches()) {
                majorVersion = Integer.parseInt(matcher.group(1));
                minorVersion = Integer.parseInt(matcher.group(2));
                revisionNumber = Integer.parseInt(matcher.group(3));
            } else {
                majorVersion = 0;
                minorVersion = 0;
                revisionNumber = 0;
            }
        }

        public int getMajorVersion() {
            return majorVersion;
        }

        public int getMinorVersion() {
            return minorVersion;
        }

        public int getRevisionNumber() {
            return revisionNumber;
        }

        @Override
        public int compareTo(Version that) {
            if (this.majorVersion != that.majorVersion) {
                return Integer.signum(this.majorVersion - that.majorVersion);
            }
            if (this.minorVersion != that.minorVersion) {
                return Integer.signum(this.minorVersion - that.minorVersion);
            }
            if (this.revisionNumber != that.revisionNumber) {
                return Integer.signum(this.revisionNumber - that.revisionNumber);
            }
            return 0;
        }
    }

    static class ActivePartition {
        private final PartitionManager partitionManager;
        private final String partition;
        private final List stateChangeListeners;
        private final PartitionRef ref;
        private final Time time;
        private PartitionInfo partitionInfo;
        private long generation;
        private String initOffset;
        private PartitionState state = PartitionState.INIT;
        private long stateStartTime;
        private PartitionClient client;
        private long lastSuccessCommitAt;
        private long currentActionId = ACTION_ID_NOOP;
        private String message;

        public ActivePartition(PartitionManager partitionManager, String partition, Time time) {
            this(partitionManager, partition, time, Collections.emptyList());
        }

        public ActivePartition(PartitionManager partitionManager, String partition, Time time,
                               List stateChangeListeners) {
            this.partitionManager = partitionManager;
            this.partition = partition;
            this.time = time;
            this.stateChangeListeners = stateChangeListeners;
            this.ref = new PartitionRef(partitionManager.getContext().getAppGuid(),
                    partitionManager.getContext().getAppGroup(), partition);
            setState(PartitionState.INIT);
        }

        public String getPartition() {
            return partition;
        }

        public PartitionRef getRef() {
            return ref;
        }

        public PartitionInfo getPartitionInfo() {
            return partitionInfo;
        }

        public void setPartitionInfo(PartitionInfo partitionInfo) {
            this.partitionInfo = partitionInfo;
        }

        public long getGeneration() {
            return generation;
        }

        public void setGeneration(long generation) {
            this.generation = generation;
        }

        public String getInitOffset() {
            return initOffset;
        }

        public void setInitOffset(String initOffset) {
            this.initOffset = initOffset;
        }

        public String offset() {
            if (client != null) {
                return client.offset();
            }
            return null;
        }

        public PartitionState getState() {
            return state;
        }

        public void setState(PartitionState state) {
            this.state = state;
            this.stateStartTime = time.millis();
            if (!stateChangeListeners.isEmpty()) {
                // whether we should call listeners in a dedicated thread?
                for (PartitionStateChangeListener partitionStateChangeListener : stateChangeListeners) {
                    IPartition iPartition = null;
                    if (client != null) {
                        iPartition = client.getPartition();
                    }
                    partitionStateChangeListener.onStateChanged(getRef(), state, iPartition);
                }
            }
        }

        public long getStateStartTime() {
            return stateStartTime;
        }

        public long getLastSuccessCommitAt() {
            return lastSuccessCommitAt;
        }

        public void setLastSuccessCommitAt(long lastSuccessCommitAt) {
            this.lastSuccessCommitAt = lastSuccessCommitAt;
        }

        public void setCurrentActionId(long currentActionId) {
            this.currentActionId = currentActionId;
        }

        public long getCurrentActionId() {
            return currentActionId;
        }

        public PartitionClient getClient() {
            return client;
        }

        public long extendLease(long sessionTimeoutMs) {
            long toMs = getLastSuccessCommitAt() + sessionTimeoutMs;
            if (getClient() != null) {
                logger.info("To extend lease of partition [{}] to [{}]", getPartition(), toMs);
                if (getClient().getNotifyController().isClosed()) {
                    logger.info("getNotifyController is closed,then open");
                    getClient().getNotifyController().open();
                }
                getClient().getNotifyController().extendLeaseTo(toMs);
            } else {
                logger.error("No client for partition {} to extend the lease", this);
            }
            return toMs;
        }

        public void setClient(PartitionClient client) {
            this.client = client;
        }

        public String getMessage(boolean withMetrics) {
            Map map = new HashMap();
            if (client != null) {
                Map metrics = client.getMetrics();
                logger.info("Partition {} metrics: {}", ref, metrics);
                if (withMetrics) {
                    map.put("metrics", client.getMetrics());
                }
            }
            if (message != null) {
                map.put("message", message);
            }
            return JSON.toJSONString(map);
        }

        public void setMessage(String message) {
            this.message = message;
        }

        @Override
        public String toString() {
            return "[" + partitionManager.getContext().getAppGuid() + "][" + partitionManager.getContext().getAppGroup()
                    + "][" + getPartition() + "][" + getGeneration() + "][" + getState() + "]";
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy