Compare commits

..

29 Commits

Author SHA1 Message Date
agapple
a7b12c7098 [maven-release-plugin] prepare release canal-1.1.3 2019-04-04 10:25:29 +08:00
agapple
2c320e8618 Merge pull request #1661 from rewerma/master
flatMessage模式下默认canal.mq.lingerMs=100
2019-04-04 10:08:34 +08:00
machey
d76d25df6c flatMessage模式下默认canal.mq.lingerMs=100 2019-04-04 00:14:27 +08:00
agapple
b95c0d03cf fixed issue #1660 ,support mysql8.0 caching_sha2_password auth 2019-04-03 23:42:41 +08:00
agapple
08af40221f Merge pull request #1658 from rewerma/master
adapter 日志打印修改
2019-04-03 12:12:56 +08:00
rewerma
d13c1eb17c Merge branch 'master' into master 2019-04-03 11:34:10 +08:00
agapple
82ed9db8ae fixed rocketmq partition out of range 2019-04-03 11:26:31 +08:00
agapple
d3195ac973 fixed issue #1626 , support alter table id first 2019-04-03 10:58:43 +08:00
agapple
15e29d276a fixed default xml config 2019-03-28 22:05:56 +08:00
agapple
d715ff64b4 fixed packet write batch 2019-03-28 21:52:51 +08:00
agapple
f63ce4ffe6 fixed mysql auth 2019-03-28 20:45:16 +08:00
agapple
a7f9a779ad fixed issue #1640 , support schema partition 2019-03-28 17:01:36 +08:00
agapple
39aeeb8c58 Merge pull request #1641 from wangsaner/master
修改canal.mq.partitionHash散列规则,使其支持同库下的不同表hash至相同分区
2019-03-28 16:58:31 +08:00
wangsaner
9826948ebb 修改canal.mq.partitionHash散列规则,
当指明pk级做分区(schema.table:pk1^pk2)时,由table.hashCode()^column.getValue().hashCode()改为database.hashCode()^column.getValue().hashCode(),从而支持同库下不同表hash至相同分区
2019-03-28 16:01:02 +08:00
wangsaner
d6ef66a281 Merge pull request #1 from alibaba/master
merge
2019-03-28 10:37:24 +08:00
agapple
97640b717b fixed compiler 2019-03-28 10:23:33 +08:00
agapple
20e7becfe9 fixed format 2019-03-28 10:19:53 +08:00
agapple
99b34ff632 Merge pull request #1639 from wangsaner/master
fixed issue #1636
2019-03-28 10:17:42 +08:00
wangsaner
739963acd0 fixed issue #1636
非flat模式,当MQ主题为多分区时,数据拆分只取了for循环里面的最后一份数据,
导致消费端只能消费到一个分区的数据
2019-03-27 21:57:04 +08:00
agapple
0ec4699149 fixed format & support Mysql Auth Switch Protocol 2019-03-27 11:54:40 +08:00
mcy
c101ae2280 Merge branch 'master' of https://github.com/rewerma/canal 2019-03-26 11:39:41 +08:00
mcy
a137deeaff 使用统一log 2019-03-26 11:29:52 +08:00
rewerma
c5be5b7a8a Merge pull request #40 from alibaba/master
merge
2019-03-26 00:43:11 +08:00
agapple
95bed6bcf6 fixed fastsql 2019-03-25 19:50:02 +08:00
agapple
661d119265 fixed issue #1625, mq dymaic topic match 2019-03-25 17:54:35 +08:00
agapple
08167c95c7 Merge pull request #1622 from rewerma/master
fix bugs
2019-03-22 18:14:14 +08:00
mcy
37c5168a7d code optimization 2019-03-22 14:49:50 +08:00
mcy
579fa72ae2 kafka consumer connector引入事务隔离级别 fix #1512 2019-03-22 13:44:02 +08:00
rewerma
844c019433 Merge pull request #39 from alibaba/master
merge
2019-03-22 09:10:43 +08:00
78 changed files with 1021 additions and 521 deletions

View File

@@ -1,10 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent> <parent>
<artifactId>canal.client-adapter</artifactId> <artifactId>canal.client-adapter</artifactId>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<version>1.1.3-SNAPSHOT</version> <version>1.1.3</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
@@ -15,7 +14,7 @@
<dependency> <dependency>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal.protocol</artifactId> <artifactId>canal.protocol</artifactId>
<version>1.1.3-SNAPSHOT</version> <version>1.1.3</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>joda-time</groupId> <groupId>joda-time</groupId>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<artifactId>canal.client-adapter</artifactId> <artifactId>canal.client-adapter</artifactId>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<version>1.1.3-SNAPSHOT</version> <version>1.1.3</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
@@ -21,7 +21,7 @@
<dependency> <dependency>
<groupId>com.alibaba.fastsql</groupId> <groupId>com.alibaba.fastsql</groupId>
<artifactId>fastsql</artifactId> <artifactId>fastsql</artifactId>
<version>2.0.0_preview_644</version> <version>2.0.0_preview_855</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.elasticsearch</groupId> <groupId>org.elasticsearch</groupId>

View File

@@ -17,6 +17,7 @@ import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.transport.client.PreBuiltTransportClient; import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.slf4j.MDC;
import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.otter.canal.client.adapter.OuterAdapter; import com.alibaba.otter.canal.client.adapter.OuterAdapter;
@@ -29,8 +30,12 @@ import com.alibaba.otter.canal.client.adapter.es.monitor.ESConfigMonitor;
import com.alibaba.otter.canal.client.adapter.es.service.ESEtlService; import com.alibaba.otter.canal.client.adapter.es.service.ESEtlService;
import com.alibaba.otter.canal.client.adapter.es.service.ESSyncService; import com.alibaba.otter.canal.client.adapter.es.service.ESSyncService;
import com.alibaba.otter.canal.client.adapter.es.support.ESTemplate; import com.alibaba.otter.canal.client.adapter.es.support.ESTemplate;
import com.alibaba.otter.canal.client.adapter.support.*; import com.alibaba.otter.canal.client.adapter.support.DatasourceConfig;
import org.slf4j.MDC; import com.alibaba.otter.canal.client.adapter.support.Dml;
import com.alibaba.otter.canal.client.adapter.support.EtlResult;
import com.alibaba.otter.canal.client.adapter.support.OuterAdapterConfig;
import com.alibaba.otter.canal.client.adapter.support.SPI;
/** /**
* ES外部适配器 * ES外部适配器
@@ -71,7 +76,6 @@ public class ESAdapter implements OuterAdapter {
@Override @Override
public void init(OuterAdapterConfig configuration, Properties envProperties) { public void init(OuterAdapterConfig configuration, Properties envProperties) {
try { try {
MDC.put("adapter", "es");
this.envProperties = envProperties; this.envProperties = envProperties;
Map<String, ESSyncConfig> esSyncConfigTmp = ESSyncConfigLoader.load(envProperties); Map<String, ESSyncConfig> esSyncConfigTmp = ESSyncConfigLoader.load(envProperties);
// 过滤不匹配的key的配置 // 过滤不匹配的key的配置
@@ -141,6 +145,7 @@ public class ESAdapter implements OuterAdapter {
} }
} }
@Override
public void sync(List<Dml> dmls) { public void sync(List<Dml> dmls) {
if (dmls == null || dmls.isEmpty()) { if (dmls == null || dmls.isEmpty()) {
return; return;
@@ -151,6 +156,7 @@ public class ESAdapter implements OuterAdapter {
} }
} }
esSyncService.commit(); // 批次统一提交 esSyncService.commit(); // 批次统一提交
} }
private void sync(Dml dml) { private void sync(Dml dml) {
@@ -241,7 +247,6 @@ public class ESAdapter implements OuterAdapter {
if (transportClient != null) { if (transportClient != null) {
transportClient.close(); transportClient.close();
} }
MDC.remove("adapter");
} }
@Override @Override

View File

@@ -9,8 +9,16 @@ import java.util.stream.Collectors;
import com.alibaba.fastsql.sql.SQLUtils; import com.alibaba.fastsql.sql.SQLUtils;
import com.alibaba.fastsql.sql.ast.SQLExpr; import com.alibaba.fastsql.sql.ast.SQLExpr;
import com.alibaba.fastsql.sql.ast.expr.*; import com.alibaba.fastsql.sql.ast.expr.SQLBinaryOpExpr;
import com.alibaba.fastsql.sql.ast.statement.*; import com.alibaba.fastsql.sql.ast.expr.SQLCaseExpr;
import com.alibaba.fastsql.sql.ast.expr.SQLIdentifierExpr;
import com.alibaba.fastsql.sql.ast.expr.SQLMethodInvokeExpr;
import com.alibaba.fastsql.sql.ast.expr.SQLPropertyExpr;
import com.alibaba.fastsql.sql.ast.statement.SQLExprTableSource;
import com.alibaba.fastsql.sql.ast.statement.SQLJoinTableSource;
import com.alibaba.fastsql.sql.ast.statement.SQLSelectStatement;
import com.alibaba.fastsql.sql.ast.statement.SQLSubqueryTableSource;
import com.alibaba.fastsql.sql.ast.statement.SQLTableSource;
import com.alibaba.fastsql.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock; import com.alibaba.fastsql.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;
import com.alibaba.fastsql.sql.dialect.mysql.parser.MySqlStatementParser; import com.alibaba.fastsql.sql.dialect.mysql.parser.MySqlStatementParser;
import com.alibaba.fastsql.sql.parser.ParserException; import com.alibaba.fastsql.sql.parser.ParserException;

View File

@@ -1,7 +1,11 @@
package com.alibaba.otter.canal.client.adapter.es.service; package com.alibaba.otter.canal.client.adapter.es.service;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.*; import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;

View File

@@ -7,7 +7,6 @@ import java.util.Map;
import javax.sql.DataSource; import javax.sql.DataSource;
import com.alibaba.otter.canal.client.adapter.support.Util;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -23,6 +22,7 @@ import com.alibaba.otter.canal.client.adapter.es.support.ESSyncUtil;
import com.alibaba.otter.canal.client.adapter.es.support.ESTemplate; import com.alibaba.otter.canal.client.adapter.es.support.ESTemplate;
import com.alibaba.otter.canal.client.adapter.support.DatasourceConfig; import com.alibaba.otter.canal.client.adapter.support.DatasourceConfig;
import com.alibaba.otter.canal.client.adapter.support.Dml; import com.alibaba.otter.canal.client.adapter.support.Dml;
import com.alibaba.otter.canal.client.adapter.support.Util;
/** /**
* ES 同步 Service * ES 同步 Service

View File

@@ -5,7 +5,13 @@ import java.io.InputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.sql.Blob; import java.sql.Blob;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.*; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.joda.time.DateTime; import org.joda.time.DateTime;

View File

@@ -1,6 +1,10 @@
package com.alibaba.otter.canal.client.adapter.es.test.sync; package com.alibaba.otter.canal.client.adapter.es.test.sync;
import java.util.*; import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource; import javax.sql.DataSource;

View File

@@ -1,6 +1,10 @@
package com.alibaba.otter.canal.client.adapter.es.test.sync; package com.alibaba.otter.canal.client.adapter.es.test.sync;
import java.util.*; import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource; import javax.sql.DataSource;

View File

@@ -1,6 +1,10 @@
package com.alibaba.otter.canal.client.adapter.es.test.sync; package com.alibaba.otter.canal.client.adapter.es.test.sync;
import java.util.*; import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource; import javax.sql.DataSource;

View File

@@ -1,6 +1,10 @@
package com.alibaba.otter.canal.client.adapter.es.test.sync; package com.alibaba.otter.canal.client.adapter.es.test.sync;
import java.util.*; import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource; import javax.sql.DataSource;

View File

@@ -1,6 +1,10 @@
package com.alibaba.otter.canal.client.adapter.es.test.sync; package com.alibaba.otter.canal.client.adapter.es.test.sync;
import java.util.*; import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource; import javax.sql.DataSource;

View File

@@ -1,6 +1,10 @@
package com.alibaba.otter.canal.client.adapter.es.test.sync; package com.alibaba.otter.canal.client.adapter.es.test.sync;
import java.util.*; import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.elasticsearch.action.get.GetResponse; import org.elasticsearch.action.get.GetResponse;
import org.junit.Assert; import org.junit.Assert;

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<artifactId>canal.client-adapter</artifactId> <artifactId>canal.client-adapter</artifactId>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<version>1.1.3-SNAPSHOT</version> <version>1.1.3</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>

View File

@@ -26,7 +26,6 @@ import com.alibaba.otter.canal.client.adapter.hbase.service.HbaseEtlService;
import com.alibaba.otter.canal.client.adapter.hbase.service.HbaseSyncService; import com.alibaba.otter.canal.client.adapter.hbase.service.HbaseSyncService;
import com.alibaba.otter.canal.client.adapter.hbase.support.HbaseTemplate; import com.alibaba.otter.canal.client.adapter.hbase.support.HbaseTemplate;
import com.alibaba.otter.canal.client.adapter.support.*; import com.alibaba.otter.canal.client.adapter.support.*;
import org.slf4j.MDC;
/** /**
* HBase外部适配器 * HBase外部适配器
@@ -60,7 +59,6 @@ public class HbaseAdapter implements OuterAdapter {
@Override @Override
public void init(OuterAdapterConfig configuration, Properties envProperties) { public void init(OuterAdapterConfig configuration, Properties envProperties) {
try { try {
MDC.put("adapter", "hbase");
this.envProperties = envProperties; this.envProperties = envProperties;
Map<String, MappingConfig> hbaseMappingTmp = MappingConfigLoader.load(envProperties); Map<String, MappingConfig> hbaseMappingTmp = MappingConfigLoader.load(envProperties);
// 过滤不匹配的key的配置 // 过滤不匹配的key的配置
@@ -104,6 +102,7 @@ public class HbaseAdapter implements OuterAdapter {
} }
} }
@Override
public void sync(List<Dml> dmls) { public void sync(List<Dml> dmls) {
if (dmls == null || dmls.isEmpty()) { if (dmls == null || dmls.isEmpty()) {
return; return;
@@ -113,7 +112,7 @@ public class HbaseAdapter implements OuterAdapter {
} }
} }
public void sync(Dml dml) { private void sync(Dml dml) {
if (dml == null) { if (dml == null) {
return; return;
} }
@@ -224,7 +223,6 @@ public class HbaseAdapter implements OuterAdapter {
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
MDC.remove("adapter");
} }
@Override @Override

View File

@@ -130,8 +130,10 @@ public class PhTypeUtil {
} else if (b[0] == 0) { } else if (b[0] == 0) {
v = false; v = false;
} }
} else if (phType == PhType.TIME || phType == PhType.DATE) { } else if (phType == PhType.DATE) {
v = new Date(decodeLong(b, 0)); v = new java.sql.Date(decodeLong(b, 0));
} else if (phType == PhType.TIME) {
v = new java.sql.Time(decodeLong(b, 0));
} else if (phType == PhType.TIMESTAMP) { } else if (phType == PhType.TIMESTAMP) {
long millisDeserialized = decodeLong(b, 0); long millisDeserialized = decodeLong(b, 0);
Timestamp ts = new Timestamp(millisDeserialized); Timestamp ts = new Timestamp(millisDeserialized);

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<artifactId>canal.client-adapter</artifactId> <artifactId>canal.client-adapter</artifactId>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<version>1.1.3-SNAPSHOT</version> <version>1.1.3</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
@@ -32,7 +32,7 @@
<dependency> <dependency>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal.client</artifactId> <artifactId>canal.client</artifactId>
<version>1.1.3-SNAPSHOT</version> <version>1.1.3</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.yaml</groupId> <groupId>org.yaml</groupId>

View File

@@ -40,11 +40,11 @@ if [ -z "$JAVA" ]; then
fi fi
fi fi
case "$#" case "$#"
in in
0 ) 0 )
;; ;;
2 ) 2 )
if [ "$1" = "debug" ]; then if [ "$1" = "debug" ]; then
DEBUG_PORT=$2 DEBUG_PORT=$2
DEBUG_SUSPEND="n" DEBUG_SUSPEND="n"
@@ -76,8 +76,8 @@ echo "cd to $bin_abs_path for workaround relative path"
cd $bin_abs_path cd $bin_abs_path
echo CLASSPATH :$CLASSPATH echo CLASSPATH :$CLASSPATH
$JAVA $JAVA_OPTS $JAVA_DEBUG_OPT $ADAPTER_OPTS -classpath .:$CLASSPATH com.alibaba.otter.canal.adapter.launcher.CanalAdapterApplication 1>>$base/logs/adapter.log 2>&1 & $JAVA $JAVA_OPTS $JAVA_DEBUG_OPT $ADAPTER_OPTS -classpath .:$CLASSPATH com.alibaba.otter.canal.adapter.launcher.CanalAdapterApplication 1>>/dev/null 2>&1 &
echo $! > $base/bin/adapter.pid echo $! > $base/bin/adapter.pid
echo "cd to $current_path for continue" echo "cd to $current_path for continue"
cd $current_path cd $current_path

View File

@@ -2,7 +2,11 @@ package com.alibaba.otter.canal.adapter.launcher.loader;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.util.*; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;

View File

@@ -1,13 +1,23 @@
package com.alibaba.otter.canal.adapter.launcher.rest; package com.alibaba.otter.canal.adapter.launcher.rest;
import java.util.*; import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.annotation.Resource; import javax.annotation.Resource;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.otter.canal.adapter.launcher.common.EtlLock; import com.alibaba.otter.canal.adapter.launcher.common.EtlLock;
import com.alibaba.otter.canal.adapter.launcher.common.SyncSwitch; import com.alibaba.otter.canal.adapter.launcher.common.SyncSwitch;

View File

@@ -8,17 +8,17 @@
</appender> </appender>
<appender name="CANAL-ROOT" class="ch.qos.logback.classic.sift.SiftingAppender"> <appender name="CANAL-ROOT" class="ch.qos.logback.classic.sift.SiftingAppender">
<discriminator> <discriminator>
<Key>adapter</Key> <Key>adapter</Key>
<DefaultValue>adapter</DefaultValue> <DefaultValue>adapter</DefaultValue>
</discriminator> </discriminator>
<sift> <sift>
<appender name="FILE-${adapter}" class="ch.qos.logback.core.rolling.RollingFileAppender"> <appender name="FILE-adapter" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>../logs/${adapter}/${adapter}.log</File> <File>../logs/adapter/adapter.log</File>
<rollingPolicy <rollingPolicy
class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- rollover daily --> <!-- rollover daily -->
<fileNamePattern>../logs/${adapter}/%d{yyyy-MM-dd}/${adapter}-%d{yyyy-MM-dd}-%i.log.gz</fileNamePattern> <fileNamePattern>../logs/adapter/%d{yyyy-MM-dd}/adapter-%d{yyyy-MM-dd}-%i.log.gz</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!-- or whenever the file size reaches 100MB --> <!-- or whenever the file size reaches 100MB -->
<maxFileSize>512MB</maxFileSize> <maxFileSize>512MB</maxFileSize>
@@ -36,14 +36,22 @@
<logger name="com.alibaba.otter.canal.client.adapter.hbase" additivity="false"> <logger name="com.alibaba.otter.canal.client.adapter.hbase" additivity="false">
<level value="DEBUG" /> <level value="DEBUG" />
<appender-ref ref="STDOUT"/>
<appender-ref ref="CANAL-ROOT" /> <appender-ref ref="CANAL-ROOT" />
</logger> </logger>
<logger name="com.alibaba.otter.canal.client.adapter.es" additivity="false"> <logger name="com.alibaba.otter.canal.client.adapter.es" additivity="false">
<level value="DEBUG" /> <level value="DEBUG" />
<appender-ref ref="STDOUT"/>
<appender-ref ref="CANAL-ROOT" /> <appender-ref ref="CANAL-ROOT" />
</logger> </logger>
<logger name="com.alibaba.otter.canal.client.adapter.rdb" additivity="false"> <logger name="com.alibaba.otter.canal.client.adapter.rdb" additivity="false">
<level value="DEBUG" /> <level value="DEBUG" />
<appender-ref ref="STDOUT"/>
<appender-ref ref="CANAL-ROOT" />
</logger>
<logger name="com.alibaba.otter.canal.client.adapter.logger" additivity="false">
<level value="DEBUG" />
<appender-ref ref="STDOUT"/>
<appender-ref ref="CANAL-ROOT" /> <appender-ref ref="CANAL-ROOT" />
</logger> </logger>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<artifactId>canal.client-adapter</artifactId> <artifactId>canal.client-adapter</artifactId>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<version>1.1.3-SNAPSHOT</version> <version>1.1.3</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>

View File

@@ -5,7 +5,6 @@ import java.util.Properties;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature; import com.alibaba.fastjson.serializer.SerializerFeature;
@@ -28,7 +27,6 @@ public class LoggerAdapterExample implements OuterAdapter {
@Override @Override
public void init(OuterAdapterConfig configuration, Properties envProperties) { public void init(OuterAdapterConfig configuration, Properties envProperties) {
MDC.put("adapter", "logger");
} }
public void sync(List<Dml> dmls) { public void sync(List<Dml> dmls) {
@@ -43,6 +41,5 @@ public class LoggerAdapterExample implements OuterAdapter {
@Override @Override
public void destroy() { public void destroy() {
MDC.remove("adapter");
} }
} }

View File

@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal.client-adapter</artifactId> <artifactId>canal.client-adapter</artifactId>
<version>1.1.3-SNAPSHOT</version> <version>1.1.3</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>canal client adapter module for otter ${project.version}</name> <name>canal client adapter module for otter ${project.version}</name>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<artifactId>canal.client-adapter</artifactId> <artifactId>canal.client-adapter</artifactId>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<version>1.1.3-SNAPSHOT</version> <version>1.1.3</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>

View File

@@ -14,7 +14,6 @@ import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.otter.canal.client.adapter.OuterAdapter; import com.alibaba.otter.canal.client.adapter.OuterAdapter;
@@ -26,12 +25,7 @@ import com.alibaba.otter.canal.client.adapter.rdb.service.RdbEtlService;
import com.alibaba.otter.canal.client.adapter.rdb.service.RdbMirrorDbSyncService; import com.alibaba.otter.canal.client.adapter.rdb.service.RdbMirrorDbSyncService;
import com.alibaba.otter.canal.client.adapter.rdb.service.RdbSyncService; import com.alibaba.otter.canal.client.adapter.rdb.service.RdbSyncService;
import com.alibaba.otter.canal.client.adapter.rdb.support.SyncUtil; import com.alibaba.otter.canal.client.adapter.rdb.support.SyncUtil;
import com.alibaba.otter.canal.client.adapter.support.DatasourceConfig; import com.alibaba.otter.canal.client.adapter.support.*;
import com.alibaba.otter.canal.client.adapter.support.Dml;
import com.alibaba.otter.canal.client.adapter.support.EtlResult;
import com.alibaba.otter.canal.client.adapter.support.OuterAdapterConfig;
import com.alibaba.otter.canal.client.adapter.support.SPI;
import com.alibaba.otter.canal.client.adapter.support.Util;
/** /**
* RDB适配器实现类 * RDB适配器实现类
@@ -76,14 +70,13 @@ public class RdbAdapter implements OuterAdapter {
*/ */
@Override @Override
public void init(OuterAdapterConfig configuration, Properties envProperties) { public void init(OuterAdapterConfig configuration, Properties envProperties) {
MDC.put("adapter", "rdb");
this.envProperties = envProperties; this.envProperties = envProperties;
Map<String, MappingConfig> rdbMappingTmp = ConfigLoader.load(envProperties); Map<String, MappingConfig> rdbMappingTmp = ConfigLoader.load(envProperties);
// 过滤不匹配的key的配置 // 过滤不匹配的key的配置
rdbMappingTmp.forEach((key, mappingConfig) -> { rdbMappingTmp.forEach((key, mappingConfig) -> {
if ((mappingConfig.getOuterAdapterKey() == null && configuration.getKey() == null) if ((mappingConfig.getOuterAdapterKey() == null && configuration.getKey() == null)
|| (mappingConfig.getOuterAdapterKey() != null && mappingConfig.getOuterAdapterKey() || (mappingConfig.getOuterAdapterKey() != null
.equalsIgnoreCase(configuration.getKey()))) { && mappingConfig.getOuterAdapterKey().equalsIgnoreCase(configuration.getKey()))) {
rdbMapping.put(key, mappingConfig); rdbMapping.put(key, mappingConfig);
} }
}); });
@@ -143,8 +136,8 @@ public class RdbAdapter implements OuterAdapter {
String threads = properties.get("threads"); String threads = properties.get("threads");
// String commitSize = properties.get("commitSize"); // String commitSize = properties.get("commitSize");
boolean skipDupException = BooleanUtils.toBoolean(configuration.getProperties() boolean skipDupException = BooleanUtils
.getOrDefault("skipDupException", "true")); .toBoolean(configuration.getProperties().getOrDefault("skipDupException", "true"));
rdbSyncService = new RdbSyncService(dataSource, rdbSyncService = new RdbSyncService(dataSource,
threads != null ? Integer.valueOf(threads) : null, threads != null ? Integer.valueOf(threads) : null,
skipDupException); skipDupException);
@@ -304,6 +297,5 @@ public class RdbAdapter implements OuterAdapter {
if (dataSource != null) { if (dataSource != null) {
dataSource.close(); dataSource.close();
} }
MDC.remove("adapter");
} }
} }

View File

@@ -1,6 +1,10 @@
package com.alibaba.otter.canal.client.adapter.rdb.service; package com.alibaba.otter.canal.client.adapter.rdb.service;
import java.sql.*; import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;

View File

@@ -4,7 +4,14 @@ import java.io.Reader;
import java.io.StringReader; import java.io.StringReader;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.sql.*; import java.sql.Blob;
import java.sql.Clob;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.Collection; import java.util.Collection;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;

View File

@@ -1,6 +1,11 @@
package com.alibaba.otter.canal.client.adapter.rdb.test.sync; package com.alibaba.otter.canal.client.adapter.rdb.test.sync;
import java.util.*; import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.1.3-SNAPSHOT</version> <version>1.1.3</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>

View File

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

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.1.3-SNAPSHOT</version> <version>1.1.3</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<artifactId>canal.common</artifactId> <artifactId>canal.common</artifactId>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.1.3-SNAPSHOT</version> <version>1.1.3</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.1.3-SNAPSHOT</version> <version>1.1.3</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>

View File

@@ -52,6 +52,8 @@ public class CanalStater {
// disable netty // disable netty
System.setProperty(CanalConstants.CANAL_WITHOUT_NETTY, "true"); System.setProperty(CanalConstants.CANAL_WITHOUT_NETTY, "true");
String autoScan = CanalController.getProperty(properties, CanalConstants.CANAL_AUTO_SCAN); String autoScan = CanalController.getProperty(properties, CanalConstants.CANAL_AUTO_SCAN);
// 设置为raw避免ByteString->Entry的二次解析
System.setProperty("canal.instance.memory.rawEntry", "false");
if ("true".equals(autoScan)) { if ("true".equals(autoScan)) {
String rootDir = CanalController.getProperty(properties, CanalConstants.CANAL_CONF_DIR); String rootDir = CanalController.getProperty(properties, CanalConstants.CANAL_CONF_DIR);
if (StringUtils.isEmpty(rootDir)) { if (StringUtils.isEmpty(rootDir)) {
@@ -68,13 +70,13 @@ public class CanalStater {
}); });
if (instanceDirs != null && instanceDirs.length > 0) { if (instanceDirs != null && instanceDirs.length > 0) {
List<String> instances = Lists.transform(Arrays.asList(instanceDirs), List<String> instances = Lists.transform(Arrays.asList(instanceDirs),
new Function<File, String>() { new Function<File, String>() {
@Override @Override
public String apply(File instanceDir) { public String apply(File instanceDir) {
return instanceDir.getName(); return instanceDir.getName();
} }
}); });
System.setProperty(CanalConstants.CANAL_DESTINATIONS, Joiner.on(",").join(instances)); System.setProperty(CanalConstants.CANAL_DESTINATIONS, Joiner.on(",").join(instances));
} }
} }

View File

@@ -108,7 +108,7 @@ canal.mq.servers = 127.0.0.1:6667
canal.mq.retries = 0 canal.mq.retries = 0
canal.mq.batchSize = 16384 canal.mq.batchSize = 16384
canal.mq.maxRequestSize = 1048576 canal.mq.maxRequestSize = 1048576
canal.mq.lingerMs = 1 canal.mq.lingerMs = 100
canal.mq.bufferMemory = 33554432 canal.mq.bufferMemory = 33554432
canal.mq.canalBatchSize = 50 canal.mq.canalBatchSize = 50
canal.mq.canalGetTimeout = 100 canal.mq.canalGetTimeout = 100

View File

@@ -33,7 +33,6 @@ canal.instance.tsdb.enable=true
canal.instance.dbUsername=canal canal.instance.dbUsername=canal
canal.instance.dbPassword=canal canal.instance.dbPassword=canal
canal.instance.connectionCharset = UTF-8 canal.instance.connectionCharset = UTF-8
canal.instance.defaultDatabaseName =test
# enable druid Decrypt database password # enable druid Decrypt database password
canal.instance.enableDruid=false canal.instance.enableDruid=false
#canal.instance.pwdPublicKey=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALK4BUxdDltRRE5/zXpVEVPUgunvscYFtEip3pmLlhrWpacX7y7GCMo2/JM6LeHmiiNdH1FWgGCpUfircSwlWKUCAwEAAQ== #canal.instance.pwdPublicKey=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALK4BUxdDltRRE5/zXpVEVPUgunvscYFtEip3pmLlhrWpacX7y7GCMo2/JM6LeHmiiNdH1FWgGCpUfircSwlWKUCAwEAAQ==
@@ -46,9 +45,9 @@ canal.instance.filter.black.regex=
# mq config # mq config
canal.mq.topic=example canal.mq.topic=example
# dynamic topic route by schema or table regex # dynamic topic route by schema or table regex
#canal.mq.dynamicTopic=.*,mytest,mytest\\..*,mytest2.user #canal.mq.dynamicTopic=mytest1.user,mytest2\\..*,.*\\..*
canal.mq.partition=0 canal.mq.partition=0
# hash partition config # hash partition config
#canal.mq.partitionsNum=4 #canal.mq.partitionsNum=3
#canal.mq.partitionHash=test.table:id^name,.*\\..* #canal.mq.partitionHash=test.table:id^name,.*\\..*
################################################# #################################################

View File

@@ -136,7 +136,7 @@
<property name="password" value="${canal.instance.dbPassword:retl}" /> <property name="password" value="${canal.instance.dbPassword:retl}" />
<property name="pwdPublicKey" value="${canal.instance.pwdPublicKey:retl}" /> <property name="pwdPublicKey" value="${canal.instance.pwdPublicKey:retl}" />
<property name="enableDruid" value="${canal.instance.enableDruid:false}" /> <property name="enableDruid" value="${canal.instance.enableDruid:false}" />
<property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:test}" /> <property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:}" />
</bean> </bean>
</property> </property>
<property name="standbyInfo"> <property name="standbyInfo">
@@ -146,7 +146,7 @@
<property name="password" value="${canal.instance.dbPassword:retl}" /> <property name="password" value="${canal.instance.dbPassword:retl}" />
<property name="pwdPublicKey" value="${canal.instance.pwdPublicKey:retl}" /> <property name="pwdPublicKey" value="${canal.instance.pwdPublicKey:retl}" />
<property name="enableDruid" value="${canal.instance.enableDruid:false}" /> <property name="enableDruid" value="${canal.instance.enableDruid:false}" />
<property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:test}" /> <property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:}" />
</bean> </bean>
</property> </property>

View File

@@ -121,7 +121,7 @@
<property name="password" value="${canal.instance.dbPassword:retl}" /> <property name="password" value="${canal.instance.dbPassword:retl}" />
<property name="pwdPublicKey" value="${canal.instance.pwdPublicKey:retl}" /> <property name="pwdPublicKey" value="${canal.instance.pwdPublicKey:retl}" />
<property name="enableDruid" value="${canal.instance.enableDruid:false}" /> <property name="enableDruid" value="${canal.instance.enableDruid:false}" />
<property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:test}" /> <property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:}" />
</bean> </bean>
</property> </property>
<property name="standbyInfo"> <property name="standbyInfo">
@@ -131,7 +131,7 @@
<property name="password" value="${canal.instance.dbPassword:retl}" /> <property name="password" value="${canal.instance.dbPassword:retl}" />
<property name="pwdPublicKey" value="${canal.instance.pwdPublicKey:retl}" /> <property name="pwdPublicKey" value="${canal.instance.pwdPublicKey:retl}" />
<property name="enableDruid" value="${canal.instance.enableDruid:false}" /> <property name="enableDruid" value="${canal.instance.enableDruid:false}" />
<property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:test}" /> <property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:}" />
</bean> </bean>
</property> </property>

View File

@@ -118,7 +118,7 @@
<property name="password" value="${canal.instance.dbPassword:retl}" /> <property name="password" value="${canal.instance.dbPassword:retl}" />
<property name="pwdPublicKey" value="${canal.instance.pwdPublicKey:retl}" /> <property name="pwdPublicKey" value="${canal.instance.pwdPublicKey:retl}" />
<property name="enableDruid" value="${canal.instance.enableDruid:false}" /> <property name="enableDruid" value="${canal.instance.enableDruid:false}" />
<property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:test}" /> <property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:}" />
</bean> </bean>
</property> </property>
<property name="standbyInfo"> <property name="standbyInfo">
@@ -128,7 +128,7 @@
<property name="password" value="${canal.instance.dbPassword:retl}" /> <property name="password" value="${canal.instance.dbPassword:retl}" />
<property name="pwdPublicKey" value="${canal.instance.pwdPublicKey:retl}" /> <property name="pwdPublicKey" value="${canal.instance.pwdPublicKey:retl}" />
<property name="enableDruid" value="${canal.instance.enableDruid:false}" /> <property name="enableDruid" value="${canal.instance.enableDruid:false}" />
<property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:test}" /> <property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:}" />
</bean> </bean>
</property> </property>
@@ -220,7 +220,7 @@
<property name="password" value="${canal.instance.dbPassword:retl}" /> <property name="password" value="${canal.instance.dbPassword:retl}" />
<property name="pwdPublicKey" value="${canal.instance.pwdPublicKey:retl}" /> <property name="pwdPublicKey" value="${canal.instance.pwdPublicKey:retl}" />
<property name="enableDruid" value="${canal.instance.enableDruid:false}" /> <property name="enableDruid" value="${canal.instance.enableDruid:false}" />
<property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:test}" /> <property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:}" />
</bean> </bean>
</property> </property>
<property name="standbyInfo"> <property name="standbyInfo">
@@ -230,7 +230,7 @@
<property name="password" value="${canal.instance.dbPassword:retl}" /> <property name="password" value="${canal.instance.dbPassword:retl}" />
<property name="pwdPublicKey" value="${canal.instance.pwdPublicKey:retl}" /> <property name="pwdPublicKey" value="${canal.instance.pwdPublicKey:retl}" />
<property name="enableDruid" value="${canal.instance.enableDruid:false}" /> <property name="enableDruid" value="${canal.instance.enableDruid:false}" />
<property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:test}" /> <property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:}" />
</bean> </bean>
</property> </property>

View File

@@ -109,7 +109,7 @@
<property name="password" value="${canal.instance.dbPassword:retl}" /> <property name="password" value="${canal.instance.dbPassword:retl}" />
<property name="pwdPublicKey" value="${canal.instance.pwdPublicKey:retl}" /> <property name="pwdPublicKey" value="${canal.instance.pwdPublicKey:retl}" />
<property name="enableDruid" value="${canal.instance.enableDruid:false}" /> <property name="enableDruid" value="${canal.instance.enableDruid:false}" />
<property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:test}" /> <property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:}" />
</bean> </bean>
</property> </property>
<property name="standbyInfo"> <property name="standbyInfo">
@@ -119,7 +119,7 @@
<property name="password" value="${canal.instance.dbPassword:retl}" /> <property name="password" value="${canal.instance.dbPassword:retl}" />
<property name="pwdPublicKey" value="${canal.instance.pwdPublicKey:retl}" /> <property name="pwdPublicKey" value="${canal.instance.pwdPublicKey:retl}" />
<property name="enableDruid" value="${canal.instance.enableDruid:false}" /> <property name="enableDruid" value="${canal.instance.enableDruid:false}" />
<property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:test}" /> <property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:}" />
</bean> </bean>
</property> </property>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.1.3-SNAPSHOT</version> <version>1.1.3</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>

View File

@@ -2,19 +2,25 @@ package com.alibaba.otter.canal.parse.driver.mysql;
import java.io.IOException; import java.io.IOException;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.security.DigestException;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.alibaba.otter.canal.parse.driver.mysql.packets.HeaderPacket; import com.alibaba.otter.canal.parse.driver.mysql.packets.HeaderPacket;
import com.alibaba.otter.canal.parse.driver.mysql.packets.client.AuthSwitchResponsePacket;
import com.alibaba.otter.canal.parse.driver.mysql.packets.client.ClientAuthenticationPacket; import com.alibaba.otter.canal.parse.driver.mysql.packets.client.ClientAuthenticationPacket;
import com.alibaba.otter.canal.parse.driver.mysql.packets.client.QuitCommandPacket; import com.alibaba.otter.canal.parse.driver.mysql.packets.client.QuitCommandPacket;
import com.alibaba.otter.canal.parse.driver.mysql.packets.server.AuthSwitchRequestMoreData;
import com.alibaba.otter.canal.parse.driver.mysql.packets.server.AuthSwitchRequestPacket;
import com.alibaba.otter.canal.parse.driver.mysql.packets.server.ErrorPacket; import com.alibaba.otter.canal.parse.driver.mysql.packets.server.ErrorPacket;
import com.alibaba.otter.canal.parse.driver.mysql.packets.server.HandshakeInitializationPacket; import com.alibaba.otter.canal.parse.driver.mysql.packets.server.HandshakeInitializationPacket;
import com.alibaba.otter.canal.parse.driver.mysql.packets.server.Reply323Packet; import com.alibaba.otter.canal.parse.driver.mysql.packets.server.Reply323Packet;
import com.alibaba.otter.canal.parse.driver.mysql.socket.SocketChannel; import com.alibaba.otter.canal.parse.driver.mysql.socket.SocketChannel;
import com.alibaba.otter.canal.parse.driver.mysql.socket.SocketChannelPool; import com.alibaba.otter.canal.parse.driver.mysql.socket.SocketChannelPool;
import com.alibaba.otter.canal.parse.driver.mysql.utils.MSC;
import com.alibaba.otter.canal.parse.driver.mysql.utils.MySQLPasswordEncrypter; import com.alibaba.otter.canal.parse.driver.mysql.utils.MySQLPasswordEncrypter;
import com.alibaba.otter.canal.parse.driver.mysql.utils.PacketManager; import com.alibaba.otter.canal.parse.driver.mysql.utils.PacketManager;
@@ -32,7 +38,7 @@ public class MysqlConnector {
private String password; private String password;
private byte charsetNumber = 33; private byte charsetNumber = 33;
private String defaultSchema = "test"; private String defaultSchema;
private int soTimeout = 30 * 1000; private int soTimeout = 30 * 1000;
private int connTimeout = 5 * 1000; private int connTimeout = 5 * 1000;
private int receiveBufferSize = 16 * 1024; private int receiveBufferSize = 16 * 1024;
@@ -150,6 +156,7 @@ public class MysqlConnector {
} }
private void negotiate(SocketChannel channel) throws IOException { private void negotiate(SocketChannel channel) throws IOException {
// https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol
HeaderPacket header = PacketManager.readHeader(channel, 4, timeout); HeaderPacket header = PacketManager.readHeader(channel, 4, timeout);
byte[] body = PacketManager.readBytes(channel, header.getPacketBodyLength(), timeout); byte[] body = PacketManager.readBytes(channel, header.getPacketBodyLength(), timeout);
if (body[0] < 0) {// check field_count if (body[0] < 0) {// check field_count
@@ -165,10 +172,14 @@ public class MysqlConnector {
} }
HandshakeInitializationPacket handshakePacket = new HandshakeInitializationPacket(); HandshakeInitializationPacket handshakePacket = new HandshakeInitializationPacket();
handshakePacket.fromBytes(body); handshakePacket.fromBytes(body);
if (handshakePacket.protocolVersion != MSC.DEFAULT_PROTOCOL_VERSION) {
// HandshakeV9
auth323(channel, (byte) (header.getPacketSequenceNumber() + 1), handshakePacket.seed);
return;
}
connectionId = handshakePacket.threadId; // 记录一下connection connectionId = handshakePacket.threadId; // 记录一下connection
logger.info("handshake initialization packet received, prepare the client authentication packet to send"); logger.info("handshake initialization packet received, prepare the client authentication packet to send");
ClientAuthenticationPacket clientAuth = new ClientAuthenticationPacket(); ClientAuthenticationPacket clientAuth = new ClientAuthenticationPacket();
clientAuth.setCharsetNumber(charsetNumber); clientAuth.setCharsetNumber(charsetNumber);
@@ -177,6 +188,7 @@ public class MysqlConnector {
clientAuth.setServerCapabilities(handshakePacket.serverCapabilities); clientAuth.setServerCapabilities(handshakePacket.serverCapabilities);
clientAuth.setDatabaseName(defaultSchema); clientAuth.setDatabaseName(defaultSchema);
clientAuth.setScrumbleBuff(joinAndCreateScrumbleBuff(handshakePacket)); clientAuth.setScrumbleBuff(joinAndCreateScrumbleBuff(handshakePacket));
clientAuth.setAuthPluginName("mysql_native_password".getBytes());
byte[] clientAuthPkgBody = clientAuth.toBytes(); byte[] clientAuthPkgBody = clientAuth.toBytes();
HeaderPacket h = new HeaderPacket(); HeaderPacket h = new HeaderPacket();
@@ -192,15 +204,71 @@ public class MysqlConnector {
body = null; body = null;
body = PacketManager.readBytes(channel, header.getPacketBodyLength(), timeout); body = PacketManager.readBytes(channel, header.getPacketBodyLength(), timeout);
assert body != null; assert body != null;
byte marker = body[0];
if (marker == -2 || marker == 1) {
byte[] authData = null;
String pluginName = null;
if (marker == 1) {
AuthSwitchRequestMoreData packet = new AuthSwitchRequestMoreData();
packet.fromBytes(body);
authData = packet.authData;
} else {
AuthSwitchRequestPacket packet = new AuthSwitchRequestPacket();
packet.fromBytes(body);
authData = packet.authData;
pluginName = packet.authName;
}
boolean isSha2Password = false;
byte[] encryptedPassword = null;
if (pluginName != null && "mysql_native_password".equals(pluginName)) {
try {
encryptedPassword = MySQLPasswordEncrypter.scramble411(getPassword().getBytes(), authData);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("can't encrypt password that will be sent to MySQL server.", e);
}
} else if (pluginName != null && "caching_sha2_password".equals(pluginName)) {
isSha2Password = true;
try {
encryptedPassword = MySQLPasswordEncrypter.scrambleCachingSha2(getPassword().getBytes(), authData);
} catch (DigestException e) {
throw new RuntimeException("can't encrypt password that will be sent to MySQL server.", e);
}
}
assert encryptedPassword != null;
AuthSwitchResponsePacket responsePacket = new AuthSwitchResponsePacket();
responsePacket.authData = encryptedPassword;
byte[] auth = responsePacket.toBytes();
h = new HeaderPacket();
h.setPacketBodyLength(auth.length);
h.setPacketSequenceNumber((byte) (header.getPacketSequenceNumber() + 1));
PacketManager.writePkg(channel, h.toBytes(), auth);
logger.info("auth switch response packet is sent out.");
header = null;
header = PacketManager.readHeader(channel, 4);
body = null;
body = PacketManager.readBytes(channel, header.getPacketBodyLength(), timeout);
assert body != null;
if (isSha2Password) {
if (body[0] == 0x01 && body[1] == 0x04) {
// password auth failed
throw new IOException("caching_sha2_password Auth failed");
}
header = null;
header = PacketManager.readHeader(channel, 4);
body = null;
body = PacketManager.readBytes(channel, header.getPacketBodyLength(), timeout);
}
}
if (body[0] < 0) { if (body[0] < 0) {
if (body[0] == -1) { if (body[0] == -1) {
ErrorPacket err = new ErrorPacket(); ErrorPacket err = new ErrorPacket();
err.fromBytes(body); err.fromBytes(body);
throw new IOException("Error When doing Client Authentication:" + err.toString()); throw new IOException("Error When doing Client Authentication:" + err.toString());
} else if (body[0] == -2) {
auth323(channel, header.getPacketSequenceNumber(), handshakePacket.seed);
// throw new
// IOException("Unexpected EOF packet at Client Authentication.");
} else { } else {
throw new IOException("unpexpected packet with field_count=" + body[0]); throw new IOException("unpexpected packet with field_count=" + body[0]);
} }

View File

@@ -0,0 +1,170 @@
package com.alibaba.otter.canal.parse.driver.mysql.packets;
/**
* https://dev.mysql.com/doc/internals/en/capability-flags.html#packet-Protocol
* ::CapabilityFlags
*/
public interface Capability {
// Use the improved version of Old Password Authentication.
// Assumed to be set since 4.1.1.
int CLIENT_LONG_PASSWORD = 0x00000001;
// Send found rows instead of affected rows in EOF_Packet.
int CLIENT_FOUND_ROWS = 0x00000002;
// https://dev.mysql.com/doc/internals/en/com-query-response.html#packet-Protocol::ColumnDefinition320
// Longer flags in Protocol::ColumnDefinition320.
// Server:Supports longer flags.
// Client:Expects longer flags.
// 执行查询sql时,除了返回结果集,还返回元数据
int CLIENT_LONG_FLAG = 0x00000004;
// 可以在handshake时,指定一个数据库名
// Database (schema) name can be specified on connect in Handshake Response
// Packet.
// Server: Supports schema-name in Handshake Response Packet.
// Client: Handshake Response Packet contains a schema-name.
int CLIENT_CONNECT_WITH_DB = 0x00000008;
// Server: Do not permit database.table.column.
int CLIENT_NO_SCHEMA = 0x00000010;
// Compression protocol supported.
// Server:Supports compression.
// Client:Switches to Compression compressed protocol after successful
// authentication.
int CLIENT_COMPRESS = 0x00000020;
// Special handling of ODBC behavior.
// No special behavior since 3.22.
int CLIENT_ODBC = 0x00000040;
// Can use LOAD DATA LOCAL.
// Server:Enables the LOCAL INFILE request of LOAD DATA|XML.
// Client:Will handle LOCAL INFILE request.
int CLIENT_LOCAL_FILES = 0x00000080;
// Server: Parser can ignore spaces before '('.
// Client: Let the parser ignore spaces before '('.
int CLIENT_IGNORE_SPACE = 0x00000100;
// Server:Supports the 4.1 protocol,
// 4.1协议中,
// OKPacket将会包含warning count
// ERR_Packet包含SQL state
// EOF_Packet包含warning count和status flags
// Client:Uses the 4.1 protocol.
// Note: this value was CLIENT_CHANGE_USER in 3.22, unused in 4.0
// If CLIENT_PROTOCOL_41 is set:
// 1、the ok packet contains a warning count.
// https://dev.mysql.com/doc/internals/en/packet-OK_Packet.html
// 2、ERR_Packet It contains a SQL state value if CLIENT_PROTOCOL_41 is
// enabled. //https://dev.mysql.com/doc/internals/en/packet-ERR_Packet.html
// 3、EOF_Packet If CLIENT_PROTOCOL_41 is enabled, the EOF packet contains a
// warning count and status flags.
// https://dev.mysql.com/doc/internals/en/packet-EOF_Packet.html
int CLIENT_PROTOCOL_41 = 0x00000200;
// wait_timeout versus wait_interactive_timeout.
// Server:Supports interactive and noninteractive clients.
// Client:Client is interactive.
int CLIENT_INTERACTIVE = 0x00000400;
// Server: Supports SSL.
// Client: Switch to SSL after sending the capability-flags.
int CLIENT_SSL = 0x00000800;
// Client: Do not issue SIGPIPE if network failures occur (libmysqlclient
// only).
int CLIENT_IGNORE_SIGPIPE = 0x00001000;
// Server: Can send status flags in EOF_Packet.
// Client:Expects status flags in EOF_Packet.
// Note:This flag is optional in 3.23, but always set by the server since
// 4.0.
int CLIENT_TRANSACTIONS = 0x00002000;
// Unused
// Note: Was named CLIENT_PROTOCOL_41 in 4.1.0.
int CLIENT_RESERVED = 0x00004000;
/**
* <pre>
* 服务端返回20 byte随机字节,客户端利用其对密码进行加密,加密算法如下:
* https://dev.mysql.com/doc/internals/en/secure-password-authentication.html#packet-Authentication::Native41
* Authentication::Native41:
* client-side expects a 20-byte random challenge
* client-side returns a 20-byte response based on the algorithm described later
* Name
* mysql_native_password
* Requires
* CLIENT_SECURE_CONNECTION
* Image description follows.
* Image description
* This method fixes a 2 short-comings of the Old Password Authentication:
* (https://dev.mysql.com/doc/internals/en/old-password-authentication.html#packet-Authentication::Old)
* using a tested, crypto-graphic hashing function which isn't broken
* knowning the content of the hash in the mysql.user table isn't enough to authenticate against the MySQL Server.
* The password is calculated by:
* SHA1( password ) XOR SHA1( "20-bytes random data from server" <concat> SHA1( SHA1( password ) ) )
* </pre>
*/
int CLIENT_SECURE_CONNECTION = 0x00008000;
// Server:Can handle multiple statements per COM_QUERY and COM_STMT_PREPARE.
// Client:May send multiple statements per COM_QUERY and COM_STMT_PREPARE.
// Note:Was named CLIENT_MULTI_QUERIES in 4.1.0, renamed later.
// Requires:CLIENT_PROTOCOL_41
int CLIENT_MULTI_STATEMENTS = 0x00010000;
// Server: Can send multiple resultsets for COM_QUERY.
// Client: Can handle multiple resultsets for COM_QUERY.
// Requires:CLIENT_PROTOCOL_41
int CLIENT_MULTI_RESULTS = 0x00020000;
// Server: Can send multiple resultsets for ComStmtExecutePacket.
// Client: Can handle multiple resultsets for ComStmtExecutePacket.
// Requires:CLIENT_PROTOCOL_41
int CLIENT_PS_MULTI_RESULTS = 0x00040000;
// Server:Sends extra data in Initial Handshake Packet and supports the
// pluggable authentication protocol.
// Client: Supports authentication plugins.
// Requires: CLIENT_PROTOCOL_41
int CLIENT_PLUGIN_AUTH = 0x00080000;
// Server: Permits connection attributes in Protocol::HandshakeResponse41.
// Client: Sends connection attributes in Protocol::HandshakeResponse41.
int CLIENT_CONNECT_ATTRS = 0x00100000;
// Server:Understands length-encoded integer for auth response data in
// Protocol::HandshakeResponse41.
// Client:Length of auth response data in Protocol::HandshakeResponse41 is a
// length-encoded integer.
// Note: The flag was introduced in 5.6.6, but had the wrong value.
int CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA = 0x00200000;
// Server: Announces support for expired password extension.
// Client: Can handle expired passwords.
int CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS = 0x00400000;
// Server: Can set SERVER_SESSION_STATE_CHANGED in the Status Flags and send
// session-state change data after a OK packet.
// Client: Expects the server to send sesson-state changes after a OK
// packet.
int CLIENT_SESSION_TRACK = 0x00800000;
/**
* Server: Can send OK after a Text Resultset. Client: Expects an OK
* (instead of EOF) after the resultset rows of a Text Resultset.
* Background:To support CLIENT_SESSION_TRACK, additional information must
* be sent after all successful commands. Although the OK packet is
* extensible, the EOF packet is not due to the overlap of its bytes with
* the content of the Text Resultset Row. Therefore, the EOF packet in the
* Text Resultset is replaced with an OK packet. EOF packets are deprecated
* as of MySQL 5.7.5.
*/
int CLIENT_DEPRECATE_EOF = 0x01000000;
}

View File

@@ -0,0 +1,21 @@
package com.alibaba.otter.canal.parse.driver.mysql.packets.client;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import com.alibaba.otter.canal.parse.driver.mysql.packets.CommandPacket;
public class AuthSwitchResponsePacket extends CommandPacket {
public byte[] authData;
public void fromBytes(byte[] data) {
}
public byte[] toBytes() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(authData);
return out.toByteArray();
}
}

View File

@@ -6,6 +6,7 @@ import java.security.NoSuchAlgorithmException;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import com.alibaba.otter.canal.parse.driver.mysql.packets.Capability;
import com.alibaba.otter.canal.parse.driver.mysql.packets.PacketWithHeaderPacket; import com.alibaba.otter.canal.parse.driver.mysql.packets.PacketWithHeaderPacket;
import com.alibaba.otter.canal.parse.driver.mysql.utils.ByteHelper; import com.alibaba.otter.canal.parse.driver.mysql.utils.ByteHelper;
import com.alibaba.otter.canal.parse.driver.mysql.utils.MSC; import com.alibaba.otter.canal.parse.driver.mysql.utils.MSC;
@@ -13,12 +14,17 @@ import com.alibaba.otter.canal.parse.driver.mysql.utils.MySQLPasswordEncrypter;
public class ClientAuthenticationPacket extends PacketWithHeaderPacket { public class ClientAuthenticationPacket extends PacketWithHeaderPacket {
private int clientCapability = Capability.CLIENT_LONG_PASSWORD | Capability.CLIENT_LONG_FLAG
| Capability.CLIENT_PROTOCOL_41 | Capability.CLIENT_INTERACTIVE
| Capability.CLIENT_TRANSACTIONS | Capability.CLIENT_SECURE_CONNECTION
| Capability.CLIENT_MULTI_STATEMENTS | Capability.CLIENT_PLUGIN_AUTH;
private String username; private String username;
private String password; private String password;
private byte charsetNumber; private byte charsetNumber;
private String databaseName; private String databaseName;
private int serverCapabilities; private int serverCapabilities;
private byte[] scrumbleBuff; private byte[] scrumbleBuff;
private byte[] authPluginName;
public void fromBytes(byte[] data) { public void fromBytes(byte[] data) {
// bypass since nowhere to use. // bypass since nowhere to use.
@@ -36,6 +42,7 @@ public class ClientAuthenticationPacket extends PacketWithHeaderPacket {
* n (Null-Terminated String) user * n (Null-Terminated String) user
* n (Length Coded Binary) scramble_buff (1 + x bytes) * n (Length Coded Binary) scramble_buff (1 + x bytes)
* n (Null-Terminated String) databasename (optional) * n (Null-Terminated String) databasename (optional)
* n (Null-Terminated String) auth plugin name (optional)
* </pre> * </pre>
* *
* @throws IOException * @throws IOException
@@ -43,13 +50,7 @@ public class ClientAuthenticationPacket extends PacketWithHeaderPacket {
public byte[] toBytes() throws IOException { public byte[] toBytes() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
// 1. write client_flags // 1. write client_flags
// 1|4|512|1024|8192|32768 ByteHelper.writeUnsignedIntLittleEndian(clientCapability, out); // remove
/**
* CLIENT_LONG_PASSWORD CLIENT_LONG_FLAG CLIENT_PROTOCOL_41
* CLIENT_INTERACTIVE CLIENT_TRANSACTIONS CLIENT_SECURE_CONNECTION
* CLIENT_MULTI_STATEMENTS;
*/
ByteHelper.writeUnsignedIntLittleEndian(1 | 4 | 512 | 8192 | 32768 | 0x00010000, out); // remove
// client_interactive // client_interactive
// feature // feature
@@ -76,6 +77,10 @@ public class ClientAuthenticationPacket extends PacketWithHeaderPacket {
if (getDatabaseName() != null) { if (getDatabaseName() != null) {
ByteHelper.writeNullTerminatedString(getDatabaseName(), out); ByteHelper.writeNullTerminatedString(getDatabaseName(), out);
} }
// 8 . (Null-Terminated String) auth plugin name (optional)
if (getAuthPluginName() != null) {
ByteHelper.writeNullTerminated(getAuthPluginName(), out);
}
// end write // end write
return out.toByteArray(); return out.toByteArray();
} }
@@ -106,6 +111,9 @@ public class ClientAuthenticationPacket extends PacketWithHeaderPacket {
public void setDatabaseName(String databaseName) { public void setDatabaseName(String databaseName) {
this.databaseName = databaseName; this.databaseName = databaseName;
if (databaseName != null) {
this.clientCapability |= Capability.CLIENT_CONNECT_WITH_DB;
}
} }
public String getDatabaseName() { public String getDatabaseName() {
@@ -128,4 +136,15 @@ public class ClientAuthenticationPacket extends PacketWithHeaderPacket {
return scrumbleBuff; return scrumbleBuff;
} }
public byte[] getAuthPluginName() {
return authPluginName;
}
public void setAuthPluginName(byte[] authPluginName) {
this.authPluginName = authPluginName;
if (authPluginName != null) {
this.clientCapability |= Capability.CLIENT_PLUGIN_AUTH;
}
}
} }

View File

@@ -0,0 +1,25 @@
package com.alibaba.otter.canal.parse.driver.mysql.packets.server;
import java.io.IOException;
import com.alibaba.otter.canal.parse.driver.mysql.packets.CommandPacket;
import com.alibaba.otter.canal.parse.driver.mysql.utils.ByteHelper;
public class AuthSwitchRequestMoreData extends CommandPacket {
public int status;
public byte[] authData;
public void fromBytes(byte[] data) {
int index = 0;
// 1. read status
status = data[index];
index += 1;
authData = ByteHelper.readNullTerminatedBytes(data, index);
}
public byte[] toBytes() throws IOException {
return null;
}
}

View File

@@ -0,0 +1,29 @@
package com.alibaba.otter.canal.parse.driver.mysql.packets.server;
import java.io.IOException;
import com.alibaba.otter.canal.parse.driver.mysql.packets.CommandPacket;
import com.alibaba.otter.canal.parse.driver.mysql.utils.ByteHelper;
public class AuthSwitchRequestPacket extends CommandPacket {
public int status;
public String authName;
public byte[] authData;
public void fromBytes(byte[] data) {
int index = 0;
// 1. read status
status = data[index];
index += 1;
byte[] authName = ByteHelper.readNullTerminatedBytes(data, index);
this.authName = new String(authName);
index += authName.length + 1;
authData = ByteHelper.readNullTerminatedBytes(data, index);
}
public byte[] toBytes() throws IOException {
return null;
}
}

View File

@@ -2,6 +2,7 @@ package com.alibaba.otter.canal.parse.driver.mysql.packets.server;
import java.io.IOException; import java.io.IOException;
import com.alibaba.otter.canal.parse.driver.mysql.packets.Capability;
import com.alibaba.otter.canal.parse.driver.mysql.packets.HeaderPacket; import com.alibaba.otter.canal.parse.driver.mysql.packets.HeaderPacket;
import com.alibaba.otter.canal.parse.driver.mysql.packets.PacketWithHeaderPacket; import com.alibaba.otter.canal.parse.driver.mysql.packets.PacketWithHeaderPacket;
import com.alibaba.otter.canal.parse.driver.mysql.utils.ByteHelper; import com.alibaba.otter.canal.parse.driver.mysql.utils.ByteHelper;
@@ -23,6 +24,7 @@ public class HandshakeInitializationPacket extends PacketWithHeaderPacket {
public byte serverCharsetNumber; public byte serverCharsetNumber;
public int serverStatus; public int serverStatus;
public byte[] restOfScrambleBuff; public byte[] restOfScrambleBuff;
public byte[] authPluginName;
public HandshakeInitializationPacket(){ public HandshakeInitializationPacket(){
} }
@@ -66,20 +68,40 @@ public class HandshakeInitializationPacket extends PacketWithHeaderPacket {
// 5. read server_capabilities // 5. read server_capabilities
this.serverCapabilities = ByteHelper.readUnsignedShortLittleEndian(data, index); this.serverCapabilities = ByteHelper.readUnsignedShortLittleEndian(data, index);
index += 2; index += 2;
// 6. read server_language if (data.length > index) {
this.serverCharsetNumber = data[index]; // 6. read server_language
index++; this.serverCharsetNumber = data[index];
// 7. read server_status index++;
this.serverStatus = ByteHelper.readUnsignedShortLittleEndian(data, index); // 7. read server_status
index += 2; this.serverStatus = ByteHelper.readUnsignedShortLittleEndian(data, index);
// 8. bypass filtered bytes index += 2;
index += 13; // 8. bypass filtered bytes
// 9. read rest of scramble_buff int capabilityFlags2 = ByteHelper.readUnsignedShortLittleEndian(data, index);
this.restOfScrambleBuff = ByteHelper.readFixedLengthBytes(data, index, 12); // 虽然Handshake index += 2;
// Initialization int capabilities = (capabilityFlags2 << 16) | this.serverCapabilities;
// Packet规定最后13个byte是剩下的scrumble, // int authPluginDataLen = -1;
// 但实际上最后一个字节是0, 不应该包含在scrumble中. // if ((capabilities & Capability.CLIENT_PLUGIN_AUTH) != 0) {
// end read // authPluginDataLen = data[index];
// }
index += 1;
index += 10;
// 9. read rest of scramble_buff
if ((capabilities & Capability.CLIENT_SECURE_CONNECTION) != 0) {
// int len = Math.max(13, authPluginDataLen - 8);
// this.authPluginDataPart2 =
// buffer.readFixedLengthString(len);// scramble2
// Packet规定最后13个byte是剩下的scrumble,
// 但实际上最后一个字节是0, 不应该包含在scrumble中.
this.restOfScrambleBuff = ByteHelper.readFixedLengthBytes(data, index, 12);
}
index += 12 + 1;
if ((capabilities & Capability.CLIENT_PLUGIN_AUTH) != 0) {
this.authPluginName = ByteHelper.readNullTerminatedBytes(data, index);
}
// end read
}
} }
/** /**

View File

@@ -1,10 +1,47 @@
package com.alibaba.otter.canal.parse.driver.mysql.utils; package com.alibaba.otter.canal.parse.driver.mysql.utils;
import java.security.DigestException;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
public class MySQLPasswordEncrypter { public class MySQLPasswordEncrypter {
private static final int CACHING_SHA2_DIGEST_LENGTH = 32;
public static byte[] scrambleCachingSha2(byte[] password, byte[] seed) throws DigestException {
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException ex) {
throw new DigestException(ex);
}
byte[] dig1 = new byte[CACHING_SHA2_DIGEST_LENGTH];
byte[] dig2 = new byte[CACHING_SHA2_DIGEST_LENGTH];
byte[] scramble1 = new byte[CACHING_SHA2_DIGEST_LENGTH];
// SHA2(src) => digest_stage1
md.update(password, 0, password.length);
md.digest(dig1, 0, CACHING_SHA2_DIGEST_LENGTH);
md.reset();
// SHA2(digest_stage1) => digest_stage2
md.update(dig1, 0, dig1.length);
md.digest(dig2, 0, CACHING_SHA2_DIGEST_LENGTH);
md.reset();
// SHA2(digest_stage2, m_rnd) => scramble_stage1
md.update(dig2, 0, dig1.length);
md.update(seed, 0, seed.length);
md.digest(scramble1, 0, CACHING_SHA2_DIGEST_LENGTH);
// XOR(digest_stage1, scramble_stage1) => scramble
byte[] mysqlScrambleBuff = new byte[CACHING_SHA2_DIGEST_LENGTH];
xorString(dig1, mysqlScrambleBuff, scramble1, CACHING_SHA2_DIGEST_LENGTH);
return mysqlScrambleBuff;
}
public static final byte[] scramble411(byte[] pass, byte[] seed) throws NoSuchAlgorithmException { public static final byte[] scramble411(byte[] pass, byte[] seed) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-1"); MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] pass1 = md.digest(pass); byte[] pass1 = md.digest(pass);
@@ -71,4 +108,13 @@ public class MySQLPasswordEncrypter {
return result; return result;
} }
private static void xorString(byte[] from, byte[] to, byte[] scramble, int length) {
int pos = 0;
int scrambleLength = scramble.length;
while (pos < length) {
to[pos] = (byte) (from[pos] ^ scramble[pos % scrambleLength]);
pos++;
}
}
} }

View File

@@ -1,5 +1,6 @@
package com.alibaba.otter.canal.parse.driver.mysql.utils; package com.alibaba.otter.canal.parse.driver.mysql.utils;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import com.alibaba.otter.canal.parse.driver.mysql.packets.HeaderPacket; import com.alibaba.otter.canal.parse.driver.mysql.packets.HeaderPacket;
@@ -28,7 +29,11 @@ public abstract class PacketManager {
} }
public static void writePkg(SocketChannel ch, byte[]... srcs) throws IOException { public static void writePkg(SocketChannel ch, byte[]... srcs) throws IOException {
ch.write(srcs); ByteArrayOutputStream out = new ByteArrayOutputStream();
for (byte[] src : srcs) {
out.write(src);
}
ch.write(out.toByteArray());
} }
public static void writeBody(SocketChannel ch, byte[] body) throws IOException { public static void writeBody(SocketChannel ch, byte[] body) throws IOException {

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.1.3-SNAPSHOT</version> <version>1.1.3</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.1.3-SNAPSHOT</version> <version>1.1.3</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.1.3-SNAPSHOT</version> <version>1.1.3</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<artifactId>canal.instance.core</artifactId> <artifactId>canal.instance.core</artifactId>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.1.3-SNAPSHOT</version> <version>1.1.3</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.1.3-SNAPSHOT</version> <version>1.1.3</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.1.3-SNAPSHOT</version> <version>1.1.3</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>

View File

@@ -140,7 +140,7 @@
<property name="address" value="${canal.instance.master.address}" /> <property name="address" value="${canal.instance.master.address}" />
<property name="username" value="${canal.instance.dbUsername:retl}" /> <property name="username" value="${canal.instance.dbUsername:retl}" />
<property name="password" value="${canal.instance.dbPassword:retl}" /> <property name="password" value="${canal.instance.dbPassword:retl}" />
<property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:retl}" /> <property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:}" />
</bean> </bean>
</property> </property>
<property name="standbyInfo"> <property name="standbyInfo">
@@ -148,7 +148,7 @@
<property name="address" value="${canal.instance.standby.address}" /> <property name="address" value="${canal.instance.standby.address}" />
<property name="username" value="${canal.instance.dbUsername:retl}" /> <property name="username" value="${canal.instance.dbUsername:retl}" />
<property name="password" value="${canal.instance.dbPassword:retl}" /> <property name="password" value="${canal.instance.dbPassword:retl}" />
<property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:retl}" /> <property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:}" />
</bean> </bean>
</property> </property>

View File

@@ -128,7 +128,7 @@
<property name="address" value="${canal.instance.master.address}" /> <property name="address" value="${canal.instance.master.address}" />
<property name="username" value="${canal.instance.dbUsername:retl}" /> <property name="username" value="${canal.instance.dbUsername:retl}" />
<property name="password" value="${canal.instance.dbPassword:retl}" /> <property name="password" value="${canal.instance.dbPassword:retl}" />
<property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:retl}" /> <property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:}" />
</bean> </bean>
</property> </property>
<property name="standbyInfo"> <property name="standbyInfo">
@@ -136,7 +136,7 @@
<property name="address" value="${canal.instance.standby.address}" /> <property name="address" value="${canal.instance.standby.address}" />
<property name="username" value="${canal.instance.dbUsername:retl}" /> <property name="username" value="${canal.instance.dbUsername:retl}" />
<property name="password" value="${canal.instance.dbPassword:retl}" /> <property name="password" value="${canal.instance.dbPassword:retl}" />
<property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:retl}" /> <property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:}" />
</bean> </bean>
</property> </property>

View File

@@ -123,7 +123,7 @@
<property name="address" value="${canal.instance.master1.address}" /> <property name="address" value="${canal.instance.master1.address}" />
<property name="username" value="${canal.instance.dbUsername:retl}" /> <property name="username" value="${canal.instance.dbUsername:retl}" />
<property name="password" value="${canal.instance.dbPassword:retl}" /> <property name="password" value="${canal.instance.dbPassword:retl}" />
<property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:retl}" /> <property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:}" />
</bean> </bean>
</property> </property>
<property name="standbyInfo"> <property name="standbyInfo">
@@ -131,7 +131,7 @@
<property name="address" value="${canal.instance.standby1.address}" /> <property name="address" value="${canal.instance.standby1.address}" />
<property name="username" value="${canal.instance.dbUsername:retl}" /> <property name="username" value="${canal.instance.dbUsername:retl}" />
<property name="password" value="${canal.instance.dbPassword:retl}" /> <property name="password" value="${canal.instance.dbPassword:retl}" />
<property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:retl}" /> <property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:}" />
</bean> </bean>
</property> </property>
@@ -203,7 +203,7 @@
<property name="address" value="${canal.instance.master2.address}" /> <property name="address" value="${canal.instance.master2.address}" />
<property name="username" value="${canal.instance.dbUsername:retl}" /> <property name="username" value="${canal.instance.dbUsername:retl}" />
<property name="password" value="${canal.instance.dbPassword:retl}" /> <property name="password" value="${canal.instance.dbPassword:retl}" />
<property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:retl}" /> <property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:}" />
</bean> </bean>
</property> </property>
<property name="standbyInfo"> <property name="standbyInfo">
@@ -211,7 +211,7 @@
<property name="address" value="${canal.instance.standby2.address}" /> <property name="address" value="${canal.instance.standby2.address}" />
<property name="username" value="${canal.instance.dbUsername:retl}" /> <property name="username" value="${canal.instance.dbUsername:retl}" />
<property name="password" value="${canal.instance.dbPassword:retl}" /> <property name="password" value="${canal.instance.dbPassword:retl}" />
<property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:retl}" /> <property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:}" />
</bean> </bean>
</property> </property>

View File

@@ -114,7 +114,7 @@
<property name="address" value="${canal.instance.master.address}" /> <property name="address" value="${canal.instance.master.address}" />
<property name="username" value="${canal.instance.dbUsername:retl}" /> <property name="username" value="${canal.instance.dbUsername:retl}" />
<property name="password" value="${canal.instance.dbPassword:retl}" /> <property name="password" value="${canal.instance.dbPassword:retl}" />
<property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:retl}" /> <property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:}" />
</bean> </bean>
</property> </property>
<property name="standbyInfo"> <property name="standbyInfo">
@@ -122,7 +122,7 @@
<property name="address" value="${canal.instance.standby.address}" /> <property name="address" value="${canal.instance.standby.address}" />
<property name="username" value="${canal.instance.dbUsername:retl}" /> <property name="username" value="${canal.instance.dbUsername:retl}" />
<property name="password" value="${canal.instance.dbPassword:retl}" /> <property name="password" value="${canal.instance.dbPassword:retl}" />
<property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:retl}" /> <property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:}" />
</bean> </bean>
</property> </property>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.1.3-SNAPSHOT</version> <version>1.1.3</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.1.3-SNAPSHOT</version> <version>1.1.3</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<artifactId>canal.parse</artifactId> <artifactId>canal.parse</artifactId>

View File

@@ -10,7 +10,6 @@ import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import com.alibaba.otter.canal.parse.driver.mysql.packets.MysqlGTIDSet;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils; import org.apache.commons.lang.math.NumberUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -21,6 +20,7 @@ import com.alibaba.otter.canal.parse.driver.mysql.MysqlQueryExecutor;
import com.alibaba.otter.canal.parse.driver.mysql.MysqlUpdateExecutor; import com.alibaba.otter.canal.parse.driver.mysql.MysqlUpdateExecutor;
import com.alibaba.otter.canal.parse.driver.mysql.packets.GTIDSet; import com.alibaba.otter.canal.parse.driver.mysql.packets.GTIDSet;
import com.alibaba.otter.canal.parse.driver.mysql.packets.HeaderPacket; import com.alibaba.otter.canal.parse.driver.mysql.packets.HeaderPacket;
import com.alibaba.otter.canal.parse.driver.mysql.packets.MysqlGTIDSet;
import com.alibaba.otter.canal.parse.driver.mysql.packets.client.BinlogDumpCommandPacket; import com.alibaba.otter.canal.parse.driver.mysql.packets.client.BinlogDumpCommandPacket;
import com.alibaba.otter.canal.parse.driver.mysql.packets.client.BinlogDumpGTIDCommandPacket; import com.alibaba.otter.canal.parse.driver.mysql.packets.client.BinlogDumpGTIDCommandPacket;
import com.alibaba.otter.canal.parse.driver.mysql.packets.client.RegisterSlaveCommandPacket; import com.alibaba.otter.canal.parse.driver.mysql.packets.client.RegisterSlaveCommandPacket;
@@ -131,9 +131,11 @@ public class MysqlConnection implements ErosaConnection {
decoder.handle(LogEvent.QUERY_EVENT); decoder.handle(LogEvent.QUERY_EVENT);
decoder.handle(LogEvent.XID_EVENT); decoder.handle(LogEvent.XID_EVENT);
LogContext context = new LogContext(); LogContext context = new LogContext();
// 若entry position存在gtid,则使用传入的gtid作为gtidSet 拼接的标准,否则同时开启gtid和tsdb时,会导致丢失gtid // 若entry position存在gtid,则使用传入的gtid作为gtidSet
// 拼接的标准,否则同时开启gtid和tsdb时,会导致丢失gtid
// 而当源端数据库gtid 有purged时会有如下类似报错 // 而当源端数据库gtid 有purged时会有如下类似报错
// 'errno = 1236, sqlstate = HY000 errmsg = The slave is connecting using CHANGE MASTER TO MASTER_AUTO_POSITION = 1 ... // 'errno = 1236, sqlstate = HY000 errmsg = The slave is connecting
// using CHANGE MASTER TO MASTER_AUTO_POSITION = 1 ...
if (StringUtils.isNotEmpty(gtid)) { if (StringUtils.isNotEmpty(gtid)) {
decoder.handle(LogEvent.GTID_LOG_EVENT); decoder.handle(LogEvent.GTID_LOG_EVENT);
context.setGtidSet(MysqlGTIDSet.parse(gtid)); context.setGtidSet(MysqlGTIDSet.parse(gtid));
@@ -417,7 +419,9 @@ public class MysqlConnection implements ErosaConnection {
// '@@global.binlog_checksum'需要去掉单引号,在mysql 5.6.29下导致master退出 // '@@global.binlog_checksum'需要去掉单引号,在mysql 5.6.29下导致master退出
update("set @master_binlog_checksum= @@global.binlog_checksum"); update("set @master_binlog_checksum= @@global.binlog_checksum");
} catch (Exception e) { } catch (Exception e) {
logger.warn("update master_binlog_checksum failed", e); if (!StringUtils.contains(e.getMessage(), "Unknown system variable")) {
logger.warn("update master_binlog_checksum failed", e);
}
} }
try { try {
@@ -434,7 +438,9 @@ public class MysqlConnection implements ErosaConnection {
// mariadb针对特殊的类型,需要设置session变量 // mariadb针对特殊的类型,需要设置session变量
update("SET @mariadb_slave_capability='" + LogEvent.MARIA_SLAVE_CAPABILITY_MINE + "'"); update("SET @mariadb_slave_capability='" + LogEvent.MARIA_SLAVE_CAPABILITY_MINE + "'");
} catch (Exception e) { } catch (Exception e) {
logger.warn("update mariadb_slave_capability failed", e); if (!StringUtils.contains(e.getMessage(), "Unknown system variable")) {
logger.warn("update mariadb_slave_capability failed", e);
}
} }
/** /**

View File

@@ -77,9 +77,7 @@ public class MemoryTableMeta implements TableMetaTSDB {
if (!StringUtils.startsWithIgnoreCase(StringUtils.trim(ddl), "flush") if (!StringUtils.startsWithIgnoreCase(StringUtils.trim(ddl), "flush")
&& !StringUtils.startsWithIgnoreCase(StringUtils.trim(ddl), "grant") && !StringUtils.startsWithIgnoreCase(StringUtils.trim(ddl), "grant")
&& !StringUtils.startsWithIgnoreCase(StringUtils.trim(ddl), "create user") && !StringUtils.startsWithIgnoreCase(StringUtils.trim(ddl), "create user")
&& !StringUtils.startsWithIgnoreCase(StringUtils.trim(ddl), "drop user") && !StringUtils.startsWithIgnoreCase(StringUtils.trim(ddl), "drop user")) {
&& !StringUtils.startsWithIgnoreCase(StringUtils.trim(ddl), "create database")
&& !StringUtils.startsWithIgnoreCase(StringUtils.trim(ddl), "drop database")) {
repository.console(ddl); repository.console(ddl);
} }
} catch (Throwable e) { } catch (Throwable e) {

View File

@@ -18,19 +18,12 @@ public class FastsqlSchemaTest {
@Test @Test
public void testSimple() throws FileNotFoundException, IOException { public void testSimple() throws FileNotFoundException, IOException {
SchemaRepository repository = new SchemaRepository(JdbcConstants.MYSQL); SchemaRepository repository = new SchemaRepository(JdbcConstants.MYSQL);
String sql = "create table yushitai_test.card_record ( id bigint auto_increment, name varchar(32) DEFAULT NULL) auto_increment=256 " String sql = "create table quniya4(name varchar(255) null,value varchar(255) null,id int not null,constraint quniya4_pk primary key (id));"
+ "ALTER TABLE yushitai_test.card_record ADD COLUMN remark2 varchar(255) DEFAULT NULL , ALGORITHM=inplace,LOCK=NONE;" + "alter table quniya4 modify id int not null first;";
+ "ALTER TABLE yushitai_test.card_record modify COLUMN name varchar(64) DEFAULT NULL , ALGORITHM=copy,LOCK=SHARED; "
+ "alter table yushitai_test.card_record add index index_name(name) ;"
+ "alter table yushitai_test.card_record add index index_name(name) ;"
+ "alter table yushitai_test.card_record add Constraint pk_id PRIMARY KEY (id);"
+ "alter table yushitai_test.card_record add Constraint pk_id PRIMARY KEY (id);"
+ "alter table yushitai_test.card_record add Constraint UNIQUE index uk_name(name);"
+ "alter table yushitai_test.card_record add Constraint UNIQUE index uk_name(name);";
repository.console(sql); repository.console(sql);
repository.setDefaultSchema("yushitai_test"); repository.setDefaultSchema("test");
SchemaObject table = repository.findTable("card_record"); SchemaObject table = repository.findTable("quniya4");
System.out.println(table.getStatement().toString()); System.out.println(table.getStatement().toString());
} }
} }

View File

@@ -18,7 +18,7 @@ import com.alibaba.otter.canal.parse.inbound.TableMeta;
* @author agapple 2017年8月1日 下午7:15:54 * @author agapple 2017年8月1日 下午7:15:54
*/ */
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/tsdb/mysql-tsdb.xml" }) @ContextConfiguration(locations = { "/tsdb/h2-tsdb.xml" })
public class MemoryTableMeta_DDL_Test { public class MemoryTableMeta_DDL_Test {
@Test @Test
@@ -52,4 +52,18 @@ public class MemoryTableMeta_DDL_Test {
Assert.assertEquals(meta.getFieldMetaByName("id").isKey(), true); Assert.assertEquals(meta.getFieldMetaByName("id").isKey(), true);
Assert.assertEquals(meta.getFieldMetaByName("name").isUnique(), true); Assert.assertEquals(meta.getFieldMetaByName("name").isUnique(), true);
} }
@Test
public void test3() throws Throwable {
MemoryTableMeta memoryTableMeta = new MemoryTableMeta();
URL url = Thread.currentThread().getContextClassLoader().getResource("dummy.txt");
File dummyFile = new File(url.getFile());
File create = new File(dummyFile.getParent() + "/ddl", "ddl_test3.sql");
String sql = StringUtils.join(IOUtils.readLines(new FileInputStream(create)), "\n");
memoryTableMeta.apply(null, "test", sql, null);
TableMeta meta = memoryTableMeta.find("test", "quniya4");
System.out.println(meta);
Assert.assertTrue(meta.getFields().get(0).getColumnName().equalsIgnoreCase("id"));
}
} }

View File

@@ -0,0 +1,3 @@
create table quniya4(name varchar(255) null,value varchar(255) null,id int not null,constraint quniya4_pk primary key (id));
alter table quniya4 modify id int not null first;
alter table quniya4 modify id int auto_increment;

View File

@@ -4,7 +4,7 @@
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>canal module for otter ${project.version}</name> <name>canal module for otter ${project.version}</name>
<version>1.1.3-SNAPSHOT</version> <version>1.1.3</version>
<url>https://github.com/alibaba/canal</url> <url>https://github.com/alibaba/canal</url>
<parent> <parent>
<groupId>org.sonatype.oss</groupId> <groupId>org.sonatype.oss</groupId>
@@ -246,7 +246,7 @@
<dependency> <dependency>
<groupId>com.alibaba.fastsql</groupId> <groupId>com.alibaba.fastsql</groupId>
<artifactId>fastsql</artifactId> <artifactId>fastsql</artifactId>
<version>2.0.0_preview_644</version> <version>2.0.0_preview_855</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.alibaba</groupId> <groupId>com.alibaba</groupId>
@@ -381,7 +381,6 @@
</configuration> </configuration>
</plugin> </plugin>
<!-- javadoc --> <!-- javadoc -->
<!--
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-javadoc-plugin</artifactId>
@@ -400,7 +399,7 @@
<additionalparam>-Xdoclint:none</additionalparam> <additionalparam>-Xdoclint:none</additionalparam>
</configuration> </configuration>
</plugin> </plugin>
-->
</plugins> </plugins>
<sourceDirectory>src/main/java</sourceDirectory> <sourceDirectory>src/main/java</sourceDirectory>
<testSourceDirectory>src/test/java</testSourceDirectory> <testSourceDirectory>src/test/java</testSourceDirectory>

View File

@@ -3,13 +3,13 @@
<parent> <parent>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<version>1.1.3-SNAPSHOT</version> <version>1.1.3</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal.prometheus</artifactId> <artifactId>canal.prometheus</artifactId>
<version>1.1.3-SNAPSHOT</version> <version>1.1.3</version>
<name>canal prometheus module for otter ${project.version}</name> <name>canal prometheus module for otter ${project.version}</name>
<dependencies> <dependencies>
<dependency> <dependency>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.1.3-SNAPSHOT</version> <version>1.1.3</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.1.3-SNAPSHOT</version> <version>1.1.3</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<artifactId>canal.server</artifactId> <artifactId>canal.server</artifactId>

View File

@@ -36,8 +36,11 @@ public class MQMessageUtils {
public List<PartitionData> apply(String pkHashConfigs) { public List<PartitionData> apply(String pkHashConfigs) {
List<PartitionData> datas = Lists.newArrayList(); List<PartitionData> datas = Lists.newArrayList();
String[] pkHashConfigArray = StringUtils.split(pkHashConfigs,
","); String[] pkHashConfigArray = StringUtils.split(StringUtils.replace(pkHashConfigs,
",",
";"),
";");
// schema.table:id^name // schema.table:id^name
for (String pkHashConfig : pkHashConfigArray) { for (String pkHashConfig : pkHashConfigArray) {
PartitionData data = new PartitionData(); PartitionData data = new PartitionData();
@@ -75,8 +78,10 @@ public class MQMessageUtils {
public List<DynamicTopicData> apply(String pkHashConfigs) { public List<DynamicTopicData> apply(String pkHashConfigs) {
List<DynamicTopicData> datas = Lists.newArrayList(); List<DynamicTopicData> datas = Lists.newArrayList();
String[] dynamicTopicArray = StringUtils.split(pkHashConfigs, String[] dynamicTopicArray = StringUtils.split(StringUtils.replace(pkHashConfigs,
","); ",",
";"),
";");
// schema.table // schema.table
for (String dynamicTopic : dynamicTopicArray) { for (String dynamicTopic : dynamicTopicArray) {
DynamicTopicData data = new DynamicTopicData(); DynamicTopicData data = new DynamicTopicData();
@@ -216,7 +221,7 @@ public class MQMessageUtils {
partitionEntries[pkHash].add(entry); partitionEntries[pkHash].add(entry);
} else { } else {
for (CanalEntry.RowData rowData : rowChange.getRowDatasList()) { for (CanalEntry.RowData rowData : rowChange.getRowDatasList()) {
int hashCode = table.hashCode(); int hashCode = database.hashCode();
if (hashMode.autoPkHash) { if (hashMode.autoPkHash) {
// isEmpty use default pkNames // isEmpty use default pkNames
for (CanalEntry.Column column : rowData.getAfterColumnsList()) { for (CanalEntry.Column column : rowData.getAfterColumnsList()) {
@@ -432,7 +437,7 @@ public class MQMessageUtils {
int idx = 0; int idx = 0;
for (Map<String, String> row : flatMessage.getData()) { for (Map<String, String> row : flatMessage.getData()) {
int hashCode = table.hashCode(); int hashCode = database.hashCode();
for (String pkName : pkNames) { for (String pkName : pkNames) {
String value = row.get(pkName); String value = row.get(pkName);
if (value == null) { if (value == null) {
@@ -509,7 +514,7 @@ public class MQMessageUtils {
} }
private static Set<String> matchTopics(String name, String dynamicTopicConfigs) { private static Set<String> matchTopics(String name, String dynamicTopicConfigs) {
String[] router = StringUtils.split(dynamicTopicConfigs, ';'); String[] router = StringUtils.split(StringUtils.replace(dynamicTopicConfigs, ",", ";"), ";");
Set<String> topics = new HashSet<>(); Set<String> topics = new HashSet<>();
for (String item : router) { for (String item : router) {
int i = item.indexOf(":"); int i = item.indexOf(":");

View File

@@ -1,5 +1,6 @@
package com.alibaba.otter.canal.kafka; package com.alibaba.otter.canal.kafka;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
@@ -138,7 +139,7 @@ public class CanalKafkaProducer implements CanalMQProducer {
private void send(MQProperties.CanalDestination canalDestination, String topicName, Message message) private void send(MQProperties.CanalDestination canalDestination, String topicName, Message message)
throws Exception { throws Exception {
if (!kafkaProperties.getFlatMessage()) { if (!kafkaProperties.getFlatMessage()) {
ProducerRecord<String, Message> record = null; List<ProducerRecord<String, Message>> records = new ArrayList<ProducerRecord<String, Message>>();
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(),
@@ -147,18 +148,16 @@ public class CanalKafkaProducer implements CanalMQProducer {
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); records.add(new ProducerRecord<String, Message>(topicName, i, null, messagePartition));
} }
} }
} else { } else {
final int partition = canalDestination.getPartition() != null ? canalDestination.getPartition() : 0; final int partition = canalDestination.getPartition() != null ? canalDestination.getPartition() : 0;
record = new ProducerRecord<>(topicName, partition, null, message); records.add(new ProducerRecord<String, Message>(topicName, partition, null, message));
} }
if (record != null) { if (!records.isEmpty()) {
if (kafkaProperties.getTransaction()) { for (ProducerRecord<String, Message> record : records) {
producer.send(record).get();
} else {
producer.send(record).get(); producer.send(record).get();
} }

View File

@@ -107,11 +107,10 @@ public class CanalRocketMQProducer implements CanalMQProducer {
@Override @Override
public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) { public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
if (index > mqs.size()) { if (index > mqs.size()) {
throw new CanalServerException("partition number is error,config num:" return mqs.get(index % mqs.size());
+ destination.getPartitionsNum() } else {
+ ", mq num: " + mqs.size()); return mqs.get(index);
} }
return mqs.get(index);
} }
}, null); }, null);
} catch (Exception e) { } catch (Exception e) {
@@ -134,7 +133,11 @@ public class CanalRocketMQProducer implements CanalMQProducer {
@Override @Override
public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) { public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
return mqs.get(partition); if (partition > mqs.size()) {
return mqs.get(partition % mqs.size());
} else {
return mqs.get(partition);
}
} }
}, null); }, null);
} }
@@ -168,11 +171,10 @@ public class CanalRocketMQProducer implements CanalMQProducer {
@Override @Override
public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) { public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
if (index > mqs.size()) { if (index > mqs.size()) {
throw new CanalServerException("partition number is error,config num:" return mqs.get(index % mqs.size());
+ destination.getPartitionsNum() } else {
+ ", mq num: " + mqs.size()); return mqs.get(index);
} }
return mqs.get(index);
} }
}, null); }, null);
} catch (Exception e) { } catch (Exception e) {
@@ -196,7 +198,11 @@ public class CanalRocketMQProducer implements CanalMQProducer {
@Override @Override
public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) { public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
return mqs.get(partition); if (partition > mqs.size()) {
return mqs.get(partition % mqs.size());
} else {
return mqs.get(partition);
}
} }
}, null); }, null);
} catch (Exception e) { } catch (Exception e) {

View File

@@ -53,9 +53,6 @@ public class CanalMQStarter {
System.setProperty("canal.instance.filter.transaction.entry", "true"); System.setProperty("canal.instance.filter.transaction.entry", "true");
} }
// 设置为raw避免ByteString->Entry的二次解析
System.setProperty("canal.instance.memory.rawEntry", "false");
canalServer = CanalServerWithEmbedded.instance(); canalServer = CanalServerWithEmbedded.instance();
// 对应每个instance启动一个worker线程 // 对应每个instance启动一个worker线程

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.1.3-SNAPSHOT</version> <version>1.1.3</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.1.3-SNAPSHOT</version> <version>1.1.3</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>