Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.taobao.drc.clusterclient.impl.DefaultPartitionManager Maven / Gradle / Ivy
Go to download
The java consumer core component for Data Transmission Service
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() + "]";
}
}
}