|
@@ -1,22 +1,22 @@
|
|
|
package org.dbsyncer.parser.flush;
|
|
|
|
|
|
import org.dbsyncer.common.config.BufferActuatorConfig;
|
|
|
-import org.dbsyncer.common.scheduled.ScheduledTaskJob;
|
|
|
-import org.dbsyncer.common.scheduled.ScheduledTaskService;
|
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
-import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.util.Assert;
|
|
|
|
|
|
-import javax.annotation.PostConstruct;
|
|
|
import java.lang.reflect.ParameterizedType;
|
|
|
+import java.time.Duration;
|
|
|
import java.time.Instant;
|
|
|
-import java.time.LocalDateTime;
|
|
|
+import java.time.temporal.ChronoUnit;
|
|
|
import java.util.LinkedHashMap;
|
|
|
import java.util.Map;
|
|
|
import java.util.Queue;
|
|
|
import java.util.concurrent.BlockingQueue;
|
|
|
import java.util.concurrent.LinkedBlockingQueue;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
import java.util.concurrent.atomic.AtomicLong;
|
|
|
+import java.util.concurrent.locks.Condition;
|
|
|
import java.util.concurrent.locks.Lock;
|
|
|
import java.util.concurrent.locks.ReentrantLock;
|
|
|
|
|
@@ -29,30 +29,41 @@ import java.util.concurrent.locks.ReentrantLock;
|
|
|
* @version 1.0.0
|
|
|
* @date 2022/3/27 17:36
|
|
|
*/
|
|
|
-public abstract class AbstractBufferActuator<Request, Response> implements BufferActuator, ScheduledTaskJob {
|
|
|
+public abstract class AbstractBufferActuator<Request extends BufferRequest, Response extends BufferResponse> implements BufferActuator {
|
|
|
|
|
|
private final Logger logger = LoggerFactory.getLogger(getClass());
|
|
|
-
|
|
|
- @Autowired
|
|
|
- private ScheduledTaskService scheduledTaskService;
|
|
|
-
|
|
|
- @Autowired
|
|
|
- private BufferActuatorConfig bufferActuatorConfig;
|
|
|
-
|
|
|
- private LocalDateTime lastBufferWarningTime;
|
|
|
-
|
|
|
- private BlockingQueue<Request> buffer;
|
|
|
-
|
|
|
- private final Lock lock = new ReentrantLock(true);
|
|
|
-
|
|
|
+ private Class<Response> responseClazz;
|
|
|
+ private final Lock TASK_LOCK = new ReentrantLock(true);
|
|
|
private volatile boolean running;
|
|
|
+ private final Lock BUFFER_LOCK = new ReentrantLock();
|
|
|
+ private final Condition IS_FULL = BUFFER_LOCK.newCondition();
|
|
|
+ private final Duration OFFER_INTERVAL = Duration.of(500, ChronoUnit.MILLIS);
|
|
|
+ private BufferActuatorConfig config;
|
|
|
+ private BlockingQueue<Request> queue;
|
|
|
+
|
|
|
+ public AbstractBufferActuator() {
|
|
|
+ int level = 5;
|
|
|
+ Class<?> aClass = getClass();
|
|
|
+ while (level > 0) {
|
|
|
+ if (aClass.getSuperclass() == AbstractBufferActuator.class) {
|
|
|
+ responseClazz = (Class<Response>) ((ParameterizedType) aClass.getGenericSuperclass()).getActualTypeArguments()[1];
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ aClass = aClass.getSuperclass();
|
|
|
+ level--;
|
|
|
+ }
|
|
|
+ Assert.notNull(responseClazz, String.format("%s的父类%s泛型参数Response为空.", getClass().getName(), AbstractBufferActuator.class.getName()));
|
|
|
+ }
|
|
|
|
|
|
- private final Class<Response> responseClazz = (Class<Response>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[1];
|
|
|
|
|
|
- @PostConstruct
|
|
|
- private void init() {
|
|
|
- buffer = new LinkedBlockingQueue(getQueueCapacity());
|
|
|
- scheduledTaskService.start(bufferActuatorConfig.getPeriodMillisecond(), this);
|
|
|
+ /**
|
|
|
+ * 初始化配置
|
|
|
+ *
|
|
|
+ * @param config
|
|
|
+ */
|
|
|
+ public void buildConfig(BufferActuatorConfig config) {
|
|
|
+ this.config = config;
|
|
|
+ this.queue = new LinkedBlockingQueue(config.getQueueCapacity());
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -71,13 +82,6 @@ public abstract class AbstractBufferActuator<Request, Response> implements Buffe
|
|
|
*/
|
|
|
protected abstract void partition(Request request, Response response);
|
|
|
|
|
|
- /**
|
|
|
- * 批处理
|
|
|
- *
|
|
|
- * @param response
|
|
|
- */
|
|
|
- protected abstract void pull(Response response);
|
|
|
-
|
|
|
/**
|
|
|
* 是否跳过分区处理
|
|
|
*
|
|
@@ -89,66 +93,85 @@ public abstract class AbstractBufferActuator<Request, Response> implements Buffe
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
- public Queue getQueue() {
|
|
|
- return buffer;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public int getQueueCapacity() {
|
|
|
- return bufferActuatorConfig.getQueueCapacity();
|
|
|
- }
|
|
|
+ /**
|
|
|
+ * 批处理
|
|
|
+ *
|
|
|
+ * @param response
|
|
|
+ */
|
|
|
+ protected abstract void pull(Response response);
|
|
|
|
|
|
@Override
|
|
|
- public boolean offer(BufferRequest request) {
|
|
|
- boolean offer = buffer.offer((Request) request);
|
|
|
- if (!offer) {
|
|
|
- LocalDateTime now = LocalDateTime.now();
|
|
|
- if (null == lastBufferWarningTime) {
|
|
|
- lastBufferWarningTime = now;
|
|
|
+ public void offer(BufferRequest request) {
|
|
|
+ boolean lock = false;
|
|
|
+ try {
|
|
|
+ lock = BUFFER_LOCK.tryLock();
|
|
|
+ if (lock) {
|
|
|
+ if (!queue.offer((Request) request)) {
|
|
|
+ logger.warn("[{}]缓存队列容量已达上限[{}], 正在阻塞重试.", this.getClass().getSimpleName(), getQueueCapacity());
|
|
|
+
|
|
|
+ // 容量上限,阻塞重试
|
|
|
+ while (!queue.offer((Request) request)) {
|
|
|
+ try {
|
|
|
+ IS_FULL.await(OFFER_INTERVAL.toMillis(), TimeUnit.MILLISECONDS);
|
|
|
+ } catch (InterruptedException e) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
- // 3s前有警告时间
|
|
|
- if (now.minusSeconds(3).isAfter(lastBufferWarningTime)) {
|
|
|
- logger.warn("[{}]缓存队列容量已达上限,建议修改参数[dbsyncer.parser.flush.buffer.actuator.queue-capacity={}], ", this.getClass().getSimpleName(), getQueueCapacity());
|
|
|
- lastBufferWarningTime = now;
|
|
|
+ } finally {
|
|
|
+ if (lock) {
|
|
|
+ BUFFER_LOCK.unlock();
|
|
|
}
|
|
|
}
|
|
|
- return offer;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public void run() {
|
|
|
+ public void batchExecute() {
|
|
|
if (running) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- final Lock bufferLock = lock;
|
|
|
boolean locked = false;
|
|
|
try {
|
|
|
- locked = bufferLock.tryLock();
|
|
|
+ locked = TASK_LOCK.tryLock();
|
|
|
if (locked) {
|
|
|
running = true;
|
|
|
- flush(buffer);
|
|
|
+ submit();
|
|
|
}
|
|
|
} catch (Exception e) {
|
|
|
- logger.error(e.getMessage());
|
|
|
+ logger.error(e.getMessage(), e);
|
|
|
} finally {
|
|
|
if (locked) {
|
|
|
running = false;
|
|
|
- bufferLock.unlock();
|
|
|
+ TASK_LOCK.unlock();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private void flush(Queue<Request> queue) throws IllegalAccessException, InstantiationException {
|
|
|
+ @Override
|
|
|
+ public Queue getQueue() {
|
|
|
+ return queue;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int getQueueCapacity() {
|
|
|
+ return config.getQueueCapacity();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Object clone() throws CloneNotSupportedException {
|
|
|
+ return super.clone();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void submit() throws IllegalAccessException, InstantiationException {
|
|
|
if (queue.isEmpty()) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
AtomicLong batchCounter = new AtomicLong();
|
|
|
Map<String, Response> map = new LinkedHashMap<>();
|
|
|
- while (!queue.isEmpty() && batchCounter.get() < bufferActuatorConfig.getBatchCount()) {
|
|
|
+ while (!queue.isEmpty() && batchCounter.get() < config.getBatchCount()) {
|
|
|
Request poll = queue.poll();
|
|
|
String key = getPartitionKey(poll);
|
|
|
if (!map.containsKey(key)) {
|
|
@@ -164,19 +187,22 @@ public abstract class AbstractBufferActuator<Request, Response> implements Buffe
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- map.forEach((key, flushTask) -> {
|
|
|
+ map.forEach((key, response) -> {
|
|
|
long now = Instant.now().toEpochMilli();
|
|
|
try {
|
|
|
- pull(flushTask);
|
|
|
+ pull(response);
|
|
|
} catch (Exception e) {
|
|
|
- logger.error("[{}]异常{}", key);
|
|
|
+ logger.error(e.getMessage(), e);
|
|
|
}
|
|
|
- final BufferResponse task = (BufferResponse) flushTask;
|
|
|
- logger.info("[{}{}]{}, {}ms", key, task.getSuffixName(), task.getTaskSize(), (Instant.now().toEpochMilli() - now));
|
|
|
+ final BufferResponse resp = (BufferResponse) response;
|
|
|
+ logger.info("[{}{}]{}, {}ms", key, resp.getSuffixName(), resp.getTaskSize(), (Instant.now().toEpochMilli() - now));
|
|
|
});
|
|
|
map.clear();
|
|
|
map = null;
|
|
|
batchCounter = null;
|
|
|
}
|
|
|
|
|
|
+ public BufferActuatorConfig getConfig() {
|
|
|
+ return config;
|
|
|
+ }
|
|
|
}
|