mirror of
https://github.com/alibaba/canal.git
synced 2026-11-04 00:30:15 +08:00
Compare commits
52 Commits
canal-1.1.
...
canal-1.1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a847660a45 | ||
|
|
4957083493 | ||
|
|
adccc0e912 | ||
|
|
8728beb4b6 | ||
|
|
5bea876ec9 | ||
|
|
6c494f8892 | ||
|
|
74489d1bba | ||
|
|
5ac0f7b710 | ||
|
|
75b4911caf | ||
|
|
95602f4784 | ||
|
|
058e1fc546 | ||
|
|
e808c6ec70 | ||
|
|
ce7948d6e7 | ||
|
|
8df6d6fed8 | ||
|
|
f9989e0856 | ||
|
|
4075a0623f | ||
|
|
37fc248ebf | ||
|
|
d0a83aad27 | ||
|
|
3b9ff26d1a | ||
|
|
72c4daba5f | ||
|
|
7427a74612 | ||
|
|
24fae6fa52 | ||
|
|
4c690fb5a3 | ||
|
|
8f088cddc0 | ||
|
|
e94ee64316 | ||
|
|
3165cb9640 | ||
|
|
2b738f4e04 | ||
|
|
16f335c381 | ||
|
|
a8bf2b641f | ||
|
|
67faf0e03e | ||
|
|
8cb85753bb | ||
|
|
df86f94409 | ||
|
|
80594444f7 | ||
|
|
d8996432b9 | ||
|
|
69df50fe17 | ||
|
|
c5736bd5f3 | ||
|
|
55030476ee | ||
|
|
cd007088db | ||
|
|
1fc880f255 | ||
|
|
8c68cf833a | ||
|
|
0de8f4691e | ||
|
|
5a2efbf9cb | ||
|
|
dfab98fce1 | ||
|
|
e5c7bbffbf | ||
|
|
e139698f6e | ||
|
|
2b8120b6ed | ||
|
|
68ebb3a957 | ||
|
|
21309cb065 | ||
|
|
386567a633 | ||
|
|
95c69bb854 | ||
|
|
6febcf1a0f | ||
|
|
fadfe1d6ca |
6
.codecov.yml
Normal file
6
.codecov.yml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
coverage:
|
||||||
|
status:
|
||||||
|
# pull-requests only
|
||||||
|
patch:
|
||||||
|
default:
|
||||||
|
threshold: 0.1%
|
||||||
8
.github/ISSUE_TEMPLATE/any-questions-on-canal.md
vendored
Normal file
8
.github/ISSUE_TEMPLATE/any-questions-on-canal.md
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
name: Any Questions on Canal
|
||||||
|
about: Ask whatever you want to know or confusion about Canal
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Question
|
||||||
|
<!-- You can ask any question about this project -->
|
||||||
29
.github/ISSUE_TEMPLATE/bug-report-for-canal.md
vendored
Normal file
29
.github/ISSUE_TEMPLATE/bug-report-for-canal.md
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
---
|
||||||
|
name: Bug Report for Canal
|
||||||
|
about: Create a report to help us improve
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- [ ] I have searched the [issues](https://github.com/alibaba/canal/issues) of this repository and believe that this is not a duplicate.
|
||||||
|
- [ ] I have checked the [FAQ](https://github.com/alibaba/canal/wiki/FAQ) of this repository and believe that this is not a duplicate.
|
||||||
|
|
||||||
|
### environment
|
||||||
|
|
||||||
|
* canal version
|
||||||
|
* mysql version
|
||||||
|
|
||||||
|
### Issue Description
|
||||||
|
|
||||||
|
|
||||||
|
### Steps to reproduce
|
||||||
|
|
||||||
|
### Expected behaviour
|
||||||
|
|
||||||
|
### Actual behaviour
|
||||||
|
|
||||||
|
|
||||||
|
If there is an exception, please attach the exception trace:
|
||||||
|
|
||||||
|
```
|
||||||
|
Just put your stack trace here!
|
||||||
|
```
|
||||||
16
.github/ISSUE_TEMPLATE/feature-request-of-canal.md
vendored
Normal file
16
.github/ISSUE_TEMPLATE/feature-request-of-canal.md
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
name: Feature Request of Canal
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Why you need it?
|
||||||
|
<!-- Is your feature request related to a problem? Please describe in details -->
|
||||||
|
|
||||||
|
|
||||||
|
## How it could be?
|
||||||
|
<!--A clear and concise description of what you want to happen. For a computer fan, you can explain more about input of the feature, and output of it.-->
|
||||||
|
|
||||||
|
|
||||||
|
## Other related information
|
||||||
|
<!-- Add any other context or screenshots about the feature request here.-->
|
||||||
13
.github/issue_template.md
vendored
13
.github/issue_template.md
vendored
@@ -1,13 +0,0 @@
|
|||||||
### environment
|
|
||||||
|
|
||||||
* canal version
|
|
||||||
* mysql version
|
|
||||||
|
|
||||||
### Issue Description
|
|
||||||
|
|
||||||
|
|
||||||
### Steps to reproduce
|
|
||||||
|
|
||||||
### Expected behaviour
|
|
||||||
|
|
||||||
### Actual behaviour
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -17,3 +17,4 @@ jtester.properties
|
|||||||
*.tar.gz
|
*.tar.gz
|
||||||
*.rpm
|
*.rpm
|
||||||
client-adapter/example/
|
client-adapter/example/
|
||||||
|
*.dat
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ language: java
|
|||||||
sudo: false # faster builds
|
sudo: false # faster builds
|
||||||
|
|
||||||
jdk:
|
jdk:
|
||||||
|
- openjdk11
|
||||||
|
- oraclejdk11
|
||||||
- openjdk8
|
- openjdk8
|
||||||
- oraclejdk8
|
- oraclejdk8
|
||||||
|
|
||||||
@@ -12,7 +14,7 @@ cache:
|
|||||||
install: true
|
install: true
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- travis_wait 30 ./mvnw clean install -DskipTests=true -Dcheckstyle.skip=true -Drat.skip=true -Dmaven.javadoc.skip=true
|
- travis_wait 30 ./mvnw clean install -DskipTests=false -Dcheckstyle.skip=true -Dmaven.javadoc.skip=true
|
||||||
|
|
||||||
after_success:
|
after_success:
|
||||||
- bash <(curl -s https://codecov.io/bash)
|
- bash <(curl -s https://codecov.io/bash)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<div class="iteye-blog-content-contain">
|
<div class="iteye-blog-content-contain">
|
||||||
|
|
||||||
[](https://travis-ci.com/alibaba/canal)
|
[](https://travis-ci.com/alibaba/canal)
|
||||||
|
[](https://codecov.io/gh/alibaba/canal)
|
||||||

|

|
||||||

|

|
||||||
[](http://isitmaintained.com/project/alibaba/canal "Average time to resolve an issue")
|
[](http://isitmaintained.com/project/alibaba/canal "Average time to resolve an issue")
|
||||||
|
|||||||
@@ -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</version>
|
<version>1.1.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>com.alibaba.otter</groupId>
|
<groupId>com.alibaba.otter</groupId>
|
||||||
@@ -14,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</version>
|
<version>1.1.4-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>joda-time</groupId>
|
<groupId>joda-time</groupId>
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
package com.alibaba.otter.canal.client.adapter.support;
|
||||||
|
|
||||||
|
import com.alibaba.druid.pool.DruidDataSource;
|
||||||
|
import com.google.common.base.Joiner;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
public abstract class AbstractEtlService {
|
||||||
|
|
||||||
|
protected Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||||
|
|
||||||
|
private String type;
|
||||||
|
private AdapterConfig config;
|
||||||
|
private final long CNT_PER_TASK = 10000L;
|
||||||
|
|
||||||
|
public AbstractEtlService(String type, AdapterConfig config){
|
||||||
|
this.type = type;
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected EtlResult importData(String sql, List<String> params) {
|
||||||
|
EtlResult etlResult = new EtlResult();
|
||||||
|
AtomicLong impCount = new AtomicLong();
|
||||||
|
List<String> errMsg = new ArrayList<>();
|
||||||
|
if (config == null) {
|
||||||
|
logger.warn("{} mapping config is null, etl go end ", type);
|
||||||
|
etlResult.setErrorMessage(type + "mapping config is null, etl go end ");
|
||||||
|
return etlResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
try {
|
||||||
|
DruidDataSource dataSource = DatasourceConfig.DATA_SOURCES.get(config.getDataSourceKey());
|
||||||
|
|
||||||
|
List<Object> values = new ArrayList<>();
|
||||||
|
// 拼接条件
|
||||||
|
if (config.getMapping().getEtlCondition() != null && params != null) {
|
||||||
|
String etlCondition = config.getMapping().getEtlCondition();
|
||||||
|
int size = params.size();
|
||||||
|
for (String param : params) {
|
||||||
|
etlCondition = etlCondition.replace("{}", "?");
|
||||||
|
values.add(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
sql += " " + etlCondition;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("etl sql : {}", sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取总数
|
||||||
|
String countSql = "SELECT COUNT(1) FROM ( " + sql + ") _CNT ";
|
||||||
|
long cnt = (Long) Util.sqlRS(dataSource, countSql, values, rs -> {
|
||||||
|
Long count = null;
|
||||||
|
try {
|
||||||
|
if (rs.next()) {
|
||||||
|
count = ((Number) rs.getObject(1)).longValue();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
return count == null ? 0L : count;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 当大于1万条记录时开启多线程
|
||||||
|
if (cnt >= 10000) {
|
||||||
|
int threadCount = Runtime.getRuntime().availableProcessors();
|
||||||
|
|
||||||
|
long offset;
|
||||||
|
long size = CNT_PER_TASK;
|
||||||
|
long workerCnt = cnt / size + (cnt % size == 0 ? 0 : 1);
|
||||||
|
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("workerCnt {} for cnt {} threadCount {}", workerCnt, cnt, threadCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
ExecutorService executor = Util.newFixedThreadPool(threadCount, 5000L);
|
||||||
|
List<Future<Boolean>> futures = new ArrayList<>();
|
||||||
|
for (long i = 0; i < workerCnt; i++) {
|
||||||
|
offset = size * i;
|
||||||
|
String sqlFinal = sql + " LIMIT " + offset + "," + size;
|
||||||
|
Future<Boolean> future = executor.submit(
|
||||||
|
() -> executeSqlImport(dataSource, sqlFinal, values, config.getMapping(), impCount, errMsg));
|
||||||
|
futures.add(future);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Future<Boolean> future : futures) {
|
||||||
|
future.get();
|
||||||
|
}
|
||||||
|
executor.shutdown();
|
||||||
|
} else {
|
||||||
|
executeSqlImport(dataSource, sql, values, config.getMapping(), impCount, errMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("数据全量导入完成, 一共导入 {} 条数据, 耗时: {}", impCount.get(), System.currentTimeMillis() - start);
|
||||||
|
etlResult.setResultMessage("导入" + type + " 数据:" + impCount.get() + " 条");
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error(e.getMessage(), e);
|
||||||
|
errMsg.add(type + " 数据导入异常 =>" + e.getMessage());
|
||||||
|
}
|
||||||
|
if (errMsg.isEmpty()) {
|
||||||
|
etlResult.setSucceeded(true);
|
||||||
|
} else {
|
||||||
|
etlResult.setErrorMessage(Joiner.on("\n").join(errMsg));
|
||||||
|
}
|
||||||
|
return etlResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract boolean executeSqlImport(DataSource ds, String sql, List<Object> values,
|
||||||
|
AdapterConfig.AdapterMapping mapping, AtomicLong impCount,
|
||||||
|
List<String> errMsg);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.alibaba.otter.canal.client.adapter.support;
|
||||||
|
|
||||||
|
public interface AdapterConfig {
|
||||||
|
String getDataSourceKey();
|
||||||
|
|
||||||
|
AdapterMapping getMapping();
|
||||||
|
|
||||||
|
interface AdapterMapping {
|
||||||
|
String getEtlCondition();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,7 +33,14 @@ public class CanalClientConfig {
|
|||||||
// aliyun ak/sk
|
// aliyun ak/sk
|
||||||
private String accessKey;
|
private String accessKey;
|
||||||
private String secretKey;
|
private String secretKey;
|
||||||
|
// 是否启用消息轨迹
|
||||||
|
private boolean enableMessageTrace;
|
||||||
|
// 在使用阿里云商业化mq服务时,如果想使用云上消息轨迹功能,请设置此配置为true
|
||||||
|
private String accessChannel;
|
||||||
|
// 用于使用开源RocketMQ时,设置自定义的消息轨迹topic
|
||||||
|
private String customizedTraceTopic;
|
||||||
|
// 开源RocketMQ命名空间
|
||||||
|
private String namespace;
|
||||||
// canal adapters 配置
|
// canal adapters 配置
|
||||||
private List<CanalAdapter> canalAdapters;
|
private List<CanalAdapter> canalAdapters;
|
||||||
|
|
||||||
@@ -133,6 +140,38 @@ public class CanalClientConfig {
|
|||||||
this.canalAdapters = canalAdapters;
|
this.canalAdapters = canalAdapters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEnableMessageTrace() {
|
||||||
|
return enableMessageTrace;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnableMessageTrace(boolean enableMessageTrace) {
|
||||||
|
this.enableMessageTrace = enableMessageTrace;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAccessChannel() {
|
||||||
|
return accessChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAccessChannel(String accessChannel) {
|
||||||
|
this.accessChannel = accessChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCustomizedTraceTopic() {
|
||||||
|
return customizedTraceTopic;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCustomizedTraceTopic(String customizedTraceTopic) {
|
||||||
|
this.customizedTraceTopic = customizedTraceTopic;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNamespace() {
|
||||||
|
return namespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNamespace(String namespace) {
|
||||||
|
this.namespace = namespace;
|
||||||
|
}
|
||||||
|
|
||||||
public static class CanalAdapter {
|
public static class CanalAdapter {
|
||||||
|
|
||||||
private String instance; // 实例名
|
private String instance; // 实例名
|
||||||
|
|||||||
@@ -76,11 +76,16 @@ public class MessageUtil {
|
|||||||
dml.getPkNames().add(column.getName());
|
dml.getPkNames().add(column.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (column.getIsNull()) {
|
||||||
|
row.put(column.getName(), null);
|
||||||
|
} else {
|
||||||
row.put(column.getName(),
|
row.put(column.getName(),
|
||||||
JdbcTypeUtil.typeConvert(dml.getTable(),column.getName(),
|
JdbcTypeUtil.typeConvert(dml.getTable(),
|
||||||
|
column.getName(),
|
||||||
column.getValue(),
|
column.getValue(),
|
||||||
column.getSqlType(),
|
column.getSqlType(),
|
||||||
column.getMysqlType()));
|
column.getMysqlType()));
|
||||||
|
}
|
||||||
// 获取update为true的字段
|
// 获取update为true的字段
|
||||||
if (column.getUpdated()) {
|
if (column.getUpdated()) {
|
||||||
updateSet.add(column.getName());
|
updateSet.add(column.getName());
|
||||||
@@ -94,13 +99,18 @@ public class MessageUtil {
|
|||||||
Map<String, Object> rowOld = new LinkedHashMap<>();
|
Map<String, Object> rowOld = new LinkedHashMap<>();
|
||||||
for (CanalEntry.Column column : rowData.getBeforeColumnsList()) {
|
for (CanalEntry.Column column : rowData.getBeforeColumnsList()) {
|
||||||
if (updateSet.contains(column.getName())) {
|
if (updateSet.contains(column.getName())) {
|
||||||
|
if (column.getIsNull()) {
|
||||||
|
rowOld.put(column.getName(), null);
|
||||||
|
} else {
|
||||||
rowOld.put(column.getName(),
|
rowOld.put(column.getName(),
|
||||||
JdbcTypeUtil.typeConvert(dml.getTable(),column.getName(),
|
JdbcTypeUtil.typeConvert(dml.getTable(),
|
||||||
|
column.getName(),
|
||||||
column.getValue(),
|
column.getValue(),
|
||||||
column.getSqlType(),
|
column.getSqlType(),
|
||||||
column.getMysqlType()));
|
column.getMysqlType()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// update操作将记录修改前的值
|
// update操作将记录修改前的值
|
||||||
if (!rowOld.isEmpty()) {
|
if (!rowOld.isEmpty()) {
|
||||||
old.add(rowOld);
|
old.add(rowOld);
|
||||||
@@ -162,8 +172,8 @@ public class MessageUtil {
|
|||||||
return dml;
|
return dml;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<Map<String, Object>> changeRows(String table, List<Map<String, String>> rows, Map<String, Integer> sqlTypes,
|
private static List<Map<String, Object>> changeRows(String table, List<Map<String, String>> rows,
|
||||||
Map<String, String> mysqlTypes) {
|
Map<String, Integer> sqlTypes, Map<String, String> mysqlTypes) {
|
||||||
List<Map<String, Object>> result = new ArrayList<>();
|
List<Map<String, Object>> result = new ArrayList<>();
|
||||||
for (Map<String, String> row : rows) {
|
for (Map<String, String> row : rows) {
|
||||||
Map<String, Object> resultRow = new LinkedHashMap<>();
|
Map<String, Object> resultRow = new LinkedHashMap<>();
|
||||||
|
|||||||
@@ -2,13 +2,11 @@ package com.alibaba.otter.canal.client.adapter.support;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.sql.Connection;
|
import java.sql.*;
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.sql.Statement;
|
|
||||||
import java.time.*;
|
import java.time.*;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
import java.util.concurrent.SynchronousQueue;
|
import java.util.concurrent.SynchronousQueue;
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
@@ -37,9 +35,9 @@ public class Util {
|
|||||||
*/
|
*/
|
||||||
public static Object sqlRS(DataSource ds, String sql, Function<ResultSet, Object> fun) {
|
public static Object sqlRS(DataSource ds, String sql, Function<ResultSet, Object> fun) {
|
||||||
try (Connection conn = ds.getConnection();
|
try (Connection conn = ds.getConnection();
|
||||||
Statement stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);) {
|
Statement stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY)) {
|
||||||
stmt.setFetchSize(Integer.MIN_VALUE);
|
stmt.setFetchSize(Integer.MIN_VALUE);
|
||||||
try (ResultSet rs = stmt.executeQuery(sql);) {
|
try (ResultSet rs = stmt.executeQuery(sql)) {
|
||||||
return fun.apply(rs);
|
return fun.apply(rs);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -48,6 +46,26 @@ public class Util {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Object sqlRS(DataSource ds, String sql, List<Object> values, Function<ResultSet, Object> fun) {
|
||||||
|
try (Connection conn = ds.getConnection()) {
|
||||||
|
try (PreparedStatement pstmt = conn
|
||||||
|
.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY)) {
|
||||||
|
pstmt.setFetchSize(Integer.MIN_VALUE);
|
||||||
|
if (values != null) {
|
||||||
|
for (int i = 0; i < values.size(); i++) {
|
||||||
|
pstmt.setObject(i + 1, values.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try (ResultSet rs = pstmt.executeQuery()) {
|
||||||
|
return fun.apply(rs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("sqlRs has error, sql: {} ", sql);
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sql执行获取resultSet
|
* sql执行获取resultSet
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -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</version>
|
<version>1.1.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>com.alibaba.otter</groupId>
|
<groupId>com.alibaba.otter</groupId>
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ 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;
|
||||||
@@ -30,12 +29,7 @@ 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.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;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ES外部适配器
|
* ES外部适配器
|
||||||
@@ -194,7 +188,6 @@ public class ESAdapter implements OuterAdapter {
|
|||||||
} else {
|
} else {
|
||||||
StringBuilder resultMsg = new StringBuilder();
|
StringBuilder resultMsg = new StringBuilder();
|
||||||
boolean resSuccess = true;
|
boolean resSuccess = true;
|
||||||
// ds不为空说明传入的是datasourceKey
|
|
||||||
for (ESSyncConfig configTmp : esSyncConfig.values()) {
|
for (ESSyncConfig configTmp : esSyncConfig.values()) {
|
||||||
// 取所有的destination为task的配置
|
// 取所有的destination为task的配置
|
||||||
if (configTmp.getDestination().equals(task)) {
|
if (configTmp.getDestination().equals(task)) {
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.alibaba.otter.canal.client.adapter.es.config;
|
package com.alibaba.otter.canal.client.adapter.es.config;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.client.adapter.support.AdapterConfig;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -11,7 +13,7 @@ import java.util.Map;
|
|||||||
* @author rewerma 2018-11-01
|
* @author rewerma 2018-11-01
|
||||||
* @version 1.0.0
|
* @version 1.0.0
|
||||||
*/
|
*/
|
||||||
public class ESSyncConfig {
|
public class ESSyncConfig implements AdapterConfig {
|
||||||
|
|
||||||
private String dataSourceKey; // 数据源key
|
private String dataSourceKey; // 数据源key
|
||||||
|
|
||||||
@@ -78,7 +80,11 @@ public class ESSyncConfig {
|
|||||||
this.esMapping = esMapping;
|
this.esMapping = esMapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ESMapping {
|
public ESMapping getMapping(){
|
||||||
|
return esMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ESMapping implements AdapterMapping{
|
||||||
|
|
||||||
private String _index;
|
private String _index;
|
||||||
private String _type;
|
private String _type;
|
||||||
|
|||||||
@@ -9,11 +9,7 @@ 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.SQLBinaryOpExpr;
|
import com.alibaba.fastsql.sql.ast.expr.*;
|
||||||
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.SQLExprTableSource;
|
||||||
import com.alibaba.fastsql.sql.ast.statement.SQLJoinTableSource;
|
import com.alibaba.fastsql.sql.ast.statement.SQLJoinTableSource;
|
||||||
import com.alibaba.fastsql.sql.ast.statement.SQLSelectStatement;
|
import com.alibaba.fastsql.sql.ast.statement.SQLSelectStatement;
|
||||||
@@ -130,6 +126,23 @@ public class SqlParser {
|
|||||||
SQLCaseExpr sqlCaseExpr = (SQLCaseExpr) expr;
|
SQLCaseExpr sqlCaseExpr = (SQLCaseExpr) expr;
|
||||||
fieldItem.setMethod(true);
|
fieldItem.setMethod(true);
|
||||||
sqlCaseExpr.getItems().forEach(item-> visitColumn(item.getConditionExpr(), fieldItem));
|
sqlCaseExpr.getItems().forEach(item-> visitColumn(item.getConditionExpr(), fieldItem));
|
||||||
|
}else if(expr instanceof SQLCharExpr) {
|
||||||
|
SQLCharExpr sqlCharExpr = (SQLCharExpr) expr;
|
||||||
|
String owner = null;
|
||||||
|
String columnName = null;
|
||||||
|
if (sqlCharExpr.getParent() instanceof SQLCaseExpr.Item) {
|
||||||
|
owner = ((SQLPropertyExpr) ((SQLCaseExpr.Item) sqlCharExpr.getParent()).getValueExpr()).getOwnernName();
|
||||||
|
columnName = ((SQLPropertyExpr) ((SQLCaseExpr.Item) sqlCharExpr.getParent()).getValueExpr()).getName();
|
||||||
|
}
|
||||||
|
if (fieldItem.getFieldName() == null) {
|
||||||
|
fieldItem.setFieldName(columnName);
|
||||||
|
fieldItem.setExpr(sqlCharExpr.toString());
|
||||||
|
}
|
||||||
|
ColumnItem columnItem = new ColumnItem();
|
||||||
|
columnItem.setColumnName(columnName);
|
||||||
|
columnItem.setOwner(owner);
|
||||||
|
fieldItem.getOwners().add(owner);
|
||||||
|
fieldItem.addColumn(columnItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +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.ArrayList;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
@@ -25,18 +20,15 @@ import org.elasticsearch.client.transport.TransportClient;
|
|||||||
import org.elasticsearch.index.query.QueryBuilders;
|
import org.elasticsearch.index.query.QueryBuilders;
|
||||||
import org.elasticsearch.rest.RestStatus;
|
import org.elasticsearch.rest.RestStatus;
|
||||||
import org.elasticsearch.search.SearchHit;
|
import org.elasticsearch.search.SearchHit;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import com.alibaba.druid.pool.DruidDataSource;
|
|
||||||
import com.alibaba.otter.canal.client.adapter.es.config.ESSyncConfig;
|
import com.alibaba.otter.canal.client.adapter.es.config.ESSyncConfig;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.config.ESSyncConfig.ESMapping;
|
import com.alibaba.otter.canal.client.adapter.es.config.ESSyncConfig.ESMapping;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.config.SchemaItem.FieldItem;
|
import com.alibaba.otter.canal.client.adapter.es.config.SchemaItem.FieldItem;
|
||||||
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.AbstractEtlService;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.support.AdapterConfig;
|
||||||
import com.alibaba.otter.canal.client.adapter.support.EtlResult;
|
import com.alibaba.otter.canal.client.adapter.support.EtlResult;
|
||||||
import com.alibaba.otter.canal.client.adapter.support.Util;
|
import com.alibaba.otter.canal.client.adapter.support.Util;
|
||||||
import com.google.common.base.Joiner;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ES ETL Service
|
* ES ETL Service
|
||||||
@@ -44,116 +36,24 @@ import com.google.common.base.Joiner;
|
|||||||
* @author rewerma 2018-11-01
|
* @author rewerma 2018-11-01
|
||||||
* @version 1.0.0
|
* @version 1.0.0
|
||||||
*/
|
*/
|
||||||
public class ESEtlService {
|
public class ESEtlService extends AbstractEtlService {
|
||||||
|
|
||||||
private static Logger logger = LoggerFactory.getLogger(ESEtlService.class);
|
|
||||||
|
|
||||||
private TransportClient transportClient;
|
private TransportClient transportClient;
|
||||||
private ESTemplate esTemplate;
|
private ESTemplate esTemplate;
|
||||||
private ESSyncConfig config;
|
private ESSyncConfig config;
|
||||||
|
|
||||||
public ESEtlService(TransportClient transportClient, ESSyncConfig config){
|
public ESEtlService(TransportClient transportClient, ESSyncConfig config){
|
||||||
|
super("ES", config);
|
||||||
this.transportClient = transportClient;
|
this.transportClient = transportClient;
|
||||||
this.esTemplate = new ESTemplate(transportClient);
|
this.esTemplate = new ESTemplate(transportClient);
|
||||||
this.config = config;
|
this.config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EtlResult importData(List<String> params) {
|
public EtlResult importData(List<String> params) {
|
||||||
EtlResult etlResult = new EtlResult();
|
|
||||||
AtomicLong impCount = new AtomicLong();
|
|
||||||
List<String> errMsg = new ArrayList<>();
|
|
||||||
String esIndex = "";
|
|
||||||
if (config == null) {
|
|
||||||
logger.warn("esSycnCofnig is null, etl go end !");
|
|
||||||
etlResult.setErrorMessage("esSycnCofnig is null, etl go end !");
|
|
||||||
return etlResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
ESMapping mapping = config.getEsMapping();
|
ESMapping mapping = config.getEsMapping();
|
||||||
|
logger.info("start etl to import data to index: {}", mapping.get_index());
|
||||||
esIndex = mapping.get_index();
|
|
||||||
DruidDataSource dataSource = DatasourceConfig.DATA_SOURCES.get(config.getDataSourceKey());
|
|
||||||
Pattern pattern = Pattern.compile(".*:(.*)://.*/(.*)\\?.*$");
|
|
||||||
Matcher matcher = pattern.matcher(dataSource.getUrl());
|
|
||||||
if (!matcher.find()) {
|
|
||||||
throw new RuntimeException("Not found the schema of jdbc-url: " + config.getDataSourceKey());
|
|
||||||
}
|
|
||||||
String schema = matcher.group(2);
|
|
||||||
|
|
||||||
logger.info("etl from db: {}, to es index: {}", schema, esIndex);
|
|
||||||
long start = System.currentTimeMillis();
|
|
||||||
try {
|
|
||||||
String sql = mapping.getSql();
|
String sql = mapping.getSql();
|
||||||
|
return importData(sql, params);
|
||||||
// 拼接条件
|
|
||||||
if (mapping.getEtlCondition() != null && params != null) {
|
|
||||||
String etlCondition = mapping.getEtlCondition();
|
|
||||||
int size = params.size();
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
etlCondition = etlCondition.replace("{" + i + "}", params.get(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
sql += " " + etlCondition;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug("etl sql : {}", mapping.getSql());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取总数
|
|
||||||
String countSql = "SELECT COUNT(1) FROM ( " + sql + ") _CNT ";
|
|
||||||
long cnt = (Long) Util.sqlRS(dataSource, countSql, rs -> {
|
|
||||||
Long count = null;
|
|
||||||
try {
|
|
||||||
if (rs.next()) {
|
|
||||||
count = ((Number) rs.getObject(1)).longValue();
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
return count == null ? 0L : count;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 当大于1万条记录时开启多线程
|
|
||||||
if (cnt >= 10000) {
|
|
||||||
int threadCount = 3; // 从配置读取默认为3
|
|
||||||
long perThreadCnt = cnt / threadCount;
|
|
||||||
ExecutorService executor = Util.newFixedThreadPool(threadCount, 5000L);
|
|
||||||
for (int i = 0; i < threadCount; i++) {
|
|
||||||
long offset = i * perThreadCnt;
|
|
||||||
Long size = null;
|
|
||||||
if (i != threadCount - 1) {
|
|
||||||
size = perThreadCnt;
|
|
||||||
}
|
|
||||||
String sqlFinal;
|
|
||||||
if (size != null) {
|
|
||||||
sqlFinal = sql + " LIMIT " + offset + "," + size;
|
|
||||||
} else {
|
|
||||||
sqlFinal = sql + " LIMIT " + offset + "," + cnt;
|
|
||||||
}
|
|
||||||
executor.execute(() -> executeSqlImport(dataSource, sqlFinal, mapping, impCount, errMsg));
|
|
||||||
}
|
|
||||||
|
|
||||||
executor.shutdown();
|
|
||||||
while (!executor.awaitTermination(3, TimeUnit.SECONDS)) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
executeSqlImport(dataSource, sql, mapping, impCount, errMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info("数据全量导入完成,一共导入 {} 条数据, 耗时: {}", impCount.get(), System.currentTimeMillis() - start);
|
|
||||||
etlResult.setResultMessage("导入ES索引 " + esIndex + " 数据:" + impCount.get() + " 条");
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e.getMessage(), e);
|
|
||||||
errMsg.add(esIndex + " etl failed! ==>" + e.getMessage());
|
|
||||||
}
|
|
||||||
if (errMsg.isEmpty()) {
|
|
||||||
etlResult.setSucceeded(true);
|
|
||||||
} else {
|
|
||||||
etlResult.setErrorMessage(Joiner.on("\n").join(errMsg));
|
|
||||||
}
|
|
||||||
return etlResult;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processFailBulkResponse(BulkResponse bulkResponse) {
|
private void processFailBulkResponse(BulkResponse bulkResponse) {
|
||||||
@@ -171,10 +71,12 @@ public class ESEtlService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean executeSqlImport(DataSource ds, String sql, ESMapping mapping, AtomicLong impCount,
|
protected boolean executeSqlImport(DataSource ds, String sql, List<Object> values,
|
||||||
|
AdapterConfig.AdapterMapping adapterMapping, AtomicLong impCount,
|
||||||
List<String> errMsg) {
|
List<String> errMsg) {
|
||||||
try {
|
try {
|
||||||
Util.sqlRS(ds, sql, rs -> {
|
ESMapping mapping = (ESMapping) adapterMapping;
|
||||||
|
Util.sqlRS(ds, sql, values, rs -> {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
try {
|
try {
|
||||||
BulkRequestBuilder bulkRequestBuilder = transportClient.prepareBulk();
|
BulkRequestBuilder bulkRequestBuilder = transportClient.prepareBulk();
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
package com.alibaba.otter.canal.client.adapter.es.service;
|
package com.alibaba.otter.canal.client.adapter.es.service;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.*;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
@@ -436,7 +433,7 @@ public class ESSyncService {
|
|||||||
Object idVal = esTemplate.getESDataFromDmlData(mapping, data, esFieldData);
|
Object idVal = esTemplate.getESDataFromDmlData(mapping, data, esFieldData);
|
||||||
|
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Single table insert ot es index, destination:{}, table: {}, index: {}, id: {}",
|
logger.trace("Single table insert to es index, destination:{}, table: {}, index: {}, id: {}",
|
||||||
config.getDestination(),
|
config.getDestination(),
|
||||||
dml.getTable(),
|
dml.getTable(),
|
||||||
mapping.get_index(),
|
mapping.get_index(),
|
||||||
@@ -459,7 +456,7 @@ public class ESSyncService {
|
|||||||
sql = ESSyncUtil.appendCondition(sql, condition);
|
sql = ESSyncUtil.appendCondition(sql, condition);
|
||||||
DataSource ds = DatasourceConfig.DATA_SOURCES.get(config.getDataSourceKey());
|
DataSource ds = DatasourceConfig.DATA_SOURCES.get(config.getDataSourceKey());
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Main table insert ot es index by query sql, destination:{}, table: {}, index: {}, sql: {}",
|
logger.trace("Main table insert to es index by query sql, destination:{}, table: {}, index: {}, sql: {}",
|
||||||
config.getDestination(),
|
config.getDestination(),
|
||||||
dml.getTable(),
|
dml.getTable(),
|
||||||
mapping.get_index(),
|
mapping.get_index(),
|
||||||
@@ -473,7 +470,7 @@ public class ESSyncService {
|
|||||||
|
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace(
|
logger.trace(
|
||||||
"Main table insert ot es index by query sql, destination:{}, table: {}, index: {}, id: {}",
|
"Main table insert to es index by query sql, destination:{}, table: {}, index: {}, id: {}",
|
||||||
config.getDestination(),
|
config.getDestination(),
|
||||||
dml.getTable(),
|
dml.getTable(),
|
||||||
mapping.get_index(),
|
mapping.get_index(),
|
||||||
@@ -517,7 +514,7 @@ public class ESSyncService {
|
|||||||
|
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace(
|
logger.trace(
|
||||||
"Main table delete ot es index by query sql, destination:{}, table: {}, index: {}, id: {}",
|
"Main table delete to es index by query sql, destination:{}, table: {}, index: {}, id: {}",
|
||||||
config.getDestination(),
|
config.getDestination(),
|
||||||
dml.getTable(),
|
dml.getTable(),
|
||||||
mapping.get_index(),
|
mapping.get_index(),
|
||||||
@@ -677,7 +674,17 @@ public class ESSyncService {
|
|||||||
private void wholeSqlOperation(ESSyncConfig config, Dml dml, Map<String, Object> data, Map<String, Object> old,
|
private void wholeSqlOperation(ESSyncConfig config, Dml dml, Map<String, Object> data, Map<String, Object> old,
|
||||||
TableItem tableItem) {
|
TableItem tableItem) {
|
||||||
ESMapping mapping = config.getEsMapping();
|
ESMapping mapping = config.getEsMapping();
|
||||||
StringBuilder sql = new StringBuilder(mapping.getSql() + " WHERE ");
|
//防止最后出现groupby 导致sql解析异常
|
||||||
|
String[] sqlSplit = mapping.getSql().split("GROUP\\ BY(?!(.*)ON)");
|
||||||
|
String sqlNoWhere = sqlSplit[0];
|
||||||
|
|
||||||
|
String sqlGroupBy = "";
|
||||||
|
|
||||||
|
if(sqlSplit.length > 1){
|
||||||
|
sqlGroupBy = "GROUP BY "+ sqlSplit[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sql = new StringBuilder(sqlNoWhere + " WHERE ");
|
||||||
|
|
||||||
for (FieldItem fkFieldItem : tableItem.getRelationTableFields().keySet()) {
|
for (FieldItem fkFieldItem : tableItem.getRelationTableFields().keySet()) {
|
||||||
String columnName = fkFieldItem.getColumn().getColumnName();
|
String columnName = fkFieldItem.getColumn().getColumnName();
|
||||||
@@ -686,6 +693,8 @@ public class ESSyncService {
|
|||||||
}
|
}
|
||||||
int len = sql.length();
|
int len = sql.length();
|
||||||
sql.delete(len - 5, len);
|
sql.delete(len - 5, len);
|
||||||
|
sql.append(sqlGroupBy);
|
||||||
|
|
||||||
DataSource ds = DatasourceConfig.DATA_SOURCES.get(config.getDataSourceKey());
|
DataSource ds = DatasourceConfig.DATA_SOURCES.get(config.getDataSourceKey());
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Join table update es index by query whole sql, destination:{}, table: {}, index: {}, sql: {}",
|
logger.trace("Join table update es index by query whole sql, destination:{}, table: {}, index: {}, sql: {}",
|
||||||
@@ -784,7 +793,7 @@ public class ESSyncService {
|
|||||||
Object idVal = esTemplate.getESDataFromDmlData(mapping, data, old, esFieldData);
|
Object idVal = esTemplate.getESDataFromDmlData(mapping, data, old, esFieldData);
|
||||||
|
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Main table update ot es index, destination:{}, table: {}, index: {}, id: {}",
|
logger.trace("Main table update to es index, destination:{}, table: {}, index: {}, id: {}",
|
||||||
config.getDestination(),
|
config.getDestination(),
|
||||||
dml.getTable(),
|
dml.getTable(),
|
||||||
mapping.get_index(),
|
mapping.get_index(),
|
||||||
@@ -807,7 +816,7 @@ public class ESSyncService {
|
|||||||
sql = ESSyncUtil.appendCondition(sql, condition);
|
sql = ESSyncUtil.appendCondition(sql, condition);
|
||||||
DataSource ds = DatasourceConfig.DATA_SOURCES.get(config.getDataSourceKey());
|
DataSource ds = DatasourceConfig.DATA_SOURCES.get(config.getDataSourceKey());
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Main table update ot es index by query sql, destination:{}, table: {}, index: {}, sql: {}",
|
logger.trace("Main table update to es index by query sql, destination:{}, table: {}, index: {}, sql: {}",
|
||||||
config.getDestination(),
|
config.getDestination(),
|
||||||
dml.getTable(),
|
dml.getTable(),
|
||||||
mapping.get_index(),
|
mapping.get_index(),
|
||||||
@@ -821,7 +830,7 @@ public class ESSyncService {
|
|||||||
|
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace(
|
logger.trace(
|
||||||
"Main table update ot es index by query sql, destination:{}, table: {}, index: {}, id: {}",
|
"Main table update to es index by query sql, destination:{}, table: {}, index: {}, id: {}",
|
||||||
config.getDestination(),
|
config.getDestination(),
|
||||||
dml.getTable(),
|
dml.getTable(),
|
||||||
mapping.get_index(),
|
mapping.get_index(),
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ public class ESSyncUtil {
|
|||||||
if (value.contains(";")) {
|
if (value.contains(";")) {
|
||||||
separator = ";";
|
separator = ";";
|
||||||
} else if (value.contains("|")) {
|
} else if (value.contains("|")) {
|
||||||
separator = "|";
|
separator = "\\|";
|
||||||
} else if (value.contains("-")) {
|
} else if (value.contains("-")) {
|
||||||
separator = "-";
|
separator = "-";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,7 @@ package com.alibaba.otter.canal.client.adapter.es.support;
|
|||||||
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.HashMap;
|
import java.util.*;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
@@ -60,6 +58,10 @@ public class ESTemplate {
|
|||||||
return bulkRequestBuilder;
|
return bulkRequestBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void resetBulkRequestBuilder() {
|
||||||
|
this.bulkRequestBuilder = this.transportClient.prepareBulk();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 插入数据
|
* 插入数据
|
||||||
*
|
*
|
||||||
@@ -136,11 +138,15 @@ public class ESTemplate {
|
|||||||
// 查询sql批量更新
|
// 查询sql批量更新
|
||||||
DataSource ds = DatasourceConfig.DATA_SOURCES.get(config.getDataSourceKey());
|
DataSource ds = DatasourceConfig.DATA_SOURCES.get(config.getDataSourceKey());
|
||||||
StringBuilder sql = new StringBuilder("SELECT * FROM (" + mapping.getSql() + ") _v WHERE ");
|
StringBuilder sql = new StringBuilder("SELECT * FROM (" + mapping.getSql() + ") _v WHERE ");
|
||||||
paramsTmp.forEach(
|
List<Object> values = new ArrayList<>();
|
||||||
(fieldName, value) -> sql.append("_v.").append(fieldName).append("=").append(value).append(" AND "));
|
paramsTmp.forEach((fieldName, value) -> {
|
||||||
|
sql.append("_v.").append(fieldName).append("=? AND ");
|
||||||
|
values.add(value);
|
||||||
|
});
|
||||||
|
//TODO 直接外部包裹sql会导致全表扫描性能低, 待优化拼接内部where条件
|
||||||
int len = sql.length();
|
int len = sql.length();
|
||||||
sql.delete(len - 4, len);
|
sql.delete(len - 4, len);
|
||||||
Integer syncCount = (Integer) Util.sqlRS(ds, sql.toString(), rs -> {
|
Integer syncCount = (Integer) Util.sqlRS(ds, sql.toString(), values, rs -> {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
try {
|
try {
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
@@ -208,11 +214,12 @@ public class ESTemplate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 如果大于批量数则提交批次
|
* 如果大于批量数则提交批次, 调用后es bulk请求后,numberOfActions不会清理,需要主动调用函数清0,否则不能起到批量请求的效果
|
||||||
*/
|
*/
|
||||||
private void commitBulk() {
|
private void commitBulk() {
|
||||||
if (getBulk().numberOfActions() >= MAX_BATCH_SIZE) {
|
if (getBulk().numberOfActions() >= MAX_BATCH_SIZE) {
|
||||||
commit();
|
commit();
|
||||||
|
resetBulkRequestBuilder();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,5 +17,5 @@ esMapping:
|
|||||||
from biz_order t"
|
from biz_order t"
|
||||||
skips:
|
skips:
|
||||||
- customer_id
|
- customer_id
|
||||||
etlCondition: "where t.c_time>='{0}'"
|
etlCondition: "where t.c_time>={}"
|
||||||
commitBatch: 3000
|
commitBatch: 3000
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ esMapping:
|
|||||||
customer_order:
|
customer_order:
|
||||||
name: customer
|
name: customer
|
||||||
sql: "select t.id, t.name, t.email from customer t"
|
sql: "select t.id, t.name, t.email from customer t"
|
||||||
etlCondition: "where t.c_time>='{0}'"
|
etlCondition: "where t.c_time>={}"
|
||||||
commitBatch: 3000
|
commitBatch: 3000
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -12,5 +12,5 @@ esMapping:
|
|||||||
left join role b on b.id=a.role_id"
|
left join role b on b.id=a.role_id"
|
||||||
# objFields:
|
# objFields:
|
||||||
# _labels: array:;
|
# _labels: array:;
|
||||||
etlCondition: "where a.c_time>='{0}'"
|
etlCondition: "where a.c_time>={}"
|
||||||
commitBatch: 3000
|
commitBatch: 3000
|
||||||
|
|||||||
@@ -4,12 +4,13 @@ import java.util.Map;
|
|||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.client.adapter.es.config.ESSyncConfig;
|
import com.alibaba.otter.canal.client.adapter.es.config.ESSyncConfig;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.config.ESSyncConfigLoader;
|
import com.alibaba.otter.canal.client.adapter.es.config.ESSyncConfigLoader;
|
||||||
import com.alibaba.otter.canal.client.adapter.support.DatasourceConfig;
|
import com.alibaba.otter.canal.client.adapter.support.DatasourceConfig;
|
||||||
|
@Ignore
|
||||||
public class ConfigLoadTest {
|
public class ConfigLoadTest {
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
|
|||||||
@@ -18,8 +18,9 @@ import org.elasticsearch.search.SearchHit;
|
|||||||
import org.elasticsearch.transport.client.PreBuiltTransportClient;
|
import org.elasticsearch.transport.client.PreBuiltTransportClient;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@Ignore
|
||||||
public class ESTest {
|
public class ESTest {
|
||||||
|
|
||||||
private TransportClient transportClient;
|
private TransportClient transportClient;
|
||||||
|
|||||||
@@ -11,13 +11,14 @@ import javax.sql.DataSource;
|
|||||||
import org.elasticsearch.action.get.GetResponse;
|
import org.elasticsearch.action.get.GetResponse;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.client.adapter.es.ESAdapter;
|
import com.alibaba.otter.canal.client.adapter.es.ESAdapter;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.config.ESSyncConfig;
|
import com.alibaba.otter.canal.client.adapter.es.config.ESSyncConfig;
|
||||||
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;
|
||||||
|
@Ignore
|
||||||
public class LabelSyncJoinSub2Test {
|
public class LabelSyncJoinSub2Test {
|
||||||
|
|
||||||
private ESAdapter esAdapter;
|
private ESAdapter esAdapter;
|
||||||
|
|||||||
@@ -11,13 +11,14 @@ import javax.sql.DataSource;
|
|||||||
import org.elasticsearch.action.get.GetResponse;
|
import org.elasticsearch.action.get.GetResponse;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.client.adapter.es.ESAdapter;
|
import com.alibaba.otter.canal.client.adapter.es.ESAdapter;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.config.ESSyncConfig;
|
import com.alibaba.otter.canal.client.adapter.es.config.ESSyncConfig;
|
||||||
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;
|
||||||
|
@Ignore
|
||||||
public class LabelSyncJoinSubTest {
|
public class LabelSyncJoinSubTest {
|
||||||
|
|
||||||
private ESAdapter esAdapter;
|
private ESAdapter esAdapter;
|
||||||
|
|||||||
@@ -11,13 +11,14 @@ import javax.sql.DataSource;
|
|||||||
import org.elasticsearch.action.get.GetResponse;
|
import org.elasticsearch.action.get.GetResponse;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.client.adapter.es.ESAdapter;
|
import com.alibaba.otter.canal.client.adapter.es.ESAdapter;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.config.ESSyncConfig;
|
import com.alibaba.otter.canal.client.adapter.es.config.ESSyncConfig;
|
||||||
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;
|
||||||
|
@Ignore
|
||||||
public class RoleSyncJoinOne2Test {
|
public class RoleSyncJoinOne2Test {
|
||||||
|
|
||||||
private ESAdapter esAdapter;
|
private ESAdapter esAdapter;
|
||||||
|
|||||||
@@ -11,13 +11,14 @@ import javax.sql.DataSource;
|
|||||||
import org.elasticsearch.action.get.GetResponse;
|
import org.elasticsearch.action.get.GetResponse;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.client.adapter.es.ESAdapter;
|
import com.alibaba.otter.canal.client.adapter.es.ESAdapter;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.config.ESSyncConfig;
|
import com.alibaba.otter.canal.client.adapter.es.config.ESSyncConfig;
|
||||||
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;
|
||||||
|
@Ignore
|
||||||
public class RoleSyncJoinOneTest {
|
public class RoleSyncJoinOneTest {
|
||||||
|
|
||||||
private ESAdapter esAdapter;
|
private ESAdapter esAdapter;
|
||||||
|
|||||||
@@ -11,13 +11,14 @@ import javax.sql.DataSource;
|
|||||||
import org.elasticsearch.action.get.GetResponse;
|
import org.elasticsearch.action.get.GetResponse;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.client.adapter.es.ESAdapter;
|
import com.alibaba.otter.canal.client.adapter.es.ESAdapter;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.config.ESSyncConfig;
|
import com.alibaba.otter.canal.client.adapter.es.config.ESSyncConfig;
|
||||||
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;
|
||||||
|
@Ignore
|
||||||
public class UserSyncJoinOneTest {
|
public class UserSyncJoinOneTest {
|
||||||
|
|
||||||
private ESAdapter esAdapter;
|
private ESAdapter esAdapter;
|
||||||
|
|||||||
@@ -9,12 +9,13 @@ import java.util.Map;
|
|||||||
import org.elasticsearch.action.get.GetResponse;
|
import org.elasticsearch.action.get.GetResponse;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.client.adapter.es.ESAdapter;
|
import com.alibaba.otter.canal.client.adapter.es.ESAdapter;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.config.ESSyncConfig;
|
import com.alibaba.otter.canal.client.adapter.es.config.ESSyncConfig;
|
||||||
import com.alibaba.otter.canal.client.adapter.support.Dml;
|
import com.alibaba.otter.canal.client.adapter.support.Dml;
|
||||||
|
@Ignore
|
||||||
public class UserSyncSingleTest {
|
public class UserSyncSingleTest {
|
||||||
|
|
||||||
private ESAdapter esAdapter;
|
private ESAdapter esAdapter;
|
||||||
|
|||||||
@@ -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</version>
|
<version>1.1.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>com.alibaba.otter</groupId>
|
<groupId>com.alibaba.otter</groupId>
|
||||||
@@ -26,6 +26,10 @@
|
|||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
<artifactId>slf4j-log4j12</artifactId>
|
<artifactId>slf4j-log4j12</artifactId>
|
||||||
</exclusion>
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>jdk.tools</groupId>
|
||||||
|
<artifactId>jdk.tools</artifactId>
|
||||||
|
</exclusion>
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import java.io.IOException;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.hbase.HBaseConfiguration;
|
import org.apache.hadoop.hbase.HBaseConfiguration;
|
||||||
@@ -25,7 +23,10 @@ import com.alibaba.otter.canal.client.adapter.hbase.monitor.HbaseConfigMonitor;
|
|||||||
import com.alibaba.otter.canal.client.adapter.hbase.service.HbaseEtlService;
|
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.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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HBase外部适配器
|
* HBase外部适配器
|
||||||
@@ -147,27 +148,16 @@ public class HbaseAdapter implements OuterAdapter {
|
|||||||
public EtlResult etl(String task, List<String> params) {
|
public EtlResult etl(String task, List<String> params) {
|
||||||
EtlResult etlResult = new EtlResult();
|
EtlResult etlResult = new EtlResult();
|
||||||
MappingConfig config = hbaseMapping.get(task);
|
MappingConfig config = hbaseMapping.get(task);
|
||||||
|
HbaseEtlService hbaseEtlService = new HbaseEtlService(hbaseTemplate, config);
|
||||||
if (config != null) {
|
if (config != null) {
|
||||||
DataSource dataSource = DatasourceConfig.DATA_SOURCES.get(config.getDataSourceKey());
|
return hbaseEtlService.importData(params);
|
||||||
if (dataSource != null) {
|
|
||||||
return HbaseEtlService.importData(dataSource, hbaseTemplate, config, params);
|
|
||||||
} else {
|
|
||||||
etlResult.setSucceeded(false);
|
|
||||||
etlResult.setErrorMessage("DataSource not found");
|
|
||||||
return etlResult;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
StringBuilder resultMsg = new StringBuilder();
|
StringBuilder resultMsg = new StringBuilder();
|
||||||
boolean resSucc = true;
|
boolean resSucc = true;
|
||||||
// ds不为空说明传入的是datasourceKey
|
|
||||||
for (MappingConfig configTmp : hbaseMapping.values()) {
|
for (MappingConfig configTmp : hbaseMapping.values()) {
|
||||||
// 取所有的destination为task的配置
|
// 取所有的destination为task的配置
|
||||||
if (configTmp.getDestination().equals(task)) {
|
if (configTmp.getDestination().equals(task)) {
|
||||||
DataSource dataSource = DatasourceConfig.DATA_SOURCES.get(configTmp.getDataSourceKey());
|
EtlResult etlRes = hbaseEtlService.importData(params);
|
||||||
if (dataSource == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
EtlResult etlRes = HbaseEtlService.importData(dataSource, hbaseTemplate, configTmp, params);
|
|
||||||
if (!etlRes.getSucceeded()) {
|
if (!etlRes.getSucceeded()) {
|
||||||
resSucc = false;
|
resSucc = false;
|
||||||
resultMsg.append(etlRes.getErrorMessage()).append("\n");
|
resultMsg.append(etlRes.getErrorMessage()).append("\n");
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.alibaba.otter.canal.client.adapter.hbase.config;
|
package com.alibaba.otter.canal.client.adapter.hbase.config;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.client.adapter.support.AdapterConfig;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -8,7 +10,7 @@ import java.util.*;
|
|||||||
* @author rewerma 2018-8-21 下午06:45:49
|
* @author rewerma 2018-8-21 下午06:45:49
|
||||||
* @version 1.0.0
|
* @version 1.0.0
|
||||||
*/
|
*/
|
||||||
public class MappingConfig {
|
public class MappingConfig implements AdapterConfig {
|
||||||
|
|
||||||
private String dataSourceKey; // 数据源key
|
private String dataSourceKey; // 数据源key
|
||||||
|
|
||||||
@@ -60,6 +62,10 @@ public class MappingConfig {
|
|||||||
this.hbaseMapping = hbaseMapping;
|
this.hbaseMapping = hbaseMapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AdapterMapping getMapping() {
|
||||||
|
return hbaseMapping;
|
||||||
|
}
|
||||||
|
|
||||||
public void validate() {
|
public void validate() {
|
||||||
if (hbaseMapping.database == null || hbaseMapping.database.isEmpty()) {
|
if (hbaseMapping.database == null || hbaseMapping.database.isEmpty()) {
|
||||||
throw new NullPointerException("hbaseMapping.database");
|
throw new NullPointerException("hbaseMapping.database");
|
||||||
@@ -179,7 +185,7 @@ public class MappingConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class HbaseMapping {
|
public static class HbaseMapping implements AdapterMapping {
|
||||||
|
|
||||||
private Mode mode = Mode.STRING; // hbase默认转换格式
|
private Mode mode = Mode.STRING; // hbase默认转换格式
|
||||||
private String database; // 数据库名或schema名
|
private String database; // 数据库名或schema名
|
||||||
|
|||||||
@@ -1,31 +1,23 @@
|
|||||||
package com.alibaba.otter.canal.client.adapter.hbase.service;
|
package com.alibaba.otter.canal.client.adapter.hbase.service;
|
||||||
|
|
||||||
import java.sql.ResultSetMetaData;
|
|
||||||
import java.sql.Timestamp;
|
import java.sql.Timestamp;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.client.adapter.support.*;
|
||||||
import org.apache.hadoop.hbase.util.Bytes;
|
import org.apache.hadoop.hbase.util.Bytes;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.client.adapter.hbase.config.MappingConfig;
|
import com.alibaba.otter.canal.client.adapter.hbase.config.MappingConfig;
|
||||||
import com.alibaba.otter.canal.client.adapter.hbase.support.HRow;
|
import com.alibaba.otter.canal.client.adapter.hbase.support.*;
|
||||||
import com.alibaba.otter.canal.client.adapter.hbase.support.HbaseTemplate;
|
|
||||||
import com.alibaba.otter.canal.client.adapter.hbase.support.PhType;
|
|
||||||
import com.alibaba.otter.canal.client.adapter.hbase.support.PhTypeUtil;
|
|
||||||
import com.alibaba.otter.canal.client.adapter.hbase.support.Type;
|
|
||||||
import com.alibaba.otter.canal.client.adapter.hbase.support.TypeUtil;
|
|
||||||
import com.alibaba.otter.canal.client.adapter.support.EtlResult;
|
|
||||||
import com.alibaba.otter.canal.client.adapter.support.JdbcTypeUtil;
|
|
||||||
import com.alibaba.otter.canal.client.adapter.support.Util;
|
|
||||||
import com.google.common.base.Joiner;
|
import com.google.common.base.Joiner;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -34,17 +26,21 @@ import com.google.common.base.Joiner;
|
|||||||
* @author rewerma @ 2018-10-20
|
* @author rewerma @ 2018-10-20
|
||||||
* @version 1.0.0
|
* @version 1.0.0
|
||||||
*/
|
*/
|
||||||
public class HbaseEtlService {
|
public class HbaseEtlService extends AbstractEtlService {
|
||||||
|
|
||||||
private static Logger logger = LoggerFactory.getLogger(HbaseEtlService.class);
|
private HbaseTemplate hbaseTemplate;
|
||||||
|
private MappingConfig config;
|
||||||
|
|
||||||
|
public HbaseEtlService(HbaseTemplate hbaseTemplate, MappingConfig config){
|
||||||
|
super("HBase", config);
|
||||||
|
this.hbaseTemplate = hbaseTemplate;
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 建表
|
* 建表
|
||||||
*
|
|
||||||
* @param hbaseTemplate
|
|
||||||
* @param config
|
|
||||||
*/
|
*/
|
||||||
public static void createTable(HbaseTemplate hbaseTemplate, MappingConfig config) {
|
private void createTable() {
|
||||||
try {
|
try {
|
||||||
// 判断hbase表是否存在,不存在则建表
|
// 判断hbase表是否存在,不存在则建表
|
||||||
MappingConfig.HbaseMapping hbaseMapping = config.getHbaseMapping();
|
MappingConfig.HbaseMapping hbaseMapping = config.getHbaseMapping();
|
||||||
@@ -60,29 +56,14 @@ public class HbaseEtlService {
|
|||||||
/**
|
/**
|
||||||
* 导入数据
|
* 导入数据
|
||||||
*
|
*
|
||||||
* @param ds 数据源
|
|
||||||
* @param hbaseTemplate hbaseTemplate
|
|
||||||
* @param config 配置
|
|
||||||
* @param params 筛选条件
|
* @param params 筛选条件
|
||||||
* @return 导入结果
|
* @return 导入结果
|
||||||
*/
|
*/
|
||||||
public static EtlResult importData(DataSource ds, HbaseTemplate hbaseTemplate, MappingConfig config,
|
public EtlResult importData(List<String> params) {
|
||||||
List<String> params) {
|
|
||||||
EtlResult etlResult = new EtlResult();
|
EtlResult etlResult = new EtlResult();
|
||||||
AtomicLong successCount = new AtomicLong();
|
|
||||||
List<String> errMsg = new ArrayList<>();
|
List<String> errMsg = new ArrayList<>();
|
||||||
String hbaseTable = "";
|
|
||||||
try {
|
try {
|
||||||
if (config == null) {
|
|
||||||
logger.error("Config is null!");
|
|
||||||
etlResult.setSucceeded(false);
|
|
||||||
etlResult.setErrorMessage("Config is null!");
|
|
||||||
return etlResult;
|
|
||||||
}
|
|
||||||
MappingConfig.HbaseMapping hbaseMapping = config.getHbaseMapping();
|
MappingConfig.HbaseMapping hbaseMapping = config.getHbaseMapping();
|
||||||
hbaseTable = hbaseMapping.getHbaseTable();
|
|
||||||
|
|
||||||
long start = System.currentTimeMillis();
|
|
||||||
|
|
||||||
if (params != null && params.size() == 1 && "rebuild".equalsIgnoreCase(params.get(0))) {
|
if (params != null && params.size() == 1 && "rebuild".equalsIgnoreCase(params.get(0))) {
|
||||||
logger.info(hbaseMapping.getHbaseTable() + " rebuild is starting!");
|
logger.info(hbaseMapping.getHbaseTable() + " rebuild is starting!");
|
||||||
@@ -95,119 +76,28 @@ public class HbaseEtlService {
|
|||||||
} else {
|
} else {
|
||||||
logger.info(hbaseMapping.getHbaseTable() + " etl is starting!");
|
logger.info(hbaseMapping.getHbaseTable() + " etl is starting!");
|
||||||
}
|
}
|
||||||
createTable(hbaseTemplate, config);
|
createTable();
|
||||||
|
|
||||||
// 拼接sql
|
// 拼接sql
|
||||||
String sql = "SELECT * FROM " + config.getHbaseMapping().getDatabase() + "." + hbaseMapping.getTable();
|
String sql = "SELECT * FROM " + config.getHbaseMapping().getDatabase() + "." + hbaseMapping.getTable();
|
||||||
|
|
||||||
// 拼接条件
|
return super.importData(sql, params);
|
||||||
if (params != null && params.size() == 1 && hbaseMapping.getEtlCondition() == null) {
|
|
||||||
AtomicBoolean stExists = new AtomicBoolean(false);
|
|
||||||
// 验证是否有SYS_TIME字段
|
|
||||||
Util.sqlRS(ds, sql, rs -> {
|
|
||||||
try {
|
|
||||||
ResultSetMetaData rsmd = rs.getMetaData();
|
|
||||||
int cnt = rsmd.getColumnCount();
|
|
||||||
for (int i = 1; i <= cnt; i++) {
|
|
||||||
String columnName = rsmd.getColumnName(i);
|
|
||||||
if ("SYS_TIME".equalsIgnoreCase(columnName)) {
|
|
||||||
stExists.set(true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
if (stExists.get()) {
|
|
||||||
sql += " WHERE SYS_TIME >= '" + params.get(0) + "' ";
|
|
||||||
}
|
|
||||||
} else if (hbaseMapping.getEtlCondition() != null && params != null) {
|
|
||||||
String etlCondition = hbaseMapping.getEtlCondition();
|
|
||||||
int size = params.size();
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
etlCondition = etlCondition.replace("{" + i + "}", params.get(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
sql += " " + etlCondition;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取总数
|
|
||||||
String countSql = "SELECT COUNT(1) FROM ( " + sql + ") _CNT ";
|
|
||||||
long cnt = (Long) Util.sqlRS(ds, countSql, rs -> {
|
|
||||||
Long count = null;
|
|
||||||
try {
|
|
||||||
if (rs.next()) {
|
|
||||||
count = ((Number) rs.getObject(1)).longValue();
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error(e.getMessage(), e);
|
logger.error(e.getMessage(), e);
|
||||||
|
errMsg.add("HBase etl error ==>" + e.getMessage());
|
||||||
}
|
}
|
||||||
return count == null ? 0 : count;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 当大于1万条记录时开启多线程
|
|
||||||
if (cnt >= 10000) {
|
|
||||||
int threadCount = 3;
|
|
||||||
long perThreadCnt = cnt / threadCount;
|
|
||||||
ExecutorService executor = Util.newFixedThreadPool(threadCount, 5000L);
|
|
||||||
for (int i = 0; i < threadCount; i++) {
|
|
||||||
long offset = i * perThreadCnt;
|
|
||||||
Long size = null;
|
|
||||||
if (i != threadCount - 1) {
|
|
||||||
size = perThreadCnt;
|
|
||||||
}
|
|
||||||
String sqlFinal;
|
|
||||||
if (size != null) {
|
|
||||||
sqlFinal = sql + " LIMIT " + offset + "," + size;
|
|
||||||
} else {
|
|
||||||
sqlFinal = sql + " LIMIT " + offset + "," + cnt;
|
|
||||||
}
|
|
||||||
executor.submit(
|
|
||||||
() -> executeSqlImport(ds, sqlFinal, hbaseMapping, hbaseTemplate, successCount, errMsg));
|
|
||||||
}
|
|
||||||
|
|
||||||
executor.shutdown();
|
|
||||||
while (!executor.awaitTermination(3, TimeUnit.SECONDS)) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
executeSqlImport(ds, sql, hbaseMapping, hbaseTemplate, successCount, errMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info(hbaseMapping.getHbaseTable() + " etl completed in: "
|
|
||||||
+ (System.currentTimeMillis() - start) / 1000 + "s!");
|
|
||||||
|
|
||||||
etlResult.setResultMessage("导入HBase表 " + hbaseMapping.getHbaseTable() + " 数据:" + successCount.get() + " 条");
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e.getMessage(), e);
|
|
||||||
errMsg.add(hbaseTable + " etl failed! ==>" + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errMsg.isEmpty()) {
|
|
||||||
etlResult.setSucceeded(true);
|
|
||||||
} else {
|
|
||||||
etlResult.setErrorMessage(Joiner.on("\n").join(errMsg));
|
etlResult.setErrorMessage(Joiner.on("\n").join(errMsg));
|
||||||
}
|
|
||||||
return etlResult;
|
return etlResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行导入
|
* 执行导入
|
||||||
*
|
|
||||||
* @param ds
|
|
||||||
* @param sql
|
|
||||||
* @param hbaseMapping
|
|
||||||
* @param hbaseTemplate
|
|
||||||
* @param successCount
|
|
||||||
* @param errMsg
|
|
||||||
* @return
|
|
||||||
*/
|
*/
|
||||||
private static boolean executeSqlImport(DataSource ds, String sql, MappingConfig.HbaseMapping hbaseMapping,
|
protected boolean executeSqlImport(DataSource ds, String sql, List<Object> values,
|
||||||
HbaseTemplate hbaseTemplate, AtomicLong successCount, List<String> errMsg) {
|
AdapterConfig.AdapterMapping mapping, AtomicLong impCount, List<String> errMsg) {
|
||||||
|
MappingConfig.HbaseMapping hbaseMapping = (MappingConfig.HbaseMapping) mapping;
|
||||||
try {
|
try {
|
||||||
Util.sqlRS(ds, sql, rs -> {
|
Util.sqlRS(ds, sql, values, rs -> {
|
||||||
int i = 1;
|
int i = 1;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -353,9 +243,9 @@ public class HbaseEtlService {
|
|||||||
complete = true;
|
complete = true;
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
successCount.incrementAndGet();
|
impCount.incrementAndGet();
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("successful import count:" + successCount.get());
|
logger.debug("successful import count:" + impCount.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ hbaseMapping:
|
|||||||
hbaseTable: MYTEST.PERSON2 # HBase表名
|
hbaseTable: MYTEST.PERSON2 # HBase表名
|
||||||
family: CF # 默认统一Family名称
|
family: CF # 默认统一Family名称
|
||||||
uppercaseQualifier: true # 字段名转大写, 默认为true
|
uppercaseQualifier: true # 字段名转大写, 默认为true
|
||||||
|
etlCondition: "where c_time>={}"
|
||||||
commitBatch: 3000 # 批量提交的大小
|
commitBatch: 3000 # 批量提交的大小
|
||||||
#rowKey: id,type # 复合字段rowKey不能和columns中的rowKey重复
|
#rowKey: id,type # 复合字段rowKey不能和columns中的rowKey重复
|
||||||
columns:
|
columns:
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.alibaba.otter.canal.client.adapter.hbase.test;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.client.adapter.hbase.support.HbaseTemplate;
|
||||||
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
import org.apache.hadoop.hbase.HBaseConfiguration;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class HBaseConnectionTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test01() {
|
||||||
|
Configuration hbaseConfig = HBaseConfiguration.create();
|
||||||
|
hbaseConfig.set("hbase.zookeeper.quorum", "127.0.0.1");
|
||||||
|
hbaseConfig.set("hbase.zookeeper.property.clientPort", "2181");
|
||||||
|
hbaseConfig.set("zookeeper.znode.parent", "/hbase");
|
||||||
|
HbaseTemplate hbaseTemplate = new HbaseTemplate(hbaseConfig);
|
||||||
|
System.out.println(hbaseTemplate.tableExists("ttt"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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</version>
|
<version>1.1.4-SNAPSHOT</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</version>
|
<version>1.1.4-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.yaml</groupId>
|
<groupId>org.yaml</groupId>
|
||||||
@@ -62,7 +62,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.rocketmq</groupId>
|
<groupId>org.apache.rocketmq</groupId>
|
||||||
<artifactId>rocketmq-client</artifactId>
|
<artifactId>rocketmq-client</artifactId>
|
||||||
<version>4.3.0</version>
|
<version>4.5.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- 单独引入kafka依赖 -->
|
<!-- 单独引入kafka依赖 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
@@ -130,7 +130,11 @@ public class CanalAdapterLoader {
|
|||||||
canalOuterAdapterGroups,
|
canalOuterAdapterGroups,
|
||||||
canalClientConfig.getAccessKey(),
|
canalClientConfig.getAccessKey(),
|
||||||
canalClientConfig.getSecretKey(),
|
canalClientConfig.getSecretKey(),
|
||||||
canalClientConfig.getFlatMessage());
|
canalClientConfig.getFlatMessage(),
|
||||||
|
canalClientConfig.isEnableMessageTrace(),
|
||||||
|
canalClientConfig.getCustomizedTraceTopic(),
|
||||||
|
canalClientConfig.getAccessChannel(),
|
||||||
|
canalClientConfig.getNamespace());
|
||||||
canalMQWorker.put(canalAdapter.getInstance() + "-rocketmq-" + group.getGroupId(), rocketMQWorker);
|
canalMQWorker.put(canalAdapter.getInstance() + "-rocketmq-" + group.getGroupId(), rocketMQWorker);
|
||||||
rocketMQWorker.start();
|
rocketMQWorker.start();
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,30 @@ public class CanalAdapterRocketMQWorker extends AbstractCanalAdapterWorker {
|
|||||||
logger.info("RocketMQ consumer config topic:{}, nameServer:{}, groupId:{}", topic, nameServers, groupId);
|
logger.info("RocketMQ consumer config topic:{}, nameServer:{}, groupId:{}", topic, nameServers, groupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CanalAdapterRocketMQWorker(CanalClientConfig canalClientConfig, String nameServers, String topic,
|
||||||
|
String groupId, List<List<OuterAdapter>> canalOuterAdapters, String accessKey,
|
||||||
|
String secretKey, boolean flatMessage, boolean enableMessageTrace,
|
||||||
|
String customizedTraceTopic, String accessChannel, String namespace) {
|
||||||
|
super(canalOuterAdapters);
|
||||||
|
this.canalClientConfig = canalClientConfig;
|
||||||
|
this.topic = topic;
|
||||||
|
this.flatMessage = flatMessage;
|
||||||
|
super.canalDestination = topic;
|
||||||
|
super.groupId = groupId;
|
||||||
|
this.connector = new RocketMQCanalConnector(nameServers,
|
||||||
|
topic,
|
||||||
|
groupId,
|
||||||
|
accessKey,
|
||||||
|
secretKey,
|
||||||
|
canalClientConfig.getBatchSize(),
|
||||||
|
flatMessage,
|
||||||
|
enableMessageTrace,
|
||||||
|
customizedTraceTopic,
|
||||||
|
accessChannel,
|
||||||
|
namespace);
|
||||||
|
logger.info("RocketMQ consumer config topic:{}, nameServer:{}, groupId:{}", topic, nameServers, groupId);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void process() {
|
protected void process() {
|
||||||
while (!running) {
|
while (!running) {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ canal.conf:
|
|||||||
mode: tcp # kafka rocketMQ
|
mode: tcp # kafka rocketMQ
|
||||||
canalServerHost: 127.0.0.1:11111
|
canalServerHost: 127.0.0.1:11111
|
||||||
# zookeeperHosts: slave1:2181
|
# zookeeperHosts: slave1:2181
|
||||||
# mqServers: 127.0.0.1:9092 #or rocketmq
|
# mqServers: 127.0.0.1:9092 #or rocketmq nameservers
|
||||||
# flatMessage: true
|
# flatMessage: true
|
||||||
batchSize: 500
|
batchSize: 500
|
||||||
syncBatchSize: 1000
|
syncBatchSize: 1000
|
||||||
@@ -18,6 +18,11 @@ canal.conf:
|
|||||||
timeout:
|
timeout:
|
||||||
accessKey:
|
accessKey:
|
||||||
secretKey:
|
secretKey:
|
||||||
|
# enableMessageTrace:
|
||||||
|
# accessChannel:
|
||||||
|
# customizedTraceTopic:
|
||||||
|
# namespace:
|
||||||
|
|
||||||
# srcDataSources:
|
# srcDataSources:
|
||||||
# defaultDS:
|
# defaultDS:
|
||||||
# url: jdbc:mysql://127.0.0.1:3306/mytest?useUnicode=true
|
# url: jdbc:mysql://127.0.0.1:3306/mytest?useUnicode=true
|
||||||
|
|||||||
@@ -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</version>
|
<version>1.1.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>com.alibaba.otter</groupId>
|
<groupId>com.alibaba.otter</groupId>
|
||||||
|
|||||||
@@ -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</version>
|
<version>1.1.4-SNAPSHOT</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>
|
||||||
|
|
||||||
|
|||||||
@@ -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</version>
|
<version>1.1.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>com.alibaba.otter</groupId>
|
<groupId>com.alibaba.otter</groupId>
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ import java.util.Map;
|
|||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
|
||||||
|
|
||||||
import org.apache.commons.lang.BooleanUtils;
|
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;
|
||||||
@@ -181,27 +179,16 @@ public class RdbAdapter implements OuterAdapter {
|
|||||||
public EtlResult etl(String task, List<String> params) {
|
public EtlResult etl(String task, List<String> params) {
|
||||||
EtlResult etlResult = new EtlResult();
|
EtlResult etlResult = new EtlResult();
|
||||||
MappingConfig config = rdbMapping.get(task);
|
MappingConfig config = rdbMapping.get(task);
|
||||||
|
RdbEtlService rdbEtlService = new RdbEtlService(dataSource, config);
|
||||||
if (config != null) {
|
if (config != null) {
|
||||||
DataSource srcDataSource = DatasourceConfig.DATA_SOURCES.get(config.getDataSourceKey());
|
return rdbEtlService.importData(params);
|
||||||
if (srcDataSource != null) {
|
|
||||||
return RdbEtlService.importData(srcDataSource, dataSource, config, params);
|
|
||||||
} else {
|
|
||||||
etlResult.setSucceeded(false);
|
|
||||||
etlResult.setErrorMessage("DataSource not found");
|
|
||||||
return etlResult;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
StringBuilder resultMsg = new StringBuilder();
|
StringBuilder resultMsg = new StringBuilder();
|
||||||
boolean resSucc = true;
|
boolean resSucc = true;
|
||||||
// ds不为空说明传入的是destination
|
|
||||||
for (MappingConfig configTmp : rdbMapping.values()) {
|
for (MappingConfig configTmp : rdbMapping.values()) {
|
||||||
// 取所有的destination为task的配置
|
// 取所有的destination为task的配置
|
||||||
if (configTmp.getDestination().equals(task)) {
|
if (configTmp.getDestination().equals(task)) {
|
||||||
DataSource srcDataSource = DatasourceConfig.DATA_SOURCES.get(configTmp.getDataSourceKey());
|
EtlResult etlRes = rdbEtlService.importData(params);
|
||||||
if (srcDataSource == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
EtlResult etlRes = RdbEtlService.importData(srcDataSource, dataSource, configTmp, params);
|
|
||||||
if (!etlRes.getSucceeded()) {
|
if (!etlRes.getSucceeded()) {
|
||||||
resSucc = false;
|
resSucc = false;
|
||||||
resultMsg.append(etlRes.getErrorMessage()).append("\n");
|
resultMsg.append(etlRes.getErrorMessage()).append("\n");
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.alibaba.otter.canal.client.adapter.rdb.config;
|
|||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.client.adapter.support.AdapterConfig;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -11,7 +12,7 @@ import org.apache.commons.lang.StringUtils;
|
|||||||
* @author rewerma 2018-11-07 下午02:41:34
|
* @author rewerma 2018-11-07 下午02:41:34
|
||||||
* @version 1.0.0
|
* @version 1.0.0
|
||||||
*/
|
*/
|
||||||
public class MappingConfig {
|
public class MappingConfig implements AdapterConfig {
|
||||||
|
|
||||||
private String dataSourceKey; // 数据源key
|
private String dataSourceKey; // 数据源key
|
||||||
|
|
||||||
@@ -73,6 +74,10 @@ public class MappingConfig {
|
|||||||
this.destination = destination;
|
this.destination = destination;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AdapterMapping getMapping() {
|
||||||
|
return dbMapping;
|
||||||
|
}
|
||||||
|
|
||||||
public void validate() {
|
public void validate() {
|
||||||
if (dbMapping.database == null || dbMapping.database.isEmpty()) {
|
if (dbMapping.database == null || dbMapping.database.isEmpty()) {
|
||||||
throw new NullPointerException("dbMapping.database");
|
throw new NullPointerException("dbMapping.database");
|
||||||
@@ -85,7 +90,7 @@ public class MappingConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class DbMapping {
|
public static class DbMapping implements AdapterMapping {
|
||||||
|
|
||||||
private boolean mirrorDb = false; // 是否镜像库
|
private boolean mirrorDb = false; // 是否镜像库
|
||||||
private String database; // 数据库名或schema名
|
private String database; // 数据库名或schema名
|
||||||
|
|||||||
@@ -1,30 +1,21 @@
|
|||||||
package com.alibaba.otter.canal.client.adapter.rdb.service;
|
package com.alibaba.otter.canal.client.adapter.rdb.service;
|
||||||
|
|
||||||
import java.sql.Connection;
|
import java.sql.*;
|
||||||
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;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import com.alibaba.otter.canal.client.adapter.rdb.config.MappingConfig;
|
import com.alibaba.otter.canal.client.adapter.rdb.config.MappingConfig;
|
||||||
import com.alibaba.otter.canal.client.adapter.rdb.config.MappingConfig.DbMapping;
|
import com.alibaba.otter.canal.client.adapter.rdb.config.MappingConfig.DbMapping;
|
||||||
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.AbstractEtlService;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.support.AdapterConfig;
|
||||||
import com.alibaba.otter.canal.client.adapter.support.EtlResult;
|
import com.alibaba.otter.canal.client.adapter.support.EtlResult;
|
||||||
import com.alibaba.otter.canal.client.adapter.support.Util;
|
import com.alibaba.otter.canal.client.adapter.support.Util;
|
||||||
import com.google.common.base.Joiner;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RDB ETL 操作业务类
|
* RDB ETL 操作业务类
|
||||||
@@ -32,139 +23,33 @@ import com.google.common.base.Joiner;
|
|||||||
* @author rewerma @ 2018-11-7
|
* @author rewerma @ 2018-11-7
|
||||||
* @version 1.0.0
|
* @version 1.0.0
|
||||||
*/
|
*/
|
||||||
public class RdbEtlService {
|
public class RdbEtlService extends AbstractEtlService {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(RdbEtlService.class);
|
private DataSource targetDS;
|
||||||
|
private MappingConfig config;
|
||||||
|
|
||||||
|
public RdbEtlService(DataSource targetDS, MappingConfig config){
|
||||||
|
super("RDB", config);
|
||||||
|
this.targetDS = targetDS;
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 导入数据
|
* 导入数据
|
||||||
*/
|
*/
|
||||||
public static EtlResult importData(DataSource srcDS, DataSource targetDS, MappingConfig config,
|
public EtlResult importData(List<String> params) {
|
||||||
List<String> params) {
|
|
||||||
EtlResult etlResult = new EtlResult();
|
|
||||||
AtomicLong successCount = new AtomicLong();
|
|
||||||
List<String> errMsg = new ArrayList<>();
|
|
||||||
String hbaseTable = "";
|
|
||||||
try {
|
|
||||||
if (config == null) {
|
|
||||||
logger.error("Config is null!");
|
|
||||||
etlResult.setSucceeded(false);
|
|
||||||
etlResult.setErrorMessage("Config is null!");
|
|
||||||
return etlResult;
|
|
||||||
}
|
|
||||||
DbMapping dbMapping = config.getDbMapping();
|
DbMapping dbMapping = config.getDbMapping();
|
||||||
|
String sql = "SELECT * FROM " + dbMapping.getDatabase() + "." + dbMapping.getTable();
|
||||||
long start = System.currentTimeMillis();
|
return importData(sql, params);
|
||||||
|
|
||||||
// 拼接sql
|
|
||||||
StringBuilder sql = new StringBuilder(
|
|
||||||
"SELECT * FROM " + dbMapping.getDatabase() + "." + dbMapping.getTable());
|
|
||||||
|
|
||||||
// 拼接条件
|
|
||||||
appendCondition(params, dbMapping, srcDS, sql);
|
|
||||||
|
|
||||||
// 获取总数
|
|
||||||
String countSql = "SELECT COUNT(1) FROM ( " + sql + ") _CNT ";
|
|
||||||
long cnt = (Long) Util.sqlRS(srcDS, countSql, rs -> {
|
|
||||||
Long count = null;
|
|
||||||
try {
|
|
||||||
if (rs.next()) {
|
|
||||||
count = ((Number) rs.getObject(1)).longValue();
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
return count == null ? 0 : count;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 当大于1万条记录时开启多线程
|
|
||||||
if (cnt >= 10000) {
|
|
||||||
int threadCount = 3;
|
|
||||||
long perThreadCnt = cnt / threadCount;
|
|
||||||
ExecutorService executor = Util.newFixedThreadPool(threadCount, 5000L);
|
|
||||||
for (int i = 0; i < threadCount; i++) {
|
|
||||||
long offset = i * perThreadCnt;
|
|
||||||
Long size = null;
|
|
||||||
if (i != threadCount - 1) {
|
|
||||||
size = perThreadCnt;
|
|
||||||
}
|
|
||||||
String sqlFinal;
|
|
||||||
if (size != null) {
|
|
||||||
sqlFinal = sql + " LIMIT " + offset + "," + size;
|
|
||||||
} else {
|
|
||||||
sqlFinal = sql + " LIMIT " + offset + "," + cnt;
|
|
||||||
}
|
|
||||||
executor
|
|
||||||
.execute(() -> executeSqlImport(srcDS, targetDS, sqlFinal, dbMapping, successCount, errMsg));
|
|
||||||
}
|
|
||||||
|
|
||||||
executor.shutdown();
|
|
||||||
while (!executor.awaitTermination(3, TimeUnit.SECONDS)) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
executeSqlImport(srcDS, targetDS, sql.toString(), dbMapping, successCount, errMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info(
|
|
||||||
dbMapping.getTable() + " etl completed in: " + (System.currentTimeMillis() - start) / 1000 + "s!");
|
|
||||||
|
|
||||||
etlResult
|
|
||||||
.setResultMessage("导入目标表 " + SyncUtil.getDbTableName(dbMapping) + " 数据:" + successCount.get() + " 条");
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e.getMessage(), e);
|
|
||||||
errMsg.add(hbaseTable + " etl failed! ==>" + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errMsg.isEmpty()) {
|
|
||||||
etlResult.setSucceeded(true);
|
|
||||||
} else {
|
|
||||||
etlResult.setErrorMessage(Joiner.on("\n").join(errMsg));
|
|
||||||
}
|
|
||||||
return etlResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void appendCondition(List<String> params, DbMapping dbMapping, DataSource ds,
|
|
||||||
StringBuilder sql) throws SQLException {
|
|
||||||
if (params != null && params.size() == 1 && dbMapping.getEtlCondition() == null) {
|
|
||||||
AtomicBoolean stExists = new AtomicBoolean(false);
|
|
||||||
// 验证是否有SYS_TIME字段
|
|
||||||
Util.sqlRS(ds, sql.toString(), rs -> {
|
|
||||||
try {
|
|
||||||
ResultSetMetaData rsmd = rs.getMetaData();
|
|
||||||
int cnt = rsmd.getColumnCount();
|
|
||||||
for (int i = 1; i <= cnt; i++) {
|
|
||||||
String columnName = rsmd.getColumnName(i);
|
|
||||||
if ("SYS_TIME".equalsIgnoreCase(columnName)) {
|
|
||||||
stExists.set(true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
if (stExists.get()) {
|
|
||||||
sql.append(" WHERE SYS_TIME >= '").append(params.get(0)).append("' ");
|
|
||||||
}
|
|
||||||
} else if (dbMapping.getEtlCondition() != null && params != null) {
|
|
||||||
String etlCondition = dbMapping.getEtlCondition();
|
|
||||||
int size = params.size();
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
etlCondition = etlCondition.replace("{" + i + "}", params.get(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
sql.append(" ").append(etlCondition);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行导入
|
* 执行导入
|
||||||
*/
|
*/
|
||||||
private static boolean executeSqlImport(DataSource srcDS, DataSource targetDS, String sql, DbMapping dbMapping,
|
protected boolean executeSqlImport(DataSource srcDS, String sql, List<Object> values,
|
||||||
AtomicLong successCount, List<String> errMsg) {
|
AdapterConfig.AdapterMapping mapping, AtomicLong impCount, List<String> errMsg) {
|
||||||
try {
|
try {
|
||||||
|
DbMapping dbMapping = (DbMapping) mapping;
|
||||||
Map<String, String> columnsMap = new LinkedHashMap<>();
|
Map<String, String> columnsMap = new LinkedHashMap<>();
|
||||||
Map<String, Integer> columnType = new LinkedHashMap<>();
|
Map<String, Integer> columnType = new LinkedHashMap<>();
|
||||||
|
|
||||||
@@ -187,18 +72,12 @@ public class RdbEtlService {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Util.sqlRS(srcDS, sql, rs -> {
|
Util.sqlRS(srcDS, sql, values, rs -> {
|
||||||
int idx = 1;
|
int idx = 1;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
boolean completed = false;
|
boolean completed = false;
|
||||||
|
|
||||||
// if (dbMapping.isMapAll()) {
|
|
||||||
// columnsMap = dbMapping.getAllColumns();
|
|
||||||
// } else {
|
|
||||||
// columnsMap = dbMapping.getTargetColumns();
|
|
||||||
// }
|
|
||||||
|
|
||||||
StringBuilder insertSql = new StringBuilder();
|
StringBuilder insertSql = new StringBuilder();
|
||||||
insertSql.append("INSERT INTO ").append(SyncUtil.getDbTableName(dbMapping)).append(" (");
|
insertSql.append("INSERT INTO ").append(SyncUtil.getDbTableName(dbMapping)).append(" (");
|
||||||
columnsMap
|
columnsMap
|
||||||
@@ -222,13 +101,13 @@ public class RdbEtlService {
|
|||||||
pstmt.clearParameters();
|
pstmt.clearParameters();
|
||||||
|
|
||||||
// 删除数据
|
// 删除数据
|
||||||
Map<String, Object> values = new LinkedHashMap<>();
|
Map<String, Object> pkVal = new LinkedHashMap<>();
|
||||||
StringBuilder deleteSql = new StringBuilder(
|
StringBuilder deleteSql = new StringBuilder(
|
||||||
"DELETE FROM " + SyncUtil.getDbTableName(dbMapping) + " WHERE ");
|
"DELETE FROM " + SyncUtil.getDbTableName(dbMapping) + " WHERE ");
|
||||||
appendCondition(dbMapping, deleteSql, values, rs);
|
appendCondition(dbMapping, deleteSql, pkVal, rs);
|
||||||
try (PreparedStatement pstmt2 = connTarget.prepareStatement(deleteSql.toString())) {
|
try (PreparedStatement pstmt2 = connTarget.prepareStatement(deleteSql.toString())) {
|
||||||
int k = 1;
|
int k = 1;
|
||||||
for (Object val : values.values()) {
|
for (Object val : pkVal.values()) {
|
||||||
pstmt2.setObject(k++, val);
|
pstmt2.setObject(k++, val);
|
||||||
}
|
}
|
||||||
pstmt2.execute();
|
pstmt2.execute();
|
||||||
@@ -264,9 +143,9 @@ public class RdbEtlService {
|
|||||||
completed = true;
|
completed = true;
|
||||||
}
|
}
|
||||||
idx++;
|
idx++;
|
||||||
successCount.incrementAndGet();
|
impCount.incrementAndGet();
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("successful import count:" + successCount.get());
|
logger.debug("successful import count:" + impCount.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!completed) {
|
if (!completed) {
|
||||||
|
|||||||
@@ -272,7 +272,7 @@ public class RdbSyncService {
|
|||||||
batchExecutor.execute(insertSql.toString(), values);
|
batchExecutor.execute(insertSql.toString(), values);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
if (skipDupException
|
if (skipDupException
|
||||||
&& (e.getMessage().contains("Duplicate entry") || e.getMessage().startsWith("ORA-00001: 违反唯一约束条件"))) {
|
&& (e.getMessage().contains("Duplicate entry") || e.getMessage().startsWith("ORA-00001:"))) {
|
||||||
// ignore
|
// ignore
|
||||||
// TODO 增加更多关系数据库的主键冲突的错误码
|
// TODO 增加更多关系数据库的主键冲突的错误码
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ dbMapping:
|
|||||||
role_id:
|
role_id:
|
||||||
c_time:
|
c_time:
|
||||||
test1:
|
test1:
|
||||||
|
etlCondition: "where c_time>={}"
|
||||||
|
commitBatch: 3000 # 批量提交的大小
|
||||||
|
|
||||||
|
|
||||||
## Mirror schema synchronize config
|
## Mirror schema synchronize config
|
||||||
|
|||||||
@@ -7,10 +7,11 @@ import java.sql.Connection;
|
|||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.alibaba.druid.pool.DruidDataSource;
|
import com.alibaba.druid.pool.DruidDataSource;
|
||||||
|
@Ignore
|
||||||
public class DBTest {
|
public class DBTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -8,11 +8,12 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.client.adapter.rdb.RdbAdapter;
|
import com.alibaba.otter.canal.client.adapter.rdb.RdbAdapter;
|
||||||
import com.alibaba.otter.canal.client.adapter.support.Dml;
|
import com.alibaba.otter.canal.client.adapter.support.Dml;
|
||||||
|
@Ignore
|
||||||
public class OracleSyncTest {
|
public class OracleSyncTest {
|
||||||
|
|
||||||
private RdbAdapter rdbAdapter;
|
private RdbAdapter rdbAdapter;
|
||||||
|
|||||||
@@ -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</version>
|
<version>1.1.4-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<groupId>com.alibaba.otter</groupId>
|
<groupId>com.alibaba.otter</groupId>
|
||||||
@@ -106,8 +106,8 @@
|
|||||||
<artifactId>rocketmq-client</artifactId>
|
<artifactId>rocketmq-client</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.aliyun.openservices</groupId>
|
<groupId>org.apache.rocketmq</groupId>
|
||||||
<artifactId>aliware-apache-rocketmq-cloud</artifactId>
|
<artifactId>rocketmq-acl</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- 客户端要使用请单独引入kafka-clients依赖 -->
|
<!-- 客户端要使用请单独引入kafka-clients依赖 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ import java.util.concurrent.LinkedBlockingQueue;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.apache.rocketmq.acl.common.AclClientRPCHook;
|
||||||
|
import org.apache.rocketmq.acl.common.SessionCredentials;
|
||||||
|
import org.apache.rocketmq.client.AccessChannel;
|
||||||
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
|
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
|
||||||
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyContext;
|
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyContext;
|
||||||
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus;
|
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus;
|
||||||
@@ -24,8 +27,6 @@ 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.aliyun.openservices.apache.api.impl.authority.SessionCredentials;
|
|
||||||
import com.aliyun.openservices.apache.api.impl.rocketmq.ClientRPCHook;
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,6 +42,7 @@ import com.google.common.collect.Lists;
|
|||||||
public class RocketMQCanalConnector implements CanalMQConnector {
|
public class RocketMQCanalConnector implements CanalMQConnector {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(RocketMQCanalConnector.class);
|
private static final Logger logger = LoggerFactory.getLogger(RocketMQCanalConnector.class);
|
||||||
|
private static final String CLOUD_ACCESS_CHANNEL = "cloud";
|
||||||
|
|
||||||
private String nameServer;
|
private String nameServer;
|
||||||
private String topic;
|
private String topic;
|
||||||
@@ -54,6 +56,26 @@ public class RocketMQCanalConnector implements CanalMQConnector {
|
|||||||
private volatile ConsumerBatchMessage lastGetBatchMessage = null;
|
private volatile ConsumerBatchMessage lastGetBatchMessage = null;
|
||||||
private String accessKey;
|
private String accessKey;
|
||||||
private String secretKey;
|
private String secretKey;
|
||||||
|
private String customizedTraceTopic;
|
||||||
|
private boolean enableMessageTrace = false;
|
||||||
|
private String accessChannel;
|
||||||
|
private String namespace;
|
||||||
|
|
||||||
|
public RocketMQCanalConnector(String nameServer, String topic, String groupName, String accessKey,
|
||||||
|
String secretKey, Integer batchSize, boolean flatMessage, boolean enableMessageTrace,
|
||||||
|
String customizedTraceTopic, String accessChannel, String namespace) {
|
||||||
|
this(nameServer, topic, groupName, accessKey, secretKey, batchSize, flatMessage, enableMessageTrace, customizedTraceTopic, accessChannel);
|
||||||
|
this.namespace = namespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RocketMQCanalConnector(String nameServer, String topic, String groupName, String accessKey,
|
||||||
|
String secretKey, Integer batchSize, boolean flatMessage, boolean enableMessageTrace,
|
||||||
|
String customizedTraceTopic, String accessChannel) {
|
||||||
|
this(nameServer, topic, groupName, accessKey, secretKey, batchSize, flatMessage);
|
||||||
|
this.enableMessageTrace = enableMessageTrace;
|
||||||
|
this.customizedTraceTopic = customizedTraceTopic;
|
||||||
|
this.accessChannel = accessChannel;
|
||||||
|
}
|
||||||
|
|
||||||
public RocketMQCanalConnector(String nameServer, String topic, String groupName, Integer batchSize,
|
public RocketMQCanalConnector(String nameServer, String topic, String groupName, Integer batchSize,
|
||||||
boolean flatMessage){
|
boolean flatMessage){
|
||||||
@@ -73,16 +95,24 @@ public class RocketMQCanalConnector implements CanalMQConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void connect() throws CanalClientException {
|
public void connect() throws CanalClientException {
|
||||||
|
|
||||||
RPCHook rpcHook = null;
|
RPCHook rpcHook = null;
|
||||||
if (null != accessKey && accessKey.length() > 0 && null != secretKey && secretKey.length() > 0) {
|
if (null != accessKey && accessKey.length() > 0 && null != secretKey && secretKey.length() > 0) {
|
||||||
SessionCredentials sessionCredentials = new SessionCredentials();
|
SessionCredentials sessionCredentials = new SessionCredentials();
|
||||||
sessionCredentials.setAccessKey(accessKey);
|
sessionCredentials.setAccessKey(accessKey);
|
||||||
sessionCredentials.setSecretKey(secretKey);
|
sessionCredentials.setSecretKey(secretKey);
|
||||||
rpcHook = new ClientRPCHook(sessionCredentials);
|
rpcHook = new AclClientRPCHook(sessionCredentials);
|
||||||
}
|
}
|
||||||
rocketMQConsumer = new DefaultMQPushConsumer(groupName, rpcHook, new AllocateMessageQueueAveragely());
|
|
||||||
|
rocketMQConsumer = new DefaultMQPushConsumer(groupName, rpcHook, new AllocateMessageQueueAveragely(), enableMessageTrace, customizedTraceTopic);
|
||||||
rocketMQConsumer.setVipChannelEnabled(false);
|
rocketMQConsumer.setVipChannelEnabled(false);
|
||||||
|
if (CLOUD_ACCESS_CHANNEL.equals(this.accessChannel)) {
|
||||||
|
rocketMQConsumer.setAccessChannel(AccessChannel.CLOUD);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!StringUtils.isEmpty(this.namespace)) {
|
||||||
|
rocketMQConsumer.setNamespace(this.namespace);
|
||||||
|
}
|
||||||
|
|
||||||
if (!StringUtils.isBlank(nameServer)) {
|
if (!StringUtils.isBlank(nameServer)) {
|
||||||
rocketMQConsumer.setNamesrvAddr(nameServer);
|
rocketMQConsumer.setNamesrvAddr(nameServer);
|
||||||
}
|
}
|
||||||
@@ -131,7 +161,9 @@ public class RocketMQCanalConnector implements CanalMQConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean process(List<MessageExt> messageExts) {
|
private boolean process(List<MessageExt> messageExts) {
|
||||||
logger.info("Get Message:{}", messageExts);
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Get Message: {}", messageExts);
|
||||||
|
}
|
||||||
List messageList = Lists.newArrayList();
|
List messageList = Lists.newArrayList();
|
||||||
for (MessageExt messageExt : messageExts) {
|
for (MessageExt messageExt : messageExts) {
|
||||||
byte[] data = messageExt.getBody();
|
byte[] data = messageExt.getBody();
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import org.apache.commons.lang.math.RandomUtils;
|
|||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.client.impl.running.ClientRunningData;
|
import com.alibaba.otter.canal.client.impl.running.ClientRunningData;
|
||||||
@@ -17,7 +18,7 @@ import com.alibaba.otter.canal.client.impl.running.ClientRunningMonitor;
|
|||||||
import com.alibaba.otter.canal.common.utils.AddressUtils;
|
import com.alibaba.otter.canal.common.utils.AddressUtils;
|
||||||
import com.alibaba.otter.canal.common.zookeeper.ZkClientx;
|
import com.alibaba.otter.canal.common.zookeeper.ZkClientx;
|
||||||
import com.alibaba.otter.canal.common.zookeeper.ZookeeperPathUtils;
|
import com.alibaba.otter.canal.common.zookeeper.ZookeeperPathUtils;
|
||||||
|
@Ignore
|
||||||
public class ClientRunningTest extends AbstractZkTest {
|
public class ClientRunningTest extends AbstractZkTest {
|
||||||
|
|
||||||
private ZkClientx zkclientx = new ZkClientx(cluster1 + ";" + cluster2);
|
private ZkClientx zkclientx = new ZkClientx(cluster1 + ";" + cluster2);
|
||||||
|
|||||||
@@ -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</version>
|
<version>1.1.4-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>canal.common</artifactId>
|
<artifactId>canal.common</artifactId>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import org.apache.commons.lang.math.RandomUtils;
|
|||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.common.zookeeper.ZkClientx;
|
import com.alibaba.otter.canal.common.zookeeper.ZkClientx;
|
||||||
@@ -15,7 +16,7 @@ import com.alibaba.otter.canal.common.zookeeper.ZookeeperPathUtils;
|
|||||||
import com.alibaba.otter.canal.common.zookeeper.running.ServerRunningData;
|
import com.alibaba.otter.canal.common.zookeeper.running.ServerRunningData;
|
||||||
import com.alibaba.otter.canal.common.zookeeper.running.ServerRunningListener;
|
import com.alibaba.otter.canal.common.zookeeper.running.ServerRunningListener;
|
||||||
import com.alibaba.otter.canal.common.zookeeper.running.ServerRunningMonitor;
|
import com.alibaba.otter.canal.common.zookeeper.running.ServerRunningMonitor;
|
||||||
|
@Ignore
|
||||||
public class ServerRunningTest extends AbstractZkTest {
|
public class ServerRunningTest extends AbstractZkTest {
|
||||||
|
|
||||||
private ZkClientx zkclientx = new ZkClientx(cluster1 + ";" + cluster2);
|
private ZkClientx zkclientx = new ZkClientx(cluster1 + ";" + cluster2);
|
||||||
|
|||||||
@@ -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</version>
|
<version>1.1.4-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<groupId>com.alibaba.otter</groupId>
|
<groupId>com.alibaba.otter</groupId>
|
||||||
|
|||||||
@@ -706,10 +706,8 @@ public class QueryLogEvent extends LogEvent {
|
|||||||
* That's why you must write status vars in growing
|
* That's why you must write status vars in growing
|
||||||
* order of code
|
* order of code
|
||||||
*/
|
*/
|
||||||
if (logger.isDebugEnabled()) {
|
logger.error("Query_log_event has unknown status vars (first has code: " + code
|
||||||
logger.debug("Query_log_event has unknown status vars (first has code: " + code
|
|
||||||
+ "), skipping the rest of them");
|
+ "), skipping the rest of them");
|
||||||
}
|
|
||||||
break; // Break loop
|
break; // Break loop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.taobao.tddl.dbsync.binlog.event;
|
|||||||
|
|
||||||
import java.util.BitSet;
|
import java.util.BitSet;
|
||||||
|
|
||||||
|
import com.taobao.tddl.dbsync.binlog.exception.TableIdNotFoundException;
|
||||||
import com.taobao.tddl.dbsync.binlog.LogBuffer;
|
import com.taobao.tddl.dbsync.binlog.LogBuffer;
|
||||||
import com.taobao.tddl.dbsync.binlog.LogContext;
|
import com.taobao.tddl.dbsync.binlog.LogContext;
|
||||||
import com.taobao.tddl.dbsync.binlog.LogEvent;
|
import com.taobao.tddl.dbsync.binlog.LogEvent;
|
||||||
@@ -179,6 +180,10 @@ public abstract class RowsLogEvent extends LogEvent {
|
|||||||
public final void fillTable(LogContext context) {
|
public final void fillTable(LogContext context) {
|
||||||
table = context.getTable(tableId);
|
table = context.getTable(tableId);
|
||||||
|
|
||||||
|
if (table == null) {
|
||||||
|
throw new TableIdNotFoundException("not found tableId:" + tableId);
|
||||||
|
}
|
||||||
|
|
||||||
// end of statement check:
|
// end of statement check:
|
||||||
if ((flags & RowsLogEvent.STMT_END_F) != 0) {
|
if ((flags & RowsLogEvent.STMT_END_F) != 0) {
|
||||||
// Now is safe to clear ignored map (clear_tables will also
|
// Now is safe to clear ignored map (clear_tables will also
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.alibaba.otter.canal.parse.exception;
|
package com.taobao.tddl.dbsync.binlog.exception;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.common.CanalException;
|
import com.alibaba.otter.canal.common.CanalException;
|
||||||
|
|
||||||
@@ -6,6 +6,7 @@ import java.sql.DriverManager;
|
|||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.taobao.tddl.dbsync.binlog.event.DeleteRowsLogEvent;
|
import com.taobao.tddl.dbsync.binlog.event.DeleteRowsLogEvent;
|
||||||
@@ -16,7 +17,7 @@ import com.taobao.tddl.dbsync.binlog.event.UpdateRowsLogEvent;
|
|||||||
import com.taobao.tddl.dbsync.binlog.event.WriteRowsLogEvent;
|
import com.taobao.tddl.dbsync.binlog.event.WriteRowsLogEvent;
|
||||||
import com.taobao.tddl.dbsync.binlog.event.XidLogEvent;
|
import com.taobao.tddl.dbsync.binlog.event.XidLogEvent;
|
||||||
import com.taobao.tddl.dbsync.binlog.event.mariadb.AnnotateRowsEvent;
|
import com.taobao.tddl.dbsync.binlog.event.mariadb.AnnotateRowsEvent;
|
||||||
|
@Ignore
|
||||||
public class DirectLogFetcherTest extends BaseLogFetcherTest {
|
public class DirectLogFetcherTest extends BaseLogFetcherTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import java.net.URL;
|
|||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.taobao.tddl.dbsync.binlog.event.DeleteRowsLogEvent;
|
import com.taobao.tddl.dbsync.binlog.event.DeleteRowsLogEvent;
|
||||||
@@ -16,7 +17,7 @@ import com.taobao.tddl.dbsync.binlog.event.UpdateRowsLogEvent;
|
|||||||
import com.taobao.tddl.dbsync.binlog.event.WriteRowsLogEvent;
|
import com.taobao.tddl.dbsync.binlog.event.WriteRowsLogEvent;
|
||||||
import com.taobao.tddl.dbsync.binlog.event.XidLogEvent;
|
import com.taobao.tddl.dbsync.binlog.event.XidLogEvent;
|
||||||
import com.taobao.tddl.dbsync.binlog.event.mariadb.AnnotateRowsEvent;
|
import com.taobao.tddl.dbsync.binlog.event.mariadb.AnnotateRowsEvent;
|
||||||
|
@Ignore
|
||||||
public class FileLogFetcherTest extends BaseLogFetcherTest {
|
public class FileLogFetcherTest extends BaseLogFetcherTest {
|
||||||
|
|
||||||
private String directory;
|
private String directory;
|
||||||
|
|||||||
@@ -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</version>
|
<version>1.1.4-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<groupId>com.alibaba.otter</groupId>
|
<groupId>com.alibaba.otter</groupId>
|
||||||
|
|||||||
@@ -48,11 +48,17 @@ public class CanalConstants {
|
|||||||
public static final String CANAL_MQ_FLATMESSAGE = ROOT + "." + "mq.flatMessage";
|
public static final String CANAL_MQ_FLATMESSAGE = ROOT + "." + "mq.flatMessage";
|
||||||
public static final String CANAL_MQ_COMPRESSION_TYPE = ROOT + "." + "mq.compressionType";
|
public static final String CANAL_MQ_COMPRESSION_TYPE = ROOT + "." + "mq.compressionType";
|
||||||
public static final String CANAL_MQ_ACKS = ROOT + "." + "mq.acks";
|
public static final String CANAL_MQ_ACKS = ROOT + "." + "mq.acks";
|
||||||
public static final String CANAL_MQ_TRANSACTION = ROOT + "." + "mq.transaction";
|
|
||||||
public static final String CANAL_MQ_PRODUCERGROUP = ROOT + "." + "mq.producerGroup";
|
public static final String CANAL_MQ_PRODUCERGROUP = ROOT + "." + "mq.producerGroup";
|
||||||
public static final String CANAL_ALIYUN_ACCESSKEY = ROOT + "." + "aliyun.accessKey";
|
public static final String CANAL_ALIYUN_ACCESSKEY = ROOT + "." + "aliyun.accessKey";
|
||||||
public static final String CANAL_ALIYUN_SECRETKEY = ROOT + "." + "aliyun.secretKey";
|
public static final String CANAL_ALIYUN_SECRETKEY = ROOT + "." + "aliyun.secretKey";
|
||||||
public static final String CANAL_MQ_PROPERTIES = ROOT + "." + "mq.properties";
|
public static final String CANAL_MQ_PROPERTIES = ROOT + "." + "mq.properties";
|
||||||
|
public static final String CANAL_MQ_ENABLE_MESSAGE_TRACE = ROOT + "." + "mq.enableMessageTrace";
|
||||||
|
public static final String CANAL_MQ_ACCESS_CHANNEL = ROOT + "." + "mq.accessChannel";
|
||||||
|
public static final String CANAL_MQ_CUSTOMIZED_TRACE_TOPIC = ROOT + "." + "mq.customizedTraceTopic";
|
||||||
|
public static final String CANAL_MQ_NAMESPACE = ROOT + "." + "mq.namespace";
|
||||||
|
public static final String CANAL_MQ_KAFKA_KERBEROS_ENABLE = ROOT + "." + "mq.kafka.kerberos.enable";
|
||||||
|
public static final String CANAL_MQ_KAFKA_KERBEROS_KRB5FILEPATH = ROOT + "." + "mq.kafka.kerberos.krb5FilePath";
|
||||||
|
public static final String CANAL_MQ_KAFKA_KERBEROS_JAASFILEPATH = ROOT + "." + "mq.kafka.kerberos.jaasFilePath";
|
||||||
|
|
||||||
public static String getInstanceModeKey(String destination) {
|
public static String getInstanceModeKey(String destination) {
|
||||||
return MessageFormat.format(INSTANCE_MODE_TEMPLATE, destination);
|
return MessageFormat.format(INSTANCE_MODE_TEMPLATE, destination);
|
||||||
|
|||||||
@@ -152,6 +152,9 @@ public class CanalController {
|
|||||||
try {
|
try {
|
||||||
MDC.put(CanalConstants.MDC_DESTINATION, String.valueOf(destination));
|
MDC.put(CanalConstants.MDC_DESTINATION, String.valueOf(destination));
|
||||||
embededCanalServer.start(destination);
|
embededCanalServer.start(destination);
|
||||||
|
if (canalMQStarter != null) {
|
||||||
|
canalMQStarter.startDestination(destination);
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
MDC.remove(CanalConstants.MDC_DESTINATION);
|
MDC.remove(CanalConstants.MDC_DESTINATION);
|
||||||
}
|
}
|
||||||
@@ -160,6 +163,9 @@ public class CanalController {
|
|||||||
public void processActiveExit() {
|
public void processActiveExit() {
|
||||||
try {
|
try {
|
||||||
MDC.put(CanalConstants.MDC_DESTINATION, String.valueOf(destination));
|
MDC.put(CanalConstants.MDC_DESTINATION, String.valueOf(destination));
|
||||||
|
if (canalMQStarter != null) {
|
||||||
|
canalMQStarter.stopDestination(destination);
|
||||||
|
}
|
||||||
embededCanalServer.stop(destination);
|
embededCanalServer.stop(destination);
|
||||||
} finally {
|
} finally {
|
||||||
MDC.remove(CanalConstants.MDC_DESTINATION);
|
MDC.remove(CanalConstants.MDC_DESTINATION);
|
||||||
@@ -234,9 +240,6 @@ public class CanalController {
|
|||||||
ServerRunningMonitor runningMonitor = ServerRunningMonitors.getRunningMonitor(destination);
|
ServerRunningMonitor runningMonitor = ServerRunningMonitors.getRunningMonitor(destination);
|
||||||
if (!config.getLazy() && !runningMonitor.isStart()) {
|
if (!config.getLazy() && !runningMonitor.isStart()) {
|
||||||
runningMonitor.start();
|
runningMonitor.start();
|
||||||
if (canalMQStarter != null) {
|
|
||||||
canalMQStarter.startDestination(destination);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -245,9 +248,6 @@ public class CanalController {
|
|||||||
// 此处的stop,代表强制退出,非HA机制,所以需要退出HA的monitor和配置信息
|
// 此处的stop,代表强制退出,非HA机制,所以需要退出HA的monitor和配置信息
|
||||||
InstanceConfig config = instanceConfigs.remove(destination);
|
InstanceConfig config = instanceConfigs.remove(destination);
|
||||||
if (config != null) {
|
if (config != null) {
|
||||||
if (canalMQStarter != null) {
|
|
||||||
canalMQStarter.stopDestination(destination);
|
|
||||||
}
|
|
||||||
embededCanalServer.stop(destination);
|
embededCanalServer.stop(destination);
|
||||||
ServerRunningMonitor runningMonitor = ServerRunningMonitors.getRunningMonitor(destination);
|
ServerRunningMonitor runningMonitor = ServerRunningMonitors.getRunningMonitor(destination);
|
||||||
if (runningMonitor.isStart()) {
|
if (runningMonitor.isStart()) {
|
||||||
|
|||||||
@@ -65,7 +65,8 @@ public class CanalStater {
|
|||||||
|
|
||||||
public boolean accept(File pathname) {
|
public boolean accept(File pathname) {
|
||||||
String filename = pathname.getName();
|
String filename = pathname.getName();
|
||||||
return pathname.isDirectory() && !"spring".equalsIgnoreCase(filename);
|
return pathname.isDirectory() && !"spring".equalsIgnoreCase(filename)
|
||||||
|
&& !"metrics".equalsIgnoreCase(filename);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (instanceDirs != null && instanceDirs.length > 0) {
|
if (instanceDirs != null && instanceDirs.length > 0) {
|
||||||
@@ -196,16 +197,52 @@ public class CanalStater {
|
|||||||
if (!StringUtils.isEmpty(aliyunSecretKey)) {
|
if (!StringUtils.isEmpty(aliyunSecretKey)) {
|
||||||
mqProperties.setAliyunSecretKey(aliyunSecretKey);
|
mqProperties.setAliyunSecretKey(aliyunSecretKey);
|
||||||
}
|
}
|
||||||
String transaction = CanalController.getProperty(properties, CanalConstants.CANAL_MQ_TRANSACTION);
|
|
||||||
if (!StringUtils.isEmpty(transaction)) {
|
|
||||||
mqProperties.setTransaction(Boolean.valueOf(transaction));
|
|
||||||
}
|
|
||||||
|
|
||||||
String producerGroup = CanalController.getProperty(properties, CanalConstants.CANAL_MQ_PRODUCERGROUP);
|
String producerGroup = CanalController.getProperty(properties, CanalConstants.CANAL_MQ_PRODUCERGROUP);
|
||||||
if (!StringUtils.isEmpty(producerGroup)) {
|
if (!StringUtils.isEmpty(producerGroup)) {
|
||||||
mqProperties.setProducerGroup(producerGroup);
|
mqProperties.setProducerGroup(producerGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String enableMessageTrace = CanalController.getProperty(properties,
|
||||||
|
CanalConstants.CANAL_MQ_ENABLE_MESSAGE_TRACE);
|
||||||
|
if (!StringUtils.isEmpty(enableMessageTrace)) {
|
||||||
|
mqProperties.setEnableMessageTrace(Boolean.valueOf(enableMessageTrace));
|
||||||
|
}
|
||||||
|
|
||||||
|
String accessChannel = CanalController.getProperty(properties, CanalConstants.CANAL_MQ_ACCESS_CHANNEL);
|
||||||
|
if (!StringUtils.isEmpty(accessChannel)) {
|
||||||
|
mqProperties.setAccessChannel(accessChannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
String customizedTraceTopic = CanalController.getProperty(properties,
|
||||||
|
CanalConstants.CANAL_MQ_CUSTOMIZED_TRACE_TOPIC);
|
||||||
|
if (!StringUtils.isEmpty(customizedTraceTopic)) {
|
||||||
|
mqProperties.setCustomizedTraceTopic(customizedTraceTopic);
|
||||||
|
}
|
||||||
|
|
||||||
|
String namespace = CanalController.getProperty(properties, CanalConstants.CANAL_MQ_NAMESPACE);
|
||||||
|
if (!StringUtils.isEmpty(namespace)) {
|
||||||
|
mqProperties.setNamespace(namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
String kafkaKerberosEnable = CanalController.getProperty(properties,
|
||||||
|
CanalConstants.CANAL_MQ_KAFKA_KERBEROS_ENABLE);
|
||||||
|
if (!StringUtils.isEmpty(kafkaKerberosEnable)) {
|
||||||
|
mqProperties.setKerberosEnable(Boolean.valueOf(kafkaKerberosEnable));
|
||||||
|
}
|
||||||
|
|
||||||
|
String kafkaKerberosKrb5Filepath = CanalController.getProperty(properties,
|
||||||
|
CanalConstants.CANAL_MQ_KAFKA_KERBEROS_KRB5FILEPATH);
|
||||||
|
if (!StringUtils.isEmpty(kafkaKerberosKrb5Filepath)) {
|
||||||
|
mqProperties.setKerberosKrb5FilePath(kafkaKerberosKrb5Filepath);
|
||||||
|
}
|
||||||
|
|
||||||
|
String kafkaKerberosJaasFilepath = CanalController.getProperty(properties,
|
||||||
|
CanalConstants.CANAL_MQ_KAFKA_KERBEROS_JAASFILEPATH);
|
||||||
|
if (!StringUtils.isEmpty(kafkaKerberosJaasFilepath)) {
|
||||||
|
mqProperties.setKerberosJaasFilePath(kafkaKerberosJaasFilepath);
|
||||||
|
}
|
||||||
|
|
||||||
for (Object key : properties.keySet()) {
|
for (Object key : properties.keySet()) {
|
||||||
key = StringUtils.trim(key.toString());
|
key = StringUtils.trim(key.toString());
|
||||||
if (((String) key).startsWith(CanalConstants.CANAL_MQ_PROPERTIES)) {
|
if (((String) key).startsWith(CanalConstants.CANAL_MQ_PROPERTIES)) {
|
||||||
|
|||||||
@@ -115,6 +115,16 @@ canal.mq.canalGetTimeout = 100
|
|||||||
canal.mq.flatMessage = true
|
canal.mq.flatMessage = true
|
||||||
canal.mq.compressionType = none
|
canal.mq.compressionType = none
|
||||||
canal.mq.acks = all
|
canal.mq.acks = all
|
||||||
# use transaction for kafka flatMessage batch produce
|
|
||||||
canal.mq.transaction = false
|
|
||||||
#canal.mq.properties. =
|
#canal.mq.properties. =
|
||||||
|
canal.mq.producerGroup = test
|
||||||
|
# Set this value to "cloud", if you want open message trace feature in aliyun.
|
||||||
|
canal.mq.accessChannel = local
|
||||||
|
# aliyun mq namespace
|
||||||
|
#canal.mq.namespace =
|
||||||
|
|
||||||
|
##################################################
|
||||||
|
######### Kafka Kerberos Info #############
|
||||||
|
##################################################
|
||||||
|
canal.mq.kafka.kerberos.enable = false
|
||||||
|
canal.mq.kafka.kerberos.krb5FilePath = "../conf/kerberos/krb5.conf"
|
||||||
|
canal.mq.kafka.kerberos.jaasFilePath = "../conf/kerberos/jaas.conf"
|
||||||
@@ -43,6 +43,7 @@
|
|||||||
<property name="testOnBorrow" value="false" />
|
<property name="testOnBorrow" value="false" />
|
||||||
<property name="testOnReturn" value="false" />
|
<property name="testOnReturn" value="false" />
|
||||||
<property name="useUnfairLock" value="true" />
|
<property name="useUnfairLock" value="true" />
|
||||||
|
<property name="validationQuery" value="SELECT 1" />
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
|
<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
|
||||||
|
|||||||
@@ -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</version>
|
<version>1.1.4-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<groupId>com.alibaba.otter</groupId>
|
<groupId>com.alibaba.otter</groupId>
|
||||||
|
|||||||
@@ -4,10 +4,11 @@ import java.io.IOException;
|
|||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.parse.driver.mysql.packets.server.ResultSetPacket;
|
import com.alibaba.otter.canal.parse.driver.mysql.packets.server.ResultSetPacket;
|
||||||
|
@Ignore
|
||||||
public class MysqlConnectorTest {
|
public class MysqlConnectorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -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</version>
|
<version>1.1.4-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<groupId>com.alibaba.otter</groupId>
|
<groupId>com.alibaba.otter</groupId>
|
||||||
|
|||||||
@@ -7,5 +7,9 @@ public abstract class AbstractRocektMQTest extends BaseCanalClientTest {
|
|||||||
public static String topic = "example";
|
public static String topic = "example";
|
||||||
public static String groupId = "group";
|
public static String groupId = "group";
|
||||||
public static String nameServers = "localhost:9876";
|
public static String nameServers = "localhost:9876";
|
||||||
|
public static String accessKey = "";
|
||||||
|
public static String secretKey = "";
|
||||||
|
public static boolean enableMessageTrace = false;
|
||||||
|
public static String accessChannel = "local";
|
||||||
|
public static String namespace = "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
package com.alibaba.otter.canal.example.rocketmq;
|
package com.alibaba.otter.canal.example.rocketmq;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.client.rocketmq.RocketMQCanalConnector;
|
||||||
|
import com.alibaba.otter.canal.protocol.Message;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.client.rocketmq.RocketMQCanalConnector;
|
|
||||||
import com.alibaba.otter.canal.example.kafka.AbstractKafkaTest;
|
|
||||||
import com.alibaba.otter.canal.protocol.Message;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Kafka client example
|
* Kafka client example
|
||||||
*
|
*
|
||||||
@@ -38,12 +35,24 @@ public class CanalRocketMQClientExample extends AbstractRocektMQTest {
|
|||||||
connector = new RocketMQCanalConnector(nameServers, topic, groupId, 500, false);
|
connector = new RocketMQCanalConnector(nameServers, topic, groupId, 500, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CanalRocketMQClientExample(String nameServers, String topic, String groupId, boolean enableMessageTrace,
|
||||||
|
String accessKey, String secretKey, String accessChannel, String namespace) {
|
||||||
|
connector = new RocketMQCanalConnector(nameServers, topic, groupId, accessKey,
|
||||||
|
secretKey, -1, false, enableMessageTrace,
|
||||||
|
null, accessChannel, namespace);
|
||||||
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
try {
|
try {
|
||||||
final CanalRocketMQClientExample rocketMQClientExample = new CanalRocketMQClientExample(nameServers,
|
final CanalRocketMQClientExample rocketMQClientExample = new CanalRocketMQClientExample(nameServers,
|
||||||
topic,
|
topic,
|
||||||
groupId);
|
groupId,
|
||||||
logger.info("## Start the rocketmq consumer: {}-{}", AbstractKafkaTest.topic, AbstractKafkaTest.groupId);
|
enableMessageTrace,
|
||||||
|
accessKey,
|
||||||
|
secretKey,
|
||||||
|
accessChannel,
|
||||||
|
namespace);
|
||||||
|
logger.info("## Start the rocketmq consumer: {}-{}", topic, groupId);
|
||||||
rocketMQClientExample.start();
|
rocketMQClientExample.start();
|
||||||
logger.info("## The canal rocketmq consumer is running now ......");
|
logger.info("## The canal rocketmq consumer is running now ......");
|
||||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||||
@@ -108,7 +117,7 @@ public class CanalRocketMQClientExample extends AbstractRocektMQTest {
|
|||||||
connector.connect();
|
connector.connect();
|
||||||
connector.subscribe();
|
connector.subscribe();
|
||||||
while (running) {
|
while (running) {
|
||||||
List<Message> messages = connector.getListWithoutAck(100L, TimeUnit.MILLISECONDS); // 获取message
|
List<Message> messages = connector.getListWithoutAck(1000L, TimeUnit.MILLISECONDS); // 获取message
|
||||||
for (Message message : messages) {
|
for (Message message : messages) {
|
||||||
long batchId = message.getId();
|
long batchId = message.getId();
|
||||||
int size = message.getEntries().size();
|
int size = message.getEntries().size();
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public class CanalRocketMQClientFlatMessageExample extends AbstractRocektMQTest
|
|||||||
final CanalRocketMQClientFlatMessageExample rocketMQClientExample = new CanalRocketMQClientFlatMessageExample(nameServers,
|
final CanalRocketMQClientFlatMessageExample rocketMQClientExample = new CanalRocketMQClientFlatMessageExample(nameServers,
|
||||||
topic,
|
topic,
|
||||||
groupId);
|
groupId);
|
||||||
logger.info("## Start the rocketmq consumer: {}-{}", AbstractKafkaTest.topic, AbstractKafkaTest.groupId);
|
logger.info("## Start the rocketmq consumer: {}-{}", topic, groupId);
|
||||||
rocketMQClientExample.start();
|
rocketMQClientExample.start();
|
||||||
logger.info("## The canal rocketmq consumer is running now ......");
|
logger.info("## The canal rocketmq consumer is running now ......");
|
||||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||||
|
|||||||
@@ -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</version>
|
<version>1.1.4-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<groupId>com.alibaba.otter</groupId>
|
<groupId>com.alibaba.otter</groupId>
|
||||||
|
|||||||
@@ -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</version>
|
<version>1.1.4-SNAPSHOT</version>
|
||||||
<relativePath>../../pom.xml</relativePath>
|
<relativePath>../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>canal.instance.core</artifactId>
|
<artifactId>canal.instance.core</artifactId>
|
||||||
|
|||||||
@@ -53,11 +53,15 @@ public class AbstractCanalInstance extends AbstractCanalLifeCycle implements Can
|
|||||||
// 处理group的模式
|
// 处理group的模式
|
||||||
List<CanalEventParser> eventParsers = ((GroupEventParser) eventParser).getEventParsers();
|
List<CanalEventParser> eventParsers = ((GroupEventParser) eventParser).getEventParsers();
|
||||||
for (CanalEventParser singleEventParser : eventParsers) {// 需要遍历启动
|
for (CanalEventParser singleEventParser : eventParsers) {// 需要遍历启动
|
||||||
|
if(singleEventParser instanceof AbstractEventParser) {
|
||||||
((AbstractEventParser) singleEventParser).setEventFilter(aviaterFilter);
|
((AbstractEventParser) singleEventParser).setEventFilter(aviaterFilter);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if(eventParser instanceof AbstractEventParser) {
|
||||||
((AbstractEventParser) eventParser).setEventFilter(aviaterFilter);
|
((AbstractEventParser) eventParser).setEventFilter(aviaterFilter);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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</version>
|
<version>1.1.4-SNAPSHOT</version>
|
||||||
<relativePath>../../pom.xml</relativePath>
|
<relativePath>../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<groupId>com.alibaba.otter</groupId>
|
<groupId>com.alibaba.otter</groupId>
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
package com.alibaba.otter.canal.instance.manager;
|
package com.alibaba.otter.canal.instance.manager;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FilenameFilter;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -8,6 +12,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import org.apache.commons.lang.BooleanUtils;
|
import org.apache.commons.lang.BooleanUtils;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.apache.commons.lang.exception.ExceptionUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
@@ -106,7 +111,42 @@ public class CanalInstanceWithManager extends AbstractCanalInstance {
|
|||||||
|
|
||||||
protected void initAlarmHandler() {
|
protected void initAlarmHandler() {
|
||||||
logger.info("init alarmHandler begin...");
|
logger.info("init alarmHandler begin...");
|
||||||
|
String alarmHandlerClass = parameters.getAlarmHandlerClass();
|
||||||
|
String alarmHandlerPluginDir = parameters.getAlarmHandlerPluginDir();
|
||||||
|
if (alarmHandlerClass == null || alarmHandlerPluginDir == null) {
|
||||||
alarmHandler = new LogAlarmHandler();
|
alarmHandler = new LogAlarmHandler();
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
File externalLibDir = new File(alarmHandlerPluginDir);
|
||||||
|
File[] jarFiles = externalLibDir.listFiles(new FilenameFilter() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean accept(File dir, String name) {
|
||||||
|
return name.endsWith(".jar");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (jarFiles == null || jarFiles.length == 0) {
|
||||||
|
throw new IllegalStateException(String.format("alarmHandlerPluginDir [%s] can't find any name endswith \".jar\" file.",
|
||||||
|
alarmHandlerPluginDir));
|
||||||
|
}
|
||||||
|
URL[] urls = new URL[jarFiles.length];
|
||||||
|
for (int i = 0; i < jarFiles.length; i++) {
|
||||||
|
urls[i] = jarFiles[i].toURI().toURL();
|
||||||
|
}
|
||||||
|
ClassLoader currentClassLoader = new URLClassLoader(urls,
|
||||||
|
CanalInstanceWithManager.class.getClassLoader());
|
||||||
|
Class<CanalAlarmHandler> _alarmClass = (Class<CanalAlarmHandler>) currentClassLoader.loadClass(alarmHandlerClass);
|
||||||
|
alarmHandler = _alarmClass.newInstance();
|
||||||
|
logger.info("init [{}] alarm handler success.", alarmHandlerClass);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
String errorMsg = String.format("init alarmHandlerPluginDir [%s] alarm handler [%s] error: %s",
|
||||||
|
alarmHandlerPluginDir,
|
||||||
|
alarmHandlerClass,
|
||||||
|
ExceptionUtils.getFullStackTrace(e));
|
||||||
|
logger.error(errorMsg);
|
||||||
|
throw new CanalException(errorMsg, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
logger.info("init alarmHandler end! \n\t load CanalAlarmHandler:{} ", alarmHandler.getClass().getName());
|
logger.info("init alarmHandler end! \n\t load CanalAlarmHandler:{} ", alarmHandler.getClass().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -120,6 +120,11 @@ public class CanalParameter implements Serializable {
|
|||||||
private Long standbyTimestamp = null;
|
private Long standbyTimestamp = null;
|
||||||
private Boolean parallel = Boolean.FALSE;
|
private Boolean parallel = Boolean.FALSE;
|
||||||
|
|
||||||
|
//自定义alarmHandler类全路径
|
||||||
|
private String alarmHandlerClass = null;
|
||||||
|
//自定义alarmHandler插件文件夹路径
|
||||||
|
private String alarmHandlerPluginDir = null;
|
||||||
|
|
||||||
public static enum RunMode {
|
public static enum RunMode {
|
||||||
|
|
||||||
/** 嵌入式 */
|
/** 嵌入式 */
|
||||||
@@ -991,6 +996,22 @@ public class CanalParameter implements Serializable {
|
|||||||
this.parallel = parallel;
|
this.parallel = parallel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getAlarmHandlerClass() {
|
||||||
|
return alarmHandlerClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAlarmHandlerClass(String alarmHandlerClass) {
|
||||||
|
this.alarmHandlerClass = alarmHandlerClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAlarmHandlerPluginDir() {
|
||||||
|
return alarmHandlerPluginDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAlarmHandlerPluginDir(String alarmHandlerPluginDir) {
|
||||||
|
this.alarmHandlerPluginDir = alarmHandlerPluginDir;
|
||||||
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return ToStringBuilder.reflectionToString(this, CanalToStringStyle.DEFAULT_STYLE);
|
return ToStringBuilder.reflectionToString(this, CanalToStringStyle.DEFAULT_STYLE);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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</version>
|
<version>1.1.4-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<groupId>com.alibaba.otter</groupId>
|
<groupId>com.alibaba.otter</groupId>
|
||||||
|
|||||||
@@ -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</version>
|
<version>1.1.4-SNAPSHOT</version>
|
||||||
<relativePath>../../pom.xml</relativePath>
|
<relativePath>../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<groupId>com.alibaba.otter</groupId>
|
<groupId>com.alibaba.otter</groupId>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.alibaba.otter.canal.instance.spring.integrated;
|
|||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.support.AbstractApplicationContext;
|
import org.springframework.context.support.AbstractApplicationContext;
|
||||||
@@ -15,6 +16,7 @@ import com.alibaba.otter.canal.instance.core.CanalInstanceGenerator;
|
|||||||
* @author zebin.xuzb @ 2012-7-13
|
* @author zebin.xuzb @ 2012-7-13
|
||||||
* @version 1.0.0
|
* @version 1.0.0
|
||||||
*/
|
*/
|
||||||
|
@Ignore
|
||||||
public class DefaultSpringInstanceTest {
|
public class DefaultSpringInstanceTest {
|
||||||
|
|
||||||
private ApplicationContext context;
|
private ApplicationContext context;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.alibaba.otter.canal.instance.spring.integrated;
|
|||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.support.AbstractApplicationContext;
|
import org.springframework.context.support.AbstractApplicationContext;
|
||||||
@@ -15,6 +16,7 @@ import com.alibaba.otter.canal.instance.core.CanalInstanceGenerator;
|
|||||||
* @author zebin.xuzb @ 2012-7-13
|
* @author zebin.xuzb @ 2012-7-13
|
||||||
* @version 1.0.0
|
* @version 1.0.0
|
||||||
*/
|
*/
|
||||||
|
@Ignore
|
||||||
public class GroupSpringInstanceTest {
|
public class GroupSpringInstanceTest {
|
||||||
|
|
||||||
private ApplicationContext context;
|
private ApplicationContext context;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.alibaba.otter.canal.instance.spring.integrated;
|
|||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.support.AbstractApplicationContext;
|
import org.springframework.context.support.AbstractApplicationContext;
|
||||||
@@ -15,6 +16,7 @@ import com.alibaba.otter.canal.instance.core.CanalInstanceGenerator;
|
|||||||
* @author zebin.xuzb @ 2012-7-13
|
* @author zebin.xuzb @ 2012-7-13
|
||||||
* @version 1.0.0
|
* @version 1.0.0
|
||||||
*/
|
*/
|
||||||
|
@Ignore
|
||||||
public class MemorySpringInstanceTest {
|
public class MemorySpringInstanceTest {
|
||||||
|
|
||||||
private ApplicationContext context;
|
private ApplicationContext context;
|
||||||
|
|||||||
@@ -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</version>
|
<version>1.1.4-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<groupId>com.alibaba.otter</groupId>
|
<groupId>com.alibaba.otter</groupId>
|
||||||
|
|||||||
@@ -14,12 +14,15 @@ import com.alibaba.otter.canal.protocol.position.LogIdentity;
|
|||||||
import com.alibaba.otter.canal.protocol.position.LogPosition;
|
import com.alibaba.otter.canal.protocol.position.LogPosition;
|
||||||
import com.alibaba.otter.canal.protocol.position.Position;
|
import com.alibaba.otter.canal.protocol.position.Position;
|
||||||
import com.alibaba.otter.canal.protocol.position.PositionRange;
|
import com.alibaba.otter.canal.protocol.position.PositionRange;
|
||||||
|
import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
@Ignore
|
||||||
public class AbstractMetaManagerTest extends AbstractZkTest {
|
public class AbstractMetaManagerTest extends AbstractZkTest {
|
||||||
|
|
||||||
private static final String MYSQL_ADDRESS = "127.0.0.1";
|
private static final String MYSQL_ADDRESS = "127.0.0.1";
|
||||||
protected ClientIdentity clientIdentity = new ClientIdentity(destination, (short) 1); ;
|
protected ClientIdentity clientIdentity = new ClientIdentity(destination, (short) 1); ;
|
||||||
|
|
||||||
|
@Test
|
||||||
public void doSubscribeTest(CanalMetaManager metaManager) {
|
public void doSubscribeTest(CanalMetaManager metaManager) {
|
||||||
ClientIdentity client1 = new ClientIdentity(destination, (short) 1);
|
ClientIdentity client1 = new ClientIdentity(destination, (short) 1);
|
||||||
metaManager.subscribe(client1);
|
metaManager.subscribe(client1);
|
||||||
@@ -39,6 +42,7 @@ public class AbstractMetaManagerTest extends AbstractZkTest {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void doBatchTest(CanalMetaManager metaManager) {
|
public void doBatchTest(CanalMetaManager metaManager) {
|
||||||
metaManager.subscribe(clientIdentity);
|
metaManager.subscribe(clientIdentity);
|
||||||
|
|
||||||
|
|||||||
@@ -8,12 +8,13 @@ import java.util.Map;
|
|||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.protocol.ClientIdentity;
|
import com.alibaba.otter.canal.protocol.ClientIdentity;
|
||||||
import com.alibaba.otter.canal.protocol.position.Position;
|
import com.alibaba.otter.canal.protocol.position.Position;
|
||||||
import com.alibaba.otter.canal.protocol.position.PositionRange;
|
import com.alibaba.otter.canal.protocol.position.PositionRange;
|
||||||
|
@Ignore
|
||||||
public class FileMixedMetaManagerTest extends AbstractMetaManagerTest {
|
public class FileMixedMetaManagerTest extends AbstractMetaManagerTest {
|
||||||
|
|
||||||
private static final String tmp = System.getProperty("java.io.tmpdir", "/tmp");
|
private static final String tmp = System.getProperty("java.io.tmpdir", "/tmp");
|
||||||
|
|||||||
@@ -3,10 +3,11 @@ package com.alibaba.otter.canal.meta;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.protocol.position.PositionRange;
|
import com.alibaba.otter.canal.protocol.position.PositionRange;
|
||||||
|
@Ignore
|
||||||
public class MemoryMetaManagerTest extends AbstractMetaManagerTest {
|
public class MemoryMetaManagerTest extends AbstractMetaManagerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import java.util.Map;
|
|||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.common.zookeeper.ZkClientx;
|
import com.alibaba.otter.canal.common.zookeeper.ZkClientx;
|
||||||
@@ -13,7 +14,7 @@ import com.alibaba.otter.canal.common.zookeeper.ZookeeperPathUtils;
|
|||||||
import com.alibaba.otter.canal.protocol.ClientIdentity;
|
import com.alibaba.otter.canal.protocol.ClientIdentity;
|
||||||
import com.alibaba.otter.canal.protocol.position.Position;
|
import com.alibaba.otter.canal.protocol.position.Position;
|
||||||
import com.alibaba.otter.canal.protocol.position.PositionRange;
|
import com.alibaba.otter.canal.protocol.position.PositionRange;
|
||||||
|
@Ignore
|
||||||
public class MixedMetaManagerTest extends AbstractMetaManagerTest {
|
public class MixedMetaManagerTest extends AbstractMetaManagerTest {
|
||||||
|
|
||||||
private ZkClientx zkclientx = new ZkClientx(cluster1 + ";" + cluster2);
|
private ZkClientx zkclientx = new ZkClientx(cluster1 + ";" + cluster2);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import java.util.Map;
|
|||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.common.zookeeper.ZkClientx;
|
import com.alibaba.otter.canal.common.zookeeper.ZkClientx;
|
||||||
@@ -13,7 +14,7 @@ import com.alibaba.otter.canal.common.zookeeper.ZookeeperPathUtils;
|
|||||||
import com.alibaba.otter.canal.protocol.ClientIdentity;
|
import com.alibaba.otter.canal.protocol.ClientIdentity;
|
||||||
import com.alibaba.otter.canal.protocol.position.Position;
|
import com.alibaba.otter.canal.protocol.position.Position;
|
||||||
import com.alibaba.otter.canal.protocol.position.PositionRange;
|
import com.alibaba.otter.canal.protocol.position.PositionRange;
|
||||||
|
@Ignore
|
||||||
public class PeriodMixedMetaManagerTest extends AbstractMetaManagerTest {
|
public class PeriodMixedMetaManagerTest extends AbstractMetaManagerTest {
|
||||||
|
|
||||||
private ZkClientx zkclientx = new ZkClientx(cluster1 + ";" + cluster2);
|
private ZkClientx zkclientx = new ZkClientx(cluster1 + ";" + cluster2);
|
||||||
|
|||||||
@@ -5,12 +5,13 @@ import java.util.Map;
|
|||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.common.zookeeper.ZkClientx;
|
import com.alibaba.otter.canal.common.zookeeper.ZkClientx;
|
||||||
import com.alibaba.otter.canal.common.zookeeper.ZookeeperPathUtils;
|
import com.alibaba.otter.canal.common.zookeeper.ZookeeperPathUtils;
|
||||||
import com.alibaba.otter.canal.protocol.position.PositionRange;
|
import com.alibaba.otter.canal.protocol.position.PositionRange;
|
||||||
|
@Ignore
|
||||||
public class ZooKeeperMetaManagerTest extends AbstractMetaManagerTest {
|
public class ZooKeeperMetaManagerTest extends AbstractMetaManagerTest {
|
||||||
|
|
||||||
private ZkClientx zkclientx = new ZkClientx(cluster1 + ";" + cluster2);
|
private ZkClientx zkclientx = new ZkClientx(cluster1 + ";" + cluster2);
|
||||||
|
|||||||
@@ -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</version>
|
<version>1.1.4-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>canal.parse</artifactId>
|
<artifactId>canal.parse</artifactId>
|
||||||
@@ -88,6 +88,9 @@
|
|||||||
<artifactId>spring-test</artifactId>
|
<artifactId>spring-test</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.annotation</groupId>
|
||||||
|
<artifactId>javax.annotation-api</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import com.alibaba.otter.canal.parse.driver.mysql.packets.GTIDSet;
|
|||||||
import com.alibaba.otter.canal.parse.driver.mysql.packets.MysqlGTIDSet;
|
import com.alibaba.otter.canal.parse.driver.mysql.packets.MysqlGTIDSet;
|
||||||
import com.alibaba.otter.canal.parse.exception.CanalParseException;
|
import com.alibaba.otter.canal.parse.exception.CanalParseException;
|
||||||
import com.alibaba.otter.canal.parse.exception.PositionNotFoundException;
|
import com.alibaba.otter.canal.parse.exception.PositionNotFoundException;
|
||||||
import com.alibaba.otter.canal.parse.exception.TableIdNotFoundException;
|
import com.taobao.tddl.dbsync.binlog.exception.TableIdNotFoundException;
|
||||||
import com.alibaba.otter.canal.parse.inbound.EventTransactionBuffer.TransactionFlushCallback;
|
import com.alibaba.otter.canal.parse.inbound.EventTransactionBuffer.TransactionFlushCallback;
|
||||||
import com.alibaba.otter.canal.parse.inbound.mysql.MysqlMultiStageCoprocessor;
|
import com.alibaba.otter.canal.parse.inbound.mysql.MysqlMultiStageCoprocessor;
|
||||||
import com.alibaba.otter.canal.parse.index.CanalLogPositionManager;
|
import com.alibaba.otter.canal.parse.index.CanalLogPositionManager;
|
||||||
@@ -242,7 +242,7 @@ public abstract class AbstractEventParser<EVENT> extends AbstractCanalLifeCycle
|
|||||||
if (parallel) {
|
if (parallel) {
|
||||||
// build stage processor
|
// build stage processor
|
||||||
multiStageCoprocessor = buildMultiStageCoprocessor();
|
multiStageCoprocessor = buildMultiStageCoprocessor();
|
||||||
if (isGTIDMode()) {
|
if (isGTIDMode() && StringUtils.isNotEmpty(startPosition.getGtid())) {
|
||||||
// 判断所属instance是否启用GTID模式,是的话调用ErosaConnection中GTID对应方法dump数据
|
// 判断所属instance是否启用GTID模式,是的话调用ErosaConnection中GTID对应方法dump数据
|
||||||
GTIDSet gtidSet = MysqlGTIDSet.parse(startPosition.getGtid());
|
GTIDSet gtidSet = MysqlGTIDSet.parse(startPosition.getGtid());
|
||||||
((MysqlMultiStageCoprocessor) multiStageCoprocessor).setGtidSet(gtidSet);
|
((MysqlMultiStageCoprocessor) multiStageCoprocessor).setGtidSet(gtidSet);
|
||||||
@@ -260,7 +260,7 @@ public abstract class AbstractEventParser<EVENT> extends AbstractCanalLifeCycle
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (isGTIDMode()) {
|
if (isGTIDMode() && StringUtils.isNotEmpty(startPosition.getGtid())) {
|
||||||
// 判断所属instance是否启用GTID模式,是的话调用ErosaConnection中GTID对应方法dump数据
|
// 判断所属instance是否启用GTID模式,是的话调用ErosaConnection中GTID对应方法dump数据
|
||||||
erosaConnection.dump(MysqlGTIDSet.parse(startPosition.getGtid()), sinkHandler);
|
erosaConnection.dump(MysqlGTIDSet.parse(startPosition.getGtid()), sinkHandler);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -521,7 +521,7 @@ public class MysqlConnection implements ErosaConnection {
|
|||||||
try {
|
try {
|
||||||
rs = query("select @@global.binlog_checksum");
|
rs = query("select @@global.binlog_checksum");
|
||||||
List<String> columnValues = rs.getFieldValues();
|
List<String> columnValues = rs.getFieldValues();
|
||||||
if (columnValues != null && columnValues.size() >= 1 && columnValues.get(0).toUpperCase().equals("CRC32")) {
|
if (columnValues != null && columnValues.size() >= 1 && columnValues.get(0) != null && columnValues.get(0).toUpperCase().equals("CRC32")) {
|
||||||
binlogChecksum = LogEvent.BINLOG_CHECKSUM_ALG_CRC32;
|
binlogChecksum = LogEvent.BINLOG_CHECKSUM_ALG_CRC32;
|
||||||
} else {
|
} else {
|
||||||
binlogChecksum = LogEvent.BINLOG_CHECKSUM_ALG_OFF;
|
binlogChecksum = LogEvent.BINLOG_CHECKSUM_ALG_OFF;
|
||||||
|
|||||||
@@ -355,13 +355,16 @@ public class MysqlEventParser extends AbstractMysqlEventParser implements CanalE
|
|||||||
// GTID模式下,CanalLogPositionManager里取最后的gtid,没有则取instanc配置中的
|
// GTID模式下,CanalLogPositionManager里取最后的gtid,没有则取instanc配置中的
|
||||||
LogPosition logPosition = getLogPositionManager().getLatestIndexBy(destination);
|
LogPosition logPosition = getLogPositionManager().getLatestIndexBy(destination);
|
||||||
if (logPosition != null) {
|
if (logPosition != null) {
|
||||||
|
// 如果以前是非GTID模式,后来调整为了GTID模式,那么为了保持兼容,需要判断gtid是否为空
|
||||||
|
if (StringUtils.isNotEmpty(logPosition.getPostion().getGtid())) {
|
||||||
return logPosition.getPostion();
|
return logPosition.getPostion();
|
||||||
}
|
}
|
||||||
|
}else {
|
||||||
if (masterPosition != null && StringUtils.isNotEmpty(masterPosition.getGtid())) {
|
if (masterPosition != null && StringUtils.isNotEmpty(masterPosition.getGtid())) {
|
||||||
return masterPosition;
|
return masterPosition;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
EntryPosition startPosition = findStartPositionInternal(connection);
|
EntryPosition startPosition = findStartPositionInternal(connection);
|
||||||
if (needTransactionPosition.get()) {
|
if (needTransactionPosition.get()) {
|
||||||
|
|||||||
@@ -441,6 +441,8 @@ public class MysqlMultiStageCoprocessor extends AbstractCanalLifeCycle implement
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleEventException(final Throwable ex, final long sequence, final Object event) {
|
public void handleEventException(final Throwable ex, final long sequence, final Object event) {
|
||||||
|
//异常上抛,否则processEvents的逻辑会默认会mark为成功执行,有丢数据风险
|
||||||
|
throw new CanalParseException(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user