From d5aa324ce455471cf2ea62d519cecf0062628001 Mon Sep 17 00:00:00 2001 From: duhengforever Date: Thu, 20 Sep 2018 15:48:52 +0800 Subject: [PATCH 1/8] Add RocketMQ canal connector --- .../adapter/support/CanalClientConfig.java | 12 +- .../adapter/loader/CanalAdapterLoader.java | 43 ++-- .../loader/CanalAdapterRocketMQWorker.java | 165 +++++++++++++ .../src/main/resources/canal-client.yml | 26 +-- client/pom.xml | 217 +++++++++--------- .../otter/canal/client/DeserializerUtil.java | 42 ++++ .../client/kafka/MessageDeserializer.java | 39 +--- .../client/rocketmq/ConsumerBatchMessage.java | 47 ++++ .../rocketmq/RocketMQCanalConnector.java | 197 ++++++++++++++++ .../RocketMQCanalConnectorProvider.java | 20 ++ .../rocketmq/RocketMQCanalListener.java | 11 + deployer/pom.xml | 2 +- .../otter/canal/deployer/CanalLauncher.java | 25 +- deployer/src/main/resources/canal.properties | 4 +- .../resources/example/instance.properties | 9 +- .../src/main/resources/{kafka.yml => mq.yml} | 2 +- server/pom.xml | 5 + .../canal/common/CanalMessageSerializer.java | 70 ++++++ .../MQProperties.java} | 12 +- .../otter/canal/kafka/CanalKafkaProducer.java | 39 +--- .../otter/canal/kafka/MessageSerializer.java | 56 +---- .../canal/rocketmq/CanalRocketMQProducer.java | 58 +++++ .../CanalMQStarter.java} | 80 +++---- .../otter/canal/spi/CanalMQProducer.java | 28 +++ 24 files changed, 881 insertions(+), 328 deletions(-) create mode 100644 client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterRocketMQWorker.java create mode 100644 client/src/main/java/com/alibaba/otter/canal/client/DeserializerUtil.java create mode 100644 client/src/main/java/com/alibaba/otter/canal/client/rocketmq/ConsumerBatchMessage.java create mode 100644 client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalConnector.java create mode 100644 client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalConnectorProvider.java create mode 100644 client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalListener.java rename deployer/src/main/resources/{kafka.yml => mq.yml} (85%) create mode 100644 server/src/main/java/com/alibaba/otter/canal/common/CanalMessageSerializer.java rename server/src/main/java/com/alibaba/otter/canal/{kafka/KafkaProperties.java => common/MQProperties.java} (92%) create mode 100644 server/src/main/java/com/alibaba/otter/canal/rocketmq/CanalRocketMQProducer.java rename server/src/main/java/com/alibaba/otter/canal/{kafka/CanalKafkaStarter.java => server/CanalMQStarter.java} (66%) create mode 100644 server/src/main/java/com/alibaba/otter/canal/spi/CanalMQProducer.java diff --git a/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/CanalClientConfig.java b/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/CanalClientConfig.java index f6fa7707..e0658138 100644 --- a/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/CanalClientConfig.java +++ b/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/CanalClientConfig.java @@ -20,7 +20,7 @@ public class CanalClientConfig { private String bootstrapServers; - private List kafkaTopics; + private List mqTopics; private List canalInstances; @@ -56,12 +56,12 @@ public class CanalClientConfig { this.bootstrapServers = bootstrapServers; } - public List getKafkaTopics() { - return kafkaTopics; + public List getMqTopics() { + return mqTopics; } - public void setKafkaTopics(List kafkaTopics) { - this.kafkaTopics = kafkaTopics; + public void setMqTopics(List mqTopics) { + this.mqTopics = mqTopics; } public List getCanalInstances() { @@ -110,7 +110,7 @@ public class CanalClientConfig { } } - public static class KafkaTopic { + public static class MQTopic { private String topic; diff --git a/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterLoader.java b/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterLoader.java index 98973f11..a705fad7 100644 --- a/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterLoader.java +++ b/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterLoader.java @@ -17,34 +17,34 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** - * 外部适配器的加载器 + * RocktMQ外部适配器的加载器 * - * @author machengyuan 2018-8-19 下午11:45:49 * @version 1.0.0 */ public class CanalAdapterLoader { - private static final Logger logger = LoggerFactory.getLogger(CanalAdapterLoader.class); + private static final Logger logger = LoggerFactory.getLogger(CanalAdapterLoader.class); - private CanalClientConfig canalClientConfig; + private CanalClientConfig canalClientConfig; - private Map canalWorkers = new HashMap<>(); + private Map canalWorkers = new HashMap<>(); private Map canalKafkaWorkers = new HashMap<>(); + private Map canalMQWorker = new HashMap<>(); - private ExtensionLoader loader; + private ExtensionLoader loader; - public CanalAdapterLoader(CanalClientConfig canalClientConfig){ + public CanalAdapterLoader(CanalClientConfig canalClientConfig) { this.canalClientConfig = canalClientConfig; } /** - * 初始化canal-client、 canal-client-kafka的适配器 + * 初始化canal-client、 canal-client-rocketmq的适配器 */ public void init() { - // canal instances 和 kafka topics 配置不能同时为空 - if (canalClientConfig.getCanalInstances() == null && canalClientConfig.getKafkaTopics() == null) { - throw new RuntimeException("Blank config property: canalInstances or canalKafkaTopics"); + // canal instances 和 rocketmq topics 配置不能同时为空 + if (canalClientConfig.getCanalInstances() == null && canalClientConfig.getMqTopics() == null) { + throw new RuntimeException("Blank config property: canalInstances or canalMQTopics"); } loader = ExtensionLoader.getExtensionLoader(CanalOuterAdapter.class, @@ -87,10 +87,10 @@ public class CanalAdapterLoader { } } - // 初始化canal-client-kafka的适配器 - if (canalClientConfig.getKafkaTopics() != null) { - for (CanalClientConfig.KafkaTopic kafkaTopic : canalClientConfig.getKafkaTopics()) { - for (CanalClientConfig.Group group : kafkaTopic.getGroups()) { + // 初始化canal-client-rocketmq的适配器 + if (canalClientConfig.getMqTopics() != null) { + for (CanalClientConfig.MQTopic topic : canalClientConfig.getMqTopics()) { + for (CanalClientConfig.Group group : topic.getGroups()) { List> canalOuterAdapterGroups = new ArrayList<>(); List canalOuterAdapters = new ArrayList<>(); @@ -102,16 +102,15 @@ public class CanalAdapterLoader { } canalOuterAdapterGroups.add(canalOuterAdapters); - // String zkServers = canalClientConfig.getZookeeperHosts(); - CanalAdapterKafkaWorker canalKafkaWorker = new CanalAdapterKafkaWorker(zkHosts, + CanalAdapterRocketMQWorker rocketMQWorker = new CanalAdapterRocketMQWorker( canalClientConfig.getBootstrapServers(), - kafkaTopic.getTopic(), + topic.getTopic(), group.getGroupId(), canalOuterAdapterGroups); - canalKafkaWorkers.put(kafkaTopic.getTopic() + "-" + group.getGroupId(), canalKafkaWorker); - canalKafkaWorker.start(); - logger.info("Start adapter for canal-client kafka topic: {} succeed", - kafkaTopic.getTopic() + "-" + group.getGroupId()); + canalMQWorker.put(topic.getTopic() + "-" + group.getGroupId(), rocketMQWorker); + rocketMQWorker.start(); + logger.info("Start adapter for canal-client rocketmq topic: {} succeed", + topic.getTopic() + "-" + group.getGroupId()); } } } diff --git a/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterRocketMQWorker.java b/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterRocketMQWorker.java new file mode 100644 index 00000000..029d9ada --- /dev/null +++ b/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterRocketMQWorker.java @@ -0,0 +1,165 @@ +package com.alibaba.otter.canal.client.adapter.loader; + +import com.alibaba.otter.canal.client.adapter.CanalOuterAdapter; +import com.alibaba.otter.canal.client.rocketmq.RocketMQCanalConnector; +import com.alibaba.otter.canal.client.rocketmq.RocketMQCanalConnectorProvider; +import com.alibaba.otter.canal.protocol.Message; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import org.apache.kafka.clients.consumer.CommitFailedException; +import org.apache.kafka.common.errors.WakeupException; + +/** + * kafka对应的client适配器工作线程 + * + * @author machengyuan 2018-8-19 下午11:30:49 + * @version 1.0.0 + */ +public class CanalAdapterRocketMQWorker extends AbstractCanalAdapterWorker { + + private RocketMQCanalConnector connector; + + private String topic; + + public CanalAdapterRocketMQWorker(String nameServers, String topic, String groupId, + List> canalOuterAdapters) { + this.canalOuterAdapters = canalOuterAdapters; + this.groupInnerExecutorService = Executors.newFixedThreadPool(canalOuterAdapters.size()); + this.topic = topic; + this.canalDestination = topic; + connector = RocketMQCanalConnectorProvider.newRocketMQConnector(nameServers, topic, groupId); + } + + @Override + public void start() { + if (!running) { + thread = new Thread(new Runnable() { + + @Override + public void run() { + process(); + } + }); + thread.setUncaughtExceptionHandler(handler); + running = true; + thread.start(); + } + } + + @Override + public void stop() { + try { + if (!running) { + return; + } + + connector.stopRunning(); + running = false; + + // if (switcher != null && !switcher.state()) { + // switcher.set(true); + // } + + if (thread != null) { + try { + thread.join(); + } catch (InterruptedException e) { + // ignore + } + } + groupInnerExecutorService.shutdown(); + logger.info("topic {} connectors' worker thread dead!", this.topic); + for (List outerAdapters : canalOuterAdapters) { + for (CanalOuterAdapter adapter : outerAdapters) { + adapter.destroy(); + } + } + logger.info("topic {} all connectors destroyed!", this.topic); + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + } + + private void process() { + while (!running) + ; + ExecutorService executor = Executors.newSingleThreadExecutor(); + final AtomicBoolean executing = new AtomicBoolean(true); + while (running) { + try { + logger.info("=============> Start to connect topic: {} <=============", this.topic); + connector.connect(); + logger.info("=============> Start to subscribe topic: {}<=============", this.topic); + connector.subscribe(); + logger.info("=============> Subscribe topic: {} succeed<=============", this.topic); + while (running) { + try { + // switcher.get(); //等待开关开启 + + final Message message = connector.getWithoutAck(1); + + executing.set(true); + if (message != null) { + executor.submit(new Runnable() { + + @Override + public void run() { + try { + if (logger.isDebugEnabled()) { + logger.debug("topic: {} batchId: {} batchSize: {} ", + topic, + message.getId(), + message.getEntries().size()); + } + long begin = System.currentTimeMillis(); + writeOut(message); + long now = System.currentTimeMillis(); + if ((System.currentTimeMillis() - begin) > 5 * 60 * 1000) { + logger.error("topic: {} batchId {} elapsed time: {} ms", + topic, + message.getId(), + now - begin); + } + if (logger.isDebugEnabled()) { + logger.debug("topic: {} batchId {} elapsed time: {} ms", + topic, + message.getId(), + now - begin); + } + connector.ack(message.getId()); + } catch (Exception e) { + logger.error(e.getMessage(), e); + } finally { + executing.compareAndSet(true, false); + } + } + }); + } else { + logger.debug("Message is null"); + } + } catch (CommitFailedException e) { + logger.warn(e.getMessage()); + } catch (Exception e) { + logger.error(e.getMessage(), e); + TimeUnit.SECONDS.sleep(1L); + } + } + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + } + + executor.shutdown(); + + try { + connector.unsubscribe(); + } catch (WakeupException e) { + // No-op. Continue process + } + connector.stopRunning(); + logger.info("=============> Disconnect topic: {} <=============", this.topic); + } +} diff --git a/client-launcher/src/main/resources/canal-client.yml b/client-launcher/src/main/resources/canal-client.yml index 12e016a7..a5fc4813 100644 --- a/client-launcher/src/main/resources/canal-client.yml +++ b/client-launcher/src/main/resources/canal-client.yml @@ -2,20 +2,20 @@ canalServerHost: 127.0.0.1:11111 #zookeeperHosts: slave1:2181 #bootstrapServers: slave1:6667,slave2:6667 -canalInstances: -- instance: example - adapterGroups: - - outAdapters: - - name: logger - - name: hbase - hosts: slave1:2181 - properties: {znodeParent: "/hbase-unsecure"} -#kafkaTopics: -#- topic: example -# groups: -# - groupId: example_g1 -# outAdapters: +#canalInstances: +#- instance: example +# adapterGroups: +# - outAdapters: # - name: logger # - name: hbase # hosts: slave1:2181 # properties: {znodeParent: "/hbase-unsecure"} +mqTopics: +- topic: example + groups: + - groupId: example_g1 + outAdapters: + - name: logger +# - name: hbase +# hosts: slave1:2181 +# properties: {znodeParent: "/hbase-unsecure"} diff --git a/client/pom.xml b/client/pom.xml index 2e48f9c5..b44e3129 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -1,108 +1,115 @@ - - 4.0.0 - - com.alibaba.otter - canal - 1.1.1-SNAPSHOT - ../pom.xml - - com.alibaba.otter - canal.client - jar - canal client module for otter ${project.version} - - - com.alibaba.otter - canal.protocol - ${project.version} - - - - org.apache.kafka - kafka-clients - 1.1.1 - provided - + + 4.0.0 + + com.alibaba.otter + canal + 1.1.1-SNAPSHOT + ../pom.xml + + com.alibaba.otter + canal.client + jar + canal client module for otter ${project.version} + + + com.alibaba.otter + canal.protocol + ${project.version} + + + + org.apache.kafka + kafka-clients + 1.1.1 + provided + + + org.apache.rocketmq + rocketmq-client + 4.3.0 + + + + junit + junit + + - - - junit - junit - - - - - - dev - - true - - env - !javadoc - - - + + + dev + + true + + env + !javadoc + + + - - javadoc - - - env - javadoc - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.9.1 - - - attach-javadocs - package - - jar - - - - - true - public - true -
${project.artifactId}-${project.version}
-
${project.artifactId}-${project.version}
- ${project.artifactId}-${project.version} - - https://github.com/alibaba/canal - - ${project.build.directory}/apidocs/apidocs/${project.version} -
-
- - org.apache.maven.plugins - maven-scm-publish-plugin - 1.0-beta-2 - - - attach-javadocs - package - - publish-scm - - - - - ${project.build.directory}/scmpublish - Publishing javadoc for ${project.artifactId}:${project.version} - ${project.build.directory}/apidocs - true - scm:git:git@github.com:alibaba/canal.git - gh-pages - - -
-
-
-
+ + javadoc + + + env + javadoc + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9.1 + + + attach-javadocs + package + + jar + + + + + true + public + true +
${project.artifactId}-${project.version}
+
${project.artifactId}-${project.version}
+ ${project.artifactId}-${project.version} + + https://github.com/alibaba/canal + + ${project.build.directory}/apidocs/apidocs/${project.version} + +
+
+ + org.apache.maven.plugins + maven-scm-publish-plugin + 1.0-beta-2 + + + attach-javadocs + package + + publish-scm + + + + + ${project.build.directory}/scmpublish + Publishing javadoc for ${project.artifactId}:${project.version} + + ${project.build.directory}/apidocs + true + scm:git:git@github.com:alibaba/canal.git + gh-pages + + +
+
+
+
diff --git a/client/src/main/java/com/alibaba/otter/canal/client/DeserializerUtil.java b/client/src/main/java/com/alibaba/otter/canal/client/DeserializerUtil.java new file mode 100644 index 00000000..54b1bb7a --- /dev/null +++ b/client/src/main/java/com/alibaba/otter/canal/client/DeserializerUtil.java @@ -0,0 +1,42 @@ +package com.alibaba.otter.canal.client; + +import com.alibaba.otter.canal.protocol.CanalEntry; +import com.alibaba.otter.canal.protocol.CanalPacket; +import com.alibaba.otter.canal.protocol.Message; +import com.alibaba.otter.canal.protocol.exception.CanalClientException; +import com.google.protobuf.ByteString; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DeserializerUtil { + private static Logger logger = LoggerFactory.getLogger(DeserializerUtil.class); + + public static Message deserializer(byte[] data) { + try { + if (data == null) { + return null; + } else { + CanalPacket.Packet p = CanalPacket.Packet.parseFrom(data); + switch (p.getType()) { + case MESSAGES: { + if (!p.getCompression().equals(CanalPacket.Compression.NONE)) { + throw new CanalClientException("compression is not supported in this connector"); + } + + CanalPacket.Messages messages = CanalPacket.Messages.parseFrom(p.getBody()); + Message result = new Message(messages.getBatchId()); + for (ByteString byteString : messages.getMessagesList()) { + result.addEntry(CanalEntry.Entry.parseFrom(byteString)); + } + return result; + } + default: + break; + } + } + } catch (Exception e) { + logger.error("Error when deserializing byte[] to message ", e); + } + return null; + } +} diff --git a/client/src/main/java/com/alibaba/otter/canal/client/kafka/MessageDeserializer.java b/client/src/main/java/com/alibaba/otter/canal/client/kafka/MessageDeserializer.java index e716d2d6..af381283 100644 --- a/client/src/main/java/com/alibaba/otter/canal/client/kafka/MessageDeserializer.java +++ b/client/src/main/java/com/alibaba/otter/canal/client/kafka/MessageDeserializer.java @@ -1,17 +1,12 @@ package com.alibaba.otter.canal.client.kafka; +import com.alibaba.otter.canal.client.DeserializerUtil; +import com.alibaba.otter.canal.protocol.Message; import java.util.Map; - import org.apache.kafka.common.serialization.Deserializer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.alibaba.otter.canal.protocol.CanalEntry; -import com.alibaba.otter.canal.protocol.CanalPacket; -import com.alibaba.otter.canal.protocol.Message; -import com.alibaba.otter.canal.protocol.exception.CanalClientException; -import com.google.protobuf.ByteString; - /** * Kafka Message类的反序列化 * @@ -27,34 +22,8 @@ public class MessageDeserializer implements Deserializer { } @Override - public Message deserialize(String topic, byte[] data) { - try { - if (data == null) { - return null; - } - else { - CanalPacket.Packet p = CanalPacket.Packet.parseFrom(data); - switch (p.getType()) { - case MESSAGES: { - if (!p.getCompression().equals(CanalPacket.Compression.NONE)) { - throw new CanalClientException("compression is not supported in this connector"); - } - - CanalPacket.Messages messages = CanalPacket.Messages.parseFrom(p.getBody()); - Message result = new Message(messages.getBatchId()); - for (ByteString byteString : messages.getMessagesList()) { - result.addEntry(CanalEntry.Entry.parseFrom(byteString)); - } - return result; - } - default: - break; - } - } - } catch (Exception e) { - logger.error("Error when deserializing byte[] to message "); - } - return null; + public Message deserialize(String topic1, byte[] data) { + return DeserializerUtil.deserializer(data); } @Override diff --git a/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/ConsumerBatchMessage.java b/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/ConsumerBatchMessage.java new file mode 100644 index 00000000..ef83a798 --- /dev/null +++ b/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/ConsumerBatchMessage.java @@ -0,0 +1,47 @@ +package com.alibaba.otter.canal.client.rocketmq; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class ConsumerBatchMessage { + private final BlockingQueue data; + private CountDownLatch latch; + private boolean hasFailure = false; + + public ConsumerBatchMessage(BlockingQueue data) { + this.data = data; + latch = new CountDownLatch(data.size()); + } + + public boolean waitFinish(long timeout) throws InterruptedException { + return latch.await(timeout, TimeUnit.MILLISECONDS); + } + + public boolean isSuccess() { + return !hasFailure; + } + + public BlockingQueue getData() { + return data; + } + + /** + * Countdown if the sub message is successful. + */ + public void ack() { + latch.countDown(); + } + + /** + * Countdown and fail-fast if the sub message is failed. + */ + public void fail() { + hasFailure = true; + // fail fast + long count = latch.getCount(); + for (int i = 0; i < count; i++) { + latch.countDown(); + } + } +} diff --git a/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalConnector.java b/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalConnector.java new file mode 100644 index 00000000..e6407308 --- /dev/null +++ b/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalConnector.java @@ -0,0 +1,197 @@ +package com.alibaba.otter.canal.client.rocketmq; + +import com.alibaba.otter.canal.client.CanalConnector; +import com.alibaba.otter.canal.client.DeserializerUtil; +import com.alibaba.otter.canal.protocol.Message; +import com.alibaba.otter.canal.protocol.exception.CanalClientException; +import java.util.List; +import java.util.Map; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import org.apache.commons.lang.StringUtils; +import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; +import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyContext; +import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus; +import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly; +import org.apache.rocketmq.client.exception.MQClientException; +import org.apache.rocketmq.common.message.MessageExt; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RocketMQCanalConnector implements CanalConnector { + private static final Logger logger = LoggerFactory.getLogger(RocketMQCanalConnector.class); + + private String nameServer; + private String topic; + private String groupName; + private volatile boolean connected = false; + private DefaultMQPushConsumer rocketMQConsumer; + private BlockingQueue> messageBlockingQueue; + Map> messageCache; + private long batchProcessTimeout = 10000; + + public RocketMQCanalConnector(String nameServer, String topic, String groupName) { + this.nameServer = nameServer; + this.topic = topic; + this.groupName = groupName; + messageBlockingQueue = new LinkedBlockingQueue<>(); + messageCache = new ConcurrentHashMap<>(); + } + + @Override + public void connect() throws CanalClientException { + rocketMQConsumer = new DefaultMQPushConsumer(groupName); + } + + @Override + public void disconnect() throws CanalClientException { + rocketMQConsumer.shutdown(); + } + + @Override + public boolean checkValid() throws CanalClientException { + return connected; + } + + @Override + public synchronized void subscribe(String filter) throws CanalClientException { + if (connected) { + return; + } + try { + rocketMQConsumer.subscribe(topic, "*"); + if (rocketMQConsumer == null) { + rocketMQConsumer = new DefaultMQPushConsumer(groupName); + if (!StringUtils.isBlank(nameServer)) { + rocketMQConsumer.setNamesrvAddr(nameServer); + } + } + rocketMQConsumer.registerMessageListener(new MessageListenerOrderly() { + @Override + public ConsumeOrderlyStatus consumeMessage(List messageExts, + ConsumeOrderlyContext context) { + context.setAutoCommit(true); + boolean isSuccess = process(messageExts); + if (isSuccess) { + return ConsumeOrderlyStatus.SUCCESS; + } else { + return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT; + } + } + }); + rocketMQConsumer.start(); + } catch (MQClientException ex) { + connected = false; + logger.error("Start RocketMQ consumer error", ex); + } + connected = true; + } + + private boolean process(List messageExts) { + BlockingQueue messageList = new LinkedBlockingQueue<>(); + for (MessageExt messageExt : messageExts) { + byte[] data = messageExt.getBody(); + Message message = DeserializerUtil.deserializer(data); + try { + messageList.put(message); + } catch (InterruptedException ex) { + logger.error("Add message error"); + } + } + ConsumerBatchMessage batchMessage = new ConsumerBatchMessage<>(messageList); + try { + messageBlockingQueue.put(batchMessage); + } catch (InterruptedException e) { + logger.error("Put message to queue error", e); + throw new RuntimeException(e); + } + boolean isCompleted; + try { + isCompleted = batchMessage.waitFinish(batchProcessTimeout); + } catch (InterruptedException e) { + logger.error("Interrupted when waiting messages to be finished.", e); + throw new RuntimeException(e); + } + boolean isSuccess = batchMessage.isSuccess(); + return isCompleted && isSuccess; + } + + @Override + public void subscribe() throws CanalClientException { + this.subscribe(null); + } + + @Override + public void unsubscribe() throws CanalClientException { + this.rocketMQConsumer.unsubscribe(this.topic); + } + + @Override + public Message get(int batchSize) throws CanalClientException { + Message message = getWithoutAck(batchSize); + ack(message.getId()); + return message; + } + + @Override + public Message get(int batchSize, Long timeout, TimeUnit unit) throws CanalClientException { + Message message = getWithoutAck(batchSize, timeout, unit); + ack(message.getId()); + return message; + } + + private Message getMessage(ConsumerBatchMessage consumerBatchMessage) { + BlockingQueue messageList = consumerBatchMessage.getData(); + if (messageList != null & messageList.size() > 0) { + Message message = messageList.poll(); + messageCache.put(message.getId(), consumerBatchMessage); + return message; + } + return null; + } + + @Override + public Message getWithoutAck(int batchSize) throws CanalClientException { + ConsumerBatchMessage batchMessage = messageBlockingQueue.poll(); + if (batchMessage != null){ + return getMessage(batchMessage); + } + return null; + } + + @Override + public Message getWithoutAck(int batchSize, Long timeout, TimeUnit unit) throws CanalClientException { + try { + ConsumerBatchMessage batchMessage = messageBlockingQueue.poll(timeout, unit); + return getMessage(batchMessage); + } catch (InterruptedException ex) { + logger.warn("Get message timeout", ex); + throw new CanalClientException("failed to fetch the data after " + timeout); + } + } + + @Override + public void ack(long batchId) throws CanalClientException { + ConsumerBatchMessage batchMessage = messageCache.get(batchId); + batchMessage.ack(); + } + + @Override + public void rollback(long batchId) throws CanalClientException { + + } + + @Override + public void rollback() throws CanalClientException { + + } + + @Override + public void stopRunning() throws CanalClientException { + this.rocketMQConsumer.shutdown(); + connected = false; + } + +} diff --git a/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalConnectorProvider.java b/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalConnectorProvider.java new file mode 100644 index 00000000..3b876f49 --- /dev/null +++ b/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalConnectorProvider.java @@ -0,0 +1,20 @@ +package com.alibaba.otter.canal.client.rocketmq; + +/** + * RocketMQ connector provider. + */ +public class RocketMQCanalConnectorProvider { + /** + * Create RocketMQ connector + * + * @param nameServers name servers for RocketMQ + * @param topic + * @param groupId + * @return {@link RocketMQCanalConnector} + */ + public static RocketMQCanalConnector newRocketMQConnector(String nameServers, String topic, + String groupId) { + return new RocketMQCanalConnector(nameServers, topic, groupId); + } + +} diff --git a/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalListener.java b/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalListener.java new file mode 100644 index 00000000..c30cd61f --- /dev/null +++ b/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalListener.java @@ -0,0 +1,11 @@ +package com.alibaba.otter.canal.client.rocketmq; + +import java.util.List; +import org.apache.rocketmq.common.message.MessageExt; + +/** + * RocketMQ message listener + */ +public interface RocketMQCanalListener { + boolean onReceive(List messageExts); +} diff --git a/deployer/pom.xml b/deployer/pom.xml index 3580869b..690b8c91 100644 --- a/deployer/pom.xml +++ b/deployer/pom.xml @@ -40,7 +40,7 @@ **/canal.properties **/spring/** **/example/** - **/kafka.yml + **/mq.yml diff --git a/deployer/src/main/java/com/alibaba/otter/canal/deployer/CanalLauncher.java b/deployer/src/main/java/com/alibaba/otter/canal/deployer/CanalLauncher.java index 2524186a..4388de47 100644 --- a/deployer/src/main/java/com/alibaba/otter/canal/deployer/CanalLauncher.java +++ b/deployer/src/main/java/com/alibaba/otter/canal/deployer/CanalLauncher.java @@ -1,24 +1,26 @@ package com.alibaba.otter.canal.deployer; +import com.alibaba.otter.canal.kafka.CanalKafkaProducer; +import com.alibaba.otter.canal.rocketmq.CanalRocketMQProducer; +import com.alibaba.otter.canal.server.CanalMQStarter; +import com.alibaba.otter.canal.spi.CanalMQProducer; import java.io.FileInputStream; import java.util.Properties; -import com.alibaba.otter.canal.kafka.CanalKafkaStarter; -import com.alibaba.otter.canal.server.CanalServerStarter; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * canal独立版本启动的入口类 - * + * * @author jianghang 2012-11-6 下午05:20:49 * @version 1.0.0 */ public class CanalLauncher { private static final String CLASSPATH_URL_PREFIX = "classpath:"; - private static final Logger logger = LoggerFactory.getLogger(CanalLauncher.class); + private static final Logger logger = LoggerFactory.getLogger(CanalLauncher.class); public static void main(String[] args) throws Throwable { try { @@ -54,15 +56,18 @@ public class CanalLauncher { }); - CanalServerStarter canalServerStarter = null; + CanalMQProducer canalMQProducer = null; String serverMode = properties.getProperty(CanalConstants.CANAL_SERVER_MODE, "tcp"); if (serverMode.equalsIgnoreCase("kafka")) { - canalServerStarter = new CanalKafkaStarter(); - } else if (serverMode.equalsIgnoreCase("rocketMQ")) { - // 预留rocketMQ启动 + canalMQProducer = new CanalKafkaProducer(); + } else if (serverMode.equalsIgnoreCase("rocketmq")) { + canalMQProducer = new CanalRocketMQProducer(); } - if (canalServerStarter != null) { - canalServerStarter.init(); + if (canalMQProducer != null) { + CanalMQStarter canalServerStarter = new CanalMQStarter(canalMQProducer); + if (canalServerStarter != null) { + canalServerStarter.init(); + } } } catch (Throwable e) { logger.error("## Something goes wrong when starting up the canal Server:", e); diff --git a/deployer/src/main/resources/canal.properties b/deployer/src/main/resources/canal.properties index 3d580bae..e2ad657b 100644 --- a/deployer/src/main/resources/canal.properties +++ b/deployer/src/main/resources/canal.properties @@ -9,8 +9,8 @@ canal.zkServers= # flush data to zk canal.zookeeper.flush.period = 1000 canal.withoutNetty = false -# tcp, kafka, rocketMQ -canal.serverMode = tcp +# tcp, kafka, RocketMQ +canal.serverMode = rocketmq # flush meta cursor/parse position to file canal.file.data.dir = ${canal.conf.dir} canal.file.flush.period = 1000 diff --git a/deployer/src/main/resources/example/instance.properties b/deployer/src/main/resources/example/instance.properties index f72b65fe..3cdbac4a 100644 --- a/deployer/src/main/resources/example/instance.properties +++ b/deployer/src/main/resources/example/instance.properties @@ -6,7 +6,7 @@ canal.instance.gtidon=false # position info -canal.instance.master.address=127.0.0.1:3306 +canal.instance.master.address=120.78.225.173:3307 canal.instance.master.journal.name= canal.instance.master.position= canal.instance.master.timestamp= @@ -18,8 +18,9 @@ canal.instance.rds.secretkey= canal.instance.rds.instanceId= # table meta tsdb info -canal.instance.tsdb.enable=true -#canal.instance.tsdb.url=jdbc:mysql://127.0.0.1:3306/canal_tsdb +#canal.instance.tsdb.enable=true +#canal.instance.tsdb.url=jdbc:mysql://120.78.225.173:3306/test +#canal.instance.mysql.slaveId = 1234 #canal.instance.tsdb.dbUsername=canal #canal.instance.tsdb.dbPassword=canal @@ -31,7 +32,7 @@ canal.instance.tsdb.enable=true # username/password canal.instance.dbUsername=canal -canal.instance.dbPassword=canal +canal.instance.dbPassword=P@ssw0rd canal.instance.connectionCharset=UTF-8 # table regex diff --git a/deployer/src/main/resources/kafka.yml b/deployer/src/main/resources/mq.yml similarity index 85% rename from deployer/src/main/resources/kafka.yml rename to deployer/src/main/resources/mq.yml index 93d4d557..d8b18f4a 100644 --- a/deployer/src/main/resources/kafka.yml +++ b/deployer/src/main/resources/mq.yml @@ -1,4 +1,4 @@ -servers: slave1:6667,slave2:6667,slave3:6667 +servers: localhost:9876 #for rocketmq: means the nameserver retries: 0 batchSize: 16384 lingerMs: 1 diff --git a/server/pom.xml b/server/pom.xml index a2eb437d..20108523 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -42,6 +42,11 @@ + + org.apache.rocketmq + rocketmq-client + 4.3.0 + org.jboss.netty diff --git a/server/src/main/java/com/alibaba/otter/canal/common/CanalMessageSerializer.java b/server/src/main/java/com/alibaba/otter/canal/common/CanalMessageSerializer.java new file mode 100644 index 00000000..55d350c6 --- /dev/null +++ b/server/src/main/java/com/alibaba/otter/canal/common/CanalMessageSerializer.java @@ -0,0 +1,70 @@ +package com.alibaba.otter.canal.common; + +import com.alibaba.otter.canal.protocol.CanalEntry; +import com.alibaba.otter.canal.protocol.CanalPacket; +import com.alibaba.otter.canal.protocol.Message; +import com.google.protobuf.ByteString; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import java.util.List; +import org.apache.kafka.common.errors.SerializationException; +import org.springframework.util.CollectionUtils; + +public class CanalMessageSerializer { + public static byte[] serializer(Message data){ + try { + if (data != null) { + if (data.getId() != -1) { + if (data.isRaw() && !CollectionUtils.isEmpty(data.getRawEntries())) { + // for performance + List rowEntries = data.getRawEntries(); + // message size + int messageSize = 0; + messageSize += CodedOutputStream.computeInt64Size(1, data.getId()); + + int dataSize = 0; + for (int i = 0; i < rowEntries.size(); i++) { + dataSize += CodedOutputStream.computeBytesSizeNoTag(rowEntries.get(i)); + } + messageSize += dataSize; + messageSize += 1 * rowEntries.size(); + // packet size + int size = 0; + size += CodedOutputStream.computeEnumSize(3, + CanalPacket.PacketType.MESSAGES.getNumber()); + size += CodedOutputStream.computeTagSize(5) + + CodedOutputStream.computeRawVarint32Size(messageSize) + + messageSize; + // build data + byte[] body = new byte[size]; + CodedOutputStream output = CodedOutputStream.newInstance(body); + output.writeEnum(3, CanalPacket.PacketType.MESSAGES.getNumber()); + + output.writeTag(5, WireFormat.WIRETYPE_LENGTH_DELIMITED); + output.writeRawVarint32(messageSize); + // message + output.writeInt64(1, data.getId()); + for (int i = 0; i < rowEntries.size(); i++) { + output.writeBytes(2, rowEntries.get(i)); + } + output.checkNoSpaceLeft(); + return body; + } else if (!CollectionUtils.isEmpty(data.getEntries())) { + CanalPacket.Messages.Builder messageBuilder = CanalPacket.Messages.newBuilder(); + for (CanalEntry.Entry entry : data.getEntries()) { + messageBuilder.addMessages(entry.toByteString()); + } + + CanalPacket.Packet.Builder packetBuilder = CanalPacket.Packet.newBuilder(); + packetBuilder.setType(CanalPacket.PacketType.MESSAGES); + packetBuilder.setBody(messageBuilder.build().toByteString()); + return packetBuilder.build().toByteArray(); + } + } + } + } catch (Exception e) { + throw new SerializationException("Error when serializing message to byte[] "); + } + return null; + } +} diff --git a/server/src/main/java/com/alibaba/otter/canal/kafka/KafkaProperties.java b/server/src/main/java/com/alibaba/otter/canal/common/MQProperties.java similarity index 92% rename from server/src/main/java/com/alibaba/otter/canal/kafka/KafkaProperties.java rename to server/src/main/java/com/alibaba/otter/canal/common/MQProperties.java index 140ce965..823a506a 100644 --- a/server/src/main/java/com/alibaba/otter/canal/kafka/KafkaProperties.java +++ b/server/src/main/java/com/alibaba/otter/canal/common/MQProperties.java @@ -1,4 +1,4 @@ -package com.alibaba.otter.canal.kafka; +package com.alibaba.otter.canal.common; import java.util.ArrayList; import java.util.HashSet; @@ -11,7 +11,7 @@ import java.util.Set; * @author machengyuan 2018-6-11 下午05:30:49 * @version 1.0.0 */ -public class KafkaProperties { +public class MQProperties { private String servers = "localhost:6667"; private int retries = 0; @@ -20,6 +20,7 @@ public class KafkaProperties { private long bufferMemory = 33554432L; private boolean filterTransactionEntry = true; private int canalBatchSize = 5; + private String producerGroup = "Canal-Producer"; private List canalDestinations = new ArrayList(); @@ -167,4 +168,11 @@ public class KafkaProperties { this.filterTransactionEntry = filterTransactionEntry; } + public String getProducerGroup() { + return producerGroup; + } + + public void setProducerGroup(String producerGroup) { + this.producerGroup = producerGroup; + } } diff --git a/server/src/main/java/com/alibaba/otter/canal/kafka/CanalKafkaProducer.java b/server/src/main/java/com/alibaba/otter/canal/kafka/CanalKafkaProducer.java index b6dba051..91531f7d 100644 --- a/server/src/main/java/com/alibaba/otter/canal/kafka/CanalKafkaProducer.java +++ b/server/src/main/java/com/alibaba/otter/canal/kafka/CanalKafkaProducer.java @@ -1,5 +1,7 @@ package com.alibaba.otter.canal.kafka; +import com.alibaba.otter.canal.common.MQProperties; +import com.alibaba.otter.canal.spi.CanalMQProducer; import java.io.IOException; import java.util.Properties; @@ -18,13 +20,14 @@ import com.alibaba.otter.canal.protocol.Message; * @author machengyuan 2018-6-11 下午05:30:49 * @version 1.0.0 */ -public class CanalKafkaProducer { +public class CanalKafkaProducer implements CanalMQProducer { - private static final Logger logger = LoggerFactory.getLogger(CanalKafkaProducer.class); + private static final Logger logger = LoggerFactory.getLogger(CanalKafkaProducer.class); private Producer producer; - public void init(KafkaProperties kafkaProperties) { + @Override + public void init(MQProperties kafkaProperties) { Properties properties = new Properties(); properties.put("bootstrap.servers", kafkaProperties.getServers()); properties.put("acks", "all"); @@ -37,6 +40,7 @@ public class CanalKafkaProducer { producer = new KafkaProducer(properties); } + @Override public void stop() { try { logger.info("## stop the kafka producer"); @@ -48,33 +52,8 @@ public class CanalKafkaProducer { } } - public void send(KafkaProperties.Topic topic, Message message) throws IOException { - // set canal.instance.filter.transaction.entry = true - - // boolean valid = false; - // if (message != null) { - // if (message.isRaw() && !message.getRawEntries().isEmpty()) { - // for (ByteString byteString : message.getRawEntries()) { - // CanalEntry.Entry entry = CanalEntry.Entry.parseFrom(byteString); - // if (entry.getEntryType() != CanalEntry.EntryType.TRANSACTIONBEGIN - // && entry.getEntryType() != CanalEntry.EntryType.TRANSACTIONEND) { - // valid = true; - // break; - // } - // } - // } else if (!message.getEntries().isEmpty()){ - // for (CanalEntry.Entry entry : message.getEntries()) { - // if (entry.getEntryType() != CanalEntry.EntryType.TRANSACTIONBEGIN - // && entry.getEntryType() != CanalEntry.EntryType.TRANSACTIONEND) { - // valid = true; - // break; - // } - // } - // } - // } - // if (!valid) { - // return; - // } + @Override + public void send(MQProperties.Topic topic, Message message) throws IOException { ProducerRecord record; if (topic.getPartition() != null) { record = new ProducerRecord(topic.getTopic(), topic.getPartition(), null, message); diff --git a/server/src/main/java/com/alibaba/otter/canal/kafka/MessageSerializer.java b/server/src/main/java/com/alibaba/otter/canal/kafka/MessageSerializer.java index 49db5aa1..67dc04bc 100644 --- a/server/src/main/java/com/alibaba/otter/canal/kafka/MessageSerializer.java +++ b/server/src/main/java/com/alibaba/otter/canal/kafka/MessageSerializer.java @@ -1,5 +1,6 @@ package com.alibaba.otter.canal.kafka; +import com.alibaba.otter.canal.common.CanalMessageSerializer; import java.util.List; import java.util.Map; @@ -29,60 +30,7 @@ public class MessageSerializer implements Serializer { @Override public byte[] serialize(String topic, Message data) { - try { - if (data != null) { - if (data.getId() != -1) { - if (data.isRaw() && !CollectionUtils.isEmpty(data.getRawEntries())) { - // for performance - List rowEntries = data.getRawEntries(); - // message size - int messageSize = 0; - messageSize += CodedOutputStream.computeInt64Size(1, data.getId()); - - int dataSize = 0; - for (int i = 0; i < rowEntries.size(); i++) { - dataSize += CodedOutputStream.computeBytesSizeNoTag(rowEntries.get(i)); - } - messageSize += dataSize; - messageSize += 1 * rowEntries.size(); - // packet size - int size = 0; - size += CodedOutputStream.computeEnumSize(3, - PacketType.MESSAGES.getNumber()); - size += CodedOutputStream.computeTagSize(5) - + CodedOutputStream.computeRawVarint32Size(messageSize) - + messageSize; - // build data - byte[] body = new byte[size]; - CodedOutputStream output = CodedOutputStream.newInstance(body); - output.writeEnum(3, PacketType.MESSAGES.getNumber()); - - output.writeTag(5, WireFormat.WIRETYPE_LENGTH_DELIMITED); - output.writeRawVarint32(messageSize); - // message - output.writeInt64(1, data.getId()); - for (int i = 0; i < rowEntries.size(); i++) { - output.writeBytes(2, rowEntries.get(i)); - } - output.checkNoSpaceLeft(); - return body; - } else if (!CollectionUtils.isEmpty(data.getEntries())) { - CanalPacket.Messages.Builder messageBuilder = CanalPacket.Messages.newBuilder(); - for (CanalEntry.Entry entry : data.getEntries()) { - messageBuilder.addMessages(entry.toByteString()); - } - - CanalPacket.Packet.Builder packetBuilder = CanalPacket.Packet.newBuilder(); - packetBuilder.setType(PacketType.MESSAGES); - packetBuilder.setBody(messageBuilder.build().toByteString()); - return packetBuilder.build().toByteArray(); - } - } - } - } catch (Exception e) { - throw new SerializationException("Error when serializing message to byte[] "); - } - return null; + return CanalMessageSerializer.serializer(data); } @Override diff --git a/server/src/main/java/com/alibaba/otter/canal/rocketmq/CanalRocketMQProducer.java b/server/src/main/java/com/alibaba/otter/canal/rocketmq/CanalRocketMQProducer.java new file mode 100644 index 00000000..5a2900de --- /dev/null +++ b/server/src/main/java/com/alibaba/otter/canal/rocketmq/CanalRocketMQProducer.java @@ -0,0 +1,58 @@ +package com.alibaba.otter.canal.rocketmq; + +import com.alibaba.otter.canal.common.CanalMessageSerializer; +import com.alibaba.otter.canal.common.MQProperties; +import com.alibaba.otter.canal.server.exception.CanalServerException; +import com.alibaba.otter.canal.spi.CanalMQProducer; +import java.util.List; +import org.apache.rocketmq.client.exception.MQBrokerException; +import org.apache.rocketmq.client.exception.MQClientException; +import org.apache.rocketmq.client.producer.DefaultMQProducer; +import org.apache.rocketmq.client.producer.MessageQueueSelector; +import org.apache.rocketmq.common.message.Message; +import org.apache.rocketmq.common.message.MessageQueue; +import org.apache.rocketmq.remoting.exception.RemotingException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CanalRocketMQProducer implements CanalMQProducer { + private static final Logger logger = LoggerFactory.getLogger(CanalRocketMQProducer.class); + + private DefaultMQProducer defaultMQProducer; + + @Override + public void init(MQProperties rocketMQProperties) { + defaultMQProducer = new DefaultMQProducer(); + defaultMQProducer.setNamesrvAddr(rocketMQProperties.getServers()); + defaultMQProducer.setProducerGroup(rocketMQProperties.getProducerGroup()); + defaultMQProducer.setRetryTimesWhenSendFailed(rocketMQProperties.getRetries()); + logger.info("##Start RocketMQ producer##"); + try { + defaultMQProducer.start(); + } catch (MQClientException ex) { + throw new CanalServerException("Start RocketMQ producer error", ex); + } + } + + @Override + public void send(MQProperties.Topic topic, com.alibaba.otter.canal.protocol.Message data) { + try { + Message message = new Message(topic.getTopic(), CanalMessageSerializer.serializer(data)); + this.defaultMQProducer.send(message, new MessageQueueSelector() { + @Override + public MessageQueue select(List mqs, Message msg, Object arg) { + // int index = (arg.hashCode() % mqs.size()); + return mqs.get(1); + } + }, null); + } catch (MQClientException | RemotingException | MQBrokerException | InterruptedException e) { + logger.error("Send message error!", e); + } + } + + @Override + public void stop() { + logger.info("## Stop RocketMQ producer##"); + this.defaultMQProducer.shutdown(); + } +} diff --git a/server/src/main/java/com/alibaba/otter/canal/kafka/CanalKafkaStarter.java b/server/src/main/java/com/alibaba/otter/canal/server/CanalMQStarter.java similarity index 66% rename from server/src/main/java/com/alibaba/otter/canal/kafka/CanalKafkaStarter.java rename to server/src/main/java/com/alibaba/otter/canal/server/CanalMQStarter.java index d244ce31..bd2fdac2 100644 --- a/server/src/main/java/com/alibaba/otter/canal/kafka/CanalKafkaStarter.java +++ b/server/src/main/java/com/alibaba/otter/canal/server/CanalMQStarter.java @@ -1,69 +1,63 @@ -package com.alibaba.otter.canal.kafka; +package com.alibaba.otter.canal.server; +import com.alibaba.otter.canal.common.MQProperties; +import com.alibaba.otter.canal.protocol.ClientIdentity; +import com.alibaba.otter.canal.protocol.Message; +import com.alibaba.otter.canal.server.embedded.CanalServerWithEmbedded; +import com.alibaba.otter.canal.spi.CanalMQProducer; import java.io.FileInputStream; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; - import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.yaml.snakeyaml.Yaml; -import com.alibaba.otter.canal.kafka.KafkaProperties.CanalDestination; -import com.alibaba.otter.canal.kafka.KafkaProperties.Topic; -import com.alibaba.otter.canal.protocol.ClientIdentity; -import com.alibaba.otter.canal.protocol.Message; -import com.alibaba.otter.canal.server.CanalServerStarter; -import com.alibaba.otter.canal.server.embedded.CanalServerWithEmbedded; - -/** - * kafka 启动类 - * - * @author machengyuan 2018-6-11 下午05:30:49 - * @version 1.0.0 - */ -public class CanalKafkaStarter implements CanalServerStarter { - - private static final Logger logger = LoggerFactory.getLogger(CanalKafkaStarter.class); +public class CanalMQStarter { + private static final Logger logger = LoggerFactory.getLogger(CanalMQStarter.class); private static final String CLASSPATH_URL_PREFIX = "classpath:"; private volatile boolean running = false; - private ExecutorService executorService; + private ExecutorService executorService; - private CanalKafkaProducer canalKafkaProducer; + private CanalMQProducer canalMQProducer; - private KafkaProperties kafkaProperties; + private MQProperties properties; + + public CanalMQStarter(CanalMQProducer canalMQProducer){ + this.canalMQProducer = canalMQProducer; + } public void init() { try { - logger.info("## load kafka configurations"); - String conf = System.getProperty("kafka.conf", "classpath:kafka.yml"); + logger.info("## load MQ configurations"); + String conf = System.getProperty("mq.conf", "classpath:mq.yml"); if (conf.startsWith(CLASSPATH_URL_PREFIX)) { conf = StringUtils.substringAfter(conf, CLASSPATH_URL_PREFIX); - kafkaProperties = new Yaml().loadAs(CanalKafkaStarter.class.getClassLoader().getResourceAsStream(conf), - KafkaProperties.class); + properties = new Yaml().loadAs(CanalMQStarter.class.getClassLoader().getResourceAsStream(conf), + MQProperties.class); } else { - kafkaProperties = new Yaml().loadAs(new FileInputStream(conf), KafkaProperties.class); + properties = new Yaml().loadAs(new FileInputStream(conf), MQProperties.class); } // 初始化 kafka producer - canalKafkaProducer = new CanalKafkaProducer(); - canalKafkaProducer.init(kafkaProperties); +// canalMQProducer = new CanalKafkaProducer(); + canalMQProducer.init(properties); // set filterTransactionEntry - if (kafkaProperties.isFilterTransactionEntry()) { + if (properties.isFilterTransactionEntry()) { System.setProperty("canal.instance.filter.transaction.entry", "true"); } // 对应每个instance启动一个worker线程 - List destinations = kafkaProperties.getCanalDestinations(); + List destinations = properties.getCanalDestinations(); executorService = Executors.newFixedThreadPool(destinations.size()); - logger.info("## start the kafka workers."); - for (final CanalDestination destination : destinations) { + logger.info("## start the MQ workers."); + for (final MQProperties.CanalDestination destination : destinations) { executorService.execute(new Runnable() { @Override @@ -73,31 +67,31 @@ public class CanalKafkaStarter implements CanalServerStarter { }); } running = true; - logger.info("## the kafka workers is running now ......"); + logger.info("## the MQ workers is running now ......"); Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { try { - logger.info("## stop the kafka workers"); + logger.info("## stop the MQ workers"); running = false; executorService.shutdown(); - canalKafkaProducer.stop(); + canalMQProducer.stop(); } catch (Throwable e) { - logger.warn("##something goes wrong when stopping kafka workers:", e); + logger.warn("##something goes wrong when stopping MQ workers:", e); } finally { - logger.info("## canal kafka is down."); + logger.info("## canal MQ is down."); } } }); } catch (Throwable e) { - logger.error("## Something goes wrong when starting up the canal kafka workers:", e); + logger.error("## Something goes wrong when starting up the canal MQ workers:", e); System.exit(0); } } - private void worker(CanalDestination destination) { + private void worker(MQProperties.CanalDestination destination) { while (!running) ; logger.info("## start the canal consumer: {}.", destination.getCanalDestination()); @@ -117,19 +111,19 @@ public class CanalKafkaStarter implements CanalServerStarter { logger.info("## the canal consumer {} is running now ......", destination.getCanalDestination()); while (running) { - Message message = server.getWithoutAck(clientIdentity, kafkaProperties.getCanalBatchSize()); // 获取指定数量的数据 + Message message = server.getWithoutAck(clientIdentity, properties.getCanalBatchSize()); // 获取指定数量的数据 long batchId = message.getId(); try { int size = message.isRaw() ? message.getRawEntries().size() : message.getEntries().size(); if (batchId != -1 && size != 0) { if (!StringUtils.isEmpty(destination.getTopic())) { - Topic topic = new Topic(); + MQProperties.Topic topic = new MQProperties.Topic(); topic.setTopic(destination.getTopic()); topic.setPartition(destination.getPartition()); destination.getTopics().add(topic); } - for (Topic topic : destination.getTopics()) { - canalKafkaProducer.send(topic, message); // 发送message到所有topic + for (MQProperties.Topic topic : destination.getTopics()) { + canalMQProducer.send(topic, message); // 发送message到所有topic } } diff --git a/server/src/main/java/com/alibaba/otter/canal/spi/CanalMQProducer.java b/server/src/main/java/com/alibaba/otter/canal/spi/CanalMQProducer.java new file mode 100644 index 00000000..b0edaf5b --- /dev/null +++ b/server/src/main/java/com/alibaba/otter/canal/spi/CanalMQProducer.java @@ -0,0 +1,28 @@ +package com.alibaba.otter.canal.spi; + +import com.alibaba.otter.canal.common.MQProperties; +import com.alibaba.otter.canal.protocol.Message; +import java.io.IOException; + +public interface CanalMQProducer { + /** + * Init producer. + * + * @param mqProperties MQ config + */ + void init(MQProperties mqProperties); + + /** + * Send canal message to related topic + * + * @param topic MQ topic + * @param message canal message + * @throws IOException + */ + void send(MQProperties.Topic topic, Message message) throws IOException; + + /** + * Stop MQ producer service + */ + void stop(); +} \ No newline at end of file From 821f3eced54b8a102fbc25f6ff92946f34fe426e Mon Sep 17 00:00:00 2001 From: duhengforever Date: Fri, 21 Sep 2018 18:41:37 +0800 Subject: [PATCH 2/8] Add mq adapter Signed-off-by: duhengforever --- .../adapter/support/CanalClientConfig.java | 9 ++++ .../adapter/loader/CanalAdapterLoader.java | 50 ++++++++++--------- .../src/main/resources/canal-client.yml | 2 + deployer/src/main/resources/mq.yml | 2 +- .../canal/rocketmq/CanalRocketMQProducer.java | 5 +- 5 files changed, 40 insertions(+), 28 deletions(-) diff --git a/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/CanalClientConfig.java b/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/CanalClientConfig.java index e0658138..6619555c 100644 --- a/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/CanalClientConfig.java +++ b/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/CanalClientConfig.java @@ -111,11 +111,20 @@ public class CanalClientConfig { } public static class MQTopic { + private String mqMode; private String topic; private List groups = new ArrayList<>(); + public String getMqMode() { + return mqMode; + } + + public void setMqMode(String mqMode) { + this.mqMode = mqMode; + } + public String getTopic() { return topic; } diff --git a/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterLoader.java b/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterLoader.java index a705fad7..84517525 100644 --- a/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterLoader.java +++ b/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterLoader.java @@ -4,6 +4,7 @@ import com.alibaba.otter.canal.client.adapter.CanalOuterAdapter; import com.alibaba.otter.canal.client.adapter.support.CanalClientConfig; import com.alibaba.otter.canal.client.adapter.support.CanalOuterAdapterConfiguration; import com.alibaba.otter.canal.client.adapter.support.ExtensionLoader; +import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -17,7 +18,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** - * RocktMQ外部适配器的加载器 + * MQ外部适配器的加载器 * * @version 1.0.0 */ @@ -29,8 +30,7 @@ public class CanalAdapterLoader { private Map canalWorkers = new HashMap<>(); - private Map canalKafkaWorkers = new HashMap<>(); - private Map canalMQWorker = new HashMap<>(); + private Map canalMQWorker = new HashMap<>(); private ExtensionLoader loader; @@ -42,7 +42,7 @@ public class CanalAdapterLoader { * 初始化canal-client、 canal-client-rocketmq的适配器 */ public void init() { - // canal instances 和 rocketmq topics 配置不能同时为空 + // canal instances 和 mq topics 配置不能同时为空 if (canalClientConfig.getCanalInstances() == null && canalClientConfig.getMqTopics() == null) { throw new RuntimeException("Blank config property: canalInstances or canalMQTopics"); } @@ -58,11 +58,6 @@ public class CanalAdapterLoader { } String zkHosts = this.canalClientConfig.getZookeeperHosts(); - // if (zkHosts == null && sa == null) { - // throw new RuntimeException("Blank config property: canalServerHost or - // zookeeperHosts"); - // } - // 初始化canal-client的适配器 if (canalClientConfig.getCanalInstances() != null) { for (CanalClientConfig.CanalInstance instance : canalClientConfig.getCanalInstances()) { @@ -96,19 +91,27 @@ public class CanalAdapterLoader { List canalOuterAdapters = new ArrayList<>(); for (CanalOuterAdapterConfiguration config : group.getOutAdapters()) { - // for (CanalOuterAdapterConfiguration config : adaptor.getOutAdapters()) { loadConnector(config, canalOuterAdapters); - // } } canalOuterAdapterGroups.add(canalOuterAdapters); - CanalAdapterRocketMQWorker rocketMQWorker = new CanalAdapterRocketMQWorker( - canalClientConfig.getBootstrapServers(), - topic.getTopic(), - group.getGroupId(), - canalOuterAdapterGroups); - canalMQWorker.put(topic.getTopic() + "-" + group.getGroupId(), rocketMQWorker); - rocketMQWorker.start(); + if (StringUtils.isBlank(topic.getMqMode()) || "rocketmq".equalsIgnoreCase(topic.getMqMode())) { + CanalAdapterRocketMQWorker rocketMQWorker = new CanalAdapterRocketMQWorker( + canalClientConfig.getBootstrapServers(), + topic.getTopic(), + group.getGroupId(), + canalOuterAdapterGroups); + canalMQWorker.put(topic.getTopic() + "-rocketmq-" + group.getGroupId(), rocketMQWorker); + rocketMQWorker.start(); + } else if ("kafka".equalsIgnoreCase(topic.getMqMode())) { + CanalAdapterKafkaWorker canalKafkaWorker = new CanalAdapterKafkaWorker(zkHosts, + canalClientConfig.getBootstrapServers(), + topic.getTopic(), + group.getGroupId(), + canalOuterAdapterGroups); + canalMQWorker.put(topic.getTopic() + "-kafka-" + group.getGroupId(), canalKafkaWorker); + canalKafkaWorker.start(); + } logger.info("Start adapter for canal-client rocketmq topic: {} succeed", topic.getTopic() + "-" + group.getGroupId()); } @@ -149,15 +152,14 @@ public class CanalAdapterLoader { } stopExecutorService.shutdown(); } - if (canalKafkaWorkers.size() > 0) { - ExecutorService stopKafkaExecutorService = Executors.newFixedThreadPool(canalKafkaWorkers.size()); - for (CanalAdapterKafkaWorker v : canalKafkaWorkers.values()) { - final CanalAdapterKafkaWorker cakw = v; + if (canalMQWorker.size() > 0) { + ExecutorService stopKafkaExecutorService = Executors.newFixedThreadPool(canalMQWorker.size()); + for (AbstractCanalAdapterWorker tmp : canalMQWorker.values()) { + final AbstractCanalAdapterWorker worker = tmp; stopKafkaExecutorService.submit(new Runnable() { - @Override public void run() { - cakw.stop(); + worker.stop(); } }); } diff --git a/client-launcher/src/main/resources/canal-client.yml b/client-launcher/src/main/resources/canal-client.yml index a5fc4813..6117fb95 100644 --- a/client-launcher/src/main/resources/canal-client.yml +++ b/client-launcher/src/main/resources/canal-client.yml @@ -10,7 +10,9 @@ canalServerHost: 127.0.0.1:11111 # - name: hbase # hosts: slave1:2181 # properties: {znodeParent: "/hbase-unsecure"} + mqTopics: +- mqMode: rocketmq - topic: example groups: - groupId: example_g1 diff --git a/deployer/src/main/resources/mq.yml b/deployer/src/main/resources/mq.yml index d8b18f4a..214bcb6f 100644 --- a/deployer/src/main/resources/mq.yml +++ b/deployer/src/main/resources/mq.yml @@ -10,7 +10,7 @@ filterTransactionEntry: true canalDestinations: - canalDestination: example topic: example - partition: + partition: 0 # 一个destination可以对应多个topic #topics: # - topic: example diff --git a/server/src/main/java/com/alibaba/otter/canal/rocketmq/CanalRocketMQProducer.java b/server/src/main/java/com/alibaba/otter/canal/rocketmq/CanalRocketMQProducer.java index 5a2900de..b7c57f2a 100644 --- a/server/src/main/java/com/alibaba/otter/canal/rocketmq/CanalRocketMQProducer.java +++ b/server/src/main/java/com/alibaba/otter/canal/rocketmq/CanalRocketMQProducer.java @@ -35,14 +35,13 @@ public class CanalRocketMQProducer implements CanalMQProducer { } @Override - public void send(MQProperties.Topic topic, com.alibaba.otter.canal.protocol.Message data) { + public void send(final MQProperties.Topic topic, com.alibaba.otter.canal.protocol.Message data) { try { Message message = new Message(topic.getTopic(), CanalMessageSerializer.serializer(data)); this.defaultMQProducer.send(message, new MessageQueueSelector() { @Override public MessageQueue select(List mqs, Message msg, Object arg) { - // int index = (arg.hashCode() % mqs.size()); - return mqs.get(1); + return mqs.get(topic.getPartition()); } }, null); } catch (MQClientException | RemotingException | MQBrokerException | InterruptedException e) { From a37a7ff1423bd3af364fd21cc452ed2784787d31 Mon Sep 17 00:00:00 2001 From: duhengforever Date: Thu, 27 Sep 2018 10:34:17 +0800 Subject: [PATCH 3/8] Add RocketMQ example and polish mq abstract class --- .../loader/AbstractCanalAdapterWorker.java | 41 ++++++ .../loader/CanalAdapterKafkaWorker.java | 50 +------ .../adapter/loader/CanalAdapterLoader.java | 8 +- .../loader/CanalAdapterRocketMQWorker.java | 53 +------ .../src/main/resources/canal-client.yml | 2 +- .../rocketmq/RocketMQCanalConnector.java | 9 +- .../rocketmq/AbstractRocektMQTest.java | 8 ++ .../rocketmq/CanalRocketMQClientExample.java | 135 ++++++++++++++++++ deployer/src/main/resources/mq.yml | 2 +- 9 files changed, 207 insertions(+), 101 deletions(-) create mode 100644 client/src/test/java/com/alibaba/otter/canal/client/running/rocketmq/AbstractRocektMQTest.java create mode 100644 client/src/test/java/com/alibaba/otter/canal/client/running/rocketmq/CanalRocketMQClientExample.java diff --git a/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/AbstractCanalAdapterWorker.java b/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/AbstractCanalAdapterWorker.java index 9dd328e3..8382d0a3 100644 --- a/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/AbstractCanalAdapterWorker.java +++ b/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/AbstractCanalAdapterWorker.java @@ -77,6 +77,47 @@ public abstract class AbstractCanalAdapterWorker { } } + protected void writeOut(Message message,String topic){ + if (logger.isDebugEnabled()) { + logger.debug("topic: {} batchId: {} batchSize: {} ", + topic, + message.getId(), + message.getEntries().size()); + } + long begin = System.currentTimeMillis(); + writeOut(message); + long now = System.currentTimeMillis(); + if ((System.currentTimeMillis() - begin) > 5 * 60 * 1000) { + logger.error("topic: {} batchId {} elapsed time: {} ms", + topic, + message.getId(), + now - begin); + } + if (logger.isDebugEnabled()) { + logger.debug("topic: {} batchId {} elapsed time: {} ms", + topic, + message.getId(), + now - begin); + } + } + + protected void stopOutAdapters(){ + if (thread != null) { + try { + thread.join(); + } catch (InterruptedException e) { + // ignore + } + } + groupInnerExecutorService.shutdown(); + logger.info("topic connectors' worker thread dead!"); + for (List outerAdapters : canalOuterAdapters) { + for (CanalOuterAdapter adapter : outerAdapters) { + adapter.destroy(); + } + } + logger.info("topic all connectors destroyed!"); + } public abstract void start(); public abstract void stop(); diff --git a/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterKafkaWorker.java b/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterKafkaWorker.java index 5bccb51a..ab26c891 100644 --- a/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterKafkaWorker.java +++ b/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterKafkaWorker.java @@ -24,10 +24,10 @@ public class CanalAdapterKafkaWorker extends AbstractCanalAdapterWorker { private KafkaCanalConnector connector; - private String topic; + private String topic; public CanalAdapterKafkaWorker(String zkServers, String bootstrapServers, String topic, String groupId, - List> canalOuterAdapters){ + List> canalOuterAdapters) { this.canalOuterAdapters = canalOuterAdapters; this.groupInnerExecutorService = Executors.newFixedThreadPool(canalOuterAdapters.size()); this.topic = topic; @@ -60,29 +60,11 @@ public class CanalAdapterKafkaWorker extends AbstractCanalAdapterWorker { if (!running) { return; } - connector.stopRunning(); running = false; - - // if (switcher != null && !switcher.state()) { - // switcher.set(true); - // } - - if (thread != null) { - try { - thread.join(); - } catch (InterruptedException e) { - // ignore - } - } - groupInnerExecutorService.shutdown(); - logger.info("topic {} connectors' worker thread dead!", this.topic); - for (List outerAdapters : canalOuterAdapters) { - for (CanalOuterAdapter adapter : outerAdapters) { - adapter.destroy(); - } - } - logger.info("topic {} all connectors destroyed!", this.topic); + logger.info("Stop topic {} out adapters begin", this.topic); + stopOutAdapters(); + logger.info("Stop topic {} out adapters end", this.topic); } catch (Exception e) { logger.error(e.getMessage(), e); } @@ -113,27 +95,7 @@ public class CanalAdapterKafkaWorker extends AbstractCanalAdapterWorker { @Override public void run() { try { - if (logger.isDebugEnabled()) { - logger.debug("topic: {} batchId: {} batchSize: {} ", - topic, - message.getId(), - message.getEntries().size()); - } - long begin = System.currentTimeMillis(); - writeOut(message); - long now = System.currentTimeMillis(); - if ((System.currentTimeMillis() - begin) > 5 * 60 * 1000) { - logger.error("topic: {} batchId {} elapsed time: {} ms", - topic, - message.getId(), - now - begin); - } - if (logger.isDebugEnabled()) { - logger.debug("topic: {} batchId {} elapsed time: {} ms", - topic, - message.getId(), - now - begin); - } + writeOut(message, topic); } catch (Exception e) { logger.error(e.getMessage(), e); } finally { diff --git a/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterLoader.java b/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterLoader.java index 84517525..0234a924 100644 --- a/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterLoader.java +++ b/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterLoader.java @@ -82,7 +82,7 @@ public class CanalAdapterLoader { } } - // 初始化canal-client-rocketmq的适配器 + // 初始化canal-client-mq的适配器 if (canalClientConfig.getMqTopics() != null) { for (CanalClientConfig.MQTopic topic : canalClientConfig.getMqTopics()) { for (CanalClientConfig.Group group : topic.getGroups()) { @@ -153,17 +153,17 @@ public class CanalAdapterLoader { stopExecutorService.shutdown(); } if (canalMQWorker.size() > 0) { - ExecutorService stopKafkaExecutorService = Executors.newFixedThreadPool(canalMQWorker.size()); + ExecutorService stopMQWokerService = Executors.newFixedThreadPool(canalMQWorker.size()); for (AbstractCanalAdapterWorker tmp : canalMQWorker.values()) { final AbstractCanalAdapterWorker worker = tmp; - stopKafkaExecutorService.submit(new Runnable() { + stopMQWokerService.submit(new Runnable() { @Override public void run() { worker.stop(); } }); } - stopKafkaExecutorService.shutdown(); + stopMQWokerService.shutdown(); } logger.info("All canal adapters destroyed"); } diff --git a/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterRocketMQWorker.java b/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterRocketMQWorker.java index 029d9ada..8c962e8d 100644 --- a/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterRocketMQWorker.java +++ b/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterRocketMQWorker.java @@ -55,29 +55,11 @@ public class CanalAdapterRocketMQWorker extends AbstractCanalAdapterWorker { if (!running) { return; } - connector.stopRunning(); running = false; - - // if (switcher != null && !switcher.state()) { - // switcher.set(true); - // } - - if (thread != null) { - try { - thread.join(); - } catch (InterruptedException e) { - // ignore - } - } - groupInnerExecutorService.shutdown(); - logger.info("topic {} connectors' worker thread dead!", this.topic); - for (List outerAdapters : canalOuterAdapters) { - for (CanalOuterAdapter adapter : outerAdapters) { - adapter.destroy(); - } - } - logger.info("topic {} all connectors destroyed!", this.topic); + logger.info("Stop topic {} out adapters begin", this.topic); + stopOutAdapters(); + logger.info("Stop topic {} out adapters end", this.topic); } catch (Exception e) { logger.error(e.getMessage(), e); } @@ -87,7 +69,6 @@ public class CanalAdapterRocketMQWorker extends AbstractCanalAdapterWorker { while (!running) ; ExecutorService executor = Executors.newSingleThreadExecutor(); - final AtomicBoolean executing = new AtomicBoolean(true); while (running) { try { logger.info("=============> Start to connect topic: {} <=============", this.topic); @@ -100,41 +81,17 @@ public class CanalAdapterRocketMQWorker extends AbstractCanalAdapterWorker { // switcher.get(); //等待开关开启 final Message message = connector.getWithoutAck(1); - - executing.set(true); if (message != null) { executor.submit(new Runnable() { @Override public void run() { try { - if (logger.isDebugEnabled()) { - logger.debug("topic: {} batchId: {} batchSize: {} ", - topic, - message.getId(), - message.getEntries().size()); - } - long begin = System.currentTimeMillis(); - writeOut(message); - long now = System.currentTimeMillis(); - if ((System.currentTimeMillis() - begin) > 5 * 60 * 1000) { - logger.error("topic: {} batchId {} elapsed time: {} ms", - topic, - message.getId(), - now - begin); - } - if (logger.isDebugEnabled()) { - logger.debug("topic: {} batchId {} elapsed time: {} ms", - topic, - message.getId(), - now - begin); - } - connector.ack(message.getId()); + writeOut(message, topic); } catch (Exception e) { logger.error(e.getMessage(), e); - } finally { - executing.compareAndSet(true, false); } + connector.ack(message.getId()); } }); } else { diff --git a/client-launcher/src/main/resources/canal-client.yml b/client-launcher/src/main/resources/canal-client.yml index 6117fb95..2556de6c 100644 --- a/client-launcher/src/main/resources/canal-client.yml +++ b/client-launcher/src/main/resources/canal-client.yml @@ -1,6 +1,6 @@ canalServerHost: 127.0.0.1:11111 #zookeeperHosts: slave1:2181 -#bootstrapServers: slave1:6667,slave2:6667 +#bootstrapServers: slave1:6667,slave2:6667 #or rocketmq nameservers #canalInstances: #- instance: example diff --git a/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalConnector.java b/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalConnector.java index e6407308..a1d249c6 100644 --- a/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalConnector.java +++ b/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalConnector.java @@ -75,6 +75,7 @@ public class RocketMQCanalConnector implements CanalConnector { context.setAutoCommit(true); boolean isSuccess = process(messageExts); if (isSuccess) { + logger.info("Dispatch success!"); return ConsumeOrderlyStatus.SUCCESS; } else { return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT; @@ -155,7 +156,7 @@ public class RocketMQCanalConnector implements CanalConnector { @Override public Message getWithoutAck(int batchSize) throws CanalClientException { ConsumerBatchMessage batchMessage = messageBlockingQueue.poll(); - if (batchMessage != null){ + if (batchMessage != null) { return getMessage(batchMessage); } return null; @@ -168,14 +169,16 @@ public class RocketMQCanalConnector implements CanalConnector { return getMessage(batchMessage); } catch (InterruptedException ex) { logger.warn("Get message timeout", ex); - throw new CanalClientException("failed to fetch the data after " + timeout); + throw new CanalClientException("Failed to fetch the data after: " + timeout); } } @Override public void ack(long batchId) throws CanalClientException { ConsumerBatchMessage batchMessage = messageCache.get(batchId); - batchMessage.ack(); + if (batchMessage != null) { + batchMessage.ack(); + } } @Override diff --git a/client/src/test/java/com/alibaba/otter/canal/client/running/rocketmq/AbstractRocektMQTest.java b/client/src/test/java/com/alibaba/otter/canal/client/running/rocketmq/AbstractRocektMQTest.java new file mode 100644 index 00000000..3e65e66e --- /dev/null +++ b/client/src/test/java/com/alibaba/otter/canal/client/running/rocketmq/AbstractRocektMQTest.java @@ -0,0 +1,8 @@ +package com.alibaba.otter.canal.client.running.rocketmq; + +public class AbstractRocektMQTest { + public static String topic = "example"; + public static String groupId = "group"; + public static String nameServers = "localhost:9876"; + +} diff --git a/client/src/test/java/com/alibaba/otter/canal/client/running/rocketmq/CanalRocketMQClientExample.java b/client/src/test/java/com/alibaba/otter/canal/client/running/rocketmq/CanalRocketMQClientExample.java new file mode 100644 index 00000000..7b15efbf --- /dev/null +++ b/client/src/test/java/com/alibaba/otter/canal/client/running/rocketmq/CanalRocketMQClientExample.java @@ -0,0 +1,135 @@ +package com.alibaba.otter.canal.client.running.rocketmq; + +import com.alibaba.otter.canal.client.rocketmq.RocketMQCanalConnector; +import com.alibaba.otter.canal.client.rocketmq.RocketMQCanalConnectorProvider; +import com.alibaba.otter.canal.client.running.kafka.AbstractKafkaTest; +import com.alibaba.otter.canal.protocol.Message; +import org.apache.kafka.common.errors.WakeupException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.Assert; + +/** + * Kafka client example + * + * @author machengyuan @ 2018-6-12 + * @version 1.0.0 + */ +public class CanalRocketMQClientExample extends AbstractRocektMQTest { + + protected final static Logger logger = LoggerFactory.getLogger(CanalRocketMQClientExample.class); + + private RocketMQCanalConnector connector; + + private static volatile boolean running = false; + + private Thread thread = null; + + private Thread.UncaughtExceptionHandler handler = new Thread.UncaughtExceptionHandler() { + + public void uncaughtException(Thread t, Throwable e) { + logger.error("parse events has an error", e); + } + }; + + public CanalRocketMQClientExample(String nameServers, String topic, + String groupId) { + connector = RocketMQCanalConnectorProvider.newRocketMQConnector(nameServers, + topic, + groupId); + } + + public static void main(String[] args) { + try { + final CanalRocketMQClientExample rocketMQClientExample = new CanalRocketMQClientExample(nameServers, + topic, groupId); + logger.info("## Start the rocketmq consumer: {}-{}", AbstractKafkaTest.topic, AbstractKafkaTest.groupId); + rocketMQClientExample.start(); + logger.info("## The canal rocketmq consumer is running now ......"); + Runtime.getRuntime().addShutdownHook(new Thread() { + + public void run() { + try { + logger.info("## Stop the rocketmq consumer"); + rocketMQClientExample.stop(); + } catch (Throwable e) { + logger.warn("## Something goes wrong when stopping rocketmq consumer:", e); + } finally { + logger.info("## Rocketmq consumer is down."); + } + } + + }); + while (running) + ; + } catch (Throwable e) { + logger.error("## Something going wrong when starting up the rocketmq consumer:", e); + System.exit(0); + } + } + + public void start() { + Assert.notNull(connector, "connector is null"); + thread = new Thread(new Runnable() { + + public void run() { + process(); + } + }); + thread.setUncaughtExceptionHandler(handler); + thread.start(); + running = true; + } + + public void stop() { + if (!running) { + return; + } + connector.stopRunning(); + running = false; + if (thread != null) { + try { + thread.join(); + } catch (InterruptedException e) { + // ignore + } + } + } + + private void process() { + while (!running) + ; + while (running) { + try { + connector.connect(); + connector.subscribe(); + while (running) { + Message message = connector.getWithoutAck(1); // 获取message + try { + if (message == null) { + continue; + } + long batchId = message.getId(); + int size = message.getEntries().size(); + if (batchId == -1 || size == 0) { + } else { + logger.info(message.toString()); + } + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + connector.ack(message.getId()); // 提交确认 + } + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + } + + try { + connector.unsubscribe(); + } catch (WakeupException e) { + // No-op. Continue process + } + connector.stopRunning(); + } +} diff --git a/deployer/src/main/resources/mq.yml b/deployer/src/main/resources/mq.yml index 214bcb6f..a19a0e8b 100644 --- a/deployer/src/main/resources/mq.yml +++ b/deployer/src/main/resources/mq.yml @@ -10,7 +10,7 @@ filterTransactionEntry: true canalDestinations: - canalDestination: example topic: example - partition: 0 + partition: 1 # 一个destination可以对应多个topic #topics: # - topic: example From af03f07cc7706cda66a98df5360b31424cf657d8 Mon Sep 17 00:00:00 2001 From: duhengforever Date: Thu, 27 Sep 2018 19:39:46 +0800 Subject: [PATCH 4/8] Modify default config --- deployer/src/main/resources/example/instance.properties | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/deployer/src/main/resources/example/instance.properties b/deployer/src/main/resources/example/instance.properties index 3cdbac4a..f72b65fe 100644 --- a/deployer/src/main/resources/example/instance.properties +++ b/deployer/src/main/resources/example/instance.properties @@ -6,7 +6,7 @@ canal.instance.gtidon=false # position info -canal.instance.master.address=120.78.225.173:3307 +canal.instance.master.address=127.0.0.1:3306 canal.instance.master.journal.name= canal.instance.master.position= canal.instance.master.timestamp= @@ -18,9 +18,8 @@ canal.instance.rds.secretkey= canal.instance.rds.instanceId= # table meta tsdb info -#canal.instance.tsdb.enable=true -#canal.instance.tsdb.url=jdbc:mysql://120.78.225.173:3306/test -#canal.instance.mysql.slaveId = 1234 +canal.instance.tsdb.enable=true +#canal.instance.tsdb.url=jdbc:mysql://127.0.0.1:3306/canal_tsdb #canal.instance.tsdb.dbUsername=canal #canal.instance.tsdb.dbPassword=canal @@ -32,7 +31,7 @@ canal.instance.rds.instanceId= # username/password canal.instance.dbUsername=canal -canal.instance.dbPassword=P@ssw0rd +canal.instance.dbPassword=canal canal.instance.connectionCharset=UTF-8 # table regex From fa8588f766630ecd2b17de5d459ded94bf1dd914 Mon Sep 17 00:00:00 2001 From: duhengforever Date: Sun, 30 Sep 2018 09:40:05 +0800 Subject: [PATCH 5/8] Modify canal-client config and polish consumer implementation --- .../adapter/loader/CanalAdapterRocketMQWorker.java | 1 + client-launcher/src/main/resources/canal-client.yml | 6 +++--- .../canal/client/rocketmq/RocketMQCanalConnector.java | 11 ++++++----- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterRocketMQWorker.java b/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterRocketMQWorker.java index 8c962e8d..98f08286 100644 --- a/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterRocketMQWorker.java +++ b/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterRocketMQWorker.java @@ -26,6 +26,7 @@ public class CanalAdapterRocketMQWorker extends AbstractCanalAdapterWorker { public CanalAdapterRocketMQWorker(String nameServers, String topic, String groupId, List> canalOuterAdapters) { + logger.info("RocketMQ consumer config topic:{}, nameServer:{}, groupId:{}", topic, nameServers, groupId); this.canalOuterAdapters = canalOuterAdapters; this.groupInnerExecutorService = Executors.newFixedThreadPool(canalOuterAdapters.size()); this.topic = topic; diff --git a/client-launcher/src/main/resources/canal-client.yml b/client-launcher/src/main/resources/canal-client.yml index 6aac0978..d2cb2175 100644 --- a/client-launcher/src/main/resources/canal-client.yml +++ b/client-launcher/src/main/resources/canal-client.yml @@ -1,6 +1,6 @@ #canalServerHost: 127.0.0.1:11111 #zookeeperHosts: slave1:2181 -bootstrapServers: slave1:6667,slave2:6667 #or rocketmq nameservers +bootstrapServers: slave1:6667,slave2:6667 #or rocketmq nameservers:host1:9876;host2:9876 flatMessage: true #canalInstances: @@ -14,9 +14,9 @@ flatMessage: true mqTopics: - mqMode: rocketmq -- topic: example + topic: example groups: - - groupId: example_g1 + - groupId: example outAdapters: - name: logger # - name: hbase diff --git a/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalConnector.java b/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalConnector.java index 0465ec6c..f24955c7 100644 --- a/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalConnector.java +++ b/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalConnector.java @@ -43,6 +43,9 @@ public class RocketMQCanalConnector implements CanalConnector { @Override public void connect() throws CanalClientException { rocketMQConsumer = new DefaultMQPushConsumer(groupName); + if (!StringUtils.isBlank(nameServer)) { + rocketMQConsumer.setNamesrvAddr(nameServer); + } } @Override @@ -61,18 +64,16 @@ public class RocketMQCanalConnector implements CanalConnector { return; } try { - rocketMQConsumer.subscribe(topic, "*"); if (rocketMQConsumer == null) { - rocketMQConsumer = new DefaultMQPushConsumer(groupName); - if (!StringUtils.isBlank(nameServer)) { - rocketMQConsumer.setNamesrvAddr(nameServer); - } + this.connect(); } + rocketMQConsumer.subscribe(topic, "*"); rocketMQConsumer.registerMessageListener(new MessageListenerOrderly() { @Override public ConsumeOrderlyStatus consumeMessage(List messageExts, ConsumeOrderlyContext context) { context.setAutoCommit(true); + logger.info("xxxx"); boolean isSuccess = process(messageExts); if (isSuccess) { logger.info("Dispatch success!"); From 3ed89877796ab113504165578b7555870b589884 Mon Sep 17 00:00:00 2001 From: duhengforever Date: Sun, 30 Sep 2018 09:49:13 +0800 Subject: [PATCH 6/8] Remove useless log print --- .../otter/canal/client/rocketmq/RocketMQCanalConnector.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalConnector.java b/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalConnector.java index f24955c7..8031e792 100644 --- a/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalConnector.java +++ b/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalConnector.java @@ -73,10 +73,8 @@ public class RocketMQCanalConnector implements CanalConnector { public ConsumeOrderlyStatus consumeMessage(List messageExts, ConsumeOrderlyContext context) { context.setAutoCommit(true); - logger.info("xxxx"); boolean isSuccess = process(messageExts); if (isSuccess) { - logger.info("Dispatch success!"); return ConsumeOrderlyStatus.SUCCESS; } else { return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT; From 9344ed121f1aba18da23c3edb6bbd210e4ba253e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=83=E9=94=8B?= Date: Sun, 30 Sep 2018 10:52:41 +0800 Subject: [PATCH 7/8] format & import --- .../client/adapter/CanalOuterAdapter.java | 2 - .../adapter/support/CanalClientConfig.java | 2 +- .../CanalOuterAdapterConfiguration.java | 6 +- .../canal/client/adapter/support/Dml.java | 4 +- .../adapter/support/ExtensionLoader.java | 90 +++++---- .../client/adapter/support/MessageUtil.java | 16 +- .../canal/client/adapter/support/SPI.java | 6 +- .../client/adapter/hbase/HbaseAdapter.java | 6 +- .../adapter/hbase/config/MappingConfig.java | 8 +- .../hbase/service/HbaseSyncService.java | 25 ++- .../adapter/hbase/support/HbaseTemplate.java | 6 +- .../client/adapter/hbase/support/PhType.java | 36 +--- .../adapter/hbase/support/PhTypeUtil.java | 22 +- .../client/adapter/hbase/support/Type.java | 16 +- .../adapter/logger/LoggerAdapterExample.java | 3 +- .../otter/canal/client/ClientLauncher.java | 7 +- .../loader/AbstractCanalAdapterWorker.java | 28 +-- .../loader/CanalAdapterKafkaWorker.java | 4 +- .../adapter/loader/CanalAdapterLoader.java | 44 ++-- .../loader/CanalAdapterRocketMQWorker.java | 19 +- .../client/CanalMessageDeserializer.java | 37 ++-- .../client/impl/ClusterCanalConnector.java | 9 +- .../client/impl/SimpleCanalConnector.java | 43 +--- .../impl/running/ClientRunningMonitor.java | 2 +- .../client/kafka/KafkaCanalConnector.java | 6 +- .../client/kafka/KafkaCanalConnectors.java | 7 +- .../client/kafka/MessageDeserializer.java | 33 --- .../kafka/running/ClientRunningMonitor.java | 9 +- .../client/rocketmq/ConsumerBatchMessage.java | 9 +- .../rocketmq/RocketMQCanalConnector.java | 33 +-- .../RocketMQCanalConnectorProvider.java | 4 +- .../rocketmq/RocketMQCanalListener.java | 2 + .../client/running/ClientRunningTest.java | 188 +++++++++--------- .../kafka/CanalKafkaClientExample.java | 3 +- .../rocketmq/AbstractRocektMQTest.java | 5 +- .../rocketmq/CanalRocketMQClientExample.java | 35 ++-- deployer/src/main/resources/canal.properties | 2 +- .../otter/canal/protocol/ClientIdentity.java | 7 +- .../canal/common/CanalMessageSerializer.java | 18 +- .../otter/canal/common/MQProperties.java | 31 ++- .../otter/canal/kafka/CanalKafkaProducer.java | 31 +-- .../otter/canal/kafka/MessageSerializer.java | 6 +- .../canal/rocketmq/CanalRocketMQProducer.java | 6 +- .../otter/canal/server/CanalMQStarter.java | 35 ++-- .../server/netty/handler/SessionHandler.java | 2 +- .../otter/canal/spi/CanalMQProducer.java | 9 +- 46 files changed, 458 insertions(+), 464 deletions(-) delete mode 100644 client/src/main/java/com/alibaba/otter/canal/client/kafka/MessageDeserializer.java diff --git a/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/CanalOuterAdapter.java b/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/CanalOuterAdapter.java index 0871eb74..5fe0a377 100644 --- a/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/CanalOuterAdapter.java +++ b/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/CanalOuterAdapter.java @@ -3,8 +3,6 @@ package com.alibaba.otter.canal.client.adapter; import com.alibaba.otter.canal.client.adapter.support.CanalOuterAdapterConfiguration; import com.alibaba.otter.canal.client.adapter.support.Dml; import com.alibaba.otter.canal.client.adapter.support.SPI; -import com.alibaba.otter.canal.protocol.FlatMessage; -import com.alibaba.otter.canal.protocol.Message; /** * 外部适配器接口 diff --git a/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/CanalClientConfig.java b/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/CanalClientConfig.java index 493d902a..d885e1cb 100644 --- a/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/CanalClientConfig.java +++ b/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/CanalClientConfig.java @@ -24,7 +24,6 @@ public class CanalClientConfig { private Boolean flatMessage = true; - private List canalInstances; public String getCanalServerHost() { @@ -122,6 +121,7 @@ public class CanalClientConfig { } public static class MQTopic { + private String mqMode; private String topic; diff --git a/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/CanalOuterAdapterConfiguration.java b/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/CanalOuterAdapterConfiguration.java index e6e34b67..790044c8 100644 --- a/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/CanalOuterAdapterConfiguration.java +++ b/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/CanalOuterAdapterConfiguration.java @@ -10,11 +10,11 @@ import java.util.Properties; */ public class CanalOuterAdapterConfiguration { - private String name; // 适配器名称, 如: logger, hbase, es + private String name; // 适配器名称, 如: logger, hbase, es - private String hosts; // 适配器内部的地址, 比如对应es该参数可以填写es的server地址 + private String hosts; // 适配器内部的地址, 比如对应es该参数可以填写es的server地址 - private String zkHosts; // 适配器内部的ZK地址, 比如对应HBase该参数可以填写HBase对应的ZK地址 + private String zkHosts; // 适配器内部的ZK地址, 比如对应HBase该参数可以填写HBase对应的ZK地址 private Properties properties; // 其余参数, 可填写适配器中的所需的配置信息 diff --git a/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/Dml.java b/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/Dml.java index fe7333e5..db05c5b4 100644 --- a/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/Dml.java +++ b/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/Dml.java @@ -90,7 +90,7 @@ public class Dml implements Serializable { @Override public String toString() { - return "Dml{" + "database='" + database + '\'' + ", table='" + table + '\'' + ", type='" + type + '\'' + ", ts=" - + ts + ", sql='" + sql + '\'' + ", data=" + data + ", old=" + old + '}'; + return "Dml{" + "database='" + database + '\'' + ", table='" + table + '\'' + ", type='" + type + '\'' + + ", ts=" + ts + ", sql='" + sql + '\'' + ", data=" + data + ", old=" + old + '}'; } } diff --git a/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/ExtensionLoader.java b/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/ExtensionLoader.java index 01b05054..b5de0d82 100644 --- a/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/ExtensionLoader.java +++ b/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/ExtensionLoader.java @@ -1,11 +1,22 @@ package com.alibaba.otter.canal.client.adapter.support; -import java.io.*; +import java.io.BufferedReader; +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.InputStreamReader; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.nio.file.Paths; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.regex.Pattern; @@ -21,8 +32,7 @@ import org.slf4j.LoggerFactory; */ public class ExtensionLoader { - private static final Logger logger = LoggerFactory - .getLogger(ExtensionLoader.class); + private static final Logger logger = LoggerFactory.getLogger(ExtensionLoader.class); private static final String SERVICES_DIRECTORY = "META-INF/services/"; @@ -30,8 +40,7 @@ public class ExtensionLoader { private static final String DEFAULT_CLASSLOADER_POLICY = "internal"; - private static final Pattern NAME_SEPARATOR = Pattern - .compile("\\s*[,]+\\s*"); + private static final Pattern NAME_SEPARATOR = Pattern.compile("\\s*[,]+\\s*"); private static final ConcurrentMap, ExtensionLoader> EXTENSION_LOADERS = new ConcurrentHashMap<>(); @@ -262,8 +271,7 @@ public class ExtensionLoader { return instance; } catch (Throwable t) { throw new IllegalStateException("Extension instance(name: " + name + ", class: " + type - + ") could not be instantiated: " + t.getMessage(), - t); + + ") could not be instantiated: " + t.getMessage(), t); } } @@ -271,8 +279,8 @@ public class ExtensionLoader { if (type == null) throw new IllegalArgumentException("Extension type == null"); if (name == null) throw new IllegalArgumentException("Extension name == null"); Class clazz = getExtensionClasses().get(name); - if (clazz == null) - throw new IllegalStateException("No such extension \"" + name + "\" for " + type.getName() + "!"); + if (clazz == null) throw new IllegalStateException("No such extension \"" + name + "\" for " + type.getName() + + "!"); return clazz; } @@ -296,10 +304,17 @@ public class ExtensionLoader { throw new IllegalStateException("failed to get class loader resource"); } String dirtyPath = url.toString(); - String jarPath = dirtyPath.replaceAll("^.*file:/", ""); // removes file:/ and everything before it - jarPath = jarPath.replaceAll("jar!.*", "jar"); // removes everything after .jar, if .jar exists in dirtyPath - jarPath = jarPath.replaceAll("%20", " "); // necessary if path has spaces within - if (!jarPath.endsWith(".jar")) { // this is needed if you plan to run the app using Spring Tools Suit play + String jarPath = dirtyPath.replaceAll("^.*file:/", ""); // removes + // file:/ and + // everything + // before it + jarPath = jarPath.replaceAll("jar!.*", "jar"); // removes everything + // after .jar, if .jar + // exists in dirtyPath + jarPath = jarPath.replaceAll("%20", " "); // necessary if path has + // spaces within + if (!jarPath.endsWith(".jar")) { // this is needed if you plan to run + // the app using Spring Tools Suit play // button. jarPath = jarPath.replaceAll("/classes/.*", "/classes/"); } @@ -327,8 +342,8 @@ public class ExtensionLoader { logger.info("extension classpath dir: " + dir); File externalLibDir = new File(dir); if (!externalLibDir.exists()) { - externalLibDir = new File( - File.separator + this.getJarDirectoryPath() + File.separator + "canal_client" + File.separator + "lib"); + externalLibDir = new File(File.separator + this.getJarDirectoryPath() + File.separator + "canal_client" + + File.separator + "lib"); } if (externalLibDir.exists()) { File[] files = externalLibDir.listFiles(new FilenameFilter() { @@ -363,7 +378,8 @@ public class ExtensionLoader { if (name.startsWith("java.") || name.startsWith("org.slf4j.") || name.startsWith("org.apache.logging") || name.startsWith("org.apache.commons.logging.")) { - // || name.startsWith("org.apache.hadoop.")) { + // || name.startsWith("org.apache.hadoop.")) + // { c = super.loadClass(name); } if (c != null) return c; @@ -386,7 +402,8 @@ public class ExtensionLoader { @SuppressWarnings("unchecked") Enumeration[] tmp = (Enumeration[]) new Enumeration[2]; - tmp[0] = findResources(name); // local class path first + tmp[0] = findResources(name); // local class + // path first // tmp[1] = super.getResources(name); return new CompoundEnumeration<>(tmp); @@ -474,12 +491,16 @@ public class ExtensionLoader { } if (line.length() > 0) { Class clazz = classLoader.loadClass(line); - // Class clazz = Class.forName(line, true, classLoader); + // Class clazz = + // Class.forName(line, true, + // classLoader); if (!type.isAssignableFrom(clazz)) { - throw new IllegalStateException( - "Error when load extension class(interface: " + type - + ", class line: " + clazz.getName() - + "), class " + clazz.getName() + throw new IllegalStateException("Error when load extension class(interface: " + + type + + ", class line: " + + clazz.getName() + + "), class " + + clazz.getName() + "is not subtype of interface."); } else { try { @@ -497,9 +518,9 @@ public class ExtensionLoader { extensionClasses.put(n, clazz); } else if (c != clazz) { cachedNames.remove(clazz); - throw new IllegalStateException( - "Duplicate extension " + type.getName() + " name " - + n + " on " + throw new IllegalStateException("Duplicate extension " + + type.getName() + + " name " + n + " on " + c.getName() + " and " + clazz.getName()); } @@ -509,9 +530,12 @@ public class ExtensionLoader { } } } catch (Throwable t) { - IllegalStateException e = new IllegalStateException( - "Failed to load extension class(interface: " + type + ", class line: " - + line + ") in " + url + IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + + type + + ", class line: " + + line + + ") in " + + url + ", cause: " + t.getMessage(), t); @@ -526,15 +550,13 @@ public class ExtensionLoader { } } catch (Throwable t) { logger.error("Exception when load extension class(interface: " + type + ", class file: " + url - + ") in " + url, - t); + + ") in " + url, t); } } // end of while urls } } catch (Throwable t) { - logger.error( - "Exception when load extension class(interface: " + type + ", description file: " + fileName + ").", - t); + logger.error("Exception when load extension class(interface: " + type + ", description file: " + fileName + + ").", t); } } diff --git a/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/MessageUtil.java b/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/MessageUtil.java index 4765ec30..1808ca8d 100644 --- a/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/MessageUtil.java +++ b/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/MessageUtil.java @@ -1,6 +1,11 @@ package com.alibaba.otter.canal.client.adapter.support; -import java.util.*; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; import com.alibaba.otter.canal.protocol.CanalEntry; import com.alibaba.otter.canal.protocol.FlatMessage; @@ -81,11 +86,10 @@ public class MessageUtil { Map rowOld = new LinkedHashMap<>(); for (CanalEntry.Column column : rowData.getBeforeColumnsList()) { if (updateSet.contains(column.getName())) { - rowOld.put(column.getName(), - JdbcTypeUtil.typeConvert(column.getName(), - column.getValue(), - column.getSqlType(), - column.getMysqlType())); + rowOld.put(column.getName(), JdbcTypeUtil.typeConvert(column.getName(), + column.getValue(), + column.getSqlType(), + column.getMysqlType())); } } // update操作将记录修改前的值 diff --git a/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/SPI.java b/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/SPI.java index 9c0116c6..7aa69938 100644 --- a/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/SPI.java +++ b/client-adapter/common/src/main/java/com/alibaba/otter/canal/client/adapter/support/SPI.java @@ -1,6 +1,10 @@ package com.alibaba.otter.canal.client.adapter.support; -import java.lang.annotation.*; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; @Documented @Retention(RetentionPolicy.RUNTIME) diff --git a/client-adapter/hbase/src/main/java/com/alibaba/otter/canal/client/adapter/hbase/HbaseAdapter.java b/client-adapter/hbase/src/main/java/com/alibaba/otter/canal/client/adapter/hbase/HbaseAdapter.java index bbd8fb1c..d75cea67 100644 --- a/client-adapter/hbase/src/main/java/com/alibaba/otter/canal/client/adapter/hbase/HbaseAdapter.java +++ b/client-adapter/hbase/src/main/java/com/alibaba/otter/canal/client/adapter/hbase/HbaseAdapter.java @@ -4,7 +4,6 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; -import com.alibaba.otter.canal.protocol.FlatMessage; import org.apache.commons.lang.StringUtils; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseConfiguration; @@ -17,9 +16,7 @@ import com.alibaba.otter.canal.client.adapter.hbase.config.MappingConfigLoader; import com.alibaba.otter.canal.client.adapter.hbase.service.HbaseSyncService; import com.alibaba.otter.canal.client.adapter.support.CanalOuterAdapterConfiguration; import com.alibaba.otter.canal.client.adapter.support.Dml; -import com.alibaba.otter.canal.client.adapter.support.MessageUtil; import com.alibaba.otter.canal.client.adapter.support.SPI; -import com.alibaba.otter.canal.protocol.Message; /** * HBase外部适配器 @@ -45,8 +42,7 @@ public class HbaseAdapter implements CanalOuterAdapter { mappingConfigCache = new HashMap<>(); for (MappingConfig mappingConfig : hbaseMapping.values()) { mappingConfigCache.put(mappingConfig.getHbaseOrm().getDatabase() + "-" - + mappingConfig.getHbaseOrm().getTable(), - mappingConfig); + + mappingConfig.getHbaseOrm().getTable(), mappingConfig); } } } diff --git a/client-adapter/hbase/src/main/java/com/alibaba/otter/canal/client/adapter/hbase/config/MappingConfig.java b/client-adapter/hbase/src/main/java/com/alibaba/otter/canal/client/adapter/hbase/config/MappingConfig.java index 0e4cbfe6..6d70bdde 100644 --- a/client-adapter/hbase/src/main/java/com/alibaba/otter/canal/client/adapter/hbase/config/MappingConfig.java +++ b/client-adapter/hbase/src/main/java/com/alibaba/otter/canal/client/adapter/hbase/config/MappingConfig.java @@ -1,6 +1,10 @@ package com.alibaba.otter.canal.client.adapter.hbase.config; -import java.util.*; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; /** * HBase表映射配置 @@ -117,7 +121,7 @@ public class MappingConfig { } public enum Mode { - STRING("STRING"), NATIVE("NATIVE"), PHOENIX("PHOENIX"); + STRING("STRING"), NATIVE("NATIVE"), PHOENIX("PHOENIX"); private String type; diff --git a/client-adapter/hbase/src/main/java/com/alibaba/otter/canal/client/adapter/hbase/service/HbaseSyncService.java b/client-adapter/hbase/src/main/java/com/alibaba/otter/canal/client/adapter/hbase/service/HbaseSyncService.java index 13f6a3ac..7e69f9d0 100644 --- a/client-adapter/hbase/src/main/java/com/alibaba/otter/canal/client/adapter/hbase/service/HbaseSyncService.java +++ b/client-adapter/hbase/src/main/java/com/alibaba/otter/canal/client/adapter/hbase/service/HbaseSyncService.java @@ -1,6 +1,10 @@ package com.alibaba.otter.canal.client.adapter.hbase.service; -import java.util.*; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import org.apache.hadoop.hbase.client.Connection; import org.apache.hadoop.hbase.util.Bytes; @@ -8,7 +12,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.otter.canal.client.adapter.hbase.config.MappingConfig; -import com.alibaba.otter.canal.client.adapter.hbase.support.*; +import com.alibaba.otter.canal.client.adapter.hbase.support.HRow; +import com.alibaba.otter.canal.client.adapter.hbase.support.HbaseTemplate; +import com.alibaba.otter.canal.client.adapter.hbase.support.PhType; +import com.alibaba.otter.canal.client.adapter.hbase.support.PhTypeUtil; +import com.alibaba.otter.canal.client.adapter.hbase.support.Type; +import com.alibaba.otter.canal.client.adapter.hbase.support.TypeUtil; import com.alibaba.otter.canal.client.adapter.support.Dml; /** @@ -63,7 +72,8 @@ public class HbaseSyncService { MappingConfig.HbaseOrm hbaseOrm = config.getHbaseOrm(); // if (!validHTable(config)) { - // logger.error("HBase table '{}' not exists", hbaseOrm.getHbaseTable()); + // logger.error("HBase table '{}' not exists", + // hbaseOrm.getHbaseTable()); // return; // } int i = 1; @@ -156,7 +166,8 @@ public class HbaseSyncService { MappingConfig.HbaseOrm hbaseOrm = config.getHbaseOrm(); // if (!validHTable(config)) { - // logger.error("HBase table '{}' not exists", hbaseOrm.getHbaseTable()); + // logger.error("HBase table '{}' not exists", + // hbaseOrm.getHbaseTable()); // return; // } @@ -248,7 +259,8 @@ public class HbaseSyncService { MappingConfig.HbaseOrm hbaseOrm = config.getHbaseOrm(); // if (!validHTable(config)) { - // logger.error("HBase table '{}' not exists", hbaseOrm.getHbaseTable()); + // logger.error("HBase table '{}' not exists", + // hbaseOrm.getHbaseTable()); // return; // } @@ -363,8 +375,7 @@ public class HbaseSyncService { * @param value 值 * @return 复合字段rowKey */ - private static byte[] typeConvert(MappingConfig.ColumnItem columnItem, MappingConfig.HbaseOrm hbaseOrm, - Object value) { + private static byte[] typeConvert(MappingConfig.ColumnItem columnItem, MappingConfig.HbaseOrm hbaseOrm, Object value) { if (value == null) { return null; } diff --git a/client-adapter/hbase/src/main/java/com/alibaba/otter/canal/client/adapter/hbase/support/HbaseTemplate.java b/client-adapter/hbase/src/main/java/com/alibaba/otter/canal/client/adapter/hbase/support/HbaseTemplate.java index 62b7ca03..1956dbfb 100644 --- a/client-adapter/hbase/src/main/java/com/alibaba/otter/canal/client/adapter/hbase/support/HbaseTemplate.java +++ b/client-adapter/hbase/src/main/java/com/alibaba/otter/canal/client/adapter/hbase/support/HbaseTemplate.java @@ -8,7 +8,11 @@ import java.util.Set; import org.apache.hadoop.hbase.HColumnDescriptor; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.TableName; -import org.apache.hadoop.hbase.client.*; +import org.apache.hadoop.hbase.client.Connection; +import org.apache.hadoop.hbase.client.Delete; +import org.apache.hadoop.hbase.client.HBaseAdmin; +import org.apache.hadoop.hbase.client.HTable; +import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.util.Bytes; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/client-adapter/hbase/src/main/java/com/alibaba/otter/canal/client/adapter/hbase/support/PhType.java b/client-adapter/hbase/src/main/java/com/alibaba/otter/canal/client/adapter/hbase/support/PhType.java index 09c63a6c..662f9348 100644 --- a/client-adapter/hbase/src/main/java/com/alibaba/otter/canal/client/adapter/hbase/support/PhType.java +++ b/client-adapter/hbase/src/main/java/com/alibaba/otter/canal/client/adapter/hbase/support/PhType.java @@ -13,37 +13,21 @@ import java.util.Date; * @version 1.0.0 */ public enum PhType { - DEFAULT(-1, "VARCHAR"), - UNSIGNED_INT(4, "UNSIGNED_INT"), - UNSIGNED_LONG(8, "UNSIGNED_LONG"), - UNSIGNED_TINYINT(1, "UNSIGNED_TINYINT"), - UNSIGNED_SMALLINT(2, "UNSIGNED_SMALLINT"), - UNSIGNED_FLOAT(4, "UNSIGNED_FLOAT"), - UNSIGNED_DOUBLE(8, "UNSIGNED_DOUBLE"), - INTEGER(4, "INTEGER"), - BIGINT(8, "BIGINT"), - TINYINT(1, "TINYINT"), - SMALLINT(2, "SMALLINT"), - FLOAT(4, "FLOAT"), - DOUBLE(8, "DOUBLE"), - DECIMAL(-1, "DECIMAL"), - BOOLEAN(1, "BOOLEAN"), - UNSIGNED_TIME(8, "UNSIGNED_TIME"), - UNSIGNED_DATE(8, "UNSIGNED_DATE"), - UNSIGNED_TIMESTAMP(12, "UNSIGNED_TIMESTAMP"), - TIME(8, "TIME"), - DATE(8, "DATE"), - TIMESTAMP(12, "TIMESTAMP"), - VARCHAR(-1, "VARCHAR"), - VARBINARY(-1, "VARBINARY"); + DEFAULT(-1, "VARCHAR"), UNSIGNED_INT(4, "UNSIGNED_INT"), UNSIGNED_LONG(8, "UNSIGNED_LONG"), + UNSIGNED_TINYINT(1, "UNSIGNED_TINYINT"), UNSIGNED_SMALLINT(2, "UNSIGNED_SMALLINT"), + UNSIGNED_FLOAT(4, "UNSIGNED_FLOAT"), UNSIGNED_DOUBLE(8, "UNSIGNED_DOUBLE"), INTEGER(4, "INTEGER"), + BIGINT(8, "BIGINT"), TINYINT(1, "TINYINT"), SMALLINT(2, "SMALLINT"), FLOAT(4, "FLOAT"), DOUBLE(8, "DOUBLE"), + DECIMAL(-1, "DECIMAL"), BOOLEAN(1, "BOOLEAN"), UNSIGNED_TIME(8, "UNSIGNED_TIME"), + UNSIGNED_DATE(8, "UNSIGNED_DATE"), UNSIGNED_TIMESTAMP(12, "UNSIGNED_TIMESTAMP"), TIME(8, "TIME"), DATE(8, "DATE"), + TIMESTAMP(12, "TIMESTAMP"), VARCHAR(-1, "VARCHAR"), VARBINARY(-1, "VARBINARY"); /** * -1:长度可变 */ - private int len; + private int len; private String type; - PhType(int len, String type) { + PhType(int len, String type){ this.len = len; this.type = type; } @@ -87,7 +71,7 @@ public enum PhType { phType = VARCHAR; } else if (BigDecimal.class.isAssignableFrom(javaType)) { phType = DECIMAL; - } else if (BigInteger.class.isAssignableFrom(javaType)) { + } else if (BigInteger.class.isAssignableFrom(javaType)) { phType = UNSIGNED_LONG; } else { phType = DEFAULT; diff --git a/client-adapter/hbase/src/main/java/com/alibaba/otter/canal/client/adapter/hbase/support/PhTypeUtil.java b/client-adapter/hbase/src/main/java/com/alibaba/otter/canal/client/adapter/hbase/support/PhTypeUtil.java index 25473681..a3a8e0b9 100644 --- a/client-adapter/hbase/src/main/java/com/alibaba/otter/canal/client/adapter/hbase/support/PhTypeUtil.java +++ b/client-adapter/hbase/src/main/java/com/alibaba/otter/canal/client/adapter/hbase/support/PhTypeUtil.java @@ -169,7 +169,8 @@ public class PhTypeUtil { private static int encodeInt(int v, byte[] b, int o) { checkForSufficientLength(b, o, Bytes.SIZEOF_INT); - b[o + 0] = (byte) ((v >> 24) ^ 0x80); // Flip sign bit so that INTEGER is binary comparable + b[o + 0] = (byte) ((v >> 24) ^ 0x80); // Flip sign bit so that INTEGER + // is binary comparable b[o + 1] = (byte) (v >> 16); b[o + 2] = (byte) (v >> 8); b[o + 3] = (byte) v; @@ -209,7 +210,8 @@ public class PhTypeUtil { private static int encodeLong(long v, byte[] b, int o) { checkForSufficientLength(b, o, Bytes.SIZEOF_LONG); - b[o + 0] = (byte) ((v >> 56) ^ 0x80); // Flip sign bit so that INTEGER is binary comparable + b[o + 0] = (byte) ((v >> 56) ^ 0x80); // Flip sign bit so that INTEGER + // is binary comparable b[o + 1] = (byte) (v >> 48); b[o + 2] = (byte) (v >> 40); b[o + 3] = (byte) (v >> 32); @@ -255,7 +257,8 @@ public class PhTypeUtil { private static int encodeShort(short v, byte[] b, int o) { checkForSufficientLength(b, o, Bytes.SIZEOF_SHORT); - b[o + 0] = (byte) ((v >> 8) ^ 0x80); // Flip sign bit so that Short is binary comparable + b[o + 0] = (byte) ((v >> 8) ^ 0x80); // Flip sign bit so that Short is + // binary comparable b[o + 1] = (byte) v; return Bytes.SIZEOF_SHORT; } @@ -287,7 +290,8 @@ public class PhTypeUtil { private static int encodeByte(byte v, byte[] b, int o) { checkForSufficientLength(b, o, Bytes.SIZEOF_BYTE); - b[o] = (byte) (v ^ 0x80); // Flip sign bit so that Short is binary comparable + b[o] = (byte) (v ^ 0x80); // Flip sign bit so that Short is binary + // comparable return Bytes.SIZEOF_BYTE; } @@ -546,7 +550,8 @@ public class PhTypeUtil { multiplyBy = 10; divideBy = BigInteger.TEN; } - // Normalize the scale based on what is necessary to end up with a base 100 + // Normalize the scale based on what is necessary to end up with a base + // 100 // decimal (i.e. 10.123e3) int digitOffset; BigInteger compareAgainst; @@ -564,7 +569,8 @@ public class PhTypeUtil { if (length <= MAX_BIG_DECIMAL_BYTES) { result[--index] = NEG_TERMINAL_BYTE; } else { - // Adjust length and offset down because we don't have enough room + // Adjust length and offset down because we don't have enough + // room length = MAX_BIG_DECIMAL_BYTES; index = offset + length; } @@ -593,8 +599,8 @@ public class PhTypeUtil { private static void checkForSufficientLength(byte[] b, int offset, int requiredLength) { if (b.length < offset + requiredLength) { - throw new RuntimeException( - "Expected length of at least " + requiredLength + " bytes, but had " + (b.length - offset)); + throw new RuntimeException("Expected length of at least " + requiredLength + " bytes, but had " + + (b.length - offset)); } } diff --git a/client-adapter/hbase/src/main/java/com/alibaba/otter/canal/client/adapter/hbase/support/Type.java b/client-adapter/hbase/src/main/java/com/alibaba/otter/canal/client/adapter/hbase/support/Type.java index 9a567191..2a44cb30 100644 --- a/client-adapter/hbase/src/main/java/com/alibaba/otter/canal/client/adapter/hbase/support/Type.java +++ b/client-adapter/hbase/src/main/java/com/alibaba/otter/canal/client/adapter/hbase/support/Type.java @@ -11,22 +11,12 @@ import java.util.Date; * @version 1.0.0 */ public enum Type { - DEFAULT("STRING"), - STRING("STRING"), - INTEGER("INTEGER"), - LONG("LONG"), - SHORT("SHORT"), - BOOLEAN("BOOLEAN"), - FLOAT("FLOAT"), - DOUBLE("DOUBLE"), - BIGDECIMAL("BIGDECIMAL"), - DATE("DATE"), - BYTE("BYTE"), - BYTES("BYTES"); + DEFAULT("STRING"), STRING("STRING"), INTEGER("INTEGER"), LONG("LONG"), SHORT("SHORT"), BOOLEAN("BOOLEAN"), + FLOAT("FLOAT"), DOUBLE("DOUBLE"), BIGDECIMAL("BIGDECIMAL"), DATE("DATE"), BYTE("BYTE"), BYTES("BYTES"); private String type; - Type(String type) { + Type(String type){ this.type = type; } diff --git a/client-adapter/logger/src/main/java/com/alibaba/otter/canal/client/adapter/logger/LoggerAdapterExample.java b/client-adapter/logger/src/main/java/com/alibaba/otter/canal/client/adapter/logger/LoggerAdapterExample.java index c72a0ee0..1885d6c2 100644 --- a/client-adapter/logger/src/main/java/com/alibaba/otter/canal/client/adapter/logger/LoggerAdapterExample.java +++ b/client-adapter/logger/src/main/java/com/alibaba/otter/canal/client/adapter/logger/LoggerAdapterExample.java @@ -14,7 +14,8 @@ import com.alibaba.otter.canal.client.adapter.support.SPI; * @author machengyuan 2018-8-19 下午11:45:38 * @version 1.0.0 */ -@SPI("logger") // logger参数对应CanalOuterAdapterConfiguration配置中的name +@SPI("logger") +// logger参数对应CanalOuterAdapterConfiguration配置中的name public class LoggerAdapterExample implements CanalOuterAdapter { private Logger logger = LoggerFactory.getLogger(this.getClass()); diff --git a/client-launcher/src/main/java/com/alibaba/otter/canal/client/ClientLauncher.java b/client-launcher/src/main/java/com/alibaba/otter/canal/client/ClientLauncher.java index c94fcd28..a705a526 100644 --- a/client-launcher/src/main/java/com/alibaba/otter/canal/client/ClientLauncher.java +++ b/client-launcher/src/main/java/com/alibaba/otter/canal/client/ClientLauncher.java @@ -1,13 +1,14 @@ package com.alibaba.otter.canal.client; -import com.alibaba.otter.canal.client.adapter.loader.CanalAdapterLoader; -import com.alibaba.otter.canal.client.adapter.support.CanalClientConfig; +import java.io.FileInputStream; + import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.yaml.snakeyaml.Yaml; -import java.io.FileInputStream; +import com.alibaba.otter.canal.client.adapter.loader.CanalAdapterLoader; +import com.alibaba.otter.canal.client.adapter.support.CanalClientConfig; public class ClientLauncher { diff --git a/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/AbstractCanalAdapterWorker.java b/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/AbstractCanalAdapterWorker.java index 0dde5211..c1f55cd9 100644 --- a/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/AbstractCanalAdapterWorker.java +++ b/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/AbstractCanalAdapterWorker.java @@ -26,10 +26,10 @@ public abstract class AbstractCanalAdapterWorker { protected final Logger logger = LoggerFactory.getLogger(this.getClass()); - protected String canalDestination; // canal实例 - protected List> canalOuterAdapters; // 外部适配器 - protected ExecutorService groupInnerExecutorService; // 组内工作线程池 - protected volatile boolean running = false; // 是否运行中 + protected String canalDestination; // canal实例 + protected List> canalOuterAdapters; // 外部适配器 + protected ExecutorService groupInnerExecutorService; // 组内工作线程池 + protected volatile boolean running = false; // 是否运行中 protected Thread thread = null; protected Thread.UncaughtExceptionHandler handler = new Thread.UncaughtExceptionHandler() { @@ -129,31 +129,22 @@ public abstract class AbstractCanalAdapterWorker { } } - protected void writeOut(Message message,String topic){ + protected void writeOut(Message message, String topic) { if (logger.isDebugEnabled()) { - logger.debug("topic: {} batchId: {} batchSize: {} ", - topic, - message.getId(), - message.getEntries().size()); + logger.debug("topic: {} batchId: {} batchSize: {} ", topic, message.getId(), message.getEntries().size()); } long begin = System.currentTimeMillis(); writeOut(message); long now = System.currentTimeMillis(); if ((System.currentTimeMillis() - begin) > 5 * 60 * 1000) { - logger.error("topic: {} batchId {} elapsed time: {} ms", - topic, - message.getId(), - now - begin); + logger.error("topic: {} batchId {} elapsed time: {} ms", topic, message.getId(), now - begin); } if (logger.isDebugEnabled()) { - logger.debug("topic: {} batchId {} elapsed time: {} ms", - topic, - message.getId(), - now - begin); + logger.debug("topic: {} batchId {} elapsed time: {} ms", topic, message.getId(), now - begin); } } - protected void stopOutAdapters(){ + protected void stopOutAdapters() { if (thread != null) { try { thread.join(); @@ -170,6 +161,7 @@ public abstract class AbstractCanalAdapterWorker { } logger.info("topic all connectors destroyed!"); } + public abstract void start(); public abstract void stop(); diff --git a/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterKafkaWorker.java b/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterKafkaWorker.java index 3775ed11..72320bb5 100644 --- a/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterKafkaWorker.java +++ b/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterKafkaWorker.java @@ -30,7 +30,7 @@ public class CanalAdapterKafkaWorker extends AbstractCanalAdapterWorker { private boolean flatMessage; public CanalAdapterKafkaWorker(String bootstrapServers, String topic, String groupId, - List> canalOuterAdapters, boolean flatMessage){ + List> canalOuterAdapters, boolean flatMessage){ this.canalOuterAdapters = canalOuterAdapters; this.groupInnerExecutorService = Executors.newFixedThreadPool(canalOuterAdapters.size()); this.topic = topic; @@ -171,4 +171,4 @@ public class CanalAdapterKafkaWorker extends AbstractCanalAdapterWorker { connector.disconnect(); logger.info("=============> Disconnect topic: {} <=============", this.topic); } -} \ No newline at end of file +} diff --git a/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterLoader.java b/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterLoader.java index 59f1b3cf..471b6a3d 100644 --- a/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterLoader.java +++ b/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterLoader.java @@ -1,9 +1,5 @@ package com.alibaba.otter.canal.client.adapter.loader; -import com.alibaba.otter.canal.client.adapter.CanalOuterAdapter; -import com.alibaba.otter.canal.client.adapter.support.CanalClientConfig; -import com.alibaba.otter.canal.client.adapter.support.CanalOuterAdapterConfiguration; -import com.alibaba.otter.canal.client.adapter.support.ExtensionLoader; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.ArrayList; @@ -12,10 +8,16 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; + import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.alibaba.otter.canal.client.adapter.CanalOuterAdapter; +import com.alibaba.otter.canal.client.adapter.support.CanalClientConfig; +import com.alibaba.otter.canal.client.adapter.support.CanalOuterAdapterConfiguration; +import com.alibaba.otter.canal.client.adapter.support.ExtensionLoader; + /** * MQ外部适配器的加载器 * @@ -23,17 +25,17 @@ import org.slf4j.LoggerFactory; */ public class CanalAdapterLoader { - private static final Logger logger = LoggerFactory.getLogger(CanalAdapterLoader.class); + private static final Logger logger = LoggerFactory.getLogger(CanalAdapterLoader.class); - private CanalClientConfig canalClientConfig; + private CanalClientConfig canalClientConfig; - private Map canalWorkers = new HashMap<>(); + private Map canalWorkers = new HashMap<>(); private Map canalMQWorker = new HashMap<>(); - private ExtensionLoader loader; + private ExtensionLoader loader; - public CanalAdapterLoader(CanalClientConfig canalClientConfig) { + public CanalAdapterLoader(CanalClientConfig canalClientConfig){ this.canalClientConfig = canalClientConfig; } @@ -46,8 +48,14 @@ public class CanalAdapterLoader { throw new RuntimeException("Blank config property: canalInstances or canalMQTopics"); } - loader = ExtensionLoader.getExtensionLoader(CanalOuterAdapter.class, - "" /* TODO canalClientConfig.getClassloaderPolicy() */); + loader = ExtensionLoader.getExtensionLoader(CanalOuterAdapter.class, "" /* + * TODO + * canalClientConfig + * . + * getClassloaderPolicy + * ( + * ) + */); String canalServerHost = this.canalClientConfig.getCanalServerHost(); SocketAddress sa = null; @@ -94,24 +102,23 @@ public class CanalAdapterLoader { } canalOuterAdapterGroups.add(canalOuterAdapters); if (StringUtils.isBlank(topic.getMqMode()) || "rocketmq".equalsIgnoreCase(topic.getMqMode())) { - CanalAdapterRocketMQWorker rocketMQWorker = new CanalAdapterRocketMQWorker( - canalClientConfig.getBootstrapServers(), + CanalAdapterRocketMQWorker rocketMQWorker = new CanalAdapterRocketMQWorker(canalClientConfig.getBootstrapServers(), topic.getTopic(), group.getGroupId(), canalOuterAdapterGroups); canalMQWorker.put(topic.getTopic() + "-rocketmq-" + group.getGroupId(), rocketMQWorker); rocketMQWorker.start(); } else if ("kafka".equalsIgnoreCase(topic.getMqMode())) { - CanalAdapterKafkaWorker canalKafkaWorker = new CanalAdapterKafkaWorker( - canalClientConfig.getBootstrapServers(), + CanalAdapterKafkaWorker canalKafkaWorker = new CanalAdapterKafkaWorker(canalClientConfig.getBootstrapServers(), topic.getTopic(), group.getGroupId(), - canalOuterAdapterGroups,canalClientConfig.getFlatMessage()); + canalOuterAdapterGroups, + canalClientConfig.getFlatMessage()); canalMQWorker.put(topic.getTopic() + "-kafka-" + group.getGroupId(), canalKafkaWorker); canalKafkaWorker.start(); } - logger.info("Start adapter for canal-client rocketmq topic: {} succeed", - topic.getTopic() + "-" + group.getGroupId()); + logger.info("Start adapter for canal-client rocketmq topic: {} succeed", topic.getTopic() + "-" + + group.getGroupId()); } } @@ -156,6 +163,7 @@ public class CanalAdapterLoader { for (AbstractCanalAdapterWorker tmp : canalMQWorker.values()) { final AbstractCanalAdapterWorker worker = tmp; stopMQWokerService.submit(new Runnable() { + @Override public void run() { worker.stop(); diff --git a/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterRocketMQWorker.java b/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterRocketMQWorker.java index 98f08286..f0f674db 100644 --- a/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterRocketMQWorker.java +++ b/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/loader/CanalAdapterRocketMQWorker.java @@ -1,16 +1,17 @@ package com.alibaba.otter.canal.client.adapter.loader; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import org.apache.kafka.clients.consumer.CommitFailedException; +import org.apache.kafka.common.errors.WakeupException; + import com.alibaba.otter.canal.client.adapter.CanalOuterAdapter; import com.alibaba.otter.canal.client.rocketmq.RocketMQCanalConnector; import com.alibaba.otter.canal.client.rocketmq.RocketMQCanalConnectorProvider; import com.alibaba.otter.canal.protocol.Message; -import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import org.apache.kafka.clients.consumer.CommitFailedException; -import org.apache.kafka.common.errors.WakeupException; /** * kafka对应的client适配器工作线程 @@ -22,10 +23,10 @@ public class CanalAdapterRocketMQWorker extends AbstractCanalAdapterWorker { private RocketMQCanalConnector connector; - private String topic; + private String topic; public CanalAdapterRocketMQWorker(String nameServers, String topic, String groupId, - List> canalOuterAdapters) { + List> canalOuterAdapters){ logger.info("RocketMQ consumer config topic:{}, nameServer:{}, groupId:{}", topic, nameServers, groupId); this.canalOuterAdapters = canalOuterAdapters; this.groupInnerExecutorService = Executors.newFixedThreadPool(canalOuterAdapters.size()); diff --git a/client/src/main/java/com/alibaba/otter/canal/client/CanalMessageDeserializer.java b/client/src/main/java/com/alibaba/otter/canal/client/CanalMessageDeserializer.java index 2e7cd5e5..b2206890 100644 --- a/client/src/main/java/com/alibaba/otter/canal/client/CanalMessageDeserializer.java +++ b/client/src/main/java/com/alibaba/otter/canal/client/CanalMessageDeserializer.java @@ -2,16 +2,19 @@ package com.alibaba.otter.canal.client; import com.alibaba.otter.canal.protocol.CanalEntry; import com.alibaba.otter.canal.protocol.CanalPacket; +import com.alibaba.otter.canal.protocol.CanalPacket.Ack; +import com.alibaba.otter.canal.protocol.CanalPacket.Compression; import com.alibaba.otter.canal.protocol.Message; import com.alibaba.otter.canal.protocol.exception.CanalClientException; import com.google.protobuf.ByteString; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class CanalMessageDeserializer { - private static Logger logger = LoggerFactory.getLogger(CanalMessageDeserializer.class); public static Message deserializer(byte[] data) { + return deserializer(data, false); + } + + public static Message deserializer(byte[] data, boolean lazyParseEntry) { try { if (data == null) { return null; @@ -19,24 +22,34 @@ public class CanalMessageDeserializer { CanalPacket.Packet p = CanalPacket.Packet.parseFrom(data); switch (p.getType()) { case MESSAGES: { -// if (!p.getCompression().equals(CanalPacket.Compression.NONE)) { -// throw new CanalClientException("compression is not supported in this connector"); -// } + if (!p.getCompression().equals(Compression.NONE) + && !p.getCompression().equals(Compression.COMPRESSIONCOMPATIBLEPROTO2)) { + throw new CanalClientException("compression is not supported in this connector"); + } CanalPacket.Messages messages = CanalPacket.Messages.parseFrom(p.getBody()); Message result = new Message(messages.getBatchId()); - for (ByteString byteString : messages.getMessagesList()) { - result.addEntry(CanalEntry.Entry.parseFrom(byteString)); + if (lazyParseEntry) { + // byteString + result.setRawEntries(messages.getMessagesList()); + } else { + for (ByteString byteString : messages.getMessagesList()) { + result.addEntry(CanalEntry.Entry.parseFrom(byteString)); + } } return result; } - default: - break; + case ACK: { + Ack ack = Ack.parseFrom(p.getBody()); + throw new CanalClientException("something goes wrong with reason: " + ack.getErrorMessage()); + } + default: { + throw new CanalClientException("unexpected packet type: " + p.getType()); + } } } } catch (Exception e) { - logger.error("Error when deserializing byte[] to message ", e); + throw new CanalClientException("deserializer failed", e); } - return null; } } diff --git a/client/src/main/java/com/alibaba/otter/canal/client/impl/ClusterCanalConnector.java b/client/src/main/java/com/alibaba/otter/canal/client/impl/ClusterCanalConnector.java index 7ec1781a..3fd9c2c5 100644 --- a/client/src/main/java/com/alibaba/otter/canal/client/impl/ClusterCanalConnector.java +++ b/client/src/main/java/com/alibaba/otter/canal/client/impl/ClusterCanalConnector.java @@ -218,8 +218,7 @@ public class ClusterCanalConnector implements CanalConnector { return; } catch (Throwable t) { logger.warn(String.format("something goes wrong when rollbacking data from server:%s", - currentConnector != null ? currentConnector.getAddress() : "null"), - t); + currentConnector != null ? currentConnector.getAddress() : "null"), t); times++; restart(); logger.info("restart the connector for next round retry."); @@ -236,8 +235,7 @@ public class ClusterCanalConnector implements CanalConnector { return; } catch (Throwable t) { logger.warn(String.format("something goes wrong when rollbacking data from server:%s", - currentConnector != null ? currentConnector.getAddress() : "null"), - t); + currentConnector != null ? currentConnector.getAddress() : "null"), t); times++; restart(); logger.info("restart the connector for next round retry."); @@ -255,8 +253,7 @@ public class ClusterCanalConnector implements CanalConnector { return; } catch (Throwable t) { logger.warn(String.format("something goes wrong when acking data from server:%s", - currentConnector != null ? currentConnector.getAddress() : "null"), - t); + currentConnector != null ? currentConnector.getAddress() : "null"), t); times++; restart(); logger.info("restart the connector for next round retry."); diff --git a/client/src/main/java/com/alibaba/otter/canal/client/impl/SimpleCanalConnector.java b/client/src/main/java/com/alibaba/otter/canal/client/impl/SimpleCanalConnector.java index 599c678c..25ad5056 100644 --- a/client/src/main/java/com/alibaba/otter/canal/client/impl/SimpleCanalConnector.java +++ b/client/src/main/java/com/alibaba/otter/canal/client/impl/SimpleCanalConnector.java @@ -18,13 +18,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.otter.canal.client.CanalConnector; +import com.alibaba.otter.canal.client.CanalMessageDeserializer; import com.alibaba.otter.canal.client.impl.running.ClientRunningData; import com.alibaba.otter.canal.client.impl.running.ClientRunningListener; import com.alibaba.otter.canal.client.impl.running.ClientRunningMonitor; import com.alibaba.otter.canal.common.utils.AddressUtils; import com.alibaba.otter.canal.common.utils.BooleanMutex; import com.alibaba.otter.canal.common.zookeeper.ZkClientx; -import com.alibaba.otter.canal.protocol.CanalEntry.Entry; import com.alibaba.otter.canal.protocol.CanalPacket.Ack; import com.alibaba.otter.canal.protocol.CanalPacket.ClientAck; import com.alibaba.otter.canal.protocol.CanalPacket.ClientAuth; @@ -32,7 +32,6 @@ import com.alibaba.otter.canal.protocol.CanalPacket.ClientRollback; import com.alibaba.otter.canal.protocol.CanalPacket.Compression; import com.alibaba.otter.canal.protocol.CanalPacket.Get; import com.alibaba.otter.canal.protocol.CanalPacket.Handshake; -import com.alibaba.otter.canal.protocol.CanalPacket.Messages; import com.alibaba.otter.canal.protocol.CanalPacket.Packet; import com.alibaba.otter.canal.protocol.CanalPacket.PacketType; import com.alibaba.otter.canal.protocol.CanalPacket.Sub; @@ -180,8 +179,8 @@ public class SimpleCanalConnector implements CanalConnector { Ack ackBody = Ack.parseFrom(ack.getBody()); if (ackBody.getErrorCode() > 0) { - throw new CanalClientException( - "something goes wrong when doing authentication: " + ackBody.getErrorMessage()); + throw new CanalClientException("something goes wrong when doing authentication: " + + ackBody.getErrorMessage()); } connected = true; @@ -320,34 +319,7 @@ public class SimpleCanalConnector implements CanalConnector { private Message receiveMessages() throws IOException { byte[] data = readNextPacket(); - Packet p = Packet.parseFrom(data); - switch (p.getType()) { - case MESSAGES: { - if (!p.getCompression().equals(Compression.NONE) - && !p.getCompression().equals(Compression.COMPRESSIONCOMPATIBLEPROTO2)) { - throw new CanalClientException("compression is not supported in this connector"); - } - - Messages messages = Messages.parseFrom(p.getBody()); - Message result = new Message(messages.getBatchId()); - if (lazyParseEntry) { - // byteString - result.setRawEntries(messages.getMessagesList()); - } else { - for (ByteString byteString : messages.getMessagesList()) { - result.addEntry(Entry.parseFrom(byteString)); - } - } - return result; - } - case ACK: { - Ack ack = Ack.parseFrom(p.getBody()); - throw new CanalClientException("something goes wrong with reason: " + ack.getErrorMessage()); - } - default: { - throw new CanalClientException("unexpected packet type: " + p.getType()); - } - } + return CanalMessageDeserializer.deserializer(data, lazyParseEntry); } public void ack(long batchId) throws CanalClientException { @@ -361,8 +333,11 @@ public class SimpleCanalConnector implements CanalConnector { .setBatchId(batchId) .build(); try { - writeWithHeader( - Packet.newBuilder().setType(PacketType.CLIENTACK).setBody(ca.toByteString()).build().toByteArray()); + writeWithHeader(Packet.newBuilder() + .setType(PacketType.CLIENTACK) + .setBody(ca.toByteString()) + .build() + .toByteArray()); } catch (IOException e) { throw new CanalClientException(e); } diff --git a/client/src/main/java/com/alibaba/otter/canal/client/impl/running/ClientRunningMonitor.java b/client/src/main/java/com/alibaba/otter/canal/client/impl/running/ClientRunningMonitor.java index 32202dc1..90a9ad71 100644 --- a/client/src/main/java/com/alibaba/otter/canal/client/impl/running/ClientRunningMonitor.java +++ b/client/src/main/java/com/alibaba/otter/canal/client/impl/running/ClientRunningMonitor.java @@ -98,7 +98,7 @@ public class ClientRunningMonitor extends AbstractCanalLifeCycle { String path = ZookeeperPathUtils.getDestinationClientRunning(this.destination, clientData.getClientId()); zkClient.unsubscribeDataChanges(path, dataListener); releaseRunning(); // 尝试一下release - //Fix issue #697 + // Fix issue #697 if (delayExector != null) { delayExector.shutdown(); } diff --git a/client/src/main/java/com/alibaba/otter/canal/client/kafka/KafkaCanalConnector.java b/client/src/main/java/com/alibaba/otter/canal/client/kafka/KafkaCanalConnector.java index ad94ba98..0c7bf545 100644 --- a/client/src/main/java/com/alibaba/otter/canal/client/kafka/KafkaCanalConnector.java +++ b/client/src/main/java/com/alibaba/otter/canal/client/kafka/KafkaCanalConnector.java @@ -13,6 +13,7 @@ import org.apache.kafka.common.TopicPartition; import org.apache.kafka.common.serialization.StringDeserializer; import com.alibaba.fastjson.JSON; +import com.alibaba.otter.canal.client.CanalMessageDeserializer; import com.alibaba.otter.canal.protocol.FlatMessage; import com.alibaba.otter.canal.protocol.Message; @@ -33,8 +34,7 @@ public class KafkaCanalConnector { private volatile boolean running = false; private boolean flatMessage; - public KafkaCanalConnector(String servers, String topic, Integer partition, String groupId, - boolean flatMessage){ + public KafkaCanalConnector(String servers, String topic, Integer partition, String groupId, boolean flatMessage){ this.topic = topic; this.partition = partition; this.flatMessage = flatMessage; @@ -50,7 +50,7 @@ public class KafkaCanalConnector { properties.put("max.poll.records", "100"); properties.put("key.deserializer", StringDeserializer.class.getName()); if (!flatMessage) { - properties.put("value.deserializer", MessageDeserializer.class.getName()); + properties.put("value.deserializer", CanalMessageDeserializer.class.getName()); } else { properties.put("value.deserializer", StringDeserializer.class.getName()); } diff --git a/client/src/main/java/com/alibaba/otter/canal/client/kafka/KafkaCanalConnectors.java b/client/src/main/java/com/alibaba/otter/canal/client/kafka/KafkaCanalConnectors.java index 436fde8f..950cdcfd 100644 --- a/client/src/main/java/com/alibaba/otter/canal/client/kafka/KafkaCanalConnectors.java +++ b/client/src/main/java/com/alibaba/otter/canal/client/kafka/KafkaCanalConnectors.java @@ -17,8 +17,7 @@ public class KafkaCanalConnectors { * @param groupId * @return */ - public static KafkaCanalConnector newKafkaConnector(String servers, String topic, Integer partition, - String groupId) { + public static KafkaCanalConnector newKafkaConnector(String servers, String topic, Integer partition, String groupId) { return new KafkaCanalConnector(servers, topic, partition, groupId, false); } @@ -44,8 +43,8 @@ public class KafkaCanalConnectors { * @param flatMessage * @return */ - public static KafkaCanalConnector newKafkaConnector( String servers, String topic, - Integer partition, String groupId,boolean flatMessage) { + public static KafkaCanalConnector newKafkaConnector(String servers, String topic, Integer partition, + String groupId, boolean flatMessage) { return new KafkaCanalConnector(servers, topic, partition, groupId, flatMessage); } } diff --git a/client/src/main/java/com/alibaba/otter/canal/client/kafka/MessageDeserializer.java b/client/src/main/java/com/alibaba/otter/canal/client/kafka/MessageDeserializer.java deleted file mode 100644 index 6de67c7a..00000000 --- a/client/src/main/java/com/alibaba/otter/canal/client/kafka/MessageDeserializer.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.alibaba.otter.canal.client.kafka; - -import com.alibaba.otter.canal.client.CanalMessageDeserializer; -import com.alibaba.otter.canal.protocol.Message; -import java.util.Map; -import org.apache.kafka.common.serialization.Deserializer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Kafka Message类的反序列化 - * - * @author machengyuan @ 2018-6-12 - * @version 1.0.0 - */ -public class MessageDeserializer implements Deserializer { - - private static Logger logger = LoggerFactory.getLogger(MessageDeserializer.class); - - @Override - public void configure(Map configs, boolean isKey) { - } - - @Override - public Message deserialize(String topic1, byte[] data) { - return CanalMessageDeserializer.deserializer(data); - } - - @Override - public void close() { - // nothing to do - } -} diff --git a/client/src/main/java/com/alibaba/otter/canal/client/kafka/running/ClientRunningMonitor.java b/client/src/main/java/com/alibaba/otter/canal/client/kafka/running/ClientRunningMonitor.java index 9a84d81e..8f0e6d92 100644 --- a/client/src/main/java/com/alibaba/otter/canal/client/kafka/running/ClientRunningMonitor.java +++ b/client/src/main/java/com/alibaba/otter/canal/client/kafka/running/ClientRunningMonitor.java @@ -24,7 +24,6 @@ import com.alibaba.otter.canal.common.zookeeper.ZkClientx; import com.alibaba.otter.canal.common.zookeeper.ZookeeperPathUtils; import com.alibaba.otter.canal.protocol.exception.CanalClientException; - /** * kafka client running状态信息 * @@ -55,15 +54,15 @@ public class ClientRunningMonitor extends AbstractCanalLifeCycle { } private static final Logger logger = LoggerFactory.getLogger(ClientRunningMonitor.class); - private ZkClientx zkClient; + private ZkClientx zkClient; private String topic; - private ClientRunningData clientData; + private ClientRunningData clientData; private IZkDataListener dataListener; - private BooleanMutex mutex = new BooleanMutex(false); + private BooleanMutex mutex = new BooleanMutex(false); private volatile boolean release = false; private volatile ClientRunningData activeData; private ScheduledExecutorService delayExector = Executors.newScheduledThreadPool(1); - private ClientRunningListener listener; + private ClientRunningListener listener; private int delayTime = 5; private static Integer virtualPort; diff --git a/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/ConsumerBatchMessage.java b/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/ConsumerBatchMessage.java index ef83a798..af156878 100644 --- a/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/ConsumerBatchMessage.java +++ b/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/ConsumerBatchMessage.java @@ -5,11 +5,12 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class ConsumerBatchMessage { - private final BlockingQueue data; - private CountDownLatch latch; - private boolean hasFailure = false; - public ConsumerBatchMessage(BlockingQueue data) { + private final BlockingQueue data; + private CountDownLatch latch; + private boolean hasFailure = false; + + public ConsumerBatchMessage(BlockingQueue data){ this.data = data; latch = new CountDownLatch(data.size()); } diff --git a/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalConnector.java b/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalConnector.java index 8031e792..0e086822 100644 --- a/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalConnector.java +++ b/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalConnector.java @@ -1,15 +1,12 @@ package com.alibaba.otter.canal.client.rocketmq; -import com.alibaba.otter.canal.client.CanalConnector; -import com.alibaba.otter.canal.client.CanalMessageDeserializer; -import com.alibaba.otter.canal.protocol.Message; -import com.alibaba.otter.canal.protocol.exception.CanalClientException; import java.util.List; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; + import org.apache.commons.lang.StringUtils; import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyContext; @@ -20,19 +17,25 @@ import org.apache.rocketmq.common.message.MessageExt; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.alibaba.otter.canal.client.CanalConnector; +import com.alibaba.otter.canal.client.CanalMessageDeserializer; +import com.alibaba.otter.canal.protocol.Message; +import com.alibaba.otter.canal.protocol.exception.CanalClientException; + public class RocketMQCanalConnector implements CanalConnector { - private static final Logger logger = LoggerFactory.getLogger(RocketMQCanalConnector.class); - private String nameServer; - private String topic; - private String groupName; - private volatile boolean connected = false; - private DefaultMQPushConsumer rocketMQConsumer; + private static final Logger logger = LoggerFactory.getLogger(RocketMQCanalConnector.class); + + private String nameServer; + private String topic; + private String groupName; + private volatile boolean connected = false; + private DefaultMQPushConsumer rocketMQConsumer; private BlockingQueue> messageBlockingQueue; - Map> messageCache; - private long batchProcessTimeout = 10000; + Map> messageCache; + private long batchProcessTimeout = 10000; - public RocketMQCanalConnector(String nameServer, String topic, String groupName) { + public RocketMQCanalConnector(String nameServer, String topic, String groupName){ this.nameServer = nameServer; this.topic = topic; this.groupName = groupName; @@ -69,9 +72,9 @@ public class RocketMQCanalConnector implements CanalConnector { } rocketMQConsumer.subscribe(topic, "*"); rocketMQConsumer.registerMessageListener(new MessageListenerOrderly() { + @Override - public ConsumeOrderlyStatus consumeMessage(List messageExts, - ConsumeOrderlyContext context) { + public ConsumeOrderlyStatus consumeMessage(List messageExts, ConsumeOrderlyContext context) { context.setAutoCommit(true); boolean isSuccess = process(messageExts); if (isSuccess) { diff --git a/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalConnectorProvider.java b/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalConnectorProvider.java index 3b876f49..f651ccb3 100644 --- a/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalConnectorProvider.java +++ b/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalConnectorProvider.java @@ -4,6 +4,7 @@ package com.alibaba.otter.canal.client.rocketmq; * RocketMQ connector provider. */ public class RocketMQCanalConnectorProvider { + /** * Create RocketMQ connector * @@ -12,8 +13,7 @@ public class RocketMQCanalConnectorProvider { * @param groupId * @return {@link RocketMQCanalConnector} */ - public static RocketMQCanalConnector newRocketMQConnector(String nameServers, String topic, - String groupId) { + public static RocketMQCanalConnector newRocketMQConnector(String nameServers, String topic, String groupId) { return new RocketMQCanalConnector(nameServers, topic, groupId); } diff --git a/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalListener.java b/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalListener.java index c30cd61f..2c4ffaaa 100644 --- a/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalListener.java +++ b/client/src/main/java/com/alibaba/otter/canal/client/rocketmq/RocketMQCanalListener.java @@ -1,11 +1,13 @@ package com.alibaba.otter.canal.client.rocketmq; import java.util.List; + import org.apache.rocketmq.common.message.MessageExt; /** * RocketMQ message listener */ public interface RocketMQCanalListener { + boolean onReceive(List messageExts); } diff --git a/client/src/test/java/com/alibaba/otter/canal/client/running/ClientRunningTest.java b/client/src/test/java/com/alibaba/otter/canal/client/running/ClientRunningTest.java index 40755671..cbdfd0ea 100644 --- a/client/src/test/java/com/alibaba/otter/canal/client/running/ClientRunningTest.java +++ b/client/src/test/java/com/alibaba/otter/canal/client/running/ClientRunningTest.java @@ -20,117 +20,117 @@ import com.alibaba.otter.canal.common.zookeeper.ZookeeperPathUtils; public class ClientRunningTest extends AbstractZkTest { - private ZkClientx zkclientx = new ZkClientx(cluster1 + ";" + cluster2); - private short clientId = 1001; + private ZkClientx zkclientx = new ZkClientx(cluster1 + ";" + cluster2); + private short clientId = 1001; - @Before - public void setUp() { - String path = ZookeeperPathUtils.getDestinationPath(destination); - zkclientx.deleteRecursive(path); + @Before + public void setUp() { + String path = ZookeeperPathUtils.getDestinationPath(destination); + zkclientx.deleteRecursive(path); - zkclientx.createPersistent(ZookeeperPathUtils.getClientIdNodePath(this.destination, clientId), true); - } + zkclientx.createPersistent(ZookeeperPathUtils.getClientIdNodePath(this.destination, clientId), true); + } - @After - public void tearDown() { - String path = ZookeeperPathUtils.getDestinationPath(destination); - zkclientx.deleteRecursive(path); - } + @After + public void tearDown() { + String path = ZookeeperPathUtils.getDestinationPath(destination); + zkclientx.deleteRecursive(path); + } - @Test - public void testOneServer() { - final CountDownLatch countLatch = new CountDownLatch(2); - ClientRunningMonitor runningMonitor = buildClientRunning(countLatch, clientId, 2088); - runningMonitor.start(); - sleep(2000L); - runningMonitor.stop(); - sleep(2000L); + @Test + public void testOneServer() { + final CountDownLatch countLatch = new CountDownLatch(2); + ClientRunningMonitor runningMonitor = buildClientRunning(countLatch, clientId, 2088); + runningMonitor.start(); + sleep(2000L); + runningMonitor.stop(); + sleep(2000L); - if (countLatch.getCount() != 0) { - Assert.fail(); - } - } + if (countLatch.getCount() != 0) { + Assert.fail(); + } + } - @Test - public void testMultiServer() { - final CountDownLatch countLatch = new CountDownLatch(30); - final ClientRunningMonitor runningMonitor1 = buildClientRunning(countLatch, clientId, 2088); - final ClientRunningMonitor runningMonitor2 = buildClientRunning(countLatch, clientId, 2089); - final ClientRunningMonitor runningMonitor3 = buildClientRunning(countLatch, clientId, 2090); - final ExecutorService executor = Executors.newFixedThreadPool(3); - executor.submit(new Runnable() { + @Test + public void testMultiServer() { + final CountDownLatch countLatch = new CountDownLatch(30); + final ClientRunningMonitor runningMonitor1 = buildClientRunning(countLatch, clientId, 2088); + final ClientRunningMonitor runningMonitor2 = buildClientRunning(countLatch, clientId, 2089); + final ClientRunningMonitor runningMonitor3 = buildClientRunning(countLatch, clientId, 2090); + final ExecutorService executor = Executors.newFixedThreadPool(3); + executor.submit(new Runnable() { - public void run() { - for (int i = 0; i < 10; i++) { - if (!runningMonitor1.isStart()) { - runningMonitor1.start(); - } - sleep(2000L + RandomUtils.nextInt(500)); - runningMonitor1.stop(); - sleep(2000L + RandomUtils.nextInt(500)); - } - } + public void run() { + for (int i = 0; i < 10; i++) { + if (!runningMonitor1.isStart()) { + runningMonitor1.start(); + } + sleep(2000L + RandomUtils.nextInt(500)); + runningMonitor1.stop(); + sleep(2000L + RandomUtils.nextInt(500)); + } + } - }); + }); - executor.submit(new Runnable() { + executor.submit(new Runnable() { - public void run() { - for (int i = 0; i < 10; i++) { - if (!runningMonitor2.isStart()) { - runningMonitor2.start(); - } - sleep(2000L + RandomUtils.nextInt(500)); - runningMonitor2.stop(); - sleep(2000L + RandomUtils.nextInt(500)); - } - } + public void run() { + for (int i = 0; i < 10; i++) { + if (!runningMonitor2.isStart()) { + runningMonitor2.start(); + } + sleep(2000L + RandomUtils.nextInt(500)); + runningMonitor2.stop(); + sleep(2000L + RandomUtils.nextInt(500)); + } + } - }); + }); - executor.submit(new Runnable() { + executor.submit(new Runnable() { - public void run() { - for (int i = 0; i < 10; i++) { - if (!runningMonitor3.isStart()) { - runningMonitor3.start(); - } - sleep(2000L + RandomUtils.nextInt(500)); - runningMonitor3.stop(); - sleep(2000L + RandomUtils.nextInt(500)); - } - } + public void run() { + for (int i = 0; i < 10; i++) { + if (!runningMonitor3.isStart()) { + runningMonitor3.start(); + } + sleep(2000L + RandomUtils.nextInt(500)); + runningMonitor3.stop(); + sleep(2000L + RandomUtils.nextInt(500)); + } + } - }); + }); - sleep(30000L); - } + sleep(30000L); + } - private ClientRunningMonitor buildClientRunning(final CountDownLatch countLatch, final short clientId, - final int port) { - ClientRunningData clientData = new ClientRunningData(); - clientData.setClientId(clientId); - clientData.setAddress(AddressUtils.getHostIp()); + private ClientRunningMonitor buildClientRunning(final CountDownLatch countLatch, final short clientId, + final int port) { + ClientRunningData clientData = new ClientRunningData(); + clientData.setClientId(clientId); + clientData.setAddress(AddressUtils.getHostIp()); - ClientRunningMonitor runningMonitor = new ClientRunningMonitor(); - runningMonitor.setDestination(destination); - runningMonitor.setZkClient(zkclientx); - runningMonitor.setClientData(clientData); - runningMonitor.setListener(new ClientRunningListener() { + ClientRunningMonitor runningMonitor = new ClientRunningMonitor(); + runningMonitor.setDestination(destination); + runningMonitor.setZkClient(zkclientx); + runningMonitor.setClientData(clientData); + runningMonitor.setListener(new ClientRunningListener() { - public InetSocketAddress processActiveEnter() { - System.out.println(String.format("clientId:%s port:%s has start", clientId, port)); - countLatch.countDown(); - return new InetSocketAddress(AddressUtils.getHostIp(), port); - } + public InetSocketAddress processActiveEnter() { + System.out.println(String.format("clientId:%s port:%s has start", clientId, port)); + countLatch.countDown(); + return new InetSocketAddress(AddressUtils.getHostIp(), port); + } - public void processActiveExit() { - countLatch.countDown(); - System.out.println(String.format("clientId:%s port:%s has stop", clientId, port)); - } + public void processActiveExit() { + countLatch.countDown(); + System.out.println(String.format("clientId:%s port:%s has stop", clientId, port)); + } - }); - runningMonitor.setDelayTime(1); - return runningMonitor; - } -} \ No newline at end of file + }); + runningMonitor.setDelayTime(1); + return runningMonitor; + } +} diff --git a/client/src/test/java/com/alibaba/otter/canal/client/running/kafka/CanalKafkaClientExample.java b/client/src/test/java/com/alibaba/otter/canal/client/running/kafka/CanalKafkaClientExample.java index 93f443cd..e9889370 100644 --- a/client/src/test/java/com/alibaba/otter/canal/client/running/kafka/CanalKafkaClientExample.java +++ b/client/src/test/java/com/alibaba/otter/canal/client/running/kafka/CanalKafkaClientExample.java @@ -41,8 +41,7 @@ public class CanalKafkaClientExample { public static void main(String[] args) { try { - final CanalKafkaClientExample kafkaCanalClientExample = new CanalKafkaClientExample( - AbstractKafkaTest.zkServers, + final CanalKafkaClientExample kafkaCanalClientExample = new CanalKafkaClientExample(AbstractKafkaTest.zkServers, AbstractKafkaTest.servers, AbstractKafkaTest.topic, AbstractKafkaTest.partition, diff --git a/client/src/test/java/com/alibaba/otter/canal/client/running/rocketmq/AbstractRocektMQTest.java b/client/src/test/java/com/alibaba/otter/canal/client/running/rocketmq/AbstractRocektMQTest.java index 3e65e66e..1b4e35d5 100644 --- a/client/src/test/java/com/alibaba/otter/canal/client/running/rocketmq/AbstractRocektMQTest.java +++ b/client/src/test/java/com/alibaba/otter/canal/client/running/rocketmq/AbstractRocektMQTest.java @@ -1,8 +1,9 @@ package com.alibaba.otter.canal.client.running.rocketmq; public class AbstractRocektMQTest { - public static String topic = "example"; - public static String groupId = "group"; + + public static String topic = "example"; + public static String groupId = "group"; public static String nameServers = "localhost:9876"; } diff --git a/client/src/test/java/com/alibaba/otter/canal/client/running/rocketmq/CanalRocketMQClientExample.java b/client/src/test/java/com/alibaba/otter/canal/client/running/rocketmq/CanalRocketMQClientExample.java index 7b15efbf..b04f5b24 100644 --- a/client/src/test/java/com/alibaba/otter/canal/client/running/rocketmq/CanalRocketMQClientExample.java +++ b/client/src/test/java/com/alibaba/otter/canal/client/running/rocketmq/CanalRocketMQClientExample.java @@ -1,13 +1,14 @@ package com.alibaba.otter.canal.client.running.rocketmq; +import org.apache.kafka.common.errors.WakeupException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.Assert; + import com.alibaba.otter.canal.client.rocketmq.RocketMQCanalConnector; import com.alibaba.otter.canal.client.rocketmq.RocketMQCanalConnectorProvider; import com.alibaba.otter.canal.client.running.kafka.AbstractKafkaTest; import com.alibaba.otter.canal.protocol.Message; -import org.apache.kafka.common.errors.WakeupException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.Assert; /** * Kafka client example @@ -17,32 +18,30 @@ import org.springframework.util.Assert; */ public class CanalRocketMQClientExample extends AbstractRocektMQTest { - protected final static Logger logger = LoggerFactory.getLogger(CanalRocketMQClientExample.class); + protected final static Logger logger = LoggerFactory.getLogger(CanalRocketMQClientExample.class); - private RocketMQCanalConnector connector; + private RocketMQCanalConnector connector; - private static volatile boolean running = false; + private static volatile boolean running = false; - private Thread thread = null; + private Thread thread = null; private Thread.UncaughtExceptionHandler handler = new Thread.UncaughtExceptionHandler() { - public void uncaughtException(Thread t, Throwable e) { - logger.error("parse events has an error", e); - } - }; + public void uncaughtException(Thread t, Throwable e) { + logger.error("parse events has an error", e); + } + }; - public CanalRocketMQClientExample(String nameServers, String topic, - String groupId) { - connector = RocketMQCanalConnectorProvider.newRocketMQConnector(nameServers, - topic, - groupId); + public CanalRocketMQClientExample(String nameServers, String topic, String groupId){ + connector = RocketMQCanalConnectorProvider.newRocketMQConnector(nameServers, topic, groupId); } public static void main(String[] args) { try { final CanalRocketMQClientExample rocketMQClientExample = new CanalRocketMQClientExample(nameServers, - topic, groupId); + topic, + groupId); logger.info("## Start the rocketmq consumer: {}-{}", AbstractKafkaTest.topic, AbstractKafkaTest.groupId); rocketMQClientExample.start(); logger.info("## The canal rocketmq consumer is running now ......"); diff --git a/deployer/src/main/resources/canal.properties b/deployer/src/main/resources/canal.properties index e2ad657b..ea096de8 100644 --- a/deployer/src/main/resources/canal.properties +++ b/deployer/src/main/resources/canal.properties @@ -10,7 +10,7 @@ canal.zkServers= canal.zookeeper.flush.period = 1000 canal.withoutNetty = false # tcp, kafka, RocketMQ -canal.serverMode = rocketmq +canal.serverMode = tcp # flush meta cursor/parse position to file canal.file.data.dir = ${canal.conf.dir} canal.file.flush.period = 1000 diff --git a/protocol/src/main/java/com/alibaba/otter/canal/protocol/ClientIdentity.java b/protocol/src/main/java/com/alibaba/otter/canal/protocol/ClientIdentity.java index aaa568e0..bae04a79 100644 --- a/protocol/src/main/java/com/alibaba/otter/canal/protocol/ClientIdentity.java +++ b/protocol/src/main/java/com/alibaba/otter/canal/protocol/ClientIdentity.java @@ -13,9 +13,10 @@ import com.alibaba.otter.canal.common.utils.CanalToStringStyle; */ public class ClientIdentity implements Serializable { - private String destination; - private short clientId; - private String filter; + private static final long serialVersionUID = -8262100681930834834L; + private String destination; + private short clientId; + private String filter; public ClientIdentity(){ diff --git a/server/src/main/java/com/alibaba/otter/canal/common/CanalMessageSerializer.java b/server/src/main/java/com/alibaba/otter/canal/common/CanalMessageSerializer.java index a4b89bd9..665dbf55 100644 --- a/server/src/main/java/com/alibaba/otter/canal/common/CanalMessageSerializer.java +++ b/server/src/main/java/com/alibaba/otter/canal/common/CanalMessageSerializer.java @@ -1,5 +1,10 @@ package com.alibaba.otter.canal.common; +import java.util.List; + +import org.apache.kafka.common.errors.SerializationException; +import org.springframework.util.CollectionUtils; + import com.alibaba.otter.canal.protocol.CanalEntry; import com.alibaba.otter.canal.protocol.CanalPacket; import com.alibaba.otter.canal.protocol.CanalPacket.PacketType; @@ -7,12 +12,11 @@ import com.alibaba.otter.canal.protocol.Message; import com.google.protobuf.ByteString; import com.google.protobuf.CodedOutputStream; import com.google.protobuf.WireFormat; -import java.util.List; -import org.apache.kafka.common.errors.SerializationException; -import org.springframework.util.CollectionUtils; public class CanalMessageSerializer { - public static byte[] serializer(Message data){ + + @SuppressWarnings("deprecation") + public static byte[] serializer(Message data) { try { if (data != null) { if (data.getId() != -1) { @@ -31,11 +35,9 @@ public class CanalMessageSerializer { messageSize += 1 * rowEntries.size(); // packet size int size = 0; - size += CodedOutputStream.computeEnumSize(3, - PacketType.MESSAGES.getNumber()); + size += CodedOutputStream.computeEnumSize(3, PacketType.MESSAGES.getNumber()); size += CodedOutputStream.computeTagSize(5) - + CodedOutputStream.computeRawVarint32Size(messageSize) - + messageSize; + + CodedOutputStream.computeRawVarint32Size(messageSize) + messageSize; // build data byte[] body = new byte[size]; CodedOutputStream output = CodedOutputStream.newInstance(body); diff --git a/server/src/main/java/com/alibaba/otter/canal/common/MQProperties.java b/server/src/main/java/com/alibaba/otter/canal/common/MQProperties.java index 0302407a..9fce04bf 100644 --- a/server/src/main/java/com/alibaba/otter/canal/common/MQProperties.java +++ b/server/src/main/java/com/alibaba/otter/canal/common/MQProperties.java @@ -12,25 +12,25 @@ import java.util.Map; */ public class MQProperties { - private String servers = "localhost:6667"; - private int retries = 0; - private int batchSize = 16384; - private int lingerMs = 1; - private long bufferMemory = 33554432L; - private boolean filterTransactionEntry = true; - private String producerGroup = "Canal-Producer"; - private int canalBatchSize = 50; - private Long canalGetTimeout; - private boolean flatMessage = true; + private String servers = "localhost:6667"; + private int retries = 0; + private int batchSize = 16384; + private int lingerMs = 1; + private long bufferMemory = 33554432L; + private boolean filterTransactionEntry = true; + private String producerGroup = "Canal-Producer"; + private int canalBatchSize = 50; + private Long canalGetTimeout; + private boolean flatMessage = true; - private List canalDestinations = new ArrayList(); + private List canalDestinations = new ArrayList(); public static class CanalDestination { - private String canalDestination; - private String topic; - private Integer partition; - private Integer partitionsNum; + private String canalDestination; + private String topic; + private Integer partition; + private Integer partitionsNum; private Map partitionHash; public String getCanalDestination() { @@ -74,7 +74,6 @@ public class MQProperties { } } - public String getServers() { return servers; } diff --git a/server/src/main/java/com/alibaba/otter/canal/kafka/CanalKafkaProducer.java b/server/src/main/java/com/alibaba/otter/canal/kafka/CanalKafkaProducer.java index fc62ad21..63827b15 100644 --- a/server/src/main/java/com/alibaba/otter/canal/kafka/CanalKafkaProducer.java +++ b/server/src/main/java/com/alibaba/otter/canal/kafka/CanalKafkaProducer.java @@ -1,12 +1,8 @@ package com.alibaba.otter.canal.kafka; -import com.alibaba.fastjson.JSON; -import com.alibaba.otter.canal.common.MQProperties; -import com.alibaba.otter.canal.protocol.FlatMessage; -import com.alibaba.otter.canal.protocol.Message; -import com.alibaba.otter.canal.spi.CanalMQProducer; import java.util.List; import java.util.Properties; + import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.Producer; import org.apache.kafka.clients.producer.ProducerRecord; @@ -14,6 +10,13 @@ import org.apache.kafka.common.serialization.StringSerializer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.alibaba.fastjson.JSON; +import com.alibaba.otter.canal.common.MQProperties; +import com.alibaba.otter.canal.protocol.FlatMessage; +import com.alibaba.otter.canal.protocol.Message; +import com.alibaba.otter.canal.spi.CanalMQProducer; +; + /** * kafka producer 主操作类 * @@ -22,13 +25,13 @@ import org.slf4j.LoggerFactory; */ public class CanalKafkaProducer implements CanalMQProducer { - private static final Logger logger = LoggerFactory.getLogger(CanalKafkaProducer.class); + private static final Logger logger = LoggerFactory.getLogger(CanalKafkaProducer.class); private Producer producer; - private Producer producer2; // 用于扁平message的数据投递 + private Producer producer2; // 用于扁平message的数据投递 - private MQProperties kafkaProperties; + private MQProperties kafkaProperties; @Override public void init(MQProperties kafkaProperties) { @@ -91,8 +94,10 @@ public class CanalKafkaProducer implements CanalMQProducer { if (flatMessages != null) { for (FlatMessage flatMessage : flatMessages) { if (canalDestination.getPartition() != null) { - ProducerRecord record = new ProducerRecord(canalDestination - .getTopic(), canalDestination.getPartition(), null, JSON.toJSONString(flatMessage)); + ProducerRecord record = new ProducerRecord(canalDestination.getTopic(), + canalDestination.getPartition(), + null, + JSON.toJSONString(flatMessage)); producer2.send(record); } else { if (canalDestination.getPartitionHash() != null @@ -104,8 +109,7 @@ public class CanalKafkaProducer implements CanalMQProducer { for (int i = 0; i < length; i++) { FlatMessage flatMessagePart = partitionFlatMessage[i]; if (flatMessagePart != null) { - ProducerRecord record = new ProducerRecord( - canalDestination.getTopic(), + ProducerRecord record = new ProducerRecord(canalDestination.getTopic(), i, null, JSON.toJSONString(flatMessagePart)); @@ -113,8 +117,7 @@ public class CanalKafkaProducer implements CanalMQProducer { } } } else { - ProducerRecord record = new ProducerRecord( - canalDestination.getTopic(), + ProducerRecord record = new ProducerRecord(canalDestination.getTopic(), 0, null, JSON.toJSONString(flatMessage)); diff --git a/server/src/main/java/com/alibaba/otter/canal/kafka/MessageSerializer.java b/server/src/main/java/com/alibaba/otter/canal/kafka/MessageSerializer.java index 59d2bc20..77caa0a6 100644 --- a/server/src/main/java/com/alibaba/otter/canal/kafka/MessageSerializer.java +++ b/server/src/main/java/com/alibaba/otter/canal/kafka/MessageSerializer.java @@ -1,9 +1,11 @@ package com.alibaba.otter.canal.kafka; +import java.util.Map; + +import org.apache.kafka.common.serialization.Serializer; + import com.alibaba.otter.canal.common.CanalMessageSerializer; import com.alibaba.otter.canal.protocol.Message; -import java.util.Map; -import org.apache.kafka.common.serialization.Serializer; /** * Kafka Message类的序列化 diff --git a/server/src/main/java/com/alibaba/otter/canal/rocketmq/CanalRocketMQProducer.java b/server/src/main/java/com/alibaba/otter/canal/rocketmq/CanalRocketMQProducer.java index 87011250..de19bae9 100644 --- a/server/src/main/java/com/alibaba/otter/canal/rocketmq/CanalRocketMQProducer.java +++ b/server/src/main/java/com/alibaba/otter/canal/rocketmq/CanalRocketMQProducer.java @@ -16,9 +16,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class CanalRocketMQProducer implements CanalMQProducer { + private static final Logger logger = LoggerFactory.getLogger(CanalRocketMQProducer.class); - private DefaultMQProducer defaultMQProducer; + private DefaultMQProducer defaultMQProducer; @Override public void init(MQProperties rocketMQProperties) { @@ -36,10 +37,11 @@ public class CanalRocketMQProducer implements CanalMQProducer { @Override public void send(final MQProperties.CanalDestination destination, com.alibaba.otter.canal.protocol.Message data, - Callback callback) { + Callback callback) { try { Message message = new Message(destination.getTopic(), CanalMessageSerializer.serializer(data)); this.defaultMQProducer.send(message, new MessageQueueSelector() { + @Override public MessageQueue select(List mqs, Message msg, Object arg) { int partition = 0; 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 0510eec5..b3ed906f 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,35 +1,38 @@ package com.alibaba.otter.canal.server; +import java.io.FileInputStream; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.yaml.snakeyaml.Yaml; + import com.alibaba.otter.canal.common.MQProperties; import com.alibaba.otter.canal.kafka.CanalKafkaProducer; import com.alibaba.otter.canal.protocol.ClientIdentity; import com.alibaba.otter.canal.protocol.Message; import com.alibaba.otter.canal.server.embedded.CanalServerWithEmbedded; import com.alibaba.otter.canal.spi.CanalMQProducer; -import java.io.FileInputStream; -import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.yaml.snakeyaml.Yaml; public class CanalMQStarter { - private static final Logger logger = LoggerFactory.getLogger(CanalMQStarter.class); + + private static final Logger logger = LoggerFactory.getLogger(CanalMQStarter.class); private static final String CLASSPATH_URL_PREFIX = "classpath:"; - private volatile boolean running = false; + private volatile boolean running = false; - private ExecutorService executorService; + private ExecutorService executorService; - private CanalMQProducer canalMQProducer; + private CanalMQProducer canalMQProducer; - private MQProperties properties; + private MQProperties properties; - public CanalMQStarter(CanalMQProducer canalMQProducer) { + public CanalMQStarter(CanalMQProducer canalMQProducer){ this.canalMQProducer = canalMQProducer; } @@ -47,7 +50,7 @@ public class CanalMQStarter { } // 初始化 kafka producer -// canalMQProducer = new CanalKafkaProducer(); + // canalMQProducer = new CanalKafkaProducer(); canalMQProducer.init(properties); // set filterTransactionEntry if (properties.isFilterTransactionEntry()) { diff --git a/server/src/main/java/com/alibaba/otter/canal/server/netty/handler/SessionHandler.java b/server/src/main/java/com/alibaba/otter/canal/server/netty/handler/SessionHandler.java index d060c026..eb664ed8 100644 --- a/server/src/main/java/com/alibaba/otter/canal/server/netty/handler/SessionHandler.java +++ b/server/src/main/java/com/alibaba/otter/canal/server/netty/handler/SessionHandler.java @@ -50,7 +50,7 @@ public class SessionHandler extends SimpleChannelHandler { this.embeddedServer = embeddedServer; } - @SuppressWarnings("deprecation") + @SuppressWarnings({ "deprecation", "deprecation" }) public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { logger.info("message receives in session handler..."); long start = System.nanoTime(); diff --git a/server/src/main/java/com/alibaba/otter/canal/spi/CanalMQProducer.java b/server/src/main/java/com/alibaba/otter/canal/spi/CanalMQProducer.java index 895514c4..9ddbc80d 100644 --- a/server/src/main/java/com/alibaba/otter/canal/spi/CanalMQProducer.java +++ b/server/src/main/java/com/alibaba/otter/canal/spi/CanalMQProducer.java @@ -1,11 +1,12 @@ package com.alibaba.otter.canal.spi; -import com.alibaba.otter.canal.common.MQProperties; -import com.alibaba.otter.canal.kafka.CanalKafkaProducer; -import com.alibaba.otter.canal.protocol.Message; import java.io.IOException; +import com.alibaba.otter.canal.common.MQProperties; +import com.alibaba.otter.canal.protocol.Message; + public interface CanalMQProducer { + /** * Init producer. * @@ -33,4 +34,4 @@ public interface CanalMQProducer { void rollback(); } -} \ No newline at end of file +} From eb2a83b975f5cae9d7372e72af0f78ee0c815edc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=83=E9=94=8B?= Date: Sun, 30 Sep 2018 11:33:48 +0800 Subject: [PATCH 8/8] fixed test --- .../client/kafka/KafkaCanalConnector.java | 3 +- .../client/kafka/MessageDeserializer.java | 31 +++++++++++++++++++ .../dbsync/binlog/event/RowsLogBuffer.java | 8 ++--- .../SimpleCanalClientPermanceTest.java | 16 ++++++---- .../server/netty/handler/SessionHandler.java | 2 +- .../otter/canal/server/ProtocolTest.java | 1 + 6 files changed, 48 insertions(+), 13 deletions(-) create mode 100644 client/src/main/java/com/alibaba/otter/canal/client/kafka/MessageDeserializer.java diff --git a/client/src/main/java/com/alibaba/otter/canal/client/kafka/KafkaCanalConnector.java b/client/src/main/java/com/alibaba/otter/canal/client/kafka/KafkaCanalConnector.java index 0c7bf545..0a3a0dd6 100644 --- a/client/src/main/java/com/alibaba/otter/canal/client/kafka/KafkaCanalConnector.java +++ b/client/src/main/java/com/alibaba/otter/canal/client/kafka/KafkaCanalConnector.java @@ -13,7 +13,6 @@ import org.apache.kafka.common.TopicPartition; import org.apache.kafka.common.serialization.StringDeserializer; import com.alibaba.fastjson.JSON; -import com.alibaba.otter.canal.client.CanalMessageDeserializer; import com.alibaba.otter.canal.protocol.FlatMessage; import com.alibaba.otter.canal.protocol.Message; @@ -50,7 +49,7 @@ public class KafkaCanalConnector { properties.put("max.poll.records", "100"); properties.put("key.deserializer", StringDeserializer.class.getName()); if (!flatMessage) { - properties.put("value.deserializer", CanalMessageDeserializer.class.getName()); + properties.put("value.deserializer", MessageDeserializer.class.getName()); } else { properties.put("value.deserializer", StringDeserializer.class.getName()); } diff --git a/client/src/main/java/com/alibaba/otter/canal/client/kafka/MessageDeserializer.java b/client/src/main/java/com/alibaba/otter/canal/client/kafka/MessageDeserializer.java new file mode 100644 index 00000000..6b78e0e4 --- /dev/null +++ b/client/src/main/java/com/alibaba/otter/canal/client/kafka/MessageDeserializer.java @@ -0,0 +1,31 @@ +package com.alibaba.otter.canal.client.kafka; + +import java.util.Map; + +import org.apache.kafka.common.serialization.Deserializer; + +import com.alibaba.otter.canal.client.CanalMessageDeserializer; +import com.alibaba.otter.canal.protocol.Message; + +/** + * Kafka Message类的反序列化 + * + * @author machengyuan @ 2018-6-12 + * @version 1.0.0 + */ +public class MessageDeserializer implements Deserializer { + + @Override + public void configure(Map configs, boolean isKey) { + } + + @Override + public Message deserialize(String topic1, byte[] data) { + return CanalMessageDeserializer.deserializer(data); + } + + @Override + public void close() { + // nothing to do + } +} diff --git a/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/event/RowsLogBuffer.java b/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/event/RowsLogBuffer.java index bc0f3dde..c1b96c3c 100644 --- a/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/event/RowsLogBuffer.java +++ b/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/event/RowsLogBuffer.java @@ -1082,10 +1082,10 @@ public final class RowsLogBuffer { buffer.fillBytes(binary, 0, len); /* Warning unsupport cloumn type */ - logger.warn(String.format("!! Unsupport column type MYSQL_TYPE_GEOMETRY: meta=%d (%04X), len = %d", - meta, - meta, - len)); + // logger.warn(String.format("!! Unsupport column type MYSQL_TYPE_GEOMETRY: meta=%d (%04X), len = %d", + // meta, + // meta, + // len)); javaType = Types.BINARY; value = binary; length = len; diff --git a/example/src/main/java/com/alibaba/otter/canal/example/SimpleCanalClientPermanceTest.java b/example/src/main/java/com/alibaba/otter/canal/example/SimpleCanalClientPermanceTest.java index c58eb869..6b1f1800 100644 --- a/example/src/main/java/com/alibaba/otter/canal/example/SimpleCanalClientPermanceTest.java +++ b/example/src/main/java/com/alibaba/otter/canal/example/SimpleCanalClientPermanceTest.java @@ -1,6 +1,8 @@ package com.alibaba.otter.canal.example; + import java.net.InetSocketAddress; import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.TimeUnit; import com.alibaba.otter.canal.client.CanalConnector; import com.alibaba.otter.canal.client.CanalConnectors; @@ -44,7 +46,7 @@ public class SimpleCanalClientPermanceTest { connector.connect(); connector.subscribe(); while (true) { - Message message = connector.getWithoutAck(batchSize); + Message message = connector.getWithoutAck(batchSize, 100L, TimeUnit.MILLISECONDS); long batchId = message.getId(); int size = message.getRawEntries().size(); sum += size; @@ -53,11 +55,13 @@ public class SimpleCanalClientPermanceTest { queue.add(batchId); if (count % 10 == 0) { end = System.currentTimeMillis(); - long tps = (perSum * 1000) / (end - start); - System.out.println(" total : " + sum + " , current : " + perSum + " , cost : " + (end - start) - + " , tps : " + tps); - start = end; - perSum = 0; + if (end - start != 0) { + long tps = (perSum * 1000) / (end - start); + System.out.println(" total : " + sum + " , current : " + perSum + " , cost : " + (end - start) + + " , tps : " + tps); + start = end; + perSum = 0; + } } } } catch (Throwable e) { diff --git a/server/src/main/java/com/alibaba/otter/canal/server/netty/handler/SessionHandler.java b/server/src/main/java/com/alibaba/otter/canal/server/netty/handler/SessionHandler.java index eb664ed8..ba0432b8 100644 --- a/server/src/main/java/com/alibaba/otter/canal/server/netty/handler/SessionHandler.java +++ b/server/src/main/java/com/alibaba/otter/canal/server/netty/handler/SessionHandler.java @@ -50,7 +50,7 @@ public class SessionHandler extends SimpleChannelHandler { this.embeddedServer = embeddedServer; } - @SuppressWarnings({ "deprecation", "deprecation" }) + @SuppressWarnings({ "deprecation" }) public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { logger.info("message receives in session handler..."); long start = System.nanoTime(); diff --git a/server/src/test/java/com/alibaba/otter/canal/server/ProtocolTest.java b/server/src/test/java/com/alibaba/otter/canal/server/ProtocolTest.java index cb078ddf..110c3d72 100644 --- a/server/src/test/java/com/alibaba/otter/canal/server/ProtocolTest.java +++ b/server/src/test/java/com/alibaba/otter/canal/server/ProtocolTest.java @@ -56,6 +56,7 @@ public class ProtocolTest { } } + @SuppressWarnings("deprecation") private byte[] buildData(Message message) throws IOException { List rowEntries = message.getRawEntries(); // message size