From 80ca4366dbe624561f6949d8c8cc4901096c7b22 Mon Sep 17 00:00:00 2001 From: "jianghang.loujh" Date: Fri, 13 Sep 2024 18:04:56 +0800 Subject: [PATCH] fixed #5270 , add CanalMQStarter stop latch --- .../otter/canal/server/CanalMQStarter.java | 78 ++++++++++++------- 1 file changed, 51 insertions(+), 27 deletions(-) diff --git a/server/src/main/java/com/alibaba/otter/canal/server/CanalMQStarter.java b/server/src/main/java/com/alibaba/otter/canal/server/CanalMQStarter.java index d64a14c4..ea53ae75 100644 --- a/server/src/main/java/com/alibaba/otter/canal/server/CanalMQStarter.java +++ b/server/src/main/java/com/alibaba/otter/canal/server/CanalMQStarter.java @@ -1,5 +1,15 @@ 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.producer.MQDestination; 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.Message; 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 { @@ -107,28 +105,33 @@ public class CanalMQStarter { public synchronized void startDestination(String destination) { CanalInstance canalInstance = canalServer.getCanalInstances().get(destination); - CanalMQRunnable canalMQRunable = canalMQWorks.get(destination); - //当zk出现session time out可能会导致instance出现多个消费者,从而出现乱序 - //因此当抢占成功的实例已经启动CanalMQRunnable时就不需要stop然后start了 - if (canalInstance != null && Objects.isNull(canalMQRunable)) { - stopDestination(destination); - CanalMQRunnable canalMQRunnable = new CanalMQRunnable(destination); - canalMQWorks.put(canalInstance.getDestination(), canalMQRunnable); - executorService.execute(canalMQRunnable); - logger.info("## Start the MQ work of destination:" + destination); + if (canalInstance != null) { + CanalMQRunnable canalMQRunnable = canalMQWorks.get(destination); + if (Objects.isNull(canalMQRunnable)) { + canalMQRunnable = new CanalMQRunnable(destination); + canalMQWorks.put(canalInstance.getDestination(), canalMQRunnable); + // 触发一下任务启动 + Future future = executorService.submit(canalMQRunnable); + canalMQRunnable.setFuture(future); + 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) { - CanalMQRunnable canalMQRunable = canalMQWorks.get(destination); - if (canalMQRunable != null) { - canalMQRunable.stop(); + CanalMQRunnable canalMQRunnable = canalMQWorks.get(destination); + if (canalMQRunnable != null) { + canalMQRunnable.stop(true); canalMQWorks.remove(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()) { try { Thread.sleep(100); @@ -210,6 +213,9 @@ public class CanalMQStarter { logger.error("process error!", e); } } + + // 确保一下关闭 + latch.countDown(); } private class CanalMQRunnable implements Runnable { @@ -222,13 +228,31 @@ public class CanalMQStarter { private AtomicBoolean running = new AtomicBoolean(true); + private CountDownLatch latch = new CountDownLatch(1); + + private Future future; + @Override public void run() { - worker(destination, running); + worker(destination, running, latch); } - public void stop() { + public void stop(boolean wait) { 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; } } }