Compare commits

..

2 Commits

Author SHA1 Message Date
bucketli
7ac605242e fix(canal):fix endless local binlog mode with exception. 2020-02-27 17:56:10 +08:00
Rg
70d8a379f2 add Rust canal client support (#2518) 2020-01-15 20:12:28 +08:00
5 changed files with 24 additions and 22 deletions

View File

@@ -84,6 +84,7 @@ canal 特别设计了 client-server 模式,交互协议使用 protobuf 3.0 , c
- canal go客户端: [https://github.com/CanalClient/canal-go](https://github.com/CanalClient/canal-go) - canal go客户端: [https://github.com/CanalClient/canal-go](https://github.com/CanalClient/canal-go)
- canal php客户端: [https://github.com/xingwenge/canal-php](https://github.com/xingwenge/canal-php) - canal php客户端: [https://github.com/xingwenge/canal-php](https://github.com/xingwenge/canal-php)
- canal Python客户端:[https://github.com/haozi3156666/canal-python](https://github.com/haozi3156666/canal-python) - canal Python客户端:[https://github.com/haozi3156666/canal-python](https://github.com/haozi3156666/canal-python)
- canal Rust客户端:[https://github.com/laohanlinux/canal-rs](https://github.com/laohanlinux/canal-rs)
canal 作为 MySQL binlog 增量获取和解析工具,可将变更记录投递到 MQ 系统中,比如 Kafka/RocketMQ,可以借助于 MQ 的多语言能力 canal 作为 MySQL binlog 增量获取和解析工具,可将变更记录投递到 MQ 系统中,比如 Kafka/RocketMQ,可以借助于 MQ 的多语言能力

View File

@@ -174,11 +174,6 @@ public abstract class AbstractCanalAdapterWorker {
if (i == retry - 1) { if (i == retry - 1) {
connector.ack(); connector.ack();
logger.error(e.getMessage() + " Error sync but ACK!"); logger.error(e.getMessage() + " Error sync but ACK!");
try {
Thread.sleep(500);
} catch (InterruptedException e1) {
// ignore
}
return true; return true;
} else { } else {
connector.rollback(); connector.rollback();

View File

@@ -61,11 +61,11 @@ public class KafkaCanalConnector implements CanalMQConnector {
batchSize = 100; batchSize = 100;
} }
properties.put("max.poll.records", batchSize.toString()); properties.put("max.poll.records", batchSize.toString());
properties.put("key.deserializer", StringDeserializer.class); properties.put("key.deserializer", StringDeserializer.class.getName());
if (!flatMessage) { if (!flatMessage) {
properties.put("value.deserializer", MessageDeserializer.class); properties.put("value.deserializer", MessageDeserializer.class.getName());
} else { } else {
properties.put("value.deserializer", StringDeserializer.class); properties.put("value.deserializer", StringDeserializer.class.getName());
} }
} }
@@ -183,13 +183,14 @@ public class KafkaCanalConnector implements CanalMQConnector {
ConsumerRecords<String, Message> records = kafkaConsumer.poll(unit.toMillis(timeout)); ConsumerRecords<String, Message> records = kafkaConsumer.poll(unit.toMillis(timeout));
if (!records.isEmpty()) {
currentOffsets.clear(); currentOffsets.clear();
for (TopicPartition topicPartition : records.partitions()) {
currentOffsets.put(topicPartition.partition(), kafkaConsumer.position(topicPartition));
}
if (!records.isEmpty()) {
List<Message> messages = new ArrayList<>(); List<Message> messages = new ArrayList<>();
for (ConsumerRecord<String, Message> record : records) { for (ConsumerRecord<String, Message> record : records) {
if (currentOffsets.get(record.partition()) == null) {
currentOffsets.put(record.partition(), record.offset());
}
messages.add(record.value()); messages.add(record.value());
} }
return messages; return messages;
@@ -220,13 +221,14 @@ public class KafkaCanalConnector implements CanalMQConnector {
ConsumerRecords<String, String> records = kafkaConsumer2.poll(unit.toMillis(timeout)); ConsumerRecords<String, String> records = kafkaConsumer2.poll(unit.toMillis(timeout));
if (!records.isEmpty()) {
currentOffsets.clear(); currentOffsets.clear();
for (TopicPartition topicPartition : records.partitions()) {
currentOffsets.put(topicPartition.partition(), kafkaConsumer2.position(topicPartition));
}
if (!records.isEmpty()) {
List<FlatMessage> flatMessages = new ArrayList<>(); List<FlatMessage> flatMessages = new ArrayList<>();
for (ConsumerRecord<String, String> record : records) { for (ConsumerRecord<String, String> record : records) {
if (currentOffsets.get(record.partition()) == null) {
currentOffsets.put(record.partition(), record.offset());
}
String flatMessageJson = record.value(); String flatMessageJson = record.value();
FlatMessage flatMessage = JSON.parseObject(flatMessageJson, FlatMessage.class); FlatMessage flatMessage = JSON.parseObject(flatMessageJson, FlatMessage.class);
flatMessages.add(flatMessage); flatMessages.add(flatMessage);
@@ -246,14 +248,12 @@ public class KafkaCanalConnector implements CanalMQConnector {
// 回滚所有分区 // 回滚所有分区
if (kafkaConsumer != null) { if (kafkaConsumer != null) {
for (Map.Entry<Integer, Long> entry : currentOffsets.entrySet()) { for (Map.Entry<Integer, Long> entry : currentOffsets.entrySet()) {
kafkaConsumer.seek(new TopicPartition(topic, entry.getKey()), currentOffsets.get(entry.getKey())); kafkaConsumer.seek(new TopicPartition(topic, entry.getKey()), entry.getValue() - 1);
kafkaConsumer.commitSync();
} }
} }
if (kafkaConsumer2 != null) { if (kafkaConsumer2 != null) {
for (Map.Entry<Integer, Long> entry : currentOffsets.entrySet()) { for (Map.Entry<Integer, Long> entry : currentOffsets.entrySet()) {
kafkaConsumer2.seek(new TopicPartition(topic, entry.getKey()), currentOffsets.get(entry.getKey())); kafkaConsumer2.seek(new TopicPartition(topic, entry.getKey()), entry.getValue() - 1);
kafkaConsumer.commitSync();
} }
} }
} }

View File

@@ -934,4 +934,7 @@ public class MysqlEventParser extends AbstractMysqlEventParser implements CanalE
this.rdsOssMode = rdsOssMode; this.rdsOssMode = rdsOssMode;
} }
public void setDumpErrorCount(int dumpErrorCount) {
this.dumpErrorCount = dumpErrorCount;
}
} }

View File

@@ -88,6 +88,9 @@ public class RdsBinlogEventParserProxy extends MysqlEventParser {
@Override @Override
public void run() { public void run() {
rdsLocalBinlogEventParser.stop(); rdsLocalBinlogEventParser.stop();
// empty the dump error count,or will go into local binlog mode again,with error
// position,never get out,fixed by bucketli
RdsBinlogEventParserProxy.this.setDumpErrorCount(0);
RdsBinlogEventParserProxy.this.start(); RdsBinlogEventParserProxy.this.start();
} }
}); });