mirror of
https://github.com/alibaba/canal.git
synced 2026-11-04 00:30:15 +08:00
@@ -48,6 +48,7 @@ public class CanalConstants {
|
|||||||
public static final String CANAL_MQ_FLATMESSAGE = ROOT + "." + "mq.flatMessage";
|
public static final String CANAL_MQ_FLATMESSAGE = ROOT + "." + "mq.flatMessage";
|
||||||
public static final String CANAL_MQ_COMPRESSION_TYPE = ROOT + "." + "mq.compressionType";
|
public static final String CANAL_MQ_COMPRESSION_TYPE = ROOT + "." + "mq.compressionType";
|
||||||
public static final String CANAL_MQ_ACKS = ROOT + "." + "mq.acks";
|
public static final String CANAL_MQ_ACKS = ROOT + "." + "mq.acks";
|
||||||
|
public static final String CANAL_MQ_TRANSACTION = ROOT + "." + "mq.transaction";
|
||||||
public static final String CANAL_ALIYUN_ACCESSKEY = ROOT + "." + "aliyun.accessKey";
|
public static final String CANAL_ALIYUN_ACCESSKEY = ROOT + "." + "aliyun.accessKey";
|
||||||
public static final String CANAL_ALIYUN_SECRETKEY = ROOT + "." + "aliyun.secretKey";
|
public static final String CANAL_ALIYUN_SECRETKEY = ROOT + "." + "aliyun.secretKey";
|
||||||
|
|
||||||
|
|||||||
@@ -150,7 +150,6 @@ public class CanalStater {
|
|||||||
if (!StringUtils.isEmpty(acks)) {
|
if (!StringUtils.isEmpty(acks)) {
|
||||||
mqProperties.setAcks(acks);
|
mqProperties.setAcks(acks);
|
||||||
}
|
}
|
||||||
|
|
||||||
String aliyunAccessKey = CanalController.getProperty(properties, CanalConstants.CANAL_ALIYUN_ACCESSKEY);
|
String aliyunAccessKey = CanalController.getProperty(properties, CanalConstants.CANAL_ALIYUN_ACCESSKEY);
|
||||||
if (!StringUtils.isEmpty(aliyunAccessKey)) {
|
if (!StringUtils.isEmpty(aliyunAccessKey)) {
|
||||||
mqProperties.setAliyunAccessKey(aliyunAccessKey);
|
mqProperties.setAliyunAccessKey(aliyunAccessKey);
|
||||||
@@ -159,6 +158,10 @@ public class CanalStater {
|
|||||||
if (!StringUtils.isEmpty(aliyunSecretKey)) {
|
if (!StringUtils.isEmpty(aliyunSecretKey)) {
|
||||||
mqProperties.setAliyunSecretKey(aliyunSecretKey);
|
mqProperties.setAliyunSecretKey(aliyunSecretKey);
|
||||||
}
|
}
|
||||||
|
String transaction = CanalController.getProperty(properties, CanalConstants.CANAL_MQ_TRANSACTION);
|
||||||
|
if (!StringUtils.isEmpty(transaction)) {
|
||||||
|
mqProperties.setTransaction(Boolean.valueOf(transaction));
|
||||||
|
}
|
||||||
return mqProperties;
|
return mqProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -114,4 +114,6 @@ canal.mq.canalBatchSize = 50
|
|||||||
canal.mq.canalGetTimeout = 100
|
canal.mq.canalGetTimeout = 100
|
||||||
canal.mq.flatMessage = true
|
canal.mq.flatMessage = true
|
||||||
canal.mq.compressionType = none
|
canal.mq.compressionType = none
|
||||||
canal.mq.acks = all
|
canal.mq.acks = all
|
||||||
|
# use transaction for kafka flatMessage batch produce
|
||||||
|
canal.mq.transaction = false
|
||||||
@@ -23,6 +23,7 @@ public class MQProperties {
|
|||||||
private String acks = "all";
|
private String acks = "all";
|
||||||
private String aliyunAccessKey = "";
|
private String aliyunAccessKey = "";
|
||||||
private String aliyunSecretKey = "";
|
private String aliyunSecretKey = "";
|
||||||
|
private boolean transaction = false; // 是否开启事务
|
||||||
|
|
||||||
public static class CanalDestination {
|
public static class CanalDestination {
|
||||||
|
|
||||||
@@ -201,4 +202,12 @@ public class MQProperties {
|
|||||||
public void setMaxRequestSize(int maxRequestSize) {
|
public void setMaxRequestSize(int maxRequestSize) {
|
||||||
this.maxRequestSize = maxRequestSize;
|
this.maxRequestSize = maxRequestSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean getTransaction() {
|
||||||
|
return transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTransaction(boolean transaction) {
|
||||||
|
this.transaction = transaction;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,56 +98,51 @@ public class CanalKafkaProducer implements CanalMQProducer {
|
|||||||
}
|
}
|
||||||
callback.commit();
|
callback.commit();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
logger.error(e.getMessage(), e);
|
||||||
callback.rollback();
|
callback.rollback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void send(MQProperties.CanalDestination canalDestination, String topicName,
|
private void send(MQProperties.CanalDestination canalDestination, String topicName,
|
||||||
Message message) throws Exception {
|
Message message) throws Exception {
|
||||||
// producer.beginTransaction();
|
|
||||||
if (!kafkaProperties.getFlatMessage()) {
|
if (!kafkaProperties.getFlatMessage()) {
|
||||||
try {
|
ProducerRecord<String, Message> record = null;
|
||||||
ProducerRecord<String, Message> record = null;
|
if (canalDestination.getPartition() != null) {
|
||||||
if (canalDestination.getPartition() != null) {
|
record = new ProducerRecord<>(topicName, canalDestination.getPartition(), null, message);
|
||||||
record = new ProducerRecord<>(topicName, canalDestination.getPartition(), null, message);
|
} else {
|
||||||
} else {
|
if (canalDestination.getPartitionHash() != null && !canalDestination.getPartitionHash().isEmpty()) {
|
||||||
if (canalDestination.getPartitionHash() != null && !canalDestination.getPartitionHash().isEmpty()) {
|
Message[] messages = MQMessageUtils.messagePartition(message,
|
||||||
Message[] messages = MQMessageUtils.messagePartition(message,
|
canalDestination.getPartitionsNum(),
|
||||||
canalDestination.getPartitionsNum(),
|
canalDestination.getPartitionHash());
|
||||||
canalDestination.getPartitionHash());
|
int length = messages.length;
|
||||||
int length = messages.length;
|
for (int i = 0; i < length; i++) {
|
||||||
for (int i = 0; i < length; i++) {
|
Message messagePartition = messages[i];
|
||||||
Message messagePartition = messages[i];
|
if (messagePartition != null) {
|
||||||
if (messagePartition != null) {
|
record = new ProducerRecord<>(topicName, i, null, messagePartition);
|
||||||
record = new ProducerRecord<>(topicName, i, null, messagePartition);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
record = new ProducerRecord<>(topicName, 0, null, message);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
record = new ProducerRecord<>(topicName, 0, null, message);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (record != null) {
|
if (record != null) {
|
||||||
// 同步发送原生message
|
// 同步发送原生message
|
||||||
producer.send(record).get();
|
producer.send(record).get();
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("Send message to kafka topic: [{}], packet: {}", topicName, message.toString());
|
logger.debug("Send message to kafka topic: [{}], packet: {}", topicName, message.toString());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e.getMessage(), e);
|
|
||||||
// producer.abortTransaction();
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 发送扁平数据json
|
// 发送扁平数据json
|
||||||
List<FlatMessage> flatMessages = MQMessageUtils.messageConverter(message);
|
List<FlatMessage> flatMessages = MQMessageUtils.messageConverter(message);
|
||||||
if (flatMessages != null) {
|
if (flatMessages != null) {
|
||||||
int idx = 0;
|
// 开启事务,需要kafka版本支持
|
||||||
int size = flatMessages.size();
|
if (kafkaProperties.getTransaction()) {
|
||||||
|
producer.beginTransaction();
|
||||||
|
}
|
||||||
for (FlatMessage flatMessage : flatMessages) {
|
for (FlatMessage flatMessage : flatMessages) {
|
||||||
idx++;
|
|
||||||
if (StringUtils.isEmpty(canalDestination.getPartitionHash())) {
|
if (StringUtils.isEmpty(canalDestination.getPartitionHash())) {
|
||||||
try {
|
try {
|
||||||
Integer partition = canalDestination.getPartition();
|
Integer partition = canalDestination.getPartition();
|
||||||
@@ -158,14 +153,15 @@ public class CanalKafkaProducer implements CanalMQProducer {
|
|||||||
partition,
|
partition,
|
||||||
null,
|
null,
|
||||||
JSON.toJSONString(flatMessage, SerializerFeature.WriteMapNullValue));
|
JSON.toJSONString(flatMessage, SerializerFeature.WriteMapNullValue));
|
||||||
if (idx != size) {
|
if (kafkaProperties.getTransaction()) {
|
||||||
producer2.send(record);
|
producer2.send(record);
|
||||||
} else {
|
} else {
|
||||||
producer2.send(record).get();
|
producer2.send(record).get();
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error(e.getMessage(), e);
|
if (kafkaProperties.getTransaction()) {
|
||||||
// producer.abortTransaction();
|
producer.abortTransaction();
|
||||||
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -182,19 +178,25 @@ public class CanalKafkaProducer implements CanalMQProducer {
|
|||||||
i,
|
i,
|
||||||
null,
|
null,
|
||||||
JSON.toJSONString(flatMessagePart, SerializerFeature.WriteMapNullValue));
|
JSON.toJSONString(flatMessagePart, SerializerFeature.WriteMapNullValue));
|
||||||
if (idx != size) {
|
if (kafkaProperties.getTransaction()) {
|
||||||
producer2.send(record);
|
producer2.send(record);
|
||||||
} else {
|
} else {
|
||||||
producer2.send(record).get();
|
producer2.send(record).get();
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error(e.getMessage(), e);
|
if (kafkaProperties.getTransaction()) {
|
||||||
// producer.abortTransaction();
|
producer.abortTransaction();
|
||||||
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (kafkaProperties.getTransaction()) {
|
||||||
|
producer.commitTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("Send flat message to kafka topic: [{}], packet: {}",
|
logger.debug("Send flat message to kafka topic: [{}], packet: {}",
|
||||||
topicName,
|
topicName,
|
||||||
|
|||||||
Reference in New Issue
Block a user