fixed #5270 , add CanalMQStarter stop latch

This commit is contained in:
jianghang.loujh
2024-09-13 18:04:56 +08:00
parent be0d945d7d
commit 80ca4366db

View File

@@ -1,5 +1,15 @@
package com.alibaba.otter.canal.server; package com.alibaba.otter.canal.server;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import com.alibaba.otter.canal.connector.core.config.MQProperties; import com.alibaba.otter.canal.connector.core.config.MQProperties;
import com.alibaba.otter.canal.connector.core.producer.MQDestination; import com.alibaba.otter.canal.connector.core.producer.MQDestination;
import com.alibaba.otter.canal.connector.core.spi.CanalMQProducer; import com.alibaba.otter.canal.connector.core.spi.CanalMQProducer;
@@ -9,18 +19,6 @@ import com.alibaba.otter.canal.instance.core.CanalMQConfig;
import com.alibaba.otter.canal.protocol.ClientIdentity; import com.alibaba.otter.canal.protocol.ClientIdentity;
import com.alibaba.otter.canal.protocol.Message; import com.alibaba.otter.canal.protocol.Message;
import com.alibaba.otter.canal.server.embedded.CanalServerWithEmbedded; import com.alibaba.otter.canal.server.embedded.CanalServerWithEmbedded;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public class CanalMQStarter { public class CanalMQStarter {
@@ -107,28 +105,33 @@ public class CanalMQStarter {
public synchronized void startDestination(String destination) { public synchronized void startDestination(String destination) {
CanalInstance canalInstance = canalServer.getCanalInstances().get(destination); CanalInstance canalInstance = canalServer.getCanalInstances().get(destination);
CanalMQRunnable canalMQRunable = canalMQWorks.get(destination); if (canalInstance != null) {
//当zk出现session time out可能会导致instance出现多个消费者,从而出现乱序 CanalMQRunnable canalMQRunnable = canalMQWorks.get(destination);
//因此当抢占成功的实例已经启动CanalMQRunnable时就不需要stop然后start了 if (Objects.isNull(canalMQRunnable)) {
if (canalInstance != null && Objects.isNull(canalMQRunable)) { canalMQRunnable = new CanalMQRunnable(destination);
stopDestination(destination); canalMQWorks.put(canalInstance.getDestination(), canalMQRunnable);
CanalMQRunnable canalMQRunnable = new CanalMQRunnable(destination); // 触发一下任务启动
canalMQWorks.put(canalInstance.getDestination(), canalMQRunnable); Future future = executorService.submit(canalMQRunnable);
executorService.execute(canalMQRunnable); canalMQRunnable.setFuture(future);
logger.info("## Start the MQ work of destination:" + destination); logger.info("## Start the MQ work of destination:" + destination);
} else {
// 主段时间内的zk出现session time out,会默认优先当前节点抢占成功
// 如果没有出发过stop动作,这里可以忽略start的启动
logger.info("## Start the MQ work of destination:" + destination + " , ignore stop");
}
} }
} }
public synchronized void stopDestination(String destination) { public synchronized void stopDestination(String destination) {
CanalMQRunnable canalMQRunable = canalMQWorks.get(destination); CanalMQRunnable canalMQRunnable = canalMQWorks.get(destination);
if (canalMQRunable != null) { if (canalMQRunnable != null) {
canalMQRunable.stop(); canalMQRunnable.stop(true);
canalMQWorks.remove(destination); canalMQWorks.remove(destination);
logger.info("## Stop the MQ work of destination:" + destination); logger.info("## Stop the MQ work of destination:" + destination);
} }
} }
private void worker(String destination, AtomicBoolean destinationRunning) { private void worker(String destination, AtomicBoolean destinationRunning, CountDownLatch latch) {
while (!running || !destinationRunning.get()) { while (!running || !destinationRunning.get()) {
try { try {
Thread.sleep(100); Thread.sleep(100);
@@ -210,6 +213,9 @@ public class CanalMQStarter {
logger.error("process error!", e); logger.error("process error!", e);
} }
} }
// 确保一下关闭
latch.countDown();
} }
private class CanalMQRunnable implements Runnable { private class CanalMQRunnable implements Runnable {
@@ -222,13 +228,31 @@ public class CanalMQStarter {
private AtomicBoolean running = new AtomicBoolean(true); private AtomicBoolean running = new AtomicBoolean(true);
private CountDownLatch latch = new CountDownLatch(1);
private Future future;
@Override @Override
public void run() { public void run() {
worker(destination, running); worker(destination, running, latch);
} }
public void stop() { public void stop(boolean wait) {
running.set(false); running.set(false);
if (wait) {
try {
// 触发一下interrupt
future.cancel(true);
// 等待MQ发送线程的正常退出
latch.await();
} catch (InterruptedException e) {
// ignore
}
}
}
public void setFuture(Future future) {
this.future = future;
} }
} }
} }