
com.huaweicloud.dws.client.worker.ActionExecutor Maven / Gradle / Ivy
package com.huaweicloud.dws.client.worker;
import com.huaweicloud.dws.client.DwsConfig;
import com.huaweicloud.dws.client.action.AbstractAction;
import com.huaweicloud.dws.client.action.PutAction;
import com.huaweicloud.dws.client.action.SqlAction;
import com.huaweicloud.dws.client.exception.DwsClientException;
import com.huaweicloud.dws.client.exception.ExceptionCode;
import com.huaweicloud.dws.client.handler.AbstractActionHandler;
import com.huaweicloud.dws.client.handler.PutActionHandler;
import com.huaweicloud.dws.client.handler.SqlActionHandler;
import com.huaweicloud.dws.client.util.AssertUtil;
import com.huaweicloud.dws.client.util.LogUtil;
import lombok.extern.slf4j.Slf4j;
import java.io.Closeable;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
/**
* @ProjectName: dws-connector
* @ClassName: ActionExecutor
* @Description: 事件执行者
* @Date: 2023/1/9 18:56
* @Version: 1.0
*/
@Slf4j
public class ActionExecutor implements Runnable, Closeable {
public static final Set RETRY_EXCEPTION_CODES = new HashSet<>(Arrays.asList(ExceptionCode.CONNECTION_ERROR, ExceptionCode.READ_ONLY, ExceptionCode.TIMEOUT, ExceptionCode.TOO_MANY_CONNECTIONS, ExceptionCode.LOCK_ERROR));
public static final int MAX_AWAIT_ACTION_TIME = 2;
private static final Random SLEEP_RANDOM = new Random();
/**
* 感知是否被关闭
*/
private final AtomicBoolean started;
/**
* 记录当前正在被执行的action,使用生产消费模式
*/
private final ActionWrapper actionWrapper = new ActionWrapper<>();
/**
* 每个并发一个数据库连接
*/
private final ConnectionProvider connectionProvider;
private final DwsConfig config;
/**
* 记录最后执行的异常
*/
private final AtomicReference lastError = new AtomicReference<>(null);
private final Map, AbstractActionHandler> handlerMap = new HashMap<>();
public ActionExecutor(AtomicBoolean started, DwsConfig config) {
this.started = started;
this.config = config;
connectionProvider = new ConnectionProvider(config);
handlerMap.put(PutAction.class, new PutActionHandler(config, connectionProvider));
handlerMap.put(SqlAction.class, new SqlActionHandler(connectionProvider, config));
}
@Override
public void run() {
// 只要没被关闭一直执行
while (started.get()) {
try {
// 每次尝试获取任务,最多等待2秒
AbstractAction> action = actionWrapper.get(MAX_AWAIT_ACTION_TIME, TimeUnit.SECONDS);
if (action != null) {
try {
handle(action);
} catch (Exception e) {
if (!action.getFuture().isDone()) {
action.getFuture().completeExceptionally(e);
}
} finally {
// 执行完不管如何消费掉数据,不然该线程将无法接受新的事件
actionWrapper.clear();
// 每次任务之心完更新所持有 连接最后使用时间,避免被释放
connectionProvider.refresh();
}
}
// 连接空闲超时
if ((System.currentTimeMillis() - connectionProvider.getLastActive()) > config.getConnectionMaxIdleMs()) {
connectionProvider.close();
}
} catch (Throwable e) {
lastError.set(e);
log.error("handle error.", e);
}
}
}
private > void handle(T action) throws DwsClientException {
// 获取该时间的执行者
AbstractActionHandler handler = handlerMap.get(action.getClass());
AssertUtil.nonNull(handler, new DwsClientException(ExceptionCode.INVALID_CONFIG, "handler is null"));
for (int i = 0; i < config.getMaxFlushRetryTimes(); i++) {
try {
LogUtil.withLogSwitch(config, () -> log.info("will hand action action type is {}", action.getClass().getName()));
// 报任何异常均将 重试
handler.handle(action);
break;
} catch (Exception e) {
log.error("handle action fail. times = {}, maxTimes = {}", i + 1, config.getMaxFlushRetryTimes(), e);
ExceptionCode code = DwsClientException.fromException(e).getCode();
if (i == config.getMaxFlushRetryTimes() - 1 || !RETRY_EXCEPTION_CODES.contains(code)) {
log.warn("failed after retry {} times, the action will end", i);
// 最后一次重试依旧失败 或者异常是不需要重试的, 将失败返回给调用者
action.getFuture().completeExceptionally(e);
break;
}
try {
if (!connectionProvider.isConnectionValid() || code == ExceptionCode.CONNECTION_ERROR) {
connectionProvider.restConnection();
}
} catch (Exception exception) {
// 不抛出异常 避免因数据库重启等短暂故障失败
log.error("JDBC connection is not valid, and reestablish connection failed.", exception);
}
try {
long sleepTime = config.getRetryBaseTime() * i + SLEEP_RANDOM.nextInt(config.getRetryRandomTime());
log.info("this handler fail. will sleep {}", sleepTime);
LogUtil.withLogSwitch(config, () -> log.info("this handler fail. will sleep {}", sleepTime));
Thread.sleep(sleepTime);
} catch (InterruptedException ex) {
log.error("unable to flush; interrupted while doing another attempt", ex);
Thread.currentThread().interrupt();
throw DwsClientException.fromException(ex);
}
}
}
}
/**
* 提交事件到执行器
* @throws DwsClientException
*/
public synchronized boolean submit(AbstractAction action) throws DwsClientException {
AssertUtil.nonNull(action, new DwsClientException(ExceptionCode.INVALID_CONFIG, "action is null"));
AssertUtil.isNull(lastError.get(), DwsClientException.fromException(lastError.get()));
AssertUtil.isTrue(started.get(), new DwsClientException(ExceptionCode.ALREADY_CLOSE, "executor is closed"));
return actionWrapper.set(action);
}
@Override
public void close() throws IOException {
connectionProvider.close();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy