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 199e04ab..771a4ad0 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
@@ -1,325 +1,326 @@
-package com.alibaba.otter.canal.client.kafka;
-
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.kafka.clients.consumer.ConsumerRecord;
-import org.apache.kafka.clients.consumer.ConsumerRecords;
-import org.apache.kafka.clients.consumer.KafkaConsumer;
-import org.apache.kafka.common.TopicPartition;
-import org.apache.kafka.common.serialization.StringDeserializer;
-
-import com.alibaba.fastjson.JSON;
-import com.alibaba.otter.canal.client.CanalMQConnector;
-import com.alibaba.otter.canal.client.impl.SimpleCanalConnector;
-import com.alibaba.otter.canal.protocol.FlatMessage;
-import com.alibaba.otter.canal.protocol.Message;
-import com.alibaba.otter.canal.protocol.exception.CanalClientException;
-import com.google.common.collect.Lists;
-
-/**
- * canal kafka 数据操作客户端
- *
- *
- * 注意点:
- * 1. 相比于canal {@linkplain SimpleCanalConnector}, 这里get和ack操作不能有并发, 必须是一个线程执行get后,内存里执行完毕ack后再取下一个get
- *
- *
- * @author machengyuan @ 2018-6-12
- * @version 1.1.1
- */
-public class KafkaCanalConnector implements CanalMQConnector {
-
- protected KafkaConsumer kafkaConsumer;
- protected KafkaConsumer kafkaConsumer2; // 用于扁平message的数据消费
- protected String topic;
- protected Integer partition;
- protected Properties properties;
- protected volatile boolean connected = false;
- protected volatile boolean running = false;
- protected boolean flatMessage;
-
- private Map currentOffsets = new ConcurrentHashMap<>();
-
- public KafkaCanalConnector(String servers, String topic, Integer partition, String groupId, Integer batchSize,
- boolean flatMessage){
- this.topic = topic;
- this.partition = partition;
- this.flatMessage = flatMessage;
-
- properties = new Properties();
- properties.put("bootstrap.servers", servers);
- properties.put("group.id", groupId);
- properties.put("enable.auto.commit", false);
- properties.put("auto.commit.interval.ms", "1000");
- properties.put("auto.offset.reset", "latest"); // 如果没有offset则从最后的offset开始读
- properties.put("request.timeout.ms", "40000"); // 必须大于session.timeout.ms的设置
- properties.put("session.timeout.ms", "30000"); // 默认为30秒
- if (batchSize == null) {
- batchSize = 100;
- }
- properties.put("max.poll.records", batchSize.toString());
- properties.put("key.deserializer", StringDeserializer.class.getName());
- if (!flatMessage) {
- properties.put("value.deserializer", MessageDeserializer.class.getName());
- } else {
- properties.put("value.deserializer", StringDeserializer.class.getName());
- }
- }
-
- /**
- * 打开连接
- */
- @Override
- public void connect() {
- if (connected) {
- return;
- }
-
- connected = true;
- if (kafkaConsumer == null && !flatMessage) {
- kafkaConsumer = new KafkaConsumer(properties);
-
- }
- if (kafkaConsumer2 == null && flatMessage) {
- kafkaConsumer2 = new KafkaConsumer(properties);
- }
- }
-
- /**
- * 关闭链接
- */
- @Override
- public void disconnect() {
- if (kafkaConsumer != null) {
- kafkaConsumer.close();
- kafkaConsumer = null;
- }
- if (kafkaConsumer2 != null) {
- kafkaConsumer2.close();
- kafkaConsumer2 = null;
- }
-
- connected = false;
- }
-
- protected void waitClientRunning() {
- running = true;
- }
-
- @Override
- public boolean checkValid() {
- return true;// 默认都放过
- }
-
- /**
- * 订阅topic
- */
- @Override
- public void subscribe() {
- waitClientRunning();
- if (!running) {
- return;
- }
-
- if (partition == null) {
- if (kafkaConsumer != null) {
- kafkaConsumer.subscribe(Collections.singletonList(topic));
- }
- if (kafkaConsumer2 != null) {
- kafkaConsumer2.subscribe(Collections.singletonList(topic));
- }
- } else {
- TopicPartition topicPartition = new TopicPartition(topic, partition);
- if (kafkaConsumer != null) {
- kafkaConsumer.assign(Collections.singletonList(topicPartition));
- }
- if (kafkaConsumer2 != null) {
- kafkaConsumer2.assign(Collections.singletonList(topicPartition));
- }
- }
- }
-
- /**
- * 取消订阅
- */
- @Override
- public void unsubscribe() {
- waitClientRunning();
- if (!running) {
- return;
- }
-
- if (kafkaConsumer != null) {
- kafkaConsumer.unsubscribe();
- }
- if (kafkaConsumer2 != null) {
- kafkaConsumer2.unsubscribe();
- }
- }
-
- @Override
- public List getList(Long timeout, TimeUnit unit) throws CanalClientException {
- waitClientRunning();
- if (!running) {
- return Lists.newArrayList();
- }
-
- List messages = getListWithoutAck(timeout, unit);
- if (messages != null && !messages.isEmpty()) {
- this.ack();
- }
- return messages;
- }
-
- @Override
- public List getListWithoutAck(Long timeout, TimeUnit unit) throws CanalClientException {
- waitClientRunning();
- if (!running) {
- return Lists.newArrayList();
- }
-
- ConsumerRecords records = kafkaConsumer.poll(unit.toMillis(timeout));
-
- currentOffsets.clear();
- for (TopicPartition topicPartition : records.partitions()) {
- currentOffsets.put(topicPartition.partition(), kafkaConsumer.position(topicPartition));
- }
-
- if (!records.isEmpty()) {
- List messages = new ArrayList<>();
- for (ConsumerRecord record : records) {
- messages.add(record.value());
- }
- return messages;
- }
- return Lists.newArrayList();
- }
-
- @Override
- public List getFlatList(Long timeout, TimeUnit unit) throws CanalClientException {
- waitClientRunning();
- if (!running) {
- return Lists.newArrayList();
- }
-
- List messages = getFlatListWithoutAck(timeout, unit);
- if (messages != null && !messages.isEmpty()) {
- this.ack();
- }
- return messages;
- }
-
- @Override
- public List getFlatListWithoutAck(Long timeout, TimeUnit unit) throws CanalClientException {
- waitClientRunning();
- if (!running) {
- return Lists.newArrayList();
- }
-
- ConsumerRecords records = kafkaConsumer2.poll(unit.toMillis(timeout));
-
- currentOffsets.clear();
- for (TopicPartition topicPartition : records.partitions()) {
- currentOffsets.put(topicPartition.partition(), kafkaConsumer2.position(topicPartition));
- }
-
- if (!records.isEmpty()) {
- List flatMessages = new ArrayList<>();
- for (ConsumerRecord record : records) {
- String flatMessageJson = record.value();
- FlatMessage flatMessage = JSON.parseObject(flatMessageJson, FlatMessage.class);
- flatMessages.add(flatMessage);
- }
-
- return flatMessages;
- }
- return Lists.newArrayList();
- }
-
- @Override
- public void rollback() {
- waitClientRunning();
- if (!running) {
- return;
- }
- // 回滚所有分区
- if (kafkaConsumer != null) {
- for (Map.Entry entry : currentOffsets.entrySet()) {
- kafkaConsumer.seek(new TopicPartition(topic, entry.getKey()), entry.getValue() - 1);
- }
- }
- if (kafkaConsumer2 != null) {
- for (Map.Entry entry : currentOffsets.entrySet()) {
- kafkaConsumer2.seek(new TopicPartition(topic, entry.getKey()), entry.getValue() - 1);
- }
- }
- }
-
- /**
- * 提交offset,如果超过 session.timeout.ms 设置的时间没有ack则会抛出异常,ack失败
- */
- @Override
- public void ack() {
- waitClientRunning();
- if (!running) {
- return;
- }
-
- if (kafkaConsumer != null) {
- kafkaConsumer.commitSync();
- }
- if (kafkaConsumer2 != null) {
- kafkaConsumer2.commitSync();
- }
- }
-
- @Override
- public void subscribe(String filter) throws CanalClientException {
- throw new CanalClientException("mq not support this method");
- }
-
- @Override
- public Message get(int batchSize) throws CanalClientException {
- throw new CanalClientException("mq not support this method");
- }
-
- @Override
- public Message get(int batchSize, Long timeout, TimeUnit unit) throws CanalClientException {
- throw new CanalClientException("mq not support this method");
- }
-
- @Override
- public Message getWithoutAck(int batchSize) throws CanalClientException {
- throw new CanalClientException("mq not support this method");
- }
-
- @Override
- public Message getWithoutAck(int batchSize, Long timeout, TimeUnit unit) throws CanalClientException {
- throw new CanalClientException("mq not support this method");
- }
-
- @Override
- public void ack(long batchId) throws CanalClientException {
- throw new CanalClientException("mq not support this method");
- }
-
- @Override
- public void rollback(long batchId) throws CanalClientException {
- throw new CanalClientException("mq not support this method");
- }
-
- /**
- * 重新设置sessionTime
- *
- * @param timeout
- * @param unit
- */
- public void setSessionTimeout(Long timeout, TimeUnit unit) {
- long t = unit.toMillis(timeout);
- properties.put("request.timeout.ms", String.valueOf(t + 60000));
- properties.put("session.timeout.ms", String.valueOf(t));
- }
-
-}
+package com.alibaba.otter.canal.client.kafka;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.kafka.clients.consumer.ConsumerRecord;
+import org.apache.kafka.clients.consumer.ConsumerRecords;
+import org.apache.kafka.clients.consumer.KafkaConsumer;
+import org.apache.kafka.common.TopicPartition;
+import org.apache.kafka.common.serialization.StringDeserializer;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.otter.canal.client.CanalMQConnector;
+import com.alibaba.otter.canal.client.impl.SimpleCanalConnector;
+import com.alibaba.otter.canal.protocol.FlatMessage;
+import com.alibaba.otter.canal.protocol.Message;
+import com.alibaba.otter.canal.protocol.exception.CanalClientException;
+import com.google.common.collect.Lists;
+
+/**
+ * canal kafka 数据操作客户端
+ *
+ *
+ * 注意点:
+ * 1. 相比于canal {@linkplain SimpleCanalConnector}, 这里get和ack操作不能有并发, 必须是一个线程执行get后,内存里执行完毕ack后再取下一个get
+ *
+ *
+ * @author machengyuan @ 2018-6-12
+ * @version 1.1.1
+ */
+public class KafkaCanalConnector implements CanalMQConnector {
+
+ protected KafkaConsumer kafkaConsumer;
+ protected KafkaConsumer kafkaConsumer2; // 用于扁平message的数据消费
+ protected String topic;
+ protected Integer partition;
+ protected Properties properties;
+ protected volatile boolean connected = false;
+ protected volatile boolean running = false;
+ protected boolean flatMessage;
+
+ private Map currentOffsets = new ConcurrentHashMap<>();
+
+ public KafkaCanalConnector(String servers, String topic, Integer partition, String groupId, Integer batchSize,
+ boolean flatMessage){
+ this.topic = topic;
+ this.partition = partition;
+ this.flatMessage = flatMessage;
+
+ properties = new Properties();
+ properties.put("bootstrap.servers", servers);
+ properties.put("group.id", groupId);
+ properties.put("enable.auto.commit", false);
+ properties.put("auto.commit.interval.ms", "1000");
+ properties.put("auto.offset.reset", "latest"); // 如果没有offset则从最后的offset开始读
+ properties.put("request.timeout.ms", "40000"); // 必须大于session.timeout.ms的设置
+ properties.put("session.timeout.ms", "30000"); // 默认为30秒
+ properties.put("isolation.level", "read_committed");
+ if (batchSize == null) {
+ batchSize = 100;
+ }
+ properties.put("max.poll.records", batchSize.toString());
+ properties.put("key.deserializer", StringDeserializer.class.getName());
+ if (!flatMessage) {
+ properties.put("value.deserializer", MessageDeserializer.class.getName());
+ } else {
+ properties.put("value.deserializer", StringDeserializer.class.getName());
+ }
+ }
+
+ /**
+ * 打开连接
+ */
+ @Override
+ public void connect() {
+ if (connected) {
+ return;
+ }
+
+ connected = true;
+ if (kafkaConsumer == null && !flatMessage) {
+ kafkaConsumer = new KafkaConsumer(properties);
+
+ }
+ if (kafkaConsumer2 == null && flatMessage) {
+ kafkaConsumer2 = new KafkaConsumer(properties);
+ }
+ }
+
+ /**
+ * 关闭链接
+ */
+ @Override
+ public void disconnect() {
+ if (kafkaConsumer != null) {
+ kafkaConsumer.close();
+ kafkaConsumer = null;
+ }
+ if (kafkaConsumer2 != null) {
+ kafkaConsumer2.close();
+ kafkaConsumer2 = null;
+ }
+
+ connected = false;
+ }
+
+ protected void waitClientRunning() {
+ running = true;
+ }
+
+ @Override
+ public boolean checkValid() {
+ return true;// 默认都放过
+ }
+
+ /**
+ * 订阅topic
+ */
+ @Override
+ public void subscribe() {
+ waitClientRunning();
+ if (!running) {
+ return;
+ }
+
+ if (partition == null) {
+ if (kafkaConsumer != null) {
+ kafkaConsumer.subscribe(Collections.singletonList(topic));
+ }
+ if (kafkaConsumer2 != null) {
+ kafkaConsumer2.subscribe(Collections.singletonList(topic));
+ }
+ } else {
+ TopicPartition topicPartition = new TopicPartition(topic, partition);
+ if (kafkaConsumer != null) {
+ kafkaConsumer.assign(Collections.singletonList(topicPartition));
+ }
+ if (kafkaConsumer2 != null) {
+ kafkaConsumer2.assign(Collections.singletonList(topicPartition));
+ }
+ }
+ }
+
+ /**
+ * 取消订阅
+ */
+ @Override
+ public void unsubscribe() {
+ waitClientRunning();
+ if (!running) {
+ return;
+ }
+
+ if (kafkaConsumer != null) {
+ kafkaConsumer.unsubscribe();
+ }
+ if (kafkaConsumer2 != null) {
+ kafkaConsumer2.unsubscribe();
+ }
+ }
+
+ @Override
+ public List getList(Long timeout, TimeUnit unit) throws CanalClientException {
+ waitClientRunning();
+ if (!running) {
+ return Lists.newArrayList();
+ }
+
+ List messages = getListWithoutAck(timeout, unit);
+ if (messages != null && !messages.isEmpty()) {
+ this.ack();
+ }
+ return messages;
+ }
+
+ @Override
+ public List getListWithoutAck(Long timeout, TimeUnit unit) throws CanalClientException {
+ waitClientRunning();
+ if (!running) {
+ return Lists.newArrayList();
+ }
+
+ ConsumerRecords records = kafkaConsumer.poll(unit.toMillis(timeout));
+
+ currentOffsets.clear();
+ for (TopicPartition topicPartition : records.partitions()) {
+ currentOffsets.put(topicPartition.partition(), kafkaConsumer.position(topicPartition));
+ }
+
+ if (!records.isEmpty()) {
+ List messages = new ArrayList<>();
+ for (ConsumerRecord record : records) {
+ messages.add(record.value());
+ }
+ return messages;
+ }
+ return Lists.newArrayList();
+ }
+
+ @Override
+ public List getFlatList(Long timeout, TimeUnit unit) throws CanalClientException {
+ waitClientRunning();
+ if (!running) {
+ return Lists.newArrayList();
+ }
+
+ List messages = getFlatListWithoutAck(timeout, unit);
+ if (messages != null && !messages.isEmpty()) {
+ this.ack();
+ }
+ return messages;
+ }
+
+ @Override
+ public List getFlatListWithoutAck(Long timeout, TimeUnit unit) throws CanalClientException {
+ waitClientRunning();
+ if (!running) {
+ return Lists.newArrayList();
+ }
+
+ ConsumerRecords records = kafkaConsumer2.poll(unit.toMillis(timeout));
+
+ currentOffsets.clear();
+ for (TopicPartition topicPartition : records.partitions()) {
+ currentOffsets.put(topicPartition.partition(), kafkaConsumer2.position(topicPartition));
+ }
+
+ if (!records.isEmpty()) {
+ List flatMessages = new ArrayList<>();
+ for (ConsumerRecord record : records) {
+ String flatMessageJson = record.value();
+ FlatMessage flatMessage = JSON.parseObject(flatMessageJson, FlatMessage.class);
+ flatMessages.add(flatMessage);
+ }
+
+ return flatMessages;
+ }
+ return Lists.newArrayList();
+ }
+
+ @Override
+ public void rollback() {
+ waitClientRunning();
+ if (!running) {
+ return;
+ }
+ // 回滚所有分区
+ if (kafkaConsumer != null) {
+ for (Map.Entry entry : currentOffsets.entrySet()) {
+ kafkaConsumer.seek(new TopicPartition(topic, entry.getKey()), entry.getValue() - 1);
+ }
+ }
+ if (kafkaConsumer2 != null) {
+ for (Map.Entry entry : currentOffsets.entrySet()) {
+ kafkaConsumer2.seek(new TopicPartition(topic, entry.getKey()), entry.getValue() - 1);
+ }
+ }
+ }
+
+ /**
+ * 提交offset,如果超过 session.timeout.ms 设置的时间没有ack则会抛出异常,ack失败
+ */
+ @Override
+ public void ack() {
+ waitClientRunning();
+ if (!running) {
+ return;
+ }
+
+ if (kafkaConsumer != null) {
+ kafkaConsumer.commitSync();
+ }
+ if (kafkaConsumer2 != null) {
+ kafkaConsumer2.commitSync();
+ }
+ }
+
+ @Override
+ public void subscribe(String filter) throws CanalClientException {
+ throw new CanalClientException("mq not support this method");
+ }
+
+ @Override
+ public Message get(int batchSize) throws CanalClientException {
+ throw new CanalClientException("mq not support this method");
+ }
+
+ @Override
+ public Message get(int batchSize, Long timeout, TimeUnit unit) throws CanalClientException {
+ throw new CanalClientException("mq not support this method");
+ }
+
+ @Override
+ public Message getWithoutAck(int batchSize) throws CanalClientException {
+ throw new CanalClientException("mq not support this method");
+ }
+
+ @Override
+ public Message getWithoutAck(int batchSize, Long timeout, TimeUnit unit) throws CanalClientException {
+ throw new CanalClientException("mq not support this method");
+ }
+
+ @Override
+ public void ack(long batchId) throws CanalClientException {
+ throw new CanalClientException("mq not support this method");
+ }
+
+ @Override
+ public void rollback(long batchId) throws CanalClientException {
+ throw new CanalClientException("mq not support this method");
+ }
+
+ /**
+ * 重新设置sessionTime
+ *
+ * @param timeout
+ * @param unit
+ */
+ public void setSessionTimeout(Long timeout, TimeUnit unit) {
+ long t = unit.toMillis(timeout);
+ properties.put("request.timeout.ms", String.valueOf(t + 60000));
+ properties.put("session.timeout.ms", String.valueOf(t));
+ }
+
+}