Compare commits
157
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a96db39420 | ||
|
|
bac1d543f1 | ||
|
|
753fad6639 | ||
|
|
bc4dceea1a | ||
|
|
7aaee7b439 | ||
|
|
e19bf1ecff | ||
|
|
a99dc0cbe8 | ||
|
|
7b8ff04d20 | ||
|
|
7ee246ca5a | ||
|
|
54c645f315 | ||
|
|
6c81ab6fdc | ||
|
|
99aefaa118 | ||
|
|
e2248b3d35 | ||
|
|
eef1ee85fb | ||
|
|
7dfec19c68 | ||
|
|
05a52f1bd3 | ||
|
|
fb0f274ba8 | ||
|
|
d71a208123 | ||
|
|
656a1d23c2 | ||
|
|
a574b91fdf | ||
|
|
e41e903984 | ||
|
|
4a22d72ef4 | ||
|
|
b7eff723a3 | ||
|
|
9b9cfe4037 | ||
|
|
ee51f338e0 | ||
|
|
4315f203a2 | ||
|
|
b12945d4a9 | ||
|
|
885f05fb6a | ||
|
|
591126ee6b | ||
|
|
af432bf10a | ||
|
|
b532e90ff4 | ||
|
|
e48bc35f35 | ||
|
|
11a5f2a0fa | ||
|
|
c775478a56 | ||
|
|
bd1f91cd9c | ||
|
|
67e85d201c | ||
|
|
6965726dfe | ||
|
|
c0001afe8f | ||
|
|
977031a666 | ||
|
|
bd3224eea1 | ||
|
|
33733aef25 | ||
|
|
25be0259a4 | ||
|
|
8114d77a20 | ||
|
|
571e7f4e25 | ||
|
|
ead91433d7 | ||
|
|
bac016f89e | ||
|
|
8804647c30 | ||
|
|
2623d08b8c | ||
|
|
622fed84ce | ||
|
|
9f5e8fd1c4 | ||
|
|
88d472ef1e | ||
|
|
2987d08161 | ||
|
|
b54bea5e33 | ||
|
|
c42ba95eed | ||
|
|
b48c434ffd | ||
|
|
2c892fce9b | ||
|
|
3bb49e19d2 | ||
|
|
f26b84ffc2 | ||
|
|
0a2254eeaf | ||
|
|
40d840375f | ||
|
|
5b6fd8094a | ||
|
|
4ce401d219 | ||
|
|
de95a6a128 | ||
|
|
9e11c3c018 | ||
|
|
d67bea8f3d | ||
|
|
997957d6e0 | ||
|
|
ea20076a32 | ||
|
|
2550d8eb65 | ||
|
|
31a0fdc807 | ||
|
|
d79431c7e5 | ||
|
|
911711248c | ||
|
|
63407dc570 | ||
|
|
3c20fd09ad | ||
|
|
eef3bd92ba | ||
|
|
474063af3b | ||
|
|
5cc85ff2de | ||
|
|
64862503ba | ||
|
|
f9e0dc7daa | ||
|
|
0c399509a9 | ||
|
|
a09cd9adb5 | ||
|
|
1369418c6a | ||
|
|
75174a63af | ||
|
|
17f4ee13e7 | ||
|
|
f0417502f8 | ||
|
|
a7720b51e4 | ||
|
|
21904ef1dc | ||
|
|
94425223bd | ||
|
|
7857df9701 | ||
|
|
64ea4ff222 | ||
|
|
85df89f777 | ||
|
|
a0f8e182ec | ||
|
|
33721d0394 | ||
|
|
91bddfeed6 | ||
|
|
25feab4af8 | ||
|
|
f66baeabf1 | ||
|
|
b1dc3821ea | ||
|
|
f5f79104eb | ||
|
|
e8d138d3bd | ||
|
|
32d9e58915 | ||
|
|
58448e559d | ||
|
|
fede3b3d1d | ||
|
|
5d8ec31bc4 | ||
|
|
82b18ebe62 | ||
|
|
66408782b0 | ||
|
|
846c8870ae | ||
|
|
8cfb07f07e | ||
|
|
d741425895 | ||
|
|
20add85ff6 | ||
|
|
a5c09c521e | ||
|
|
c94276b1ac | ||
|
|
272d575c78 | ||
|
|
693f8d501a | ||
|
|
bb1b6b9862 | ||
|
|
8ed82caaef | ||
|
|
de1dd70609 | ||
|
|
09429c241f | ||
|
|
9a332940d7 | ||
|
|
362974b1c2 | ||
|
|
435db30b7b | ||
|
|
e7de8289cd | ||
|
|
5065fea963 | ||
|
|
6989eb3d2b | ||
|
|
df24596092 | ||
|
|
43c23ffb97 | ||
|
|
e7658879bc | ||
|
|
df60eda679 | ||
|
|
b81e9dfeb8 | ||
|
|
b79af46054 | ||
|
|
90bf9e6de5 | ||
|
|
182163c3bd | ||
|
|
e4726972de | ||
|
|
985892cf7e | ||
|
|
746d51e621 | ||
|
|
cdd077b758 | ||
|
|
72ae7ba8d7 | ||
|
|
b82c8bca83 | ||
|
|
576406a4ce | ||
|
|
b5b08e92e3 | ||
|
|
9f6021cf36 | ||
|
|
0e3d719a39 | ||
|
|
afbf125231 | ||
|
|
b2b2a82554 | ||
|
|
30a1b067a8 | ||
|
|
beaca165d0 | ||
|
|
c21c18458b | ||
|
|
5e761e14fa | ||
|
|
18418ebd98 | ||
|
|
92f04f86cb | ||
|
|
0d5505aa2c | ||
|
|
bd56438eca | ||
|
|
b9a157657f | ||
|
|
523e8b85e6 | ||
|
|
ab74b2c82c | ||
|
|
265a8e8594 | ||
|
|
4fc7e5f7d9 | ||
|
|
9cc8caa834 | ||
|
|
a71375dad5 |
+1
-1
@@ -14,7 +14,7 @@ cache:
|
|||||||
install: true
|
install: true
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- travis_wait 30 ./mvnw clean install -DskipTests=false -Dcheckstyle.skip=true -Dmaven.javadoc.skip=true
|
- travis_wait 30 ./mvnw -T 1C 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)
|
||||||
|
|||||||
@@ -90,10 +90,13 @@ canal 作为 MySQL binlog 增量获取和解析工具,可将变更记录投递
|
|||||||
|
|
||||||
- 参考文档: [Canal Kafka/RocketMQ QuickStart](https://github.com/alibaba/canal/wiki/Canal-Kafka-RocketMQ-QuickStart)
|
- 参考文档: [Canal Kafka/RocketMQ QuickStart](https://github.com/alibaba/canal/wiki/Canal-Kafka-RocketMQ-QuickStart)
|
||||||
|
|
||||||
## 相关开源
|
## 相关开源&产品
|
||||||
|
|
||||||
- [canal 消费端开源项目: Otter](http://github.com/alibaba/otter)
|
- [canal 消费端开源项目: Otter](http://github.com/alibaba/otter)
|
||||||
- [阿里巴巴去 Oracle 数据迁移同步工具: yugong](http://github.com/alibaba/yugong)
|
- [阿里巴巴去 Oracle 数据迁移同步工具: yugong](http://github.com/alibaba/yugong)
|
||||||
|
- [阿里巴巴离线同步开源项目 DataX](https://github.com/alibaba/datax)
|
||||||
|
- [阿里巴巴数据库连接池开源项目 Druid](https://github.com/alibaba/druid)
|
||||||
|
- [阿里巴巴实时数据同步工具 DTS](https://www.aliyun.com/product/dts)
|
||||||
|
|
||||||
## 问题反馈
|
## 问题反馈
|
||||||
- 报告 issue: [github issues](https://github.com/alibaba/canal/issues)
|
- 报告 issue: [github issues](https://github.com/alibaba/canal/issues)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>canal.admin</artifactId>
|
<artifactId>canal.admin</artifactId>
|
||||||
<groupId>com.alibaba.otter</groupId>
|
<groupId>com.alibaba.otter</groupId>
|
||||||
<version>1.1.5-SNAPSHOT</version>
|
<version>1.1.7-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>canal.admin</artifactId>
|
<artifactId>canal.admin</artifactId>
|
||||||
<groupId>com.alibaba.otter</groupId>
|
<groupId>com.alibaba.otter</groupId>
|
||||||
<version>1.1.5-SNAPSHOT</version>
|
<version>1.1.7-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>canal.admin-web</artifactId>
|
<artifactId>canal.admin-web</artifactId>
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
sh stop.sh
|
case $(uname) in
|
||||||
|
Linux)
|
||||||
|
bin_abs_path=$(readlink -f $(dirname $0))
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
bin_abs_path=$(cd $(dirname $0) ||exit ; pwd)
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
sh startup.sh
|
sh "$bin_abs_path"/stop.sh
|
||||||
|
sh "$bin_abs_path"/startup.sh
|
||||||
|
|||||||
@@ -56,14 +56,26 @@ in
|
|||||||
exit;;
|
exit;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
JavaVersion=`$JAVA -version 2>&1 |awk 'NR==1{ gsub(/"/,""); print $3 }' | awk -F '.' '{print $1}'`
|
||||||
str=`file -L $JAVA | grep 64-bit`
|
str=`file -L $JAVA | grep 64-bit`
|
||||||
if [ -n "$str" ]; then
|
|
||||||
JAVA_OPTS="-server -Xms2048m -Xmx3072m"
|
JAVA_OPTS="$JAVA_OPTS -Xss256k -XX:+AggressiveOpts -XX:-UseBiasedLocking -XX:-OmitStackTraceInFastThrow -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$base/logs"
|
||||||
|
if [ $JavaVersion -ge 11 ] ; then
|
||||||
|
#JAVA_OPTS="$JAVA_OPTS -Xlog:gc*:$base_log/gc.log:time "
|
||||||
|
JAVA_OPTS="$JAVA_OPTS"
|
||||||
else
|
else
|
||||||
JAVA_OPTS="-server -Xms1024m -Xmx1024m"
|
#JAVA_OPTS="$JAVA_OPTS -Xloggc:$base/logs/canal/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime"
|
||||||
|
JAVA_OPTS="$JAVA_OPTS -XX:+UseFastAccessorMethods -XX:+PrintAdaptiveSizePolicy -XX:+PrintTenuringDistribution"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$str" ]; then
|
||||||
|
# JAVA_OPTS="-server -Xms2048m -Xmx3072m -Xmn1024m -XX:SurvivorRatio=2 -XX:PermSize=96m -XX:MaxPermSize=256m -XX:MaxTenuringThreshold=15 -XX:+DisableExplicitGC $JAVA_OPTS"
|
||||||
|
# For G1
|
||||||
|
JAVA_OPTS="-server -Xms2g -Xmx3g -XX:+UseG1GC -XX:MaxGCPauseMillis=250 -XX:+UseGCOverheadLimit -XX:+ExplicitGCInvokesConcurrent $JAVA_OPTS"
|
||||||
|
else
|
||||||
|
JAVA_OPTS="-server -Xms1024m -Xmx1024m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:MaxPermSize=128m $JAVA_OPTS"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
JAVA_OPTS="$JAVA_OPTS -XX:+UseG1GC -XX:MaxGCPauseMillis=250 -XX:+UseGCOverheadLimit -XX:+ExplicitGCInvokesConcurrent -XX:+PrintAdaptiveSizePolicy -XX:+PrintTenuringDistribution"
|
|
||||||
JAVA_OPTS=" $JAVA_OPTS -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -Dfile.encoding=UTF-8"
|
JAVA_OPTS=" $JAVA_OPTS -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -Dfile.encoding=UTF-8"
|
||||||
CANAL_OPTS="-DappName=canal-admin"
|
CANAL_OPTS="-DappName=canal-admin"
|
||||||
|
|
||||||
@@ -81,4 +93,4 @@ $JAVA $JAVA_OPTS $JAVA_DEBUG_OPT $CANAL_OPTS -classpath .:$CLASSPATH com.alibaba
|
|||||||
echo $! > $base/bin/admin.pid
|
echo $! > $base/bin/admin.pid
|
||||||
|
|
||||||
echo "cd to $current_path for continue"
|
echo "cd to $current_path for continue"
|
||||||
cd $current_path
|
cd $current_path
|
||||||
|
|||||||
@@ -12,6 +12,6 @@ public class Threads {
|
|||||||
pool,
|
pool,
|
||||||
0L,
|
0L,
|
||||||
TimeUnit.MILLISECONDS,
|
TimeUnit.MILLISECONDS,
|
||||||
new ArrayBlockingQueue<Runnable>(pool * 20),
|
new ArrayBlockingQueue<>(pool * 20),
|
||||||
DaemonThreadFactory.daemonThreadFactory);
|
DaemonThreadFactory.daemonThreadFactory);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
package com.alibaba.otter.canal.admin.config;
|
package com.alibaba.otter.canal.admin.config;
|
||||||
|
|
||||||
|
import io.ebean.EbeanServer;
|
||||||
|
import io.ebean.EbeanServerFactory;
|
||||||
|
import io.ebean.config.ServerConfig;
|
||||||
|
import io.ebean.config.UnderscoreNamingConvention;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -8,11 +13,6 @@ import javax.sql.DataSource;
|
|||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
import io.ebean.EbeanServer;
|
|
||||||
import io.ebean.EbeanServerFactory;
|
|
||||||
import io.ebean.config.ServerConfig;
|
|
||||||
import io.ebean.config.UnderscoreNamingConvention;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ebean 配置
|
* Ebean 配置
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -84,7 +84,9 @@ public class WebConfig implements WebMvcConfigurer {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.addPathPatterns("/api/**")
|
.addPathPatterns("/api/**")
|
||||||
.excludePathPatterns("/api/**/config/**")
|
.excludePathPatterns("/api/**/config/server_polling")
|
||||||
|
.excludePathPatterns("/api/**/config/instances_polling")
|
||||||
|
.excludePathPatterns("/api/**/config/instance_polling/**")
|
||||||
.excludePathPatterns("/api/**/user/login")
|
.excludePathPatterns("/api/**/user/login")
|
||||||
.excludePathPatterns("/api/**/user/logout")
|
.excludePathPatterns("/api/**/user/logout")
|
||||||
.excludePathPatterns("/api/**/user/info");
|
.excludePathPatterns("/api/**/user/info");
|
||||||
|
|||||||
+1
-3
@@ -108,9 +108,7 @@ public class SimpleAdminConnector implements AdminConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
connected = true;
|
connected = true;
|
||||||
} catch (IOException e) {
|
} catch (IOException | NoSuchAlgorithmException e) {
|
||||||
throw new ServiceException(e);
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new ServiceException(e);
|
throw new ServiceException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-2
@@ -2,11 +2,13 @@ package com.alibaba.otter.canal.admin.connector;
|
|||||||
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.admin.config.SpringContext;
|
import org.assertj.core.util.Strings;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.admin.config.SpringContext;
|
||||||
|
|
||||||
public class SimpleAdminConnectors {
|
public class SimpleAdminConnectors {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(SimpleAdminConnectors.class);
|
private static final Logger logger = LoggerFactory.getLogger(SimpleAdminConnectors.class);
|
||||||
@@ -25,7 +27,8 @@ public class SimpleAdminConnectors {
|
|||||||
connector.connect();
|
connector.connect();
|
||||||
return function.apply(connector);
|
return function.apply(connector);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("connect to ip:{},port:{},user:{},password:{}, failed", ip, port, user,passwd);
|
logger.error("connect to ip:{},port:{},user:{},password:{}, failed",
|
||||||
|
ip, port, user, getDesensitizationPassword(passwd));
|
||||||
logger.error(e.getMessage());
|
logger.error(e.getMessage());
|
||||||
} finally {
|
} finally {
|
||||||
connector.disconnect();
|
connector.disconnect();
|
||||||
@@ -33,4 +36,14 @@ public class SimpleAdminConnectors {
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String getDesensitizationPassword(String password) {
|
||||||
|
String defaultPassword = "******";
|
||||||
|
|
||||||
|
if (Strings.isNullOrEmpty(password) || password.length() < 6) {
|
||||||
|
return defaultPassword;
|
||||||
|
} else {
|
||||||
|
return String.format("%s******", password.substring(0, 3));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-4
@@ -4,10 +4,17 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PutMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSONArray;
|
import com.alibaba.fastjson2.JSONArray;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import com.alibaba.otter.canal.admin.model.BaseModel;
|
import com.alibaba.otter.canal.admin.model.BaseModel;
|
||||||
import com.alibaba.otter.canal.admin.model.CanalCluster;
|
import com.alibaba.otter.canal.admin.model.CanalCluster;
|
||||||
import com.alibaba.otter.canal.admin.model.NodeServer;
|
import com.alibaba.otter.canal.admin.model.NodeServer;
|
||||||
@@ -22,7 +29,7 @@ public class CanalClusterController {
|
|||||||
CanalClusterService canalClusterServic;
|
CanalClusterService canalClusterServic;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
NodeServerService nodeServerService;
|
NodeServerService nodeServerService;
|
||||||
|
|
||||||
@GetMapping(value = "/clusters")
|
@GetMapping(value = "/clusters")
|
||||||
public BaseModel<List<CanalCluster>> clusters(CanalCluster canalCluster, @PathVariable String env) {
|
public BaseModel<List<CanalCluster>> clusters(CanalCluster canalCluster, @PathVariable String env) {
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,5 @@
|
|||||||
package com.alibaba.otter.canal.admin.controller;
|
package com.alibaba.otter.canal.admin.controller;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.admin.common.TemplateConfigLoader;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
@@ -9,6 +8,7 @@ import org.springframework.web.bind.annotation.RequestBody;
|
|||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.admin.common.TemplateConfigLoader;
|
||||||
import com.alibaba.otter.canal.admin.model.BaseModel;
|
import com.alibaba.otter.canal.admin.model.BaseModel;
|
||||||
import com.alibaba.otter.canal.admin.model.CanalConfig;
|
import com.alibaba.otter.canal.admin.model.CanalConfig;
|
||||||
import com.alibaba.otter.canal.admin.service.CanalConfigService;
|
import com.alibaba.otter.canal.admin.service.CanalConfigService;
|
||||||
|
|||||||
+2
-3
@@ -3,7 +3,6 @@ package com.alibaba.otter.canal.admin.controller;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.admin.common.TemplateConfigLoader;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
@@ -15,6 +14,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.admin.common.TemplateConfigLoader;
|
||||||
import com.alibaba.otter.canal.admin.model.BaseModel;
|
import com.alibaba.otter.canal.admin.model.BaseModel;
|
||||||
import com.alibaba.otter.canal.admin.model.CanalInstanceConfig;
|
import com.alibaba.otter.canal.admin.model.CanalInstanceConfig;
|
||||||
import com.alibaba.otter.canal.admin.model.Pager;
|
import com.alibaba.otter.canal.admin.model.Pager;
|
||||||
@@ -143,8 +143,7 @@ public class CanalInstanceController {
|
|||||||
* @return 是否成功
|
* @return 是否成功
|
||||||
*/
|
*/
|
||||||
@PutMapping(value = "/instance/status/{id}")
|
@PutMapping(value = "/instance/status/{id}")
|
||||||
public BaseModel<Boolean> instanceStart(@PathVariable Long id, @RequestParam String option,
|
public BaseModel<Boolean> instanceStart(@PathVariable Long id, @RequestParam String option, @PathVariable String env) {
|
||||||
@PathVariable String env) {
|
|
||||||
return BaseModel.getInstance(canalInstanceConfigService.instanceOperation(id, option));
|
return BaseModel.getInstance(canalInstanceConfigService.instanceOperation(id, option));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -46,14 +46,15 @@ public class PollingConfigController {
|
|||||||
public BaseModel<CanalConfig> canalConfigPoll(@RequestHeader String user, @RequestHeader String passwd,
|
public BaseModel<CanalConfig> canalConfigPoll(@RequestHeader String user, @RequestHeader String passwd,
|
||||||
@RequestParam String ip, @RequestParam Integer port,
|
@RequestParam String ip, @RequestParam Integer port,
|
||||||
@RequestParam String md5, @RequestParam boolean register,
|
@RequestParam String md5, @RequestParam boolean register,
|
||||||
@RequestParam String cluster, @PathVariable String env) {
|
@RequestParam String cluster, @RequestParam String name,
|
||||||
|
@PathVariable String env) {
|
||||||
if (!auth(user, passwd)) {
|
if (!auth(user, passwd)) {
|
||||||
throw new RuntimeException("auth :" + user + " is failed");
|
throw new RuntimeException("auth :" + user + " is failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StringUtils.isEmpty(md5) && register) {
|
if (StringUtils.isEmpty(md5) && register) {
|
||||||
// do something
|
// do something
|
||||||
pollingConfigService.autoRegister(ip, port, cluster);
|
pollingConfigService.autoRegister(ip, port, cluster, StringUtils.trimToNull(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
CanalConfig canalConfig = pollingConfigService.getChangedConfig(ip, port, md5);
|
CanalConfig canalConfig = pollingConfigService.getChangedConfig(ip, port, md5);
|
||||||
|
|||||||
+21
-13
@@ -1,20 +1,28 @@
|
|||||||
package com.alibaba.otter.canal.admin.controller;
|
package com.alibaba.otter.canal.admin.controller;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.admin.service.UserService;
|
|
||||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
|
||||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
|
|
||||||
import com.alibaba.otter.canal.admin.model.BaseModel;
|
|
||||||
import com.alibaba.otter.canal.admin.model.User;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PutMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.admin.model.BaseModel;
|
||||||
|
import com.alibaba.otter.canal.admin.model.User;
|
||||||
|
import com.alibaba.otter.canal.admin.service.UserService;
|
||||||
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
|
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户管理控制层
|
* 用户管理控制层
|
||||||
*
|
*
|
||||||
@@ -26,9 +34,9 @@ import java.util.concurrent.TimeUnit;
|
|||||||
public class UserController {
|
public class UserController {
|
||||||
|
|
||||||
public static final LoadingCache<String, User> loginUsers = Caffeine.newBuilder()
|
public static final LoadingCache<String, User> loginUsers = Caffeine.newBuilder()
|
||||||
.maximumSize(10_000)
|
.maximumSize(10_000)
|
||||||
.expireAfterAccess(30, TimeUnit.MINUTES)
|
.expireAfterAccess(30, TimeUnit.MINUTES)
|
||||||
.build(key -> null); // 用户登录信息缓存
|
.build(key -> null); // 用户登录信息缓存
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
UserService userService;
|
UserService userService;
|
||||||
|
|||||||
+3
-2
@@ -1,7 +1,5 @@
|
|||||||
package com.alibaba.otter.canal.admin.handler;
|
package com.alibaba.otter.canal.admin.handler;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.admin.common.exception.ServiceException;
|
|
||||||
import com.alibaba.otter.canal.admin.model.BaseModel;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
@@ -10,6 +8,9 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
|
|||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.admin.common.exception.ServiceException;
|
||||||
|
import com.alibaba.otter.canal.admin.model.BaseModel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MVC异常拦截器
|
* MVC异常拦截器
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -3,10 +3,11 @@ package com.alibaba.otter.canal.admin.model;
|
|||||||
import io.ebean.Finder;
|
import io.ebean.Finder;
|
||||||
import io.ebean.annotation.WhenModified;
|
import io.ebean.annotation.WhenModified;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Canal集群信息实体类
|
* Canal集群信息实体类
|
||||||
|
|||||||
+12
-6
@@ -1,12 +1,18 @@
|
|||||||
package com.alibaba.otter.canal.admin.model;
|
package com.alibaba.otter.canal.admin.model;
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
import javax.persistence.*;
|
|
||||||
|
|
||||||
import io.ebean.Finder;
|
import io.ebean.Finder;
|
||||||
import io.ebean.annotation.WhenModified;
|
import io.ebean.annotation.WhenModified;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.Transient;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Canal实例配置信息实体类
|
* Canal实例配置信息实体类
|
||||||
*
|
*
|
||||||
@@ -44,14 +50,14 @@ public class CanalInstanceConfig extends Model {
|
|||||||
private String name;
|
private String name;
|
||||||
private String content;
|
private String content;
|
||||||
private String contentMd5;
|
private String contentMd5;
|
||||||
private String status; // 1: 正常 0: 停止
|
private String status; // 1: 正常 0: 停止
|
||||||
@WhenModified
|
@WhenModified
|
||||||
private Date modifiedTime;
|
private Date modifiedTime;
|
||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
private String clusterServerId;
|
private String clusterServerId;
|
||||||
@Transient
|
@Transient
|
||||||
private String runningStatus = "0"; // 1: 运行中 0: 停止
|
private String runningStatus = "0"; // 1: 运行中 0: 停止
|
||||||
|
|
||||||
public void init() {
|
public void init() {
|
||||||
status = "1";
|
status = "1";
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package com.alibaba.otter.canal.admin.model;
|
package com.alibaba.otter.canal.admin.model;
|
||||||
|
|
||||||
|
import io.ebean.Ebean;
|
||||||
|
import io.ebean.EbeanServer;
|
||||||
|
|
||||||
import javax.persistence.MappedSuperclass;
|
import javax.persistence.MappedSuperclass;
|
||||||
import javax.persistence.OptimisticLockException;
|
import javax.persistence.OptimisticLockException;
|
||||||
|
|
||||||
import org.apache.commons.beanutils.PropertyUtils;
|
import org.apache.commons.beanutils.PropertyUtils;
|
||||||
|
|
||||||
import io.ebean.Ebean;
|
|
||||||
import io.ebean.EbeanServer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EBean Model扩展类
|
* EBean Model扩展类
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -5,7 +5,13 @@ import io.ebean.annotation.WhenModified;
|
|||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import javax.persistence.*;
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 节点信息实体类
|
* 节点信息实体类
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ public class Pager<T> implements Serializable {
|
|||||||
private static final long serialVersionUID = -986577815091763517L;
|
private static final long serialVersionUID = -986577815091763517L;
|
||||||
|
|
||||||
private Long count = 0L;
|
private Long count = 0L;
|
||||||
private List<T> items = new ArrayList<T>();
|
private List<T> items = new ArrayList<>();
|
||||||
private Integer page = 1;
|
private Integer page = 1;
|
||||||
private Integer size = 20;
|
private Integer size = 20;
|
||||||
private Long offset = 0L;
|
private Long offset = 0L;
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ package com.alibaba.otter.canal.admin.model;
|
|||||||
import io.ebean.Finder;
|
import io.ebean.Finder;
|
||||||
import io.ebean.annotation.WhenCreated;
|
import io.ebean.annotation.WhenCreated;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
import javax.persistence.Transient;
|
import javax.persistence.Transient;
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户信息实体类
|
* 用户信息实体类
|
||||||
|
|||||||
+2
-2
@@ -1,10 +1,10 @@
|
|||||||
package com.alibaba.otter.canal.admin.service;
|
package com.alibaba.otter.canal.admin.service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.admin.model.NodeServer;
|
import com.alibaba.otter.canal.admin.model.NodeServer;
|
||||||
import com.alibaba.otter.canal.admin.model.Pager;
|
import com.alibaba.otter.canal.admin.model.Pager;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public interface NodeServerService {
|
public interface NodeServerService {
|
||||||
|
|
||||||
void save(NodeServer nodeServer);
|
void save(NodeServer nodeServer);
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ import com.alibaba.otter.canal.admin.model.CanalInstanceConfig;
|
|||||||
|
|
||||||
public interface PollingConfigService {
|
public interface PollingConfigService {
|
||||||
|
|
||||||
public boolean autoRegister(String ip, Integer adminPort, String cluster);
|
public boolean autoRegister(String ip, Integer adminPort, String cluster, String name);
|
||||||
|
|
||||||
CanalConfig getChangedConfig(String ip, Integer port, String md5);
|
CanalConfig getChangedConfig(String ip, Integer port, String md5);
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -109,12 +109,12 @@ public class CanalInstanceServiceImpl implements CanalInstanceService {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Future<Void> f :futures){
|
for (Future<Void> f : futures) {
|
||||||
try {
|
try {
|
||||||
f.get(3, TimeUnit.SECONDS);
|
f.get(3, TimeUnit.SECONDS);
|
||||||
} catch (InterruptedException | ExecutionException e) {
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
// ignore
|
// ignore
|
||||||
} catch (TimeoutException e){
|
} catch (TimeoutException e) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -154,12 +154,12 @@ public class NodeServerServiceImpl implements NodeServerService {
|
|||||||
return !status;
|
return !status;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
for (Future<Boolean> f :futures){
|
for (Future<Boolean> f : futures) {
|
||||||
try {
|
try {
|
||||||
f.get(3, TimeUnit.SECONDS);
|
f.get(3, TimeUnit.SECONDS);
|
||||||
} catch (InterruptedException | ExecutionException e) {
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
// ignore
|
// ignore
|
||||||
} catch (TimeoutException e){
|
} catch (TimeoutException e) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -2,6 +2,7 @@ package com.alibaba.otter.canal.admin.service.impl;
|
|||||||
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
@@ -28,11 +29,11 @@ public class PollingConfigServiceImpl implements PollingConfigService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
CanalClusterService canalClusterService;
|
CanalClusterService canalClusterService;
|
||||||
|
|
||||||
public boolean autoRegister(String ip, Integer adminPort, String cluster) {
|
public boolean autoRegister(String ip, Integer adminPort, String cluster, String name) {
|
||||||
NodeServer server = NodeServer.find.query().where().eq("ip", ip).eq("adminPort", adminPort).findOne();
|
NodeServer server = NodeServer.find.query().where().eq("ip", ip).eq("adminPort", adminPort).findOne();
|
||||||
if (server == null) {
|
if (server == null) {
|
||||||
server = new NodeServer();
|
server = new NodeServer();
|
||||||
server.setName(ip);
|
server.setName(Optional.ofNullable(name).orElse(ip));
|
||||||
server.setIp(ip);
|
server.setIp(ip);
|
||||||
server.setAdminPort(adminPort);
|
server.setAdminPort(adminPort);
|
||||||
server.setTcpPort(adminPort + 1);
|
server.setTcpPort(adminPort + 1);
|
||||||
|
|||||||
+5
@@ -21,6 +21,8 @@ public class UserServiceImpl implements UserService {
|
|||||||
|
|
||||||
private static byte[] seeds = "canal is best!".getBytes();
|
private static byte[] seeds = "canal is best!".getBytes();
|
||||||
|
|
||||||
|
private static final Integer PASSWORD_LENGTH = 6;
|
||||||
|
|
||||||
public User find4Login(String username, String password) {
|
public User find4Login(String username, String password) {
|
||||||
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
|
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
|
||||||
return null;
|
return null;
|
||||||
@@ -43,6 +45,9 @@ public class UserServiceImpl implements UserService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void update(User user) {
|
public void update(User user) {
|
||||||
|
if (user.getPassword().length() < PASSWORD_LENGTH) {
|
||||||
|
throw new ServiceException("The new password is too short,must more than 6 digits");
|
||||||
|
}
|
||||||
User userTmp = User.find.query().where().eq("username", user.getUsername()).findOne();
|
User userTmp = User.find.query().where().eq("username", user.getUsername()).findOne();
|
||||||
if (userTmp == null) {
|
if (userTmp == null) {
|
||||||
throw new ServiceException();
|
throw new ServiceException();
|
||||||
|
|||||||
@@ -8,20 +8,24 @@ canal.register.ip =
|
|||||||
canal.port = 11111
|
canal.port = 11111
|
||||||
canal.metrics.pull.port = 11112
|
canal.metrics.pull.port = 11112
|
||||||
# canal instance user/passwd
|
# canal instance user/passwd
|
||||||
canal.user = canal
|
# canal.user = canal
|
||||||
canal.passwd = E3619321C1A937C46A0D8BD1DAC39F93B27D4458
|
# canal.passwd = E3619321C1A937C46A0D8BD1DAC39F93B27D4458
|
||||||
|
|
||||||
# canal admin config
|
# canal admin config
|
||||||
canal.admin.manager = 127.0.0.1:8089
|
#canal.admin.manager = 127.0.0.1:8089
|
||||||
canal.admin.port = 11110
|
canal.admin.port = 11110
|
||||||
canal.admin.user = admin
|
canal.admin.user = admin
|
||||||
canal.admin.passwd = 4ACFE3202A5FF5CF467898FC58AAB1D615029441
|
canal.admin.passwd = 4ACFE3202A5FF5CF467898FC58AAB1D615029441
|
||||||
|
# admin auto register
|
||||||
|
#canal.admin.register.auto = true
|
||||||
|
#canal.admin.register.cluster =
|
||||||
|
#canal.admin.register.name =
|
||||||
|
|
||||||
canal.zkServers =
|
canal.zkServers =
|
||||||
# flush data to zk
|
# flush data to zk
|
||||||
canal.zookeeper.flush.period = 1000
|
canal.zookeeper.flush.period = 1000
|
||||||
canal.withoutNetty = false
|
canal.withoutNetty = false
|
||||||
# tcp, kafka, RocketMQ
|
# tcp, kafka, rocketMQ, rabbitMQ
|
||||||
canal.serverMode = tcp
|
canal.serverMode = tcp
|
||||||
# flush meta cursor/parse position to file
|
# flush meta cursor/parse position to file
|
||||||
canal.file.data.dir = ${canal.conf.dir}
|
canal.file.data.dir = ${canal.conf.dir}
|
||||||
@@ -60,6 +64,9 @@ canal.instance.filter.query.ddl = false
|
|||||||
canal.instance.filter.table.error = false
|
canal.instance.filter.table.error = false
|
||||||
canal.instance.filter.rows = false
|
canal.instance.filter.rows = false
|
||||||
canal.instance.filter.transaction.entry = false
|
canal.instance.filter.transaction.entry = false
|
||||||
|
canal.instance.filter.dml.insert = false
|
||||||
|
canal.instance.filter.dml.update = false
|
||||||
|
canal.instance.filter.dml.delete = false
|
||||||
|
|
||||||
# binlog format/image check
|
# binlog format/image check
|
||||||
canal.instance.binlog.format = ROW,STATEMENT,MIXED
|
canal.instance.binlog.format = ROW,STATEMENT,MIXED
|
||||||
@@ -86,19 +93,18 @@ canal.instance.tsdb.snapshot.interval = 24
|
|||||||
# purge snapshot expire , default 360 hour(15 days)
|
# purge snapshot expire , default 360 hour(15 days)
|
||||||
canal.instance.tsdb.snapshot.expire = 360
|
canal.instance.tsdb.snapshot.expire = 360
|
||||||
|
|
||||||
# aliyun ak/sk , support rds/mq
|
|
||||||
canal.aliyun.accessKey =
|
|
||||||
canal.aliyun.secretKey =
|
|
||||||
|
|
||||||
#################################################
|
#################################################
|
||||||
######### destinations #############
|
######### destinations #############
|
||||||
#################################################
|
#################################################
|
||||||
canal.destinations =
|
canal.destinations =
|
||||||
# conf root dir
|
# conf root dir
|
||||||
canal.conf.dir = ../conf
|
canal.conf.dir = ../conf
|
||||||
# auto scan instance dir add/remove and start/stop instance
|
# auto scan instance dir add/remove and start/stop instance
|
||||||
canal.auto.scan = true
|
canal.auto.scan = true
|
||||||
canal.auto.scan.interval = 5
|
canal.auto.scan.interval = 5
|
||||||
|
# set this value to 'true' means that when binlog pos not found, skip to latest.
|
||||||
|
# WARN: pls keep 'false' in production env, or if you know what you want.
|
||||||
|
canal.auto.reset.latest.pos.mode = false
|
||||||
|
|
||||||
canal.instance.tsdb.spring.xml = classpath:spring/tsdb/h2-tsdb.xml
|
canal.instance.tsdb.spring.xml = classpath:spring/tsdb/h2-tsdb.xml
|
||||||
#canal.instance.tsdb.spring.xml = classpath:spring/tsdb/mysql-tsdb.xml
|
#canal.instance.tsdb.spring.xml = classpath:spring/tsdb/mysql-tsdb.xml
|
||||||
@@ -111,29 +117,58 @@ canal.instance.global.spring.xml = classpath:spring/file-instance.xml
|
|||||||
#canal.instance.global.spring.xml = classpath:spring/default-instance.xml
|
#canal.instance.global.spring.xml = classpath:spring/default-instance.xml
|
||||||
|
|
||||||
##################################################
|
##################################################
|
||||||
######### MQ #############
|
######### MQ Properties #############
|
||||||
##################################################
|
##################################################
|
||||||
canal.mq.servers = 127.0.0.1:6667
|
# aliyun ak/sk , support rds/mq
|
||||||
canal.mq.retries = 0
|
canal.aliyun.accessKey =
|
||||||
canal.mq.batchSize = 16384
|
canal.aliyun.secretKey =
|
||||||
canal.mq.maxRequestSize = 1048576
|
canal.aliyun.uid=
|
||||||
canal.mq.lingerMs = 100
|
|
||||||
canal.mq.bufferMemory = 33554432
|
canal.mq.flatMessage = true
|
||||||
canal.mq.canalBatchSize = 50
|
canal.mq.canalBatchSize = 50
|
||||||
canal.mq.canalGetTimeout = 100
|
canal.mq.canalGetTimeout = 100
|
||||||
canal.mq.flatMessage = true
|
|
||||||
canal.mq.compressionType = none
|
|
||||||
canal.mq.acks = all
|
|
||||||
#canal.mq.properties. =
|
|
||||||
canal.mq.producerGroup = test
|
|
||||||
# Set this value to "cloud", if you want open message trace feature in aliyun.
|
# Set this value to "cloud", if you want open message trace feature in aliyun.
|
||||||
canal.mq.accessChannel = local
|
canal.mq.accessChannel = local
|
||||||
# aliyun mq namespace
|
|
||||||
#canal.mq.namespace =
|
canal.mq.database.hash = true
|
||||||
|
canal.mq.send.thread.size = 30
|
||||||
|
canal.mq.build.thread.size = 8
|
||||||
|
|
||||||
##################################################
|
##################################################
|
||||||
######### Kafka Kerberos Info #############
|
######### Kafka #############
|
||||||
##################################################
|
##################################################
|
||||||
canal.mq.kafka.kerberos.enable = false
|
kafka.bootstrap.servers = 127.0.0.1:6667
|
||||||
canal.mq.kafka.kerberos.krb5FilePath = "../conf/kerberos/krb5.conf"
|
kafka.acks = all
|
||||||
canal.mq.kafka.kerberos.jaasFilePath = "../conf/kerberos/jaas.conf"
|
kafka.compression.type = none
|
||||||
|
kafka.batch.size = 16384
|
||||||
|
kafka.linger.ms = 1
|
||||||
|
kafka.max.request.size = 1048576
|
||||||
|
kafka.buffer.memory = 33554432
|
||||||
|
kafka.max.in.flight.requests.per.connection = 1
|
||||||
|
kafka.retries = 0
|
||||||
|
|
||||||
|
kafka.kerberos.enable = false
|
||||||
|
kafka.kerberos.krb5.file = "../conf/kerberos/krb5.conf"
|
||||||
|
kafka.kerberos.jaas.file = "../conf/kerberos/jaas.conf"
|
||||||
|
|
||||||
|
##################################################
|
||||||
|
######### RocketMQ #############
|
||||||
|
##################################################
|
||||||
|
rocketmq.producer.group = test
|
||||||
|
rocketmq.enable.message.trace = false
|
||||||
|
rocketmq.customized.trace.topic =
|
||||||
|
rocketmq.namespace =
|
||||||
|
rocketmq.namesrv.addr = 127.0.0.1:9876
|
||||||
|
rocketmq.retry.times.when.send.failed = 0
|
||||||
|
rocketmq.vip.channel.enabled = false
|
||||||
|
rocketmq.tag =
|
||||||
|
|
||||||
|
##################################################
|
||||||
|
######### RabbitMQ #############
|
||||||
|
##################################################
|
||||||
|
rabbitmq.host =
|
||||||
|
rabbitmq.virtual.host =
|
||||||
|
rabbitmq.exchange =
|
||||||
|
rabbitmq.username =
|
||||||
|
rabbitmq.password =
|
||||||
|
rabbitmq.deliveryMode =
|
||||||
+18
-2
@@ -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.admin</artifactId>
|
<artifactId>canal.admin</artifactId>
|
||||||
<version>1.1.5-SNAPSHOT</version>
|
<version>1.1.7-SNAPSHOT</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<name>canal admin module for otter ${project.version}</name>
|
<name>canal admin module for otter ${project.version}</name>
|
||||||
<modules>
|
<modules>
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
<java_source_version>1.8</java_source_version>
|
<java_source_version>1.8</java_source_version>
|
||||||
<java_target_version>1.8</java_target_version>
|
<java_target_version>1.8</java_target_version>
|
||||||
<file_encoding>UTF-8</file_encoding>
|
<file_encoding>UTF-8</file_encoding>
|
||||||
|
<log4j_version>2.17.0</log4j_version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
@@ -69,7 +70,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>mysql</groupId>
|
<groupId>mysql</groupId>
|
||||||
<artifactId>mysql-connector-java</artifactId>
|
<artifactId>mysql-connector-java</artifactId>
|
||||||
<version>8.0.17</version>
|
<version>5.1.48</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||||
@@ -91,6 +92,21 @@
|
|||||||
<artifactId>ebean</artifactId>
|
<artifactId>ebean</artifactId>
|
||||||
<version>11.41.1</version>
|
<version>11.41.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-core</artifactId>
|
||||||
|
<version>${log4j_version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-api</artifactId>
|
||||||
|
<version>${log4j_version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-to-slf4j</artifactId>
|
||||||
|
<version>${log4j_version}</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
|
|||||||
@@ -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.5-SNAPSHOT</version>
|
<version>1.1.7-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
@@ -17,6 +17,11 @@
|
|||||||
<artifactId>canal.protocol</artifactId>
|
<artifactId>canal.protocol</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.otter</groupId>
|
||||||
|
<artifactId>connector.core</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework</groupId>
|
<groupId>org.springframework</groupId>
|
||||||
<artifactId>spring-core</artifactId>
|
<artifactId>spring-core</artifactId>
|
||||||
@@ -49,7 +54,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.alibaba</groupId>
|
<groupId>com.alibaba</groupId>
|
||||||
<artifactId>druid</artifactId>
|
<artifactId>druid</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.yaml</groupId>
|
<groupId>org.yaml</groupId>
|
||||||
<artifactId>snakeyaml</artifactId>
|
<artifactId>snakeyaml</artifactId>
|
||||||
|
|||||||
+1
-1
@@ -140,7 +140,7 @@ public class YmlConfigBinder {
|
|||||||
MutablePropertySources propertySources = new MutablePropertySources();
|
MutablePropertySources propertySources = new MutablePropertySources();
|
||||||
propertySources.addFirst(propertySource);
|
propertySources.addFirst(propertySource);
|
||||||
|
|
||||||
PropertiesConfigurationFactory<Object> factory = new PropertiesConfigurationFactory<Object>(target);
|
PropertiesConfigurationFactory<Object> factory = new PropertiesConfigurationFactory<>(target);
|
||||||
factory.setPropertySources(propertySources);
|
factory.setPropertySources(propertySources);
|
||||||
factory.setIgnoreInvalidFields(true);
|
factory.setIgnoreInvalidFields(true);
|
||||||
factory.setIgnoreUnknownFields(true);
|
factory.setIgnoreUnknownFields(true);
|
||||||
|
|||||||
+3
-3
@@ -25,7 +25,7 @@ class DefaultPropertyNamePatternsMatcher implements PropertyNamePatternsMatcher
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected DefaultPropertyNamePatternsMatcher(char[] delimiters, boolean ignoreCase, String... names){
|
protected DefaultPropertyNamePatternsMatcher(char[] delimiters, boolean ignoreCase, String... names){
|
||||||
this(delimiters, ignoreCase, new HashSet<String>(Arrays.asList(names)));
|
this(delimiters, ignoreCase, new HashSet<>(Arrays.asList(names)));
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultPropertyNamePatternsMatcher(char[] delimiters, boolean ignoreCase, Set<String> names){
|
DefaultPropertyNamePatternsMatcher(char[] delimiters, boolean ignoreCase, Set<String> names){
|
||||||
@@ -70,8 +70,8 @@ class DefaultPropertyNamePatternsMatcher implements PropertyNamePatternsMatcher
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int i = 0; i < match.length; i++) {
|
for (boolean b : match) {
|
||||||
if (match[i]) {
|
if (b) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -277,7 +277,7 @@ public class PropertiesConfigurationFactory<T> implements FactoryBean<T>, Applic
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Set<String> getNames(Iterable<String> prefixes) {
|
private Set<String> getNames(Iterable<String> prefixes) {
|
||||||
Set<String> names = new LinkedHashSet<String>();
|
Set<String> names = new LinkedHashSet<>();
|
||||||
if (this.target != null) {
|
if (this.target != null) {
|
||||||
PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(this.target.getClass());
|
PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(this.target.getClass());
|
||||||
for (PropertyDescriptor descriptor : descriptors) {
|
for (PropertyDescriptor descriptor : descriptors) {
|
||||||
@@ -318,7 +318,7 @@ public class PropertiesConfigurationFactory<T> implements FactoryBean<T>, Applic
|
|||||||
// We can filter properties to those starting with the target name, but
|
// We can filter properties to those starting with the target name, but
|
||||||
// we can't do a complete filter since we need to trigger the
|
// we can't do a complete filter since we need to trigger the
|
||||||
// unknown fields check
|
// unknown fields check
|
||||||
Set<String> relaxedNames = new HashSet<String>();
|
Set<String> relaxedNames = new HashSet<>();
|
||||||
for (String relaxedTargetName : relaxedTargetNames) {
|
for (String relaxedTargetName : relaxedTargetNames) {
|
||||||
relaxedNames.add(relaxedTargetName);
|
relaxedNames.add(relaxedTargetName);
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-16
@@ -9,23 +9,9 @@ package com.alibaba.otter.canal.client.adapter.config.bind;
|
|||||||
*/
|
*/
|
||||||
interface PropertyNamePatternsMatcher {
|
interface PropertyNamePatternsMatcher {
|
||||||
|
|
||||||
PropertyNamePatternsMatcher ALL = new PropertyNamePatternsMatcher() {
|
PropertyNamePatternsMatcher ALL = propertyName -> true;
|
||||||
|
|
||||||
@Override
|
PropertyNamePatternsMatcher NONE = propertyName -> false;
|
||||||
public boolean matches(String propertyName) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
PropertyNamePatternsMatcher NONE = new PropertyNamePatternsMatcher() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(String propertyName) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return {@code true} of the property name matches.
|
* Return {@code true} of the property name matches.
|
||||||
|
|||||||
+2
-2
@@ -37,9 +37,9 @@ public class PropertySourcesPropertyValues implements PropertyValues {
|
|||||||
|
|
||||||
private final PropertyNamePatternsMatcher includes;
|
private final PropertyNamePatternsMatcher includes;
|
||||||
|
|
||||||
private final Map<String, PropertyValue> propertyValues = new LinkedHashMap<String, PropertyValue>();
|
private final Map<String, PropertyValue> propertyValues = new LinkedHashMap<>();
|
||||||
|
|
||||||
private final ConcurrentHashMap<String, PropertySource<?>> collectionOwners = new ConcurrentHashMap<String, PropertySource<?>>();
|
private final ConcurrentHashMap<String, PropertySource<?>> collectionOwners = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private final boolean resolvePlaceholders;
|
private final boolean resolvePlaceholders;
|
||||||
|
|
||||||
|
|||||||
+14
-14
@@ -32,7 +32,7 @@ public class RelaxedDataBinder extends DataBinder {
|
|||||||
private static final Set<Class<?>> EXCLUDED_EDITORS;
|
private static final Set<Class<?>> EXCLUDED_EDITORS;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
Set<Class<?>> excluded = new HashSet<Class<?>>();
|
Set<Class<?>> excluded = new HashSet<>();
|
||||||
excluded.add(FileEditor.class);
|
excluded.add(FileEditor.class);
|
||||||
EXCLUDED_EDITORS = Collections.unmodifiableSet(excluded);
|
EXCLUDED_EDITORS = Collections.unmodifiableSet(excluded);
|
||||||
}
|
}
|
||||||
@@ -43,7 +43,7 @@ public class RelaxedDataBinder extends DataBinder {
|
|||||||
|
|
||||||
private boolean ignoreNestedProperties;
|
private boolean ignoreNestedProperties;
|
||||||
|
|
||||||
private MultiValueMap<String, String> nameAliases = new LinkedMultiValueMap<String, String>();
|
private MultiValueMap<String, String> nameAliases = new LinkedMultiValueMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@link RelaxedDataBinder} instance.
|
* Create a new {@link RelaxedDataBinder} instance.
|
||||||
@@ -89,7 +89,7 @@ public class RelaxedDataBinder extends DataBinder {
|
|||||||
* @param aliases a map of property name to aliases
|
* @param aliases a map of property name to aliases
|
||||||
*/
|
*/
|
||||||
public void setNameAliases(Map<String, List<String>> aliases) {
|
public void setNameAliases(Map<String, List<String>> aliases) {
|
||||||
this.nameAliases = new LinkedMultiValueMap<String, String>(aliases);
|
this.nameAliases = new LinkedMultiValueMap<>(aliases);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -129,8 +129,8 @@ public class RelaxedDataBinder extends DataBinder {
|
|||||||
BeanWrapper wrapper = new BeanWrapperImpl(target);
|
BeanWrapper wrapper = new BeanWrapperImpl(target);
|
||||||
wrapper.setConversionService(new RelaxedConversionService(getConversionService()));
|
wrapper.setConversionService(new RelaxedConversionService(getConversionService()));
|
||||||
wrapper.setAutoGrowNestedPaths(true);
|
wrapper.setAutoGrowNestedPaths(true);
|
||||||
List<PropertyValue> sortedValues = new ArrayList<PropertyValue>();
|
List<PropertyValue> sortedValues = new ArrayList<>();
|
||||||
Set<String> modifiedNames = new HashSet<String>();
|
Set<String> modifiedNames = new HashSet<>();
|
||||||
List<String> sortedNames = getSortedPropertyNames(propertyValues);
|
List<String> sortedNames = getSortedPropertyNames(propertyValues);
|
||||||
for (String name : sortedNames) {
|
for (String name : sortedNames) {
|
||||||
PropertyValue propertyValue = propertyValues.getPropertyValue(name);
|
PropertyValue propertyValue = propertyValues.getPropertyValue(name);
|
||||||
@@ -143,7 +143,7 @@ public class RelaxedDataBinder extends DataBinder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private List<String> getSortedPropertyNames(MutablePropertyValues propertyValues) {
|
private List<String> getSortedPropertyNames(MutablePropertyValues propertyValues) {
|
||||||
List<String> names = new LinkedList<String>();
|
List<String> names = new LinkedList<>();
|
||||||
for (PropertyValue propertyValue : propertyValues.getPropertyValueList()) {
|
for (PropertyValue propertyValue : propertyValues.getPropertyValueList()) {
|
||||||
names.add(propertyValue.getName());
|
names.add(propertyValue.getName());
|
||||||
}
|
}
|
||||||
@@ -161,7 +161,7 @@ public class RelaxedDataBinder extends DataBinder {
|
|||||||
* @param names the names to sort
|
* @param names the names to sort
|
||||||
*/
|
*/
|
||||||
private void sortPropertyNames(List<String> names) {
|
private void sortPropertyNames(List<String> names) {
|
||||||
for (String name : new ArrayList<String>(names)) {
|
for (String name : new ArrayList<>(names)) {
|
||||||
int propertyIndex = names.indexOf(name);
|
int propertyIndex = names.indexOf(name);
|
||||||
RelaxedDataBinder.BeanPath path = new RelaxedDataBinder.BeanPath(name);
|
RelaxedDataBinder.BeanPath path = new RelaxedDataBinder.BeanPath(name);
|
||||||
for (String prefix : path.prefixes()) {
|
for (String prefix : path.prefixes()) {
|
||||||
@@ -319,7 +319,7 @@ public class RelaxedDataBinder extends DataBinder {
|
|||||||
}
|
}
|
||||||
Object extend = new LinkedHashMap<String, Object>();
|
Object extend = new LinkedHashMap<String, Object>();
|
||||||
if (!elementDescriptor.isMap() && path.isArrayIndex(index)) {
|
if (!elementDescriptor.isMap() && path.isArrayIndex(index)) {
|
||||||
extend = new ArrayList<Object>();
|
extend = new ArrayList<>();
|
||||||
}
|
}
|
||||||
wrapper.setPropertyValue(path.prefix(index + 1), extend);
|
wrapper.setPropertyValue(path.prefix(index + 1), extend);
|
||||||
}
|
}
|
||||||
@@ -347,7 +347,7 @@ public class RelaxedDataBinder extends DataBinder {
|
|||||||
}
|
}
|
||||||
Object extend = new LinkedHashMap<String, Object>();
|
Object extend = new LinkedHashMap<String, Object>();
|
||||||
if (descriptor.isCollection()) {
|
if (descriptor.isCollection()) {
|
||||||
extend = new ArrayList<Object>();
|
extend = new ArrayList<>();
|
||||||
}
|
}
|
||||||
if (descriptor.getType().equals(Object.class) && path.isLastNode(index)) {
|
if (descriptor.getType().equals(Object.class) && path.isLastNode(index)) {
|
||||||
extend = BLANK;
|
extend = BLANK;
|
||||||
@@ -411,7 +411,7 @@ public class RelaxedDataBinder extends DataBinder {
|
|||||||
if (aliases == null) {
|
if (aliases == null) {
|
||||||
return Collections.singleton(name);
|
return Collections.singleton(name);
|
||||||
}
|
}
|
||||||
List<String> nameAndAliases = new ArrayList<String>(aliases.size() + 1);
|
List<String> nameAndAliases = new ArrayList<>(aliases.size() + 1);
|
||||||
nameAndAliases.add(name);
|
nameAndAliases.add(name);
|
||||||
nameAndAliases.addAll(aliases);
|
nameAndAliases.addAll(aliases);
|
||||||
return nameAndAliases;
|
return nameAndAliases;
|
||||||
@@ -473,7 +473,7 @@ public class RelaxedDataBinder extends DataBinder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<String> prefixes() {
|
public List<String> prefixes() {
|
||||||
List<String> prefixes = new ArrayList<String>();
|
List<String> prefixes = new ArrayList<>();
|
||||||
for (int index = 1; index < this.nodes.size(); index++) {
|
for (int index = 1; index < this.nodes.size(); index++) {
|
||||||
prefixes.add(prefix(index));
|
prefixes.add(prefix(index));
|
||||||
}
|
}
|
||||||
@@ -485,7 +485,7 @@ public class RelaxedDataBinder extends DataBinder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private List<PathNode> splitPath(String path) {
|
private List<PathNode> splitPath(String path) {
|
||||||
List<PathNode> nodes = new ArrayList<PathNode>();
|
List<PathNode> nodes = new ArrayList<>();
|
||||||
String current = extractIndexedPaths(path, nodes);
|
String current = extractIndexedPaths(path, nodes);
|
||||||
for (String name : StringUtils.delimitedListToStringArray(current, ".")) {
|
for (String name : StringUtils.delimitedListToStringArray(current, ".")) {
|
||||||
if (StringUtils.hasText(name)) {
|
if (StringUtils.hasText(name)) {
|
||||||
@@ -518,7 +518,7 @@ public class RelaxedDataBinder extends DataBinder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void collapseKeys(int index) {
|
public void collapseKeys(int index) {
|
||||||
List<PathNode> revised = new ArrayList<PathNode>();
|
List<PathNode> revised = new ArrayList<>();
|
||||||
for (int i = 0; i < index; i++) {
|
for (int i = 0; i < index; i++) {
|
||||||
revised.add(this.nodes.get(i));
|
revised.add(this.nodes.get(i));
|
||||||
}
|
}
|
||||||
@@ -668,7 +668,7 @@ public class RelaxedDataBinder extends DataBinder {
|
|||||||
private static final Set<String> BENIGN_PROPERTY_SOURCE_NAMES;
|
private static final Set<String> BENIGN_PROPERTY_SOURCE_NAMES;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
Set<String> names = new HashSet<String>();
|
Set<String> names = new HashSet<>();
|
||||||
names.add(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME);
|
names.add(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME);
|
||||||
names.add(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME);
|
names.add(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME);
|
||||||
BENIGN_PROPERTY_SOURCE_NAMES = Collections.unmodifiableSet(names);
|
BENIGN_PROPERTY_SOURCE_NAMES = Collections.unmodifiableSet(names);
|
||||||
|
|||||||
+1
-1
@@ -24,7 +24,7 @@ public final class RelaxedNames implements Iterable<String> {
|
|||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
private final Set<String> values = new LinkedHashSet<String>();
|
private final Set<String> values = new LinkedHashSet<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@link RelaxedNames} instance.
|
* Create a new {@link RelaxedNames} instance.
|
||||||
|
|||||||
+3
-3
@@ -22,7 +22,7 @@ import org.springframework.util.StringUtils;
|
|||||||
*/
|
*/
|
||||||
public class CompositePropertySource extends EnumerablePropertySource<Object> {
|
public class CompositePropertySource extends EnumerablePropertySource<Object> {
|
||||||
|
|
||||||
private final Set<PropertySource<?>> propertySources = new LinkedHashSet<PropertySource<?>>();
|
private final Set<PropertySource<?>> propertySources = new LinkedHashSet<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@code CompositePropertySource}.
|
* Create a new {@code CompositePropertySource}.
|
||||||
@@ -56,7 +56,7 @@ public class CompositePropertySource extends EnumerablePropertySource<Object> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getPropertyNames() {
|
public String[] getPropertyNames() {
|
||||||
Set<String> names = new LinkedHashSet<String>();
|
Set<String> names = new LinkedHashSet<>();
|
||||||
for (PropertySource<?> propertySource : this.propertySources) {
|
for (PropertySource<?> propertySource : this.propertySources) {
|
||||||
if (!(propertySource instanceof EnumerablePropertySource)) {
|
if (!(propertySource instanceof EnumerablePropertySource)) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
@@ -83,7 +83,7 @@ public class CompositePropertySource extends EnumerablePropertySource<Object> {
|
|||||||
* @since 4.1
|
* @since 4.1
|
||||||
*/
|
*/
|
||||||
public void addFirstPropertySource(PropertySource<?> propertySource) {
|
public void addFirstPropertySource(PropertySource<?> propertySource) {
|
||||||
List<PropertySource<?>> existing = new ArrayList<PropertySource<?>>(this.propertySources);
|
List<PropertySource<?>> existing = new ArrayList<>(this.propertySources);
|
||||||
this.propertySources.clear();
|
this.propertySources.clear();
|
||||||
this.propertySources.add(propertySource);
|
this.propertySources.add(propertySource);
|
||||||
this.propertySources.addAll(existing);
|
this.propertySources.addAll(existing);
|
||||||
|
|||||||
+1
-1
@@ -28,7 +28,7 @@ public class MutablePropertySources implements PropertySources {
|
|||||||
|
|
||||||
private final Log logger;
|
private final Log logger;
|
||||||
|
|
||||||
private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<PropertySource<?>>();
|
private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@link MutablePropertySources}
|
* Create a new {@link MutablePropertySources}
|
||||||
|
|||||||
+4
-4
@@ -31,7 +31,7 @@ public class SpringProfileDocumentMatcher implements YamlProcessor.DocumentMatch
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void addActiveProfiles(String... profiles) {
|
public void addActiveProfiles(String... profiles) {
|
||||||
LinkedHashSet<String> set = new LinkedHashSet<String>(Arrays.asList(this.activeProfiles));
|
LinkedHashSet<String> set = new LinkedHashSet<>(Arrays.asList(this.activeProfiles));
|
||||||
Collections.addAll(set, profiles);
|
Collections.addAll(set, profiles);
|
||||||
this.activeProfiles = set.toArray(new String[set.size()]);
|
this.activeProfiles = set.toArray(new String[set.size()]);
|
||||||
}
|
}
|
||||||
@@ -67,14 +67,14 @@ public class SpringProfileDocumentMatcher implements YamlProcessor.DocumentMatch
|
|||||||
|
|
||||||
private ProfilesMatcher getProfilesMatcher() {
|
private ProfilesMatcher getProfilesMatcher() {
|
||||||
return (this.activeProfiles.length != 0 ? new ActiveProfilesMatcher(
|
return (this.activeProfiles.length != 0 ? new ActiveProfilesMatcher(
|
||||||
new HashSet<String>(Arrays.asList(this.activeProfiles))) : new EmptyProfilesMatcher());
|
new HashSet<>(Arrays.asList(this.activeProfiles))) : new EmptyProfilesMatcher());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<String> extractProfiles(List<String> profiles, ProfileType type) {
|
private Set<String> extractProfiles(List<String> profiles, ProfileType type) {
|
||||||
if (CollectionUtils.isEmpty(profiles)) {
|
if (CollectionUtils.isEmpty(profiles)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Set<String> extractedProfiles = new HashSet<String>();
|
Set<String> extractedProfiles = new HashSet<>();
|
||||||
for (String candidate : profiles) {
|
for (String candidate : profiles) {
|
||||||
ProfileType candidateType = ProfileType.POSITIVE;
|
ProfileType candidateType = ProfileType.POSITIVE;
|
||||||
if (candidate.startsWith("!")) {
|
if (candidate.startsWith("!")) {
|
||||||
@@ -167,7 +167,7 @@ public class SpringProfileDocumentMatcher implements YamlProcessor.DocumentMatch
|
|||||||
*/
|
*/
|
||||||
static class SpringProperties {
|
static class SpringProperties {
|
||||||
|
|
||||||
private List<String> profiles = new ArrayList<String>();
|
private List<String> profiles = new ArrayList<>();
|
||||||
|
|
||||||
public List<String> getProfiles() {
|
public List<String> getProfiles() {
|
||||||
return this.profiles;
|
return this.profiles;
|
||||||
|
|||||||
+4
-7
@@ -130,8 +130,7 @@ public abstract class YamlProcessor {
|
|||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("Loading from YAML: " + resource);
|
logger.debug("Loading from YAML: " + resource);
|
||||||
}
|
}
|
||||||
Reader reader = new UnicodeReader(resource.getInputStream());
|
try (Reader reader = new UnicodeReader(resource.getInputStream())) {
|
||||||
try {
|
|
||||||
for (Object object : yaml.loadAll(reader)) {
|
for (Object object : yaml.loadAll(reader)) {
|
||||||
if (object != null && process(asMap(object), callback)) {
|
if (object != null && process(asMap(object), callback)) {
|
||||||
count++;
|
count++;
|
||||||
@@ -142,10 +141,8 @@ public abstract class YamlProcessor {
|
|||||||
}
|
}
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"Loaded " + count + " document" + (count > 1 ? "s" : "") + " from YAML resource: " + resource);
|
"Loaded " + count + " document" + (count > 1 ? "s" : "") + " from YAML resource: " + resource);
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
reader.close();
|
|
||||||
}
|
}
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
handleProcessError(resource, ex);
|
handleProcessError(resource, ex);
|
||||||
@@ -166,7 +163,7 @@ public abstract class YamlProcessor {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private Map<String, Object> asMap(Object object) {
|
private Map<String, Object> asMap(Object object) {
|
||||||
// YAML can have numbers as keys
|
// YAML can have numbers as keys
|
||||||
Map<String, Object> result = new LinkedHashMap<String, Object>();
|
Map<String, Object> result = new LinkedHashMap<>();
|
||||||
if (!(object instanceof Map)) {
|
if (!(object instanceof Map)) {
|
||||||
// A document can be a text literal
|
// A document can be a text literal
|
||||||
result.put("document", object);
|
result.put("document", object);
|
||||||
@@ -249,7 +246,7 @@ public abstract class YamlProcessor {
|
|||||||
* @since 4.1.3
|
* @since 4.1.3
|
||||||
*/
|
*/
|
||||||
protected final Map<String, Object> getFlattenedMap(Map<String, Object> source) {
|
protected final Map<String, Object> getFlattenedMap(Map<String, Object> source) {
|
||||||
Map<String, Object> result = new LinkedHashMap<String, Object>();
|
Map<String, Object> result = new LinkedHashMap<>();
|
||||||
buildFlattenedMap(result, source, null);
|
buildFlattenedMap(result, source, null);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-9
@@ -3,7 +3,6 @@ package com.alibaba.otter.canal.client.adapter.config.common;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
@@ -72,14 +71,8 @@ public class YamlPropertySourceLoader implements PropertySourceLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Object> process() {
|
public Map<String, Object> process() {
|
||||||
final Map<String, Object> result = new LinkedHashMap<String, Object>();
|
final Map<String, Object> result = new LinkedHashMap<>();
|
||||||
process(new MatchCallback() {
|
process((properties, map) -> result.putAll(getFlattenedMap(map)));
|
||||||
|
|
||||||
@Override
|
|
||||||
public void process(Properties properties, Map<String, Object> map) {
|
|
||||||
result.putAll(getFlattenedMap(map));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+22
-1
@@ -3,6 +3,7 @@ package com.alibaba.otter.canal.client.adapter.support;
|
|||||||
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.Properties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 配置信息类
|
* 配置信息类
|
||||||
@@ -27,7 +28,7 @@ public class CanalClientConfig {
|
|||||||
// 重试次数
|
// 重试次数
|
||||||
private Integer retries;
|
private Integer retries;
|
||||||
// 消费超时时间
|
// 消费超时时间
|
||||||
private Long timeout;
|
private Long timeout = 500L;
|
||||||
// 模式 tcp kafka rocketMQ
|
// 模式 tcp kafka rocketMQ
|
||||||
private String mode = "tcp";
|
private String mode = "tcp";
|
||||||
// aliyun ak/sk
|
// aliyun ak/sk
|
||||||
@@ -39,6 +40,8 @@ public class CanalClientConfig {
|
|||||||
// rabbitmq vhost
|
// rabbitmq vhost
|
||||||
private String vhost = "/";
|
private String vhost = "/";
|
||||||
|
|
||||||
|
private Properties consumerProperties;
|
||||||
|
|
||||||
private Long resourceOwnerId;
|
private Long resourceOwnerId;
|
||||||
// 是否启用消息轨迹
|
// 是否启用消息轨迹
|
||||||
private boolean enableMessageTrace;
|
private boolean enableMessageTrace;
|
||||||
@@ -51,6 +54,8 @@ public class CanalClientConfig {
|
|||||||
// canal adapters 配置
|
// canal adapters 配置
|
||||||
private List<CanalAdapter> canalAdapters;
|
private List<CanalAdapter> canalAdapters;
|
||||||
|
|
||||||
|
private Boolean terminateOnException = false;
|
||||||
|
|
||||||
public String getCanalServerHost() {
|
public String getCanalServerHost() {
|
||||||
return canalServerHost;
|
return canalServerHost;
|
||||||
}
|
}
|
||||||
@@ -163,6 +168,14 @@ public class CanalClientConfig {
|
|||||||
this.vhost = vhost;
|
this.vhost = vhost;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Properties getConsumerProperties() {
|
||||||
|
return consumerProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConsumerProperties(Properties consumerProperties) {
|
||||||
|
this.consumerProperties = consumerProperties;
|
||||||
|
}
|
||||||
|
|
||||||
public Long getResourceOwnerId() {
|
public Long getResourceOwnerId() {
|
||||||
return resourceOwnerId;
|
return resourceOwnerId;
|
||||||
}
|
}
|
||||||
@@ -211,6 +224,14 @@ public class CanalClientConfig {
|
|||||||
this.namespace = namespace;
|
this.namespace = namespace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Boolean getTerminateOnException() {
|
||||||
|
return terminateOnException;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTerminateOnException(Boolean terminateOnException) {
|
||||||
|
this.terminateOnException = terminateOnException;
|
||||||
|
}
|
||||||
|
|
||||||
public static class CanalAdapter {
|
public static class CanalAdapter {
|
||||||
|
|
||||||
private String instance; // 实例名
|
private String instance; // 实例名
|
||||||
|
|||||||
+17
-22
@@ -6,6 +6,7 @@ import java.io.InputStreamReader;
|
|||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLClassLoader;
|
import java.net.URLClassLoader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -259,7 +260,7 @@ public class ExtensionLoader<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
|
Map<String, Class<?>> extensionClasses = new HashMap<>();
|
||||||
|
|
||||||
// 1. plugin folder,customized extension classLoader (jar_dir/plugin)
|
// 1. plugin folder,customized extension classLoader (jar_dir/plugin)
|
||||||
String dir = File.separator + this.getJarDirectoryPath() + File.separator + "plugin";
|
String dir = File.separator + this.getJarDirectoryPath() + File.separator + "plugin";
|
||||||
@@ -317,10 +318,8 @@ public class ExtensionLoader<T> {
|
|||||||
while (urls.hasMoreElements()) {
|
while (urls.hasMoreElements()) {
|
||||||
URL url = urls.nextElement();
|
URL url = urls.nextElement();
|
||||||
try {
|
try {
|
||||||
BufferedReader reader = null;
|
try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), StandardCharsets.UTF_8))) {
|
||||||
try {
|
String line;
|
||||||
reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));
|
|
||||||
String line = null;
|
|
||||||
while ((line = reader.readLine()) != null) {
|
while ((line = reader.readLine()) != null) {
|
||||||
final int ci = line.indexOf('#');
|
final int ci = line.indexOf('#');
|
||||||
if (ci >= 0) line = line.substring(0, ci);
|
if (ci >= 0) line = line.substring(0, ci);
|
||||||
@@ -340,10 +339,10 @@ public class ExtensionLoader<T> {
|
|||||||
// classLoader);
|
// classLoader);
|
||||||
if (!type.isAssignableFrom(clazz)) {
|
if (!type.isAssignableFrom(clazz)) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Error when load extension class(interface: " + type
|
"Error when load extension class(interface: " + type
|
||||||
+ ", class line: " + clazz.getName()
|
+ ", class line: " + clazz.getName()
|
||||||
+ "), class " + clazz.getName()
|
+ "), class " + clazz.getName()
|
||||||
+ "is not subtype of interface.");
|
+ "is not subtype of interface.");
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
clazz.getConstructor(type);
|
clazz.getConstructor(type);
|
||||||
@@ -361,10 +360,10 @@ public class ExtensionLoader<T> {
|
|||||||
} else if (c != clazz) {
|
} else if (c != clazz) {
|
||||||
cachedNames.remove(clazz);
|
cachedNames.remove(clazz);
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Duplicate extension " + type.getName() + " name "
|
"Duplicate extension " + type.getName() + " name "
|
||||||
+ n + " on "
|
+ n + " on "
|
||||||
+ c.getName() + " and "
|
+ c.getName() + " and "
|
||||||
+ clazz.getName());
|
+ clazz.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -373,19 +372,15 @@ public class ExtensionLoader<T> {
|
|||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
IllegalStateException e = new IllegalStateException(
|
IllegalStateException e = new IllegalStateException(
|
||||||
"Failed to load extension class(interface: " + type + ", class line: "
|
"Failed to load extension class(interface: " + type + ", class line: "
|
||||||
+ line + ") in " + url
|
+ line + ") in " + url
|
||||||
+ ", cause: "
|
+ ", cause: "
|
||||||
+ t.getMessage(),
|
+ t.getMessage(),
|
||||||
t);
|
t);
|
||||||
exceptions.put(line, e);
|
exceptions.put(line, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // end of while read lines
|
} // end of while read lines
|
||||||
} finally {
|
|
||||||
if (reader != null) {
|
|
||||||
reader.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
logger.error("Exception when load extension class(interface: " + type + ", class file: " + url
|
logger.error("Exception when load extension class(interface: " + type + ", class file: " + url
|
||||||
|
|||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
package com.alibaba.otter.canal.client.adapter.support;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by @author zhuchao on @date 2021/11/11.
|
||||||
|
*/
|
||||||
|
public class FileName2KeyMapping {
|
||||||
|
|
||||||
|
private static Map<String, String> MAP = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public static void register(String type, String fileName, String key) {
|
||||||
|
MAP.putIfAbsent(join(type, fileName), key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void unregister(String type, String fileName) {
|
||||||
|
MAP.remove(join(type, fileName));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getKey(String type, String fileName) {
|
||||||
|
return MAP.get(join(type, fileName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String join(String type, String fileName) {
|
||||||
|
return type + "|" + fileName;
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -55,7 +55,7 @@ public class MappingConfigsLoader {
|
|||||||
}
|
}
|
||||||
if (filePath.exists()) {
|
if (filePath.exists()) {
|
||||||
String fileName = filePath.getName();
|
String fileName = filePath.getName();
|
||||||
if (!fileName.endsWith(".yml")) {
|
if (!(fileName.endsWith(".yml") || fileName.endsWith(".yaml"))) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try (InputStream in = new FileInputStream(filePath)) {
|
try (InputStream in = new FileInputStream(filePath)) {
|
||||||
|
|||||||
+23
-20
@@ -2,8 +2,8 @@ package com.alibaba.otter.canal.client.adapter.support;
|
|||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.connector.core.consumer.CommonMessage;
|
||||||
import com.alibaba.otter.canal.protocol.CanalEntry;
|
import com.alibaba.otter.canal.protocol.CanalEntry;
|
||||||
import com.alibaba.otter.canal.protocol.FlatMessage;
|
|
||||||
import com.alibaba.otter.canal.protocol.Message;
|
import com.alibaba.otter.canal.protocol.Message;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -19,7 +19,7 @@ public class MessageUtil {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
List<CanalEntry.Entry> entries = message.getEntries();
|
List<CanalEntry.Entry> entries = message.getEntries();
|
||||||
List<Dml> dmls = new ArrayList<Dml>(entries.size());
|
List<Dml> dmls = new ArrayList<>(entries.size());
|
||||||
for (CanalEntry.Entry entry : entries) {
|
for (CanalEntry.Entry entry : entries) {
|
||||||
if (entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONBEGIN
|
if (entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONBEGIN
|
||||||
|| entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONEND) {
|
|| entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONEND) {
|
||||||
@@ -131,10 +131,13 @@ public class MessageUtil {
|
|||||||
return dmls;
|
return dmls;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Dml> flatMessage2Dml(String destination, String groupId, List<FlatMessage> flatMessages) {
|
public static List<Dml> flatMessage2Dml(String destination, String groupId, List<CommonMessage> commonMessages) {
|
||||||
List<Dml> dmls = new ArrayList<Dml>(flatMessages.size());
|
if (commonMessages == null) {
|
||||||
for (FlatMessage flatMessage : flatMessages) {
|
return new ArrayList<>();
|
||||||
Dml dml = flatMessage2Dml(destination, groupId, flatMessage);
|
}
|
||||||
|
List<Dml> dmls = new ArrayList<>(commonMessages.size());
|
||||||
|
for (CommonMessage commonMessage : commonMessages) {
|
||||||
|
Dml dml = flatMessage2Dml(destination, groupId, commonMessage);
|
||||||
if (dml != null) {
|
if (dml != null) {
|
||||||
dmls.add(dml);
|
dmls.add(dml);
|
||||||
}
|
}
|
||||||
@@ -143,31 +146,31 @@ public class MessageUtil {
|
|||||||
return dmls;
|
return dmls;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Dml flatMessage2Dml(String destination, String groupId, FlatMessage flatMessage) {
|
public static Dml flatMessage2Dml(String destination, String groupId, CommonMessage commonMessage) {
|
||||||
if (flatMessage == null) {
|
if (commonMessage == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Dml dml = new Dml();
|
Dml dml = new Dml();
|
||||||
dml.setDestination(destination);
|
dml.setDestination(destination);
|
||||||
dml.setGroupId(groupId);
|
dml.setGroupId(groupId);
|
||||||
dml.setDatabase(flatMessage.getDatabase());
|
dml.setDatabase(commonMessage.getDatabase());
|
||||||
dml.setTable(flatMessage.getTable());
|
dml.setTable(commonMessage.getTable());
|
||||||
dml.setPkNames(flatMessage.getPkNames());
|
dml.setPkNames(commonMessage.getPkNames());
|
||||||
dml.setIsDdl(flatMessage.getIsDdl());
|
dml.setIsDdl(commonMessage.getIsDdl());
|
||||||
dml.setType(flatMessage.getType());
|
dml.setType(commonMessage.getType());
|
||||||
dml.setTs(flatMessage.getTs());
|
dml.setTs(commonMessage.getTs());
|
||||||
dml.setEs(flatMessage.getEs());
|
dml.setEs(commonMessage.getEs());
|
||||||
dml.setSql(flatMessage.getSql());
|
dml.setSql(commonMessage.getSql());
|
||||||
// if (flatMessage.getSqlType() == null || flatMessage.getMysqlType() == null) {
|
// if (flatMessage.getSqlType() == null || flatMessage.getMysqlType() == null) {
|
||||||
// throw new RuntimeException("SqlType or mysqlType is null");
|
// throw new RuntimeException("SqlType or mysqlType is null");
|
||||||
// }
|
// }
|
||||||
List<Map<String, String>> data = flatMessage.getData();
|
List<Map<String, Object>> data = commonMessage.getData();
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
dml.setData(changeRows(dml.getTable(), data, flatMessage.getSqlType(), flatMessage.getMysqlType()));
|
dml.setData(data);
|
||||||
}
|
}
|
||||||
List<Map<String, String>> old = flatMessage.getOld();
|
List<Map<String, Object>> old = commonMessage.getOld();
|
||||||
if (old != null) {
|
if (old != null) {
|
||||||
dml.setOld(changeRows(dml.getTable(), old, flatMessage.getSqlType(), flatMessage.getMysqlType()));
|
dml.setOld(old);
|
||||||
}
|
}
|
||||||
return dml;
|
return dml;
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-4
@@ -7,7 +7,8 @@ import java.util.Enumeration;
|
|||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
public class URLClassExtensionLoader extends URLClassLoader {
|
public class URLClassExtensionLoader extends URLClassLoader {
|
||||||
public URLClassExtensionLoader(URL[] urls) {
|
|
||||||
|
public URLClassExtensionLoader(URL[] urls){
|
||||||
super(urls);
|
super(urls);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,9 +19,11 @@ public class URLClassExtensionLoader extends URLClassLoader {
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name.startsWith("java.") || name.startsWith("org.slf4j.")
|
if (name.startsWith("java.") || name.startsWith("org.slf4j.") || name.startsWith("org.apache.logging")
|
||||||
|| name.startsWith("org.apache.logging")
|
|| name.startsWith("org.apache.zookeeper.") || name.startsWith("org.I0Itec.zkclient.")
|
||||||
|| name.startsWith("org.apache.commons.logging.")) {
|
|| name.startsWith("org.apache.commons.logging.")
|
||||||
|
|| name.startsWith("com.alibaba.druid")
|
||||||
|
) {
|
||||||
// || name.startsWith("org.apache.hadoop."))
|
// || name.startsWith("org.apache.hadoop."))
|
||||||
// {
|
// {
|
||||||
c = super.loadClass(name);
|
c = super.loadClass(name);
|
||||||
|
|||||||
+2
@@ -30,6 +30,8 @@ public class Util {
|
|||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(Util.class);
|
private static final Logger logger = LoggerFactory.getLogger(Util.class);
|
||||||
|
|
||||||
|
public static final String AUTO_GENERATED_PREFIX = "AUTO_GENERATED_";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通过DS执行sql
|
* 通过DS执行sql
|
||||||
*/
|
*/
|
||||||
|
|||||||
-11
@@ -1,24 +1,13 @@
|
|||||||
package com.alibaba.otter.canal.client.adapter.support;
|
package com.alibaba.otter.canal.client.adapter.support;
|
||||||
|
|
||||||
import static org.mockito.AdditionalMatchers.or;
|
|
||||||
import static org.mockito.Matchers.isA;
|
|
||||||
import static org.mockito.Matchers.isNull;
|
|
||||||
import static org.powermock.api.mockito.PowerMockito.mockStatic;
|
|
||||||
|
|
||||||
import com.alibaba.otter.canal.client.adapter.support.JdbcTypeUtil;
|
|
||||||
import com.alibaba.otter.canal.client.adapter.support.Util;
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.rules.ExpectedException;
|
import org.junit.rules.ExpectedException;
|
||||||
import org.junit.rules.Timeout;
|
import org.junit.rules.Timeout;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.powermock.api.mockito.PowerMockito;
|
|
||||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
|
||||||
import org.powermock.modules.junit4.PowerMockRunner;
|
import org.powermock.modules.junit4.PowerMockRunner;
|
||||||
|
|
||||||
import java.lang.reflect.Array;
|
|
||||||
|
|
||||||
@RunWith(PowerMockRunner.class)
|
@RunWith(PowerMockRunner.class)
|
||||||
public class JdbcTypeUtilTest {
|
public class JdbcTypeUtilTest {
|
||||||
|
|
||||||
|
|||||||
-2
@@ -3,9 +3,7 @@ package com.alibaba.otter.canal.client.adapter.support;
|
|||||||
import static org.mockito.AdditionalMatchers.or;
|
import static org.mockito.AdditionalMatchers.or;
|
||||||
import static org.mockito.Matchers.isA;
|
import static org.mockito.Matchers.isA;
|
||||||
import static org.mockito.Matchers.isNull;
|
import static org.mockito.Matchers.isNull;
|
||||||
import static org.powermock.api.mockito.PowerMockito.mockStatic;
|
|
||||||
|
|
||||||
import com.alibaba.otter.canal.client.adapter.support.Util;
|
|
||||||
import com.diffblue.deeptestutils.mock.DTUMemberMatcher;
|
import com.diffblue.deeptestutils.mock.DTUMemberMatcher;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<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.5-SNAPSHOT</version>
|
<version>1.1.7-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
@@ -29,22 +27,22 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.elasticsearch</groupId>
|
<groupId>org.elasticsearch</groupId>
|
||||||
<artifactId>elasticsearch</artifactId>
|
<artifactId>elasticsearch</artifactId>
|
||||||
<version>6.4.3</version>
|
<version>6.8.22</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.elasticsearch.client</groupId>
|
<groupId>org.elasticsearch.client</groupId>
|
||||||
<artifactId>transport</artifactId>
|
<artifactId>transport</artifactId>
|
||||||
<version>6.4.3</version>
|
<version>6.8.22</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.elasticsearch.client</groupId>
|
<groupId>org.elasticsearch.client</groupId>
|
||||||
<artifactId>elasticsearch-rest-client</artifactId>
|
<artifactId>elasticsearch-rest-client</artifactId>
|
||||||
<version>6.4.3</version>
|
<version>6.8.22</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.elasticsearch.client</groupId>
|
<groupId>org.elasticsearch.client</groupId>
|
||||||
<artifactId>elasticsearch-rest-high-level-client</artifactId>
|
<artifactId>elasticsearch-rest-high-level-client</artifactId>
|
||||||
<version>6.4.3</version>
|
<version>6.8.22</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
+4
-3
@@ -5,6 +5,10 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
|
import org.elasticsearch.action.search.SearchResponse;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.ESAdapter;
|
import com.alibaba.otter.canal.client.adapter.es.core.ESAdapter;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.config.ESSyncConfig;
|
import com.alibaba.otter.canal.client.adapter.es.core.config.ESSyncConfig;
|
||||||
import com.alibaba.otter.canal.client.adapter.es6x.etl.ESEtlService;
|
import com.alibaba.otter.canal.client.adapter.es6x.etl.ESEtlService;
|
||||||
@@ -14,9 +18,6 @@ import com.alibaba.otter.canal.client.adapter.support.DatasourceConfig;
|
|||||||
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.OuterAdapterConfig;
|
import com.alibaba.otter.canal.client.adapter.support.OuterAdapterConfig;
|
||||||
import com.alibaba.otter.canal.client.adapter.support.SPI;
|
import com.alibaba.otter.canal.client.adapter.support.SPI;
|
||||||
import org.elasticsearch.action.search.SearchResponse;
|
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ES 6.x 外部适配器
|
* ES 6.x 外部适配器
|
||||||
|
|||||||
+111
-113
@@ -10,7 +10,6 @@ import java.util.concurrent.atomic.AtomicLong;
|
|||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.elasticsearch.action.bulk.BulkResponse;
|
|
||||||
import org.elasticsearch.action.search.SearchResponse;
|
import org.elasticsearch.action.search.SearchResponse;
|
||||||
import org.elasticsearch.index.query.QueryBuilders;
|
import org.elasticsearch.index.query.QueryBuilders;
|
||||||
import org.elasticsearch.search.SearchHit;
|
import org.elasticsearch.search.SearchHit;
|
||||||
@@ -79,119 +78,118 @@ public class ESEtlService extends AbstractEtlService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 如果是主键字段则不插入
|
// 如果是主键字段则不插入
|
||||||
if (fieldItem.getFieldName().equals(mapping.get_id())) {
|
if (fieldItem.getFieldName().equals(mapping.get_id())) {
|
||||||
idVal = esTemplate.getValFromRS(mapping, rs, fieldName, fieldName);
|
idVal = esTemplate.getValFromRS(mapping, rs, fieldName, fieldName);
|
||||||
} else {
|
} else {
|
||||||
Object val = esTemplate.getValFromRS(mapping, rs, fieldName, fieldName);
|
Object val = esTemplate.getValFromRS(mapping, rs, fieldName, fieldName);
|
||||||
esFieldData.put(Util.cleanColumn(fieldName), val);
|
esFieldData.put(Util.cleanColumn(fieldName), val);
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mapping.getRelations().isEmpty()) {
|
|
||||||
mapping.getRelations().forEach((relationField, relationMapping) -> {
|
|
||||||
Map<String, Object> relations = new HashMap<>();
|
|
||||||
relations.put("name", relationMapping.getName());
|
|
||||||
if (StringUtils.isNotEmpty(relationMapping.getParent())) {
|
|
||||||
FieldItem parentFieldItem = mapping.getSchemaItem()
|
|
||||||
.getSelectFields()
|
|
||||||
.get(relationMapping.getParent());
|
|
||||||
Object parentVal;
|
|
||||||
try {
|
|
||||||
parentVal = esTemplate.getValFromRS(mapping,
|
|
||||||
rs,
|
|
||||||
parentFieldItem.getFieldName(),
|
|
||||||
parentFieldItem.getFieldName());
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
if (parentVal != null) {
|
|
||||||
relations.put("parent", parentVal.toString());
|
|
||||||
esFieldData.put("$parent_routing", parentVal.toString());
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
esFieldData.put(Util.cleanColumn(relationField), relations);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (idVal != null) {
|
|
||||||
String parentVal = (String) esFieldData.remove("$parent_routing");
|
|
||||||
if (mapping.isUpsert()) {
|
|
||||||
ESUpdateRequest esUpdateRequest = this.esConnection.new ES6xUpdateRequest(
|
|
||||||
mapping.get_index(),
|
|
||||||
mapping.get_type(),
|
|
||||||
idVal.toString()).setDoc(esFieldData).setDocAsUpsert(true);
|
|
||||||
|
|
||||||
if (StringUtils.isNotEmpty(parentVal)) {
|
|
||||||
esUpdateRequest.setRouting(parentVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
esBulkRequest.add(esUpdateRequest);
|
|
||||||
} else {
|
|
||||||
ESIndexRequest esIndexRequest = this.esConnection.new ES6xIndexRequest(mapping
|
|
||||||
.get_index(), mapping.get_type(), idVal.toString()).setSource(esFieldData);
|
|
||||||
if (StringUtils.isNotEmpty(parentVal)) {
|
|
||||||
esIndexRequest.setRouting(parentVal);
|
|
||||||
}
|
|
||||||
esBulkRequest.add(esIndexRequest);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
idVal = esFieldData.get(mapping.getPk());
|
|
||||||
ESSearchRequest esSearchRequest = this.esConnection.new ESSearchRequest(mapping.get_index(),
|
|
||||||
mapping.get_type()).setQuery(QueryBuilders.termQuery(mapping.getPk(), idVal))
|
|
||||||
.size(10000);
|
|
||||||
SearchResponse response = esSearchRequest.getResponse();
|
|
||||||
for (SearchHit hit : response.getHits()) {
|
|
||||||
ESUpdateRequest esUpdateRequest = this.esConnection.new ES6xUpdateRequest(mapping
|
|
||||||
.get_index(), mapping.get_type(), hit.getId()).setDoc(esFieldData);
|
|
||||||
esBulkRequest.add(esUpdateRequest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (esBulkRequest.numberOfActions() % mapping.getCommitBatch() == 0
|
|
||||||
&& esBulkRequest.numberOfActions() > 0) {
|
|
||||||
long esBatchBegin = System.currentTimeMillis();
|
|
||||||
ESBulkResponse rp = esBulkRequest.bulk();
|
|
||||||
if (rp.hasFailures()) {
|
|
||||||
rp.processFailBulkResponse("全量数据 etl 异常 ");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (logger.isTraceEnabled()) {
|
|
||||||
logger.trace("全量数据批量导入批次耗时: {}, es执行时间: {}, 批次大小: {}, index; {}",
|
|
||||||
(System.currentTimeMillis() - batchBegin),
|
|
||||||
(System.currentTimeMillis() - esBatchBegin),
|
|
||||||
esBulkRequest.numberOfActions(),
|
|
||||||
mapping.get_index());
|
|
||||||
}
|
|
||||||
batchBegin = System.currentTimeMillis();
|
|
||||||
esBulkRequest.resetBulk();
|
|
||||||
}
|
|
||||||
count++;
|
|
||||||
impCount.incrementAndGet();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (esBulkRequest.numberOfActions() > 0) {
|
|
||||||
long esBatchBegin = System.currentTimeMillis();
|
|
||||||
ESBulkResponse rp = esBulkRequest.bulk();
|
|
||||||
if (rp.hasFailures()) {
|
|
||||||
rp.processFailBulkResponse("全量数据 etl 异常 ");
|
|
||||||
}
|
|
||||||
if (logger.isTraceEnabled()) {
|
|
||||||
logger.trace("全量数据批量导入最后批次耗时: {}, es执行时间: {}, 批次大小: {}, index; {}",
|
|
||||||
(System.currentTimeMillis() - batchBegin),
|
|
||||||
(System.currentTimeMillis() - esBatchBegin),
|
|
||||||
esBulkRequest.numberOfActions(),
|
|
||||||
mapping.get_index());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e.getMessage(), e);
|
|
||||||
errMsg.add(mapping.get_index() + " etl failed! ==>" + e.getMessage());
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
}
|
||||||
return count;
|
|
||||||
});
|
}
|
||||||
|
|
||||||
|
if (!mapping.getRelations().isEmpty()) {
|
||||||
|
mapping.getRelations().forEach((relationField, relationMapping) -> {
|
||||||
|
Map<String, Object> relations = new HashMap<>();
|
||||||
|
relations.put("name", relationMapping.getName());
|
||||||
|
if (StringUtils.isNotEmpty(relationMapping.getParent())) {
|
||||||
|
FieldItem parentFieldItem = mapping.getSchemaItem()
|
||||||
|
.getSelectFields()
|
||||||
|
.get(relationMapping.getParent());
|
||||||
|
Object parentVal;
|
||||||
|
try {
|
||||||
|
parentVal = esTemplate.getValFromRS(mapping,
|
||||||
|
rs,
|
||||||
|
parentFieldItem.getFieldName(),
|
||||||
|
parentFieldItem.getFieldName());
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
if (parentVal != null) {
|
||||||
|
relations.put("parent", parentVal.toString());
|
||||||
|
esFieldData.put("$parent_routing", parentVal.toString());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
esFieldData.put(Util.cleanColumn(relationField), relations);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idVal != null) {
|
||||||
|
String parentVal = (String) esFieldData.remove("$parent_routing");
|
||||||
|
if (mapping.isUpsert()) {
|
||||||
|
ESUpdateRequest esUpdateRequest = this.esConnection.new ES6xUpdateRequest(mapping.get_index(),
|
||||||
|
mapping.get_type(),
|
||||||
|
idVal.toString()).setDoc(esFieldData).setDocAsUpsert(true);
|
||||||
|
|
||||||
|
if (StringUtils.isNotEmpty(parentVal)) {
|
||||||
|
esUpdateRequest.setRouting(parentVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
esBulkRequest.add(esUpdateRequest);
|
||||||
|
} else {
|
||||||
|
ESIndexRequest esIndexRequest = this.esConnection.new ES6xIndexRequest(mapping.get_index(),
|
||||||
|
mapping.get_type(),
|
||||||
|
idVal.toString()).setSource(esFieldData);
|
||||||
|
if (StringUtils.isNotEmpty(parentVal)) {
|
||||||
|
esIndexRequest.setRouting(parentVal);
|
||||||
|
}
|
||||||
|
esBulkRequest.add(esIndexRequest);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
idVal = esFieldData.get(mapping.getPk());
|
||||||
|
ESSearchRequest esSearchRequest = this.esConnection.new ESSearchRequest(mapping.get_index(),
|
||||||
|
mapping.get_type()).setQuery(QueryBuilders.termQuery(mapping.getPk(), idVal)).size(10000);
|
||||||
|
SearchResponse response = esSearchRequest.getResponse();
|
||||||
|
for (SearchHit hit : response.getHits()) {
|
||||||
|
ESUpdateRequest esUpdateRequest = this.esConnection.new ES6xUpdateRequest(mapping.get_index(),
|
||||||
|
mapping.get_type(),
|
||||||
|
hit.getId()).setDoc(esFieldData);
|
||||||
|
esBulkRequest.add(esUpdateRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (esBulkRequest.numberOfActions() % mapping.getCommitBatch() == 0 && esBulkRequest.numberOfActions() > 0) {
|
||||||
|
long esBatchBegin = System.currentTimeMillis();
|
||||||
|
ESBulkResponse rp = esBulkRequest.bulk();
|
||||||
|
if (rp.hasFailures()) {
|
||||||
|
rp.processFailBulkResponse("全量数据 etl 异常 ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logger.isTraceEnabled()) {
|
||||||
|
logger.trace("全量数据批量导入批次耗时: {}, es执行时间: {}, 批次大小: {}, index; {}",
|
||||||
|
(System.currentTimeMillis() - batchBegin),
|
||||||
|
(System.currentTimeMillis() - esBatchBegin),
|
||||||
|
esBulkRequest.numberOfActions(),
|
||||||
|
mapping.get_index());
|
||||||
|
}
|
||||||
|
batchBegin = System.currentTimeMillis();
|
||||||
|
esBulkRequest.resetBulk();
|
||||||
|
}
|
||||||
|
count++;
|
||||||
|
impCount.incrementAndGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (esBulkRequest.numberOfActions() > 0) {
|
||||||
|
long esBatchBegin = System.currentTimeMillis();
|
||||||
|
ESBulkResponse rp = esBulkRequest.bulk();
|
||||||
|
if (rp.hasFailures()) {
|
||||||
|
rp.processFailBulkResponse("全量数据 etl 异常 ");
|
||||||
|
}
|
||||||
|
if (logger.isTraceEnabled()) {
|
||||||
|
logger.trace("全量数据批量导入最后批次耗时: {}, es执行时间: {}, 批次大小: {}, index; {}",
|
||||||
|
(System.currentTimeMillis() - batchBegin),
|
||||||
|
(System.currentTimeMillis() - esBatchBegin),
|
||||||
|
esBulkRequest.numberOfActions(),
|
||||||
|
mapping.get_index());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error(e.getMessage(), e);
|
||||||
|
errMsg.add(mapping.get_index() + " etl failed! ==>" + e.getMessage());
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
} );
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
+12
-4
@@ -2,7 +2,11 @@ package com.alibaba.otter.canal.client.adapter.es6x.support;
|
|||||||
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
@@ -20,8 +24,8 @@ import org.slf4j.LoggerFactory;
|
|||||||
import com.alibaba.otter.canal.client.adapter.es.core.config.ESSyncConfig;
|
import com.alibaba.otter.canal.client.adapter.es.core.config.ESSyncConfig;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.config.ESSyncConfig.ESMapping;
|
import com.alibaba.otter.canal.client.adapter.es.core.config.ESSyncConfig.ESMapping;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.config.SchemaItem;
|
import com.alibaba.otter.canal.client.adapter.es.core.config.SchemaItem;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.config.SchemaItem.FieldItem;
|
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.config.SchemaItem.ColumnItem;
|
import com.alibaba.otter.canal.client.adapter.es.core.config.SchemaItem.ColumnItem;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.es.core.config.SchemaItem.FieldItem;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.support.ESBulkRequest;
|
import com.alibaba.otter.canal.client.adapter.es.core.support.ESBulkRequest;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.support.ESBulkRequest.ESBulkResponse;
|
import com.alibaba.otter.canal.client.adapter.es.core.support.ESBulkRequest.ESBulkResponse;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.support.ESBulkRequest.ESDeleteRequest;
|
import com.alibaba.otter.canal.client.adapter.es.core.support.ESBulkRequest.ESDeleteRequest;
|
||||||
@@ -312,13 +316,17 @@ public class ES6xTemplate implements ESTemplate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getESDataFromDmlData(ESSyncConfig.ESMapping mapping, Map<String, Object> dmlData,
|
public Object getESDataFromDmlData(ESSyncConfig.ESMapping mapping,String owner, Map<String, Object> dmlData,
|
||||||
Map<String, Object> dmlOld, Map<String, Object> esFieldData) {
|
Map<String, Object> dmlOld, Map<String, Object> esFieldData) {
|
||||||
SchemaItem schemaItem = mapping.getSchemaItem();
|
SchemaItem schemaItem = mapping.getSchemaItem();
|
||||||
String idFieldName = mapping.get_id() == null ? mapping.getPk() : mapping.get_id();
|
String idFieldName = mapping.get_id() == null ? mapping.getPk() : mapping.get_id();
|
||||||
Object resultIdVal = null;
|
Object resultIdVal = null;
|
||||||
for (FieldItem fieldItem : schemaItem.getSelectFields().values()) {
|
for (FieldItem fieldItem : schemaItem.getSelectFields().values()) {
|
||||||
String columnName = fieldItem.getColumnItems().iterator().next().getColumnName();
|
ColumnItem columnItem = fieldItem.getColumnItems().iterator().next();
|
||||||
|
if (!columnItem.getOwner().equals(owner)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String columnName = columnItem.getColumnName();
|
||||||
|
|
||||||
if (fieldItem.getFieldName().equals(idFieldName)) {
|
if (fieldItem.getFieldName().equals(idFieldName)) {
|
||||||
resultIdVal = getValFromData(mapping, dmlData, fieldItem.getFieldName(), columnName);
|
resultIdVal = getValFromData(mapping, dmlData, fieldItem.getFieldName(), columnName);
|
||||||
|
|||||||
+36
-16
@@ -2,10 +2,12 @@ package com.alibaba.otter.canal.client.adapter.es6x.support;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.support.ESBulkRequest;
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.apache.http.HttpHost;
|
import org.apache.http.HttpHost;
|
||||||
import org.apache.http.auth.AuthScope;
|
import org.apache.http.auth.AuthScope;
|
||||||
@@ -27,7 +29,11 @@ import org.elasticsearch.action.search.SearchRequestBuilder;
|
|||||||
import org.elasticsearch.action.search.SearchResponse;
|
import org.elasticsearch.action.search.SearchResponse;
|
||||||
import org.elasticsearch.action.update.UpdateRequest;
|
import org.elasticsearch.action.update.UpdateRequest;
|
||||||
import org.elasticsearch.action.update.UpdateRequestBuilder;
|
import org.elasticsearch.action.update.UpdateRequestBuilder;
|
||||||
import org.elasticsearch.client.*;
|
import org.elasticsearch.client.RequestOptions;
|
||||||
|
import org.elasticsearch.client.RestClient;
|
||||||
|
import org.elasticsearch.client.RestClientBuilder;
|
||||||
|
import org.elasticsearch.client.RestHighLevelClient;
|
||||||
|
import org.elasticsearch.client.RestHighLevelClientExt;
|
||||||
import org.elasticsearch.client.transport.TransportClient;
|
import org.elasticsearch.client.transport.TransportClient;
|
||||||
import org.elasticsearch.cluster.metadata.MappingMetaData;
|
import org.elasticsearch.cluster.metadata.MappingMetaData;
|
||||||
import org.elasticsearch.common.collect.ImmutableOpenMap;
|
import org.elasticsearch.common.collect.ImmutableOpenMap;
|
||||||
@@ -40,6 +46,8 @@ import org.elasticsearch.transport.client.PreBuiltTransportClient;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.client.adapter.es.core.support.ESBulkRequest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ES 连接器, Transport Rest 两种方式
|
* ES 连接器, Transport Rest 两种方式
|
||||||
*
|
*
|
||||||
@@ -51,7 +59,7 @@ public class ESConnection {
|
|||||||
private static final Logger logger = LoggerFactory.getLogger(ESConnection.class);
|
private static final Logger logger = LoggerFactory.getLogger(ESConnection.class);
|
||||||
|
|
||||||
public enum ESClientMode {
|
public enum ESClientMode {
|
||||||
TRANSPORT, REST
|
TRANSPORT, REST
|
||||||
}
|
}
|
||||||
|
|
||||||
private ESClientMode mode;
|
private ESClientMode mode;
|
||||||
@@ -73,23 +81,15 @@ public class ESConnection {
|
|||||||
Integer.parseInt(host.substring(i + 1))));
|
Integer.parseInt(host.substring(i + 1))));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
HttpHost[] httpHosts = new HttpHost[hosts.length];
|
HttpHost[] httpHosts = Arrays.stream(hosts).map(this::createHttpHost).toArray(HttpHost[]::new);
|
||||||
for (int i = 0; i < hosts.length; i++) {
|
|
||||||
String host = hosts[i];
|
|
||||||
int j = host.indexOf(":");
|
|
||||||
HttpHost httpHost = new HttpHost(InetAddress.getByName(host.substring(0, j)),
|
|
||||||
Integer.parseInt(host.substring(j + 1)));
|
|
||||||
httpHosts[i] = httpHost;
|
|
||||||
}
|
|
||||||
RestClientBuilder restClientBuilder = RestClient.builder(httpHosts);
|
RestClientBuilder restClientBuilder = RestClient.builder(httpHosts);
|
||||||
String nameAndPwd = properties.get("security.auth");
|
String nameAndPwd = properties.get("security.auth");
|
||||||
if (StringUtils.isNotEmpty(nameAndPwd) && nameAndPwd.contains(":")) {
|
if (StringUtils.isNotEmpty(nameAndPwd) && nameAndPwd.contains(":")) {
|
||||||
String[] nameAndPwdArr = nameAndPwd.split(":");
|
String[] nameAndPwdArr = nameAndPwd.split(":");
|
||||||
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
|
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
|
||||||
credentialsProvider.setCredentials(AuthScope.ANY,
|
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(nameAndPwdArr[0],
|
||||||
new UsernamePasswordCredentials(nameAndPwdArr[0], nameAndPwdArr[1]));
|
nameAndPwdArr[1]));
|
||||||
restClientBuilder.setHttpClientConfigCallback(
|
restClientBuilder.setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider));
|
||||||
httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider));
|
|
||||||
}
|
}
|
||||||
restHighLevelClient = new RestHighLevelClient(restClientBuilder);
|
restHighLevelClient = new RestHighLevelClient(restClientBuilder);
|
||||||
}
|
}
|
||||||
@@ -150,7 +150,13 @@ public class ESConnection {
|
|||||||
logger.error(e.getMessage(), e);
|
logger.error(e.getMessage(), e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
mappingMetaData = mappings.get(index).get(type);
|
|
||||||
|
//通过别名查询mapping返回的是真实索引名称,mappings.get(index)返回null,为兼容别名情况修改如下:
|
||||||
|
ImmutableOpenMap<String, MappingMetaData> esIndex = mappings.get(index);
|
||||||
|
if(esIndex == null){
|
||||||
|
esIndex = mappings.valuesIt().next();
|
||||||
|
}
|
||||||
|
mappingMetaData = esIndex.get(type);
|
||||||
}
|
}
|
||||||
return mappingMetaData;
|
return mappingMetaData;
|
||||||
}
|
}
|
||||||
@@ -418,6 +424,7 @@ public class ESConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public ESBulkResponse bulk() {
|
public ESBulkResponse bulk() {
|
||||||
if (mode == ESClientMode.TRANSPORT) {
|
if (mode == ESClientMode.TRANSPORT) {
|
||||||
BulkResponse responses = bulkRequestBuilder.execute().actionGet();
|
BulkResponse responses = bulkRequestBuilder.execute().actionGet();
|
||||||
@@ -502,4 +509,17 @@ public class ESConnection {
|
|||||||
public void setRestHighLevelClient(RestHighLevelClient restHighLevelClient) {
|
public void setRestHighLevelClient(RestHighLevelClient restHighLevelClient) {
|
||||||
this.restHighLevelClient = restHighLevelClient;
|
this.restHighLevelClient = restHighLevelClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private HttpHost createHttpHost(String uriStr) {
|
||||||
|
URI uri = URI.create(uriStr);
|
||||||
|
if (!org.springframework.util.StringUtils.hasLength(uri.getUserInfo())) {
|
||||||
|
return HttpHost.create(uri.toString());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return HttpHost.create(new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(), uri.getPath(),
|
||||||
|
uri.getQuery(), uri.getFragment()).toString());
|
||||||
|
} catch (URISyntaxException ex) {
|
||||||
|
throw new IllegalStateException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-1
@@ -1,6 +1,10 @@
|
|||||||
package com.alibaba.otter.canal.client.adapter.es6x.test.sync;
|
package com.alibaba.otter.canal.client.adapter.es6x.test.sync;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
|
|||||||
+5
-1
@@ -1,6 +1,10 @@
|
|||||||
package com.alibaba.otter.canal.client.adapter.es6x.test.sync;
|
package com.alibaba.otter.canal.client.adapter.es6x.test.sync;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
|
|||||||
+5
-1
@@ -1,6 +1,10 @@
|
|||||||
package com.alibaba.otter.canal.client.adapter.es6x.test.sync;
|
package com.alibaba.otter.canal.client.adapter.es6x.test.sync;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
|
|||||||
+5
-1
@@ -1,6 +1,10 @@
|
|||||||
package com.alibaba.otter.canal.client.adapter.es6x.test.sync;
|
package com.alibaba.otter.canal.client.adapter.es6x.test.sync;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
|
|||||||
+5
-1
@@ -1,6 +1,10 @@
|
|||||||
package com.alibaba.otter.canal.client.adapter.es6x.test.sync;
|
package com.alibaba.otter.canal.client.adapter.es6x.test.sync;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
|
|||||||
+5
-1
@@ -1,6 +1,10 @@
|
|||||||
package com.alibaba.otter.canal.client.adapter.es6x.test.sync;
|
package com.alibaba.otter.canal.client.adapter.es6x.test.sync;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.elasticsearch.action.get.GetResponse;
|
import org.elasticsearch.action.get.GetResponse;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<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.5-SNAPSHOT</version>
|
<version>1.1.7-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|||||||
+4
-4
@@ -5,20 +5,20 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.client.adapter.es7x.etl.ESEtlService;
|
import javax.sql.DataSource;
|
||||||
import com.alibaba.otter.canal.client.adapter.support.DatasourceConfig;
|
|
||||||
import org.elasticsearch.action.search.SearchResponse;
|
import org.elasticsearch.action.search.SearchResponse;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.ESAdapter;
|
import com.alibaba.otter.canal.client.adapter.es.core.ESAdapter;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.config.ESSyncConfig;
|
import com.alibaba.otter.canal.client.adapter.es.core.config.ESSyncConfig;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.es7x.etl.ESEtlService;
|
||||||
import com.alibaba.otter.canal.client.adapter.es7x.support.ES7xTemplate;
|
import com.alibaba.otter.canal.client.adapter.es7x.support.ES7xTemplate;
|
||||||
import com.alibaba.otter.canal.client.adapter.es7x.support.ESConnection;
|
import com.alibaba.otter.canal.client.adapter.es7x.support.ESConnection;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.support.DatasourceConfig;
|
||||||
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.OuterAdapterConfig;
|
import com.alibaba.otter.canal.client.adapter.support.OuterAdapterConfig;
|
||||||
import com.alibaba.otter.canal.client.adapter.support.SPI;
|
import com.alibaba.otter.canal.client.adapter.support.SPI;
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ES 7.x 外部适配器
|
* ES 7.x 外部适配器
|
||||||
*
|
*
|
||||||
|
|||||||
+12
-5
@@ -2,7 +2,11 @@ package com.alibaba.otter.canal.client.adapter.es7x.support;
|
|||||||
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
@@ -283,8 +287,7 @@ public class ES7xTemplate implements ESTemplate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getESDataFromDmlData(ESMapping mapping, Map<String, Object> dmlData,
|
public Object getESDataFromDmlData(ESMapping mapping, Map<String, Object> dmlData, Map<String, Object> esFieldData) {
|
||||||
Map<String, Object> esFieldData) {
|
|
||||||
SchemaItem schemaItem = mapping.getSchemaItem();
|
SchemaItem schemaItem = mapping.getSchemaItem();
|
||||||
String idFieldName = mapping.get_id() == null ? mapping.getPk() : mapping.get_id();
|
String idFieldName = mapping.get_id() == null ? mapping.getPk() : mapping.get_id();
|
||||||
Object resultIdVal = null;
|
Object resultIdVal = null;
|
||||||
@@ -308,13 +311,17 @@ public class ES7xTemplate implements ESTemplate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getESDataFromDmlData(ESMapping mapping, Map<String, Object> dmlData, Map<String, Object> dmlOld,
|
public Object getESDataFromDmlData(ESMapping mapping,String owner, Map<String, Object> dmlData, Map<String, Object> dmlOld,
|
||||||
Map<String, Object> esFieldData) {
|
Map<String, Object> esFieldData) {
|
||||||
SchemaItem schemaItem = mapping.getSchemaItem();
|
SchemaItem schemaItem = mapping.getSchemaItem();
|
||||||
String idFieldName = mapping.get_id() == null ? mapping.getPk() : mapping.get_id();
|
String idFieldName = mapping.get_id() == null ? mapping.getPk() : mapping.get_id();
|
||||||
Object resultIdVal = null;
|
Object resultIdVal = null;
|
||||||
for (FieldItem fieldItem : schemaItem.getSelectFields().values()) {
|
for (FieldItem fieldItem : schemaItem.getSelectFields().values()) {
|
||||||
String columnName = fieldItem.getColumnItems().iterator().next().getColumnName();
|
ColumnItem columnItem = fieldItem.getColumnItems().iterator().next();
|
||||||
|
if (!columnItem.getOwner().equals(owner)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String columnName = columnItem.getColumnName();
|
||||||
|
|
||||||
if (fieldItem.getFieldName().equals(idFieldName)) {
|
if (fieldItem.getFieldName().equals(idFieldName)) {
|
||||||
resultIdVal = getValFromData(mapping, dmlData, fieldItem.getFieldName(), columnName);
|
resultIdVal = getValFromData(mapping, dmlData, fieldItem.getFieldName(), columnName);
|
||||||
|
|||||||
+22
-13
@@ -2,7 +2,10 @@ package com.alibaba.otter.canal.client.adapter.es7x.support;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
@@ -54,11 +57,12 @@ public class ESConnection {
|
|||||||
private static final Logger logger = LoggerFactory.getLogger(ESConnection.class);
|
private static final Logger logger = LoggerFactory.getLogger(ESConnection.class);
|
||||||
|
|
||||||
public enum ESClientMode {
|
public enum ESClientMode {
|
||||||
TRANSPORT, REST
|
TRANSPORT, REST
|
||||||
}
|
}
|
||||||
|
|
||||||
private ESClientMode mode;
|
private ESClientMode mode;
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
private TransportClient transportClient;
|
private TransportClient transportClient;
|
||||||
|
|
||||||
private RestHighLevelClient restHighLevelClient;
|
private RestHighLevelClient restHighLevelClient;
|
||||||
@@ -76,23 +80,15 @@ public class ESConnection {
|
|||||||
Integer.parseInt(host.substring(i + 1))));
|
Integer.parseInt(host.substring(i + 1))));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
HttpHost[] httpHosts = new HttpHost[hosts.length];
|
HttpHost[] httpHosts = Arrays.stream(hosts).map(this::createHttpHost).toArray(HttpHost[]::new);
|
||||||
for (int i = 0; i < hosts.length; i++) {
|
|
||||||
String host = hosts[i];
|
|
||||||
int j = host.indexOf(":");
|
|
||||||
HttpHost httpHost = new HttpHost(InetAddress.getByName(host.substring(0, j)),
|
|
||||||
Integer.parseInt(host.substring(j + 1)));
|
|
||||||
httpHosts[i] = httpHost;
|
|
||||||
}
|
|
||||||
RestClientBuilder restClientBuilder = RestClient.builder(httpHosts);
|
RestClientBuilder restClientBuilder = RestClient.builder(httpHosts);
|
||||||
String nameAndPwd = properties.get("security.auth");
|
String nameAndPwd = properties.get("security.auth");
|
||||||
if (StringUtils.isNotEmpty(nameAndPwd) && nameAndPwd.contains(":")) {
|
if (StringUtils.isNotEmpty(nameAndPwd) && nameAndPwd.contains(":")) {
|
||||||
String[] nameAndPwdArr = nameAndPwd.split(":");
|
String[] nameAndPwdArr = nameAndPwd.split(":");
|
||||||
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
|
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
|
||||||
credentialsProvider.setCredentials(AuthScope.ANY,
|
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(nameAndPwdArr[0],
|
||||||
new UsernamePasswordCredentials(nameAndPwdArr[0], nameAndPwdArr[1]));
|
nameAndPwdArr[1]));
|
||||||
restClientBuilder.setHttpClientConfigCallback(
|
restClientBuilder.setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider));
|
||||||
httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider));
|
|
||||||
}
|
}
|
||||||
restHighLevelClient = new RestHighLevelClient(restClientBuilder);
|
restHighLevelClient = new RestHighLevelClient(restClientBuilder);
|
||||||
}
|
}
|
||||||
@@ -501,4 +497,17 @@ public class ESConnection {
|
|||||||
public void setRestHighLevelClient(RestHighLevelClient restHighLevelClient) {
|
public void setRestHighLevelClient(RestHighLevelClient restHighLevelClient) {
|
||||||
this.restHighLevelClient = restHighLevelClient;
|
this.restHighLevelClient = restHighLevelClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private HttpHost createHttpHost(String uriStr) {
|
||||||
|
URI uri = URI.create(uriStr);
|
||||||
|
if (!org.springframework.util.StringUtils.hasLength(uri.getUserInfo())) {
|
||||||
|
return HttpHost.create(uri.toString());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return HttpHost.create(new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(), uri.getPath(),
|
||||||
|
uri.getQuery(), uri.getFragment()).toString());
|
||||||
|
} catch (URISyntaxException ex) {
|
||||||
|
throw new IllegalStateException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -24,6 +24,7 @@ import org.junit.Test;
|
|||||||
@Ignore
|
@Ignore
|
||||||
public class ES7xTest {
|
public class ES7xTest {
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
private TransportClient transportClient;
|
private TransportClient transportClient;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@@ -65,8 +66,9 @@ public class ES7xTest {
|
|||||||
relations.put("parent", "2");
|
relations.put("parent", "2");
|
||||||
|
|
||||||
BulkRequestBuilder bulkRequestBuilder = transportClient.prepareBulk();
|
BulkRequestBuilder bulkRequestBuilder = transportClient.prepareBulk();
|
||||||
bulkRequestBuilder
|
bulkRequestBuilder.add(transportClient.prepareIndex("test", "osm", "2_4")
|
||||||
.add(transportClient.prepareIndex("test", "osm", "2_4").setRouting("2").setSource(esFieldData));
|
.setRouting("2")
|
||||||
|
.setSource(esFieldData));
|
||||||
commit(bulkRequestBuilder);
|
commit(bulkRequestBuilder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -8,9 +8,9 @@ import org.elasticsearch.cluster.metadata.MappingMetaData;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.client.adapter.es7x.support.ESConnection;
|
import com.alibaba.otter.canal.client.adapter.es7x.support.ESConnection;
|
||||||
import org.springframework.util.Assert;
|
|
||||||
|
|
||||||
@Ignore
|
@Ignore
|
||||||
public class ESConnectionTest {
|
public class ESConnectionTest {
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<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.5-SNAPSHOT</version>
|
<version>1.1.7-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
@@ -22,8 +20,9 @@
|
|||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.alibaba.fastsql</groupId>
|
<groupId>com.alibaba</groupId>
|
||||||
<artifactId>fastsql</artifactId>
|
<artifactId>druid</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
+54
-30
@@ -1,6 +1,5 @@
|
|||||||
package com.alibaba.otter.canal.client.adapter.es.core;
|
package com.alibaba.otter.canal.client.adapter.es.core;
|
||||||
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
@@ -8,7 +7,6 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.support.ESTemplate;
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
|
||||||
import com.alibaba.druid.pool.DruidDataSource;
|
import com.alibaba.druid.pool.DruidDataSource;
|
||||||
@@ -19,10 +17,8 @@ import com.alibaba.otter.canal.client.adapter.es.core.config.SchemaItem;
|
|||||||
import com.alibaba.otter.canal.client.adapter.es.core.config.SqlParser;
|
import com.alibaba.otter.canal.client.adapter.es.core.config.SqlParser;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.monitor.ESConfigMonitor;
|
import com.alibaba.otter.canal.client.adapter.es.core.monitor.ESConfigMonitor;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.service.ESSyncService;
|
import com.alibaba.otter.canal.client.adapter.es.core.service.ESSyncService;
|
||||||
import com.alibaba.otter.canal.client.adapter.support.DatasourceConfig;
|
import com.alibaba.otter.canal.client.adapter.es.core.support.ESTemplate;
|
||||||
import com.alibaba.otter.canal.client.adapter.support.Dml;
|
import com.alibaba.otter.canal.client.adapter.support.*;
|
||||||
import com.alibaba.otter.canal.client.adapter.support.EtlResult;
|
|
||||||
import com.alibaba.otter.canal.client.adapter.support.OuterAdapterConfig;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ES外部适配器
|
* ES外部适配器
|
||||||
@@ -43,6 +39,8 @@ public abstract class ESAdapter implements OuterAdapter {
|
|||||||
|
|
||||||
protected Properties envProperties;
|
protected Properties envProperties;
|
||||||
|
|
||||||
|
protected OuterAdapterConfig configuration;
|
||||||
|
|
||||||
public ESSyncService getEsSyncService() {
|
public ESSyncService getEsSyncService() {
|
||||||
return esSyncService;
|
return esSyncService;
|
||||||
}
|
}
|
||||||
@@ -59,23 +57,13 @@ public abstract class ESAdapter implements OuterAdapter {
|
|||||||
public void init(OuterAdapterConfig configuration, Properties envProperties) {
|
public void init(OuterAdapterConfig configuration, Properties envProperties) {
|
||||||
try {
|
try {
|
||||||
this.envProperties = envProperties;
|
this.envProperties = envProperties;
|
||||||
|
this.configuration = configuration;
|
||||||
Map<String, ESSyncConfig> esSyncConfigTmp = ESSyncConfigLoader.load(envProperties);
|
Map<String, ESSyncConfig> esSyncConfigTmp = ESSyncConfigLoader.load(envProperties);
|
||||||
// 过滤不匹配的key的配置
|
// 过滤不匹配的key的配置
|
||||||
esSyncConfigTmp.forEach((key, config) -> {
|
esSyncConfigTmp.forEach((key, config) -> {
|
||||||
if ((config.getOuterAdapterKey() == null && configuration.getKey() == null)
|
addConfig(key, config);
|
||||||
|| (config.getOuterAdapterKey() != null
|
|
||||||
&& config.getOuterAdapterKey().equalsIgnoreCase(configuration.getKey()))) {
|
|
||||||
esSyncConfig.put(key, config);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
for (Map.Entry<String, ESSyncConfig> entry : esSyncConfig.entrySet()) {
|
|
||||||
String configName = entry.getKey();
|
|
||||||
ESSyncConfig config = entry.getValue();
|
|
||||||
|
|
||||||
addSyncConfigToCache(configName, config);
|
|
||||||
}
|
|
||||||
|
|
||||||
esSyncService = new ESSyncService(esTemplate);
|
esSyncService = new ESSyncService(esTemplate);
|
||||||
|
|
||||||
esConfigMonitor = new ESConfigMonitor();
|
esConfigMonitor = new ESConfigMonitor();
|
||||||
@@ -139,7 +127,7 @@ public abstract class ESAdapter implements OuterAdapter {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addSyncConfigToCache(String configName, ESSyncConfig config) {
|
private void addSyncConfigToCache(String configName, ESSyncConfig config) {
|
||||||
Properties envProperties = this.envProperties;
|
Properties envProperties = this.envProperties;
|
||||||
SchemaItem schemaItem = SqlParser.parse(config.getEsMapping().getSql());
|
SchemaItem schemaItem = SqlParser.parse(config.getEsMapping().getSql());
|
||||||
config.getEsMapping().setSchemaItem(schemaItem);
|
config.getEsMapping().setSchemaItem(schemaItem);
|
||||||
@@ -154,24 +142,60 @@ public abstract class ESAdapter implements OuterAdapter {
|
|||||||
throw new RuntimeException("Not found the schema of jdbc-url: " + config.getDataSourceKey());
|
throw new RuntimeException("Not found the schema of jdbc-url: " + config.getDataSourceKey());
|
||||||
}
|
}
|
||||||
String schema = matcher.group(2);
|
String schema = matcher.group(2);
|
||||||
|
|
||||||
schemaItem.getAliasTableItems().values().forEach(tableItem -> {
|
schemaItem.getAliasTableItems().values().forEach(tableItem -> {
|
||||||
Map<String, ESSyncConfig> esSyncConfigMap;
|
Map<String, ESSyncConfig> esSyncConfigMap;
|
||||||
if (envProperties != null
|
String schemaKey = tableItem.getSchema() == null ? schema : tableItem.getSchema();
|
||||||
&& !"tcp".equalsIgnoreCase(envProperties.getProperty("canal.conf.mode"))) {
|
if (envProperties != null && !"tcp".equalsIgnoreCase(envProperties.getProperty("canal.conf.mode"))) {
|
||||||
esSyncConfigMap = dbTableEsSyncConfig
|
esSyncConfigMap = dbTableEsSyncConfig
|
||||||
.computeIfAbsent(StringUtils.trimToEmpty(config.getDestination()) + "-"
|
.computeIfAbsent(StringUtils.trimToEmpty(config.getDestination()) + "-"
|
||||||
+ StringUtils.trimToEmpty(config.getGroupId()) + "_" + schema + "-"
|
+ StringUtils.trimToEmpty(config.getGroupId()) + "_" + schemaKey + "-"
|
||||||
+ tableItem.getTableName(),
|
+ tableItem.getTableName(),
|
||||||
k -> new ConcurrentHashMap<>());
|
k -> new ConcurrentHashMap<>());
|
||||||
} else {
|
} else {
|
||||||
esSyncConfigMap = dbTableEsSyncConfig
|
esSyncConfigMap = dbTableEsSyncConfig.computeIfAbsent(
|
||||||
.computeIfAbsent(StringUtils.trimToEmpty(config.getDestination()) + "_" + schema + "-"
|
StringUtils.trimToEmpty(config.getDestination()) + "_" + schemaKey + "-" + tableItem.getTableName(),
|
||||||
+ tableItem.getTableName(),
|
k -> new ConcurrentHashMap<>());
|
||||||
k -> new ConcurrentHashMap<>());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
esSyncConfigMap.put(configName, config);
|
esSyncConfigMap.put(configName, config);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean addConfig(String fileName, ESSyncConfig config) {
|
||||||
|
if (match(config)) {
|
||||||
|
esSyncConfig.put(fileName, config);
|
||||||
|
addSyncConfigToCache(fileName, config);
|
||||||
|
FileName2KeyMapping.register(getClass().getAnnotation(SPI.class).value(), fileName, configuration.getKey());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateConfig(String fileName, ESSyncConfig config) {
|
||||||
|
if (config.getOuterAdapterKey() != null && !config.getOuterAdapterKey().equals(configuration.getKey())) {
|
||||||
|
// 理论上不允许改这个 因为本身就是通过这个关联起Adapter和Config的
|
||||||
|
throw new RuntimeException("not allow to change outAdapterKey");
|
||||||
|
}
|
||||||
|
esSyncConfig.put(fileName, config);
|
||||||
|
addSyncConfigToCache(fileName, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteConfig(String fileName) {
|
||||||
|
esSyncConfig.remove(fileName);
|
||||||
|
for (Map<String, ESSyncConfig> configMap : dbTableEsSyncConfig.values()) {
|
||||||
|
if (configMap != null) {
|
||||||
|
configMap.remove(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FileName2KeyMapping.unregister(getClass().getAnnotation(SPI.class).value(), fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean match(ESSyncConfig config) {
|
||||||
|
boolean sameMatch = config.getOuterAdapterKey() != null
|
||||||
|
&& config.getOuterAdapterKey().equalsIgnoreCase(configuration.getKey());
|
||||||
|
boolean prefixMatch = config.getOuterAdapterKey() == null && configuration.getKey()
|
||||||
|
.startsWith(StringUtils
|
||||||
|
.join(new String[] { Util.AUTO_GENERATED_PREFIX, config.getDestination(), config.getGroupId() }, '-'));
|
||||||
|
return sameMatch || prefixMatch;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-12
@@ -1,20 +1,31 @@
|
|||||||
package com.alibaba.otter.canal.client.adapter.es.core.config;
|
package com.alibaba.otter.canal.client.adapter.es.core.config;
|
||||||
|
|
||||||
import static com.alibaba.fastsql.sql.ast.expr.SQLBinaryOperator.BooleanAnd;
|
import static com.alibaba.druid.sql.ast.expr.SQLBinaryOperator.BooleanAnd;
|
||||||
import static com.alibaba.fastsql.sql.ast.expr.SQLBinaryOperator.Equality;
|
import static com.alibaba.druid.sql.ast.expr.SQLBinaryOperator.Equality;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import com.alibaba.fastsql.sql.SQLUtils;
|
import com.alibaba.druid.sql.SQLUtils;
|
||||||
import com.alibaba.fastsql.sql.ast.SQLExpr;
|
import com.alibaba.druid.sql.ast.SQLExpr;
|
||||||
import com.alibaba.fastsql.sql.ast.expr.*;
|
import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;
|
||||||
import com.alibaba.fastsql.sql.ast.statement.*;
|
import com.alibaba.druid.sql.ast.expr.SQLCaseExpr;
|
||||||
import com.alibaba.fastsql.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;
|
import com.alibaba.druid.sql.ast.expr.SQLCharExpr;
|
||||||
import com.alibaba.fastsql.sql.dialect.mysql.parser.MySqlStatementParser;
|
import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
|
||||||
import com.alibaba.fastsql.sql.parser.ParserException;
|
import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;
|
||||||
import com.alibaba.fastsql.sql.parser.SQLStatementParser;
|
import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr;
|
||||||
|
import com.alibaba.druid.sql.ast.statement.SQLExprTableSource;
|
||||||
|
import com.alibaba.druid.sql.ast.statement.SQLJoinTableSource;
|
||||||
|
import com.alibaba.druid.sql.ast.statement.SQLSelectGroupByClause;
|
||||||
|
import com.alibaba.druid.sql.ast.statement.SQLSelectItem;
|
||||||
|
import com.alibaba.druid.sql.ast.statement.SQLSelectStatement;
|
||||||
|
import com.alibaba.druid.sql.ast.statement.SQLSubqueryTableSource;
|
||||||
|
import com.alibaba.druid.sql.ast.statement.SQLTableSource;
|
||||||
|
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;
|
||||||
|
import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;
|
||||||
|
import com.alibaba.druid.sql.parser.ParserException;
|
||||||
|
import com.alibaba.druid.sql.parser.SQLStatementParser;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.config.SchemaItem.ColumnItem;
|
import com.alibaba.otter.canal.client.adapter.es.core.config.SchemaItem.ColumnItem;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.config.SchemaItem.FieldItem;
|
import com.alibaba.otter.canal.client.adapter.es.core.config.SchemaItem.FieldItem;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.config.SchemaItem.RelationFieldsPair;
|
import com.alibaba.otter.canal.client.adapter.es.core.config.SchemaItem.RelationFieldsPair;
|
||||||
@@ -219,8 +230,7 @@ public class SqlParser {
|
|||||||
}
|
}
|
||||||
FieldItem rightFieldItem = new FieldItem();
|
FieldItem rightFieldItem = new FieldItem();
|
||||||
visitColumn(sqlBinaryOpExpr.getRight(), rightFieldItem);
|
visitColumn(sqlBinaryOpExpr.getRight(), rightFieldItem);
|
||||||
if (rightFieldItem.getColumnItems().size() != 1 || rightFieldItem.isMethod()
|
if (rightFieldItem.getColumnItems().size() != 1 || rightFieldItem.isMethod() || rightFieldItem.isBinaryOp()) {
|
||||||
|| rightFieldItem.isBinaryOp()) {
|
|
||||||
throw new UnsupportedOperationException("Unsupported for complex of on-condition");
|
throw new UnsupportedOperationException("Unsupported for complex of on-condition");
|
||||||
}
|
}
|
||||||
tableItem.getRelationFields().add(new RelationFieldsPair(leftFieldItem, rightFieldItem));
|
tableItem.getRelationFields().add(new RelationFieldsPair(leftFieldItem, rightFieldItem));
|
||||||
|
|||||||
+30
-48
@@ -1,13 +1,12 @@
|
|||||||
package com.alibaba.otter.canal.client.adapter.es.core.monitor;
|
package com.alibaba.otter.canal.client.adapter.es.core.monitor;
|
||||||
|
|
||||||
import java.io.File;
|
import com.alibaba.otter.canal.client.adapter.config.YmlConfigBinder;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.ESAdapter;
|
import com.alibaba.otter.canal.client.adapter.es.core.ESAdapter;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.es.core.config.ESSyncConfig;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.support.MappingConfigsLoader;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.support.Util;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Properties;
|
||||||
import org.apache.commons.io.filefilter.FileFilterUtils;
|
import org.apache.commons.io.filefilter.FileFilterUtils;
|
||||||
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
|
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
|
||||||
import org.apache.commons.io.monitor.FileAlterationMonitor;
|
import org.apache.commons.io.monitor.FileAlterationMonitor;
|
||||||
@@ -15,15 +14,6 @@ import org.apache.commons.io.monitor.FileAlterationObserver;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import com.alibaba.druid.pool.DruidDataSource;
|
|
||||||
import com.alibaba.otter.canal.client.adapter.config.YmlConfigBinder;
|
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.config.ESSyncConfig;
|
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.config.SchemaItem;
|
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.config.SqlParser;
|
|
||||||
import com.alibaba.otter.canal.client.adapter.support.DatasourceConfig;
|
|
||||||
import com.alibaba.otter.canal.client.adapter.support.MappingConfigsLoader;
|
|
||||||
import com.alibaba.otter.canal.client.adapter.support.Util;
|
|
||||||
|
|
||||||
public class ESConfigMonitor {
|
public class ESConfigMonitor {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(ESConfigMonitor.class);
|
private static final Logger logger = LoggerFactory.getLogger(ESConfigMonitor.class);
|
||||||
@@ -43,7 +33,7 @@ public class ESConfigMonitor {
|
|||||||
File confDir = Util.getConfDirPath(adapterName);
|
File confDir = Util.getConfDirPath(adapterName);
|
||||||
try {
|
try {
|
||||||
FileAlterationObserver observer = new FileAlterationObserver(confDir,
|
FileAlterationObserver observer = new FileAlterationObserver(confDir,
|
||||||
FileFilterUtils.and(FileFilterUtils.fileFileFilter(), FileFilterUtils.suffixFileFilter("yml")));
|
FileFilterUtils.and(FileFilterUtils.fileFileFilter(), FileFilterUtils.suffixFileFilter("yml")));
|
||||||
FileListener listener = new FileListener();
|
FileListener listener = new FileListener();
|
||||||
observer.addListener(listener);
|
observer.addListener(listener);
|
||||||
fileMonitor = new FileAlterationMonitor(3000, observer);
|
fileMonitor = new FileAlterationMonitor(3000, observer);
|
||||||
@@ -70,12 +60,20 @@ public class ESConfigMonitor {
|
|||||||
try {
|
try {
|
||||||
// 加载新增的配置文件
|
// 加载新增的配置文件
|
||||||
String configContent = MappingConfigsLoader.loadConfig(adapterName + File.separator + file.getName());
|
String configContent = MappingConfigsLoader.loadConfig(adapterName + File.separator + file.getName());
|
||||||
ESSyncConfig config = YmlConfigBinder
|
ESSyncConfig config = YmlConfigBinder.bindYmlToObj(null,
|
||||||
.bindYmlToObj(null, configContent, ESSyncConfig.class, null, envProperties);
|
configContent,
|
||||||
|
ESSyncConfig.class,
|
||||||
|
null,
|
||||||
|
envProperties);
|
||||||
if (config != null) {
|
if (config != null) {
|
||||||
|
// 这里要记得设置esVersion bugfix
|
||||||
|
config.setEsVersion(adapterName);
|
||||||
config.validate();
|
config.validate();
|
||||||
addConfigToCache(file, config);
|
boolean result = esAdapter.addConfig(file.getName(), config);
|
||||||
logger.info("Add a new es mapping config: {} to canal adapter", file.getName());
|
if (result) {
|
||||||
|
logger.info("Add a new es mapping config: {} to canal adapter",
|
||||||
|
file.getName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error(e.getMessage(), e);
|
logger.error(e.getMessage(), e);
|
||||||
@@ -89,23 +87,24 @@ public class ESConfigMonitor {
|
|||||||
try {
|
try {
|
||||||
if (esAdapter.getEsSyncConfig().containsKey(file.getName())) {
|
if (esAdapter.getEsSyncConfig().containsKey(file.getName())) {
|
||||||
// 加载配置文件
|
// 加载配置文件
|
||||||
String configContent = MappingConfigsLoader
|
String configContent = MappingConfigsLoader.loadConfig(adapterName + File.separator
|
||||||
.loadConfig(adapterName + File.separator + file.getName());
|
+ file.getName());
|
||||||
if (configContent == null) {
|
if (configContent == null) {
|
||||||
onFileDelete(file);
|
onFileDelete(file);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ESSyncConfig config = YmlConfigBinder
|
ESSyncConfig config = YmlConfigBinder.bindYmlToObj(null,
|
||||||
.bindYmlToObj(null, configContent, ESSyncConfig.class, null, envProperties);
|
configContent,
|
||||||
|
ESSyncConfig.class,
|
||||||
|
null,
|
||||||
|
envProperties);
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// 这里要记得设置esVersion bugfix
|
||||||
|
config.setEsVersion(adapterName);
|
||||||
config.validate();
|
config.validate();
|
||||||
if (esAdapter.getEsSyncConfig().containsKey(file.getName())) {
|
esAdapter.updateConfig(file.getName(), config);
|
||||||
deleteConfigFromCache(file);
|
|
||||||
}
|
|
||||||
addConfigToCache(file, config);
|
|
||||||
|
|
||||||
logger.info("Change a es mapping config: {} of canal adapter", file.getName());
|
logger.info("Change a es mapping config: {} of canal adapter", file.getName());
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -119,29 +118,12 @@ public class ESConfigMonitor {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (esAdapter.getEsSyncConfig().containsKey(file.getName())) {
|
if (esAdapter.getEsSyncConfig().containsKey(file.getName())) {
|
||||||
deleteConfigFromCache(file);
|
esAdapter.deleteConfig(file.getName());
|
||||||
|
|
||||||
logger.info("Delete a es mapping config: {} of canal adapter", file.getName());
|
logger.info("Delete a es mapping config: {} of canal adapter", file.getName());
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error(e.getMessage(), e);
|
logger.error(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addConfigToCache(File file, ESSyncConfig config) {
|
|
||||||
esAdapter.getEsSyncConfig().put(file.getName(), config);
|
|
||||||
|
|
||||||
esAdapter.addSyncConfigToCache(file.getName(), config);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void deleteConfigFromCache(File file) {
|
|
||||||
esAdapter.getEsSyncConfig().remove(file.getName());
|
|
||||||
for (Map<String, ESSyncConfig> configMap : esAdapter.getDbTableEsSyncConfig().values()) {
|
|
||||||
if (configMap != null) {
|
|
||||||
configMap.remove(file.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-9
@@ -4,19 +4,19 @@ import java.util.*;
|
|||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
import com.alibaba.fastsql.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;
|
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.config.SqlParser;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;
|
||||||
import com.alibaba.fastjson.serializer.SerializerFeature;
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import com.alibaba.fastjson2.JSONWriter;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.config.ESSyncConfig;
|
import com.alibaba.otter.canal.client.adapter.es.core.config.ESSyncConfig;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.config.ESSyncConfig.ESMapping;
|
import com.alibaba.otter.canal.client.adapter.es.core.config.ESSyncConfig.ESMapping;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.config.SchemaItem;
|
import com.alibaba.otter.canal.client.adapter.es.core.config.SchemaItem;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.config.SchemaItem.ColumnItem;
|
import com.alibaba.otter.canal.client.adapter.es.core.config.SchemaItem.ColumnItem;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.config.SchemaItem.FieldItem;
|
import com.alibaba.otter.canal.client.adapter.es.core.config.SchemaItem.FieldItem;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.config.SchemaItem.TableItem;
|
import com.alibaba.otter.canal.client.adapter.es.core.config.SchemaItem.TableItem;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.es.core.config.SqlParser;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.support.ESSyncUtil;
|
import com.alibaba.otter.canal.client.adapter.es.core.support.ESSyncUtil;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.support.ESTemplate;
|
import com.alibaba.otter.canal.client.adapter.es.core.support.ESTemplate;
|
||||||
import com.alibaba.otter.canal.client.adapter.support.DatasourceConfig;
|
import com.alibaba.otter.canal.client.adapter.support.DatasourceConfig;
|
||||||
@@ -75,7 +75,7 @@ public class ESSyncService {
|
|||||||
esSyncConfigs
|
esSyncConfigs
|
||||||
.forEach(esSyncConfig -> configIndexes.append(esSyncConfig.getEsMapping().get_index()).append(" "));
|
.forEach(esSyncConfig -> configIndexes.append(esSyncConfig.getEsMapping().get_index()).append(" "));
|
||||||
logger.debug("DML: {} \nAffected indexes: {}",
|
logger.debug("DML: {} \nAffected indexes: {}",
|
||||||
JSON.toJSONString(dml, SerializerFeature.WriteMapNullValue),
|
JSON.toJSONString(dml, JSONWriter.Feature.WriteNulls),
|
||||||
configIndexes.toString());
|
configIndexes.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -205,7 +205,7 @@ public class ESSyncService {
|
|||||||
|
|
||||||
if (schemaItem.getAliasTableItems().size() == 1 && schemaItem.isAllFieldsSimple()) {
|
if (schemaItem.getAliasTableItems().size() == 1 && schemaItem.isAllFieldsSimple()) {
|
||||||
// ------单表 & 所有字段都为简单字段------
|
// ------单表 & 所有字段都为简单字段------
|
||||||
singleTableSimpleFiledUpdate(config, dml, data, old);
|
singleTableSimpleFiledUpdate(config, schemaItem.getMainTable().getAlias(), dml, data, old);
|
||||||
} else {
|
} else {
|
||||||
// ------主表 查询sql来更新------
|
// ------主表 查询sql来更新------
|
||||||
if (schemaItem.getMainTable().getTableName().equalsIgnoreCase(dml.getTable())) {
|
if (schemaItem.getMainTable().getTableName().equalsIgnoreCase(dml.getTable())) {
|
||||||
@@ -259,7 +259,7 @@ public class ESSyncService {
|
|||||||
|
|
||||||
// 判断主键和所更新的字段是否全为简单字段
|
// 判断主键和所更新的字段是否全为简单字段
|
||||||
if (idFieldSimple && allUpdateFieldSimple && !fkChanged) {
|
if (idFieldSimple && allUpdateFieldSimple && !fkChanged) {
|
||||||
singleTableSimpleFiledUpdate(config, dml, data, old);
|
singleTableSimpleFiledUpdate(config, schemaItem.getMainTable().getAlias(), dml, data, old);
|
||||||
} else {
|
} else {
|
||||||
mainTableUpdate(config, dml, data, old);
|
mainTableUpdate(config, dml, data, old);
|
||||||
}
|
}
|
||||||
@@ -806,12 +806,12 @@ public class ESSyncService {
|
|||||||
* @param data 单行data数据
|
* @param data 单行data数据
|
||||||
* @param old 单行old数据
|
* @param old 单行old数据
|
||||||
*/
|
*/
|
||||||
private void singleTableSimpleFiledUpdate(ESSyncConfig config, Dml dml, Map<String, Object> data,
|
private void singleTableSimpleFiledUpdate(ESSyncConfig config, String owner, Dml dml, Map<String, Object> data,
|
||||||
Map<String, Object> old) {
|
Map<String, Object> old) {
|
||||||
ESMapping mapping = config.getEsMapping();
|
ESMapping mapping = config.getEsMapping();
|
||||||
Map<String, Object> esFieldData = new LinkedHashMap<>();
|
Map<String, Object> esFieldData = new LinkedHashMap<>();
|
||||||
|
|
||||||
Object idVal = esTemplate.getESDataFromDmlData(mapping, data, old, esFieldData);
|
Object idVal = esTemplate.getESDataFromDmlData(mapping, owner, data, old, esFieldData);
|
||||||
|
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Main table update to es index, destination:{}, table: {}, index: {}, id: {}",
|
logger.trace("Main table update to es index, destination:{}, table: {}, index: {}, id: {}",
|
||||||
|
|||||||
+167
-146
@@ -5,14 +5,20 @@ import java.io.InputStream;
|
|||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.sql.Blob;
|
import java.sql.Blob;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.commons.codec.binary.Base64;
|
import org.apache.commons.codec.binary.Base64;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson2.JSON;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.config.ESSyncConfig.ESMapping;
|
import com.alibaba.otter.canal.client.adapter.es.core.config.ESSyncConfig.ESMapping;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.config.SchemaItem;
|
import com.alibaba.otter.canal.client.adapter.es.core.config.SchemaItem;
|
||||||
import com.alibaba.otter.canal.client.adapter.es.core.config.SchemaItem.ColumnItem;
|
import com.alibaba.otter.canal.client.adapter.es.core.config.SchemaItem.ColumnItem;
|
||||||
@@ -57,172 +63,187 @@ public class ESSyncUtil {
|
|||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
Object res = null;
|
Object res = null;
|
||||||
if ("integer".equals(esType)) {
|
switch (esType) {
|
||||||
if (val instanceof Number) {
|
case "integer":
|
||||||
res = ((Number) val).intValue();
|
if (val instanceof Number) {
|
||||||
} else {
|
res = ((Number) val).intValue();
|
||||||
res = Integer.parseInt(val.toString());
|
|
||||||
}
|
|
||||||
} else if ("long".equals(esType)) {
|
|
||||||
if (val instanceof Number) {
|
|
||||||
res = ((Number) val).longValue();
|
|
||||||
} else {
|
|
||||||
res = Long.parseLong(val.toString());
|
|
||||||
}
|
|
||||||
} else if ("short".equals(esType)) {
|
|
||||||
if (val instanceof Number) {
|
|
||||||
res = ((Number) val).shortValue();
|
|
||||||
} else {
|
|
||||||
res = Short.parseShort(val.toString());
|
|
||||||
}
|
|
||||||
} else if ("byte".equals(esType)) {
|
|
||||||
if (val instanceof Number) {
|
|
||||||
res = ((Number) val).byteValue();
|
|
||||||
} else {
|
|
||||||
res = Byte.parseByte(val.toString());
|
|
||||||
}
|
|
||||||
} else if ("double".equals(esType)) {
|
|
||||||
if (val instanceof Number) {
|
|
||||||
res = ((Number) val).doubleValue();
|
|
||||||
} else {
|
|
||||||
res = Double.parseDouble(val.toString());
|
|
||||||
}
|
|
||||||
} else if ("float".equals(esType) || "half_float".equals(esType) || "scaled_float".equals(esType)) {
|
|
||||||
if (val instanceof Number) {
|
|
||||||
res = ((Number) val).floatValue();
|
|
||||||
} else {
|
|
||||||
res = Float.parseFloat(val.toString());
|
|
||||||
}
|
|
||||||
} else if ("boolean".equals(esType)) {
|
|
||||||
if (val instanceof Boolean) {
|
|
||||||
res = val;
|
|
||||||
} else if (val instanceof Number) {
|
|
||||||
int v = ((Number) val).intValue();
|
|
||||||
res = v != 0;
|
|
||||||
} else {
|
|
||||||
res = Boolean.parseBoolean(val.toString());
|
|
||||||
}
|
|
||||||
} else if ("date".equals(esType)) {
|
|
||||||
if (val instanceof java.sql.Time) {
|
|
||||||
DateTime dateTime = new DateTime(((java.sql.Time) val).getTime());
|
|
||||||
if (dateTime.getMillisOfSecond() != 0) {
|
|
||||||
res = dateTime.toString("HH:mm:ss.SSS");
|
|
||||||
} else {
|
} else {
|
||||||
res = dateTime.toString("HH:mm:ss");
|
res = Integer.parseInt(val.toString());
|
||||||
}
|
}
|
||||||
} else if (val instanceof java.sql.Timestamp) {
|
break;
|
||||||
DateTime dateTime = new DateTime(((java.sql.Timestamp) val).getTime());
|
case "long":
|
||||||
if (dateTime.getMillisOfSecond() != 0) {
|
if (val instanceof Number) {
|
||||||
res = dateTime.toString("yyyy-MM-dd'T'HH:mm:ss.SSS" + Util.timeZone);
|
res = ((Number) val).longValue();
|
||||||
} else {
|
} else {
|
||||||
res = dateTime.toString("yyyy-MM-dd'T'HH:mm:ss" + Util.timeZone);
|
res = Long.parseLong(val.toString());
|
||||||
}
|
}
|
||||||
} else if (val instanceof java.sql.Date || val instanceof Date) {
|
break;
|
||||||
DateTime dateTime;
|
case "short":
|
||||||
if (val instanceof java.sql.Date) {
|
if (val instanceof Number) {
|
||||||
dateTime = new DateTime(((java.sql.Date) val).getTime());
|
res = ((Number) val).shortValue();
|
||||||
} else {
|
} else {
|
||||||
dateTime = new DateTime(((Date) val).getTime());
|
res = Short.parseShort(val.toString());
|
||||||
}
|
}
|
||||||
if (dateTime.getHourOfDay() == 0 && dateTime.getMinuteOfHour() == 0 && dateTime.getSecondOfMinute() == 0
|
break;
|
||||||
&& dateTime.getMillisOfSecond() == 0) {
|
case "byte":
|
||||||
res = dateTime.toString("yyyy-MM-dd");
|
if (val instanceof Number) {
|
||||||
|
res = ((Number) val).byteValue();
|
||||||
} else {
|
} else {
|
||||||
|
res = Byte.parseByte(val.toString());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "double":
|
||||||
|
if (val instanceof Number) {
|
||||||
|
res = ((Number) val).doubleValue();
|
||||||
|
} else {
|
||||||
|
res = Double.parseDouble(val.toString());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "float":
|
||||||
|
case "half_float":
|
||||||
|
case "scaled_float":
|
||||||
|
if (val instanceof Number) {
|
||||||
|
res = ((Number) val).floatValue();
|
||||||
|
} else {
|
||||||
|
res = Float.parseFloat(val.toString());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "boolean":
|
||||||
|
if (val instanceof Boolean) {
|
||||||
|
res = val;
|
||||||
|
} else if (val instanceof Number) {
|
||||||
|
int v = ((Number) val).intValue();
|
||||||
|
res = v != 0;
|
||||||
|
} else {
|
||||||
|
res = Boolean.parseBoolean(val.toString());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "date":
|
||||||
|
if (val instanceof java.sql.Time) {
|
||||||
|
DateTime dateTime = new DateTime(((java.sql.Time) val).getTime());
|
||||||
|
if (dateTime.getMillisOfSecond() != 0) {
|
||||||
|
res = dateTime.toString("HH:mm:ss.SSS");
|
||||||
|
} else {
|
||||||
|
res = dateTime.toString("HH:mm:ss");
|
||||||
|
}
|
||||||
|
} else if (val instanceof java.sql.Timestamp) {
|
||||||
|
DateTime dateTime = new DateTime(((java.sql.Timestamp) val).getTime());
|
||||||
if (dateTime.getMillisOfSecond() != 0) {
|
if (dateTime.getMillisOfSecond() != 0) {
|
||||||
res = dateTime.toString("yyyy-MM-dd'T'HH:mm:ss.SSS" + Util.timeZone);
|
res = dateTime.toString("yyyy-MM-dd'T'HH:mm:ss.SSS" + Util.timeZone);
|
||||||
} else {
|
} else {
|
||||||
res = dateTime.toString("yyyy-MM-dd'T'HH:mm:ss" + Util.timeZone);
|
res = dateTime.toString("yyyy-MM-dd'T'HH:mm:ss" + Util.timeZone);
|
||||||
}
|
}
|
||||||
}
|
} else if (val instanceof java.sql.Date || val instanceof Date) {
|
||||||
} else if (val instanceof Long) {
|
DateTime dateTime;
|
||||||
DateTime dateTime = new DateTime(((Long) val).longValue());
|
if (val instanceof java.sql.Date) {
|
||||||
if (dateTime.getHourOfDay() == 0 && dateTime.getMinuteOfHour() == 0 && dateTime.getSecondOfMinute() == 0
|
dateTime = new DateTime(((java.sql.Date) val).getTime());
|
||||||
&& dateTime.getMillisOfSecond() == 0) {
|
} else {
|
||||||
res = dateTime.toString("yyyy-MM-dd");
|
dateTime = new DateTime(((Date) val).getTime());
|
||||||
} else if (dateTime.getMillisOfSecond() != 0) {
|
}
|
||||||
res = dateTime.toString("yyyy-MM-dd'T'HH:mm:ss.SSS" + Util.timeZone);
|
if (dateTime.getHourOfDay() == 0 && dateTime.getMinuteOfHour() == 0 && dateTime.getSecondOfMinute() == 0
|
||||||
} else {
|
&& dateTime.getMillisOfSecond() == 0) {
|
||||||
res = dateTime.toString("yyyy-MM-dd'T'HH:mm:ss" + Util.timeZone);
|
res = dateTime.toString("yyyy-MM-dd");
|
||||||
}
|
} else {
|
||||||
} else if (val instanceof String) {
|
|
||||||
String v = ((String) val).trim();
|
|
||||||
if (v.length() > 18 && v.charAt(4) == '-' && v.charAt(7) == '-' && v.charAt(10) == ' '
|
|
||||||
&& v.charAt(13) == ':' && v.charAt(16) == ':') {
|
|
||||||
String dt = v.substring(0, 10) + "T" + v.substring(11);
|
|
||||||
Date date = Util.parseDate(dt);
|
|
||||||
if (date != null) {
|
|
||||||
DateTime dateTime = new DateTime(date);
|
|
||||||
if (dateTime.getMillisOfSecond() != 0) {
|
if (dateTime.getMillisOfSecond() != 0) {
|
||||||
res = dateTime.toString("yyyy-MM-dd'T'HH:mm:ss.SSS" + Util.timeZone);
|
res = dateTime.toString("yyyy-MM-dd'T'HH:mm:ss.SSS" + Util.timeZone);
|
||||||
} else {
|
} else {
|
||||||
res = dateTime.toString("yyyy-MM-dd'T'HH:mm:ss" + Util.timeZone);
|
res = dateTime.toString("yyyy-MM-dd'T'HH:mm:ss" + Util.timeZone);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (v.length() == 10 && v.charAt(4) == '-' && v.charAt(7) == '-') {
|
} else if (val instanceof Long) {
|
||||||
Date date = Util.parseDate(v);
|
DateTime dateTime = new DateTime(((Long) val).longValue());
|
||||||
if (date != null) {
|
if (dateTime.getHourOfDay() == 0 && dateTime.getMinuteOfHour() == 0 && dateTime.getSecondOfMinute() == 0
|
||||||
DateTime dateTime = new DateTime(date);
|
&& dateTime.getMillisOfSecond() == 0) {
|
||||||
res = dateTime.toString("yyyy-MM-dd");
|
res = dateTime.toString("yyyy-MM-dd");
|
||||||
|
} else if (dateTime.getMillisOfSecond() != 0) {
|
||||||
|
res = dateTime.toString("yyyy-MM-dd'T'HH:mm:ss.SSS" + Util.timeZone);
|
||||||
|
} else {
|
||||||
|
res = dateTime.toString("yyyy-MM-dd'T'HH:mm:ss" + Util.timeZone);
|
||||||
|
}
|
||||||
|
} else if (val instanceof String) {
|
||||||
|
String v = ((String) val).trim();
|
||||||
|
if (v.length() > 18 && v.charAt(4) == '-' && v.charAt(7) == '-' && v.charAt(10) == ' '
|
||||||
|
&& v.charAt(13) == ':' && v.charAt(16) == ':') {
|
||||||
|
String dt = v.substring(0, 10) + "T" + v.substring(11);
|
||||||
|
Date date = Util.parseDate(dt);
|
||||||
|
if (date != null) {
|
||||||
|
DateTime dateTime = new DateTime(date);
|
||||||
|
if (dateTime.getMillisOfSecond() != 0) {
|
||||||
|
res = dateTime.toString("yyyy-MM-dd'T'HH:mm:ss.SSS" + Util.timeZone);
|
||||||
|
} else {
|
||||||
|
res = dateTime.toString("yyyy-MM-dd'T'HH:mm:ss" + Util.timeZone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (v.length() == 10 && v.charAt(4) == '-' && v.charAt(7) == '-') {
|
||||||
|
Date date = Util.parseDate(v);
|
||||||
|
if (date != null) {
|
||||||
|
DateTime dateTime = new DateTime(date);
|
||||||
|
res = dateTime.toString("yyyy-MM-dd");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
} else if ("binary".equals(esType)) {
|
case "binary":
|
||||||
if (val instanceof byte[]) {
|
if (val instanceof byte[]) {
|
||||||
Base64 base64 = new Base64();
|
Base64 base64 = new Base64();
|
||||||
res = base64.encodeAsString((byte[]) val);
|
res = base64.encodeAsString((byte[]) val);
|
||||||
} else if (val instanceof Blob) {
|
} else if (val instanceof Blob) {
|
||||||
byte[] b = blobToBytes((Blob) val);
|
byte[] b = blobToBytes((Blob) val);
|
||||||
Base64 base64 = new Base64();
|
Base64 base64 = new Base64();
|
||||||
res = base64.encodeAsString(b);
|
res = base64.encodeAsString(b);
|
||||||
} else if (val instanceof String) {
|
} else if (val instanceof String) {
|
||||||
// 对应canal中的单字节编码
|
// 对应canal中的单字节编码
|
||||||
byte[] b = ((String) val).getBytes(StandardCharsets.ISO_8859_1);
|
byte[] b = ((String) val).getBytes(StandardCharsets.ISO_8859_1);
|
||||||
Base64 base64 = new Base64();
|
Base64 base64 = new Base64();
|
||||||
res = base64.encodeAsString(b);
|
res = base64.encodeAsString(b);
|
||||||
}
|
|
||||||
} else if ("geo_point".equals(esType)) {
|
|
||||||
if (!(val instanceof String)) {
|
|
||||||
logger.error("es type is geo_point, but source type is not String");
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!((String) val).contains(",")) {
|
|
||||||
logger.error("es type is geo_point, source value not contains ',' separator");
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
String[] point = ((String) val).split(",");
|
|
||||||
Map<String, Double> location = new HashMap<>();
|
|
||||||
location.put("lat", Double.valueOf(point[0].trim()));
|
|
||||||
location.put("lon", Double.valueOf(point[1].trim()));
|
|
||||||
return location;
|
|
||||||
} else if ("array".equals(esType)) {
|
|
||||||
if ("".equals(val.toString().trim())) {
|
|
||||||
res = new ArrayList<>();
|
|
||||||
} else {
|
|
||||||
String value = val.toString();
|
|
||||||
String separator = ",";
|
|
||||||
if (!value.contains(",")) {
|
|
||||||
if (value.contains(";")) {
|
|
||||||
separator = ";";
|
|
||||||
} else if (value.contains("|")) {
|
|
||||||
separator = "\\|";
|
|
||||||
} else if (value.contains("-")) {
|
|
||||||
separator = "-";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
String[] values = value.split(separator);
|
break;
|
||||||
return Arrays.asList(values);
|
case "geo_point":
|
||||||
}
|
if (!(val instanceof String)) {
|
||||||
} else if ("object".equals(esType)) {
|
logger.error("es type is geo_point, but source type is not String");
|
||||||
if ("".equals(val.toString().trim())) {
|
return val;
|
||||||
res = new HashMap<>();
|
}
|
||||||
} else {
|
|
||||||
res = JSON.parseObject(val.toString(), Map.class);
|
if (!((String) val).contains(",")) {
|
||||||
}
|
logger.error("es type is geo_point, source value not contains ',' separator");
|
||||||
} else {
|
return val;
|
||||||
// 其他类全以字符串处理
|
}
|
||||||
res = val.toString();
|
|
||||||
|
String[] point = ((String) val).split(",");
|
||||||
|
Map<String, Double> location = new HashMap<>();
|
||||||
|
location.put("lat", Double.valueOf(point[0].trim()));
|
||||||
|
location.put("lon", Double.valueOf(point[1].trim()));
|
||||||
|
return location;
|
||||||
|
case "array":
|
||||||
|
if ("".equals(val.toString().trim())) {
|
||||||
|
res = new ArrayList<>();
|
||||||
|
} else {
|
||||||
|
String value = val.toString();
|
||||||
|
String separator = ",";
|
||||||
|
if (!value.contains(",")) {
|
||||||
|
if (value.contains(";")) {
|
||||||
|
separator = ";";
|
||||||
|
} else if (value.contains("|")) {
|
||||||
|
separator = "\\|";
|
||||||
|
} else if (value.contains("-")) {
|
||||||
|
separator = "-";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String[] values = value.split(separator);
|
||||||
|
return Arrays.asList(values);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "object":
|
||||||
|
if ("".equals(val.toString().trim())) {
|
||||||
|
res = new HashMap<>();
|
||||||
|
} else {
|
||||||
|
res = JSON.parseObject(val.toString(), Map.class);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// 其他类全以字符串处理
|
||||||
|
res = val.toString();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
|||||||
+2
-4
@@ -57,13 +57,11 @@ public interface ESTemplate {
|
|||||||
|
|
||||||
Object getIdValFromRS(ESMapping mapping, ResultSet resultSet) throws SQLException;
|
Object getIdValFromRS(ESMapping mapping, ResultSet resultSet) throws SQLException;
|
||||||
|
|
||||||
Object getESDataFromRS(ESMapping mapping, ResultSet resultSet, Map<String, Object> dmlOld,
|
Object getESDataFromRS(ESMapping mapping, ResultSet resultSet, Map<String, Object> dmlOld, Map<String, Object> esFieldData) throws SQLException;
|
||||||
Map<String, Object> esFieldData) throws SQLException;
|
|
||||||
|
|
||||||
Object getValFromData(ESMapping mapping, Map<String, Object> dmlData, String fieldName, String columnName);
|
Object getValFromData(ESMapping mapping, Map<String, Object> dmlData, String fieldName, String columnName);
|
||||||
|
|
||||||
Object getESDataFromDmlData(ESMapping mapping, Map<String, Object> dmlData, Map<String, Object> esFieldData);
|
Object getESDataFromDmlData(ESMapping mapping, Map<String, Object> dmlData, Map<String, Object> esFieldData);
|
||||||
|
|
||||||
Object getESDataFromDmlData(ESMapping mapping, Map<String, Object> dmlData, Map<String, Object> dmlOld,
|
Object getESDataFromDmlData(ESMapping mapping,String owner, Map<String, Object> dmlData, Map<String, Object> dmlOld, Map<String, Object> esFieldData);
|
||||||
Map<String, Object> esFieldData);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.5-SNAPSHOT</version>
|
<version>1.1.7-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.hbase</groupId>
|
<groupId>org.apache.hbase</groupId>
|
||||||
<artifactId>hbase-client</artifactId>
|
<artifactId>hbase-shaded-client</artifactId>
|
||||||
<version>1.1.2</version>
|
<version>1.1.2</version>
|
||||||
<exclusions>
|
<exclusions>
|
||||||
<exclusion>
|
<exclusion>
|
||||||
|
|||||||
+81
-38
@@ -1,9 +1,25 @@
|
|||||||
package com.alibaba.otter.canal.client.adapter.hbase;
|
package com.alibaba.otter.canal.client.adapter.hbase;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.client.adapter.OuterAdapter;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.hbase.config.MappingConfig;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.hbase.config.MappingConfigLoader;
|
||||||
|
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.HbaseSyncService;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.hbase.support.HbaseTemplate;
|
||||||
|
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.FileName2KeyMapping;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.support.OuterAdapterConfig;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.support.SPI;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.support.Util;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
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;
|
||||||
@@ -16,18 +32,6 @@ import org.apache.hadoop.hbase.filter.FirstKeyOnlyFilter;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.client.adapter.OuterAdapter;
|
|
||||||
import com.alibaba.otter.canal.client.adapter.hbase.config.MappingConfig;
|
|
||||||
import com.alibaba.otter.canal.client.adapter.hbase.config.MappingConfigLoader;
|
|
||||||
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.HbaseSyncService;
|
|
||||||
import com.alibaba.otter.canal.client.adapter.hbase.support.HbaseTemplate;
|
|
||||||
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外部适配器
|
||||||
*
|
*
|
||||||
@@ -49,6 +53,8 @@ public class HbaseAdapter implements OuterAdapter {
|
|||||||
|
|
||||||
private Properties envProperties;
|
private Properties envProperties;
|
||||||
|
|
||||||
|
private OuterAdapterConfig configuration;
|
||||||
|
|
||||||
public Map<String, MappingConfig> getHbaseMapping() {
|
public Map<String, MappingConfig> getHbaseMapping() {
|
||||||
return hbaseMapping;
|
return hbaseMapping;
|
||||||
}
|
}
|
||||||
@@ -61,33 +67,12 @@ public class HbaseAdapter implements OuterAdapter {
|
|||||||
public void init(OuterAdapterConfig configuration, Properties envProperties) {
|
public void init(OuterAdapterConfig configuration, Properties envProperties) {
|
||||||
try {
|
try {
|
||||||
this.envProperties = envProperties;
|
this.envProperties = envProperties;
|
||||||
|
this.configuration = configuration;
|
||||||
Map<String, MappingConfig> hbaseMappingTmp = MappingConfigLoader.load(envProperties);
|
Map<String, MappingConfig> hbaseMappingTmp = MappingConfigLoader.load(envProperties);
|
||||||
// 过滤不匹配的key的配置
|
// 过滤不匹配的key的配置
|
||||||
hbaseMappingTmp.forEach((key, mappingConfig) -> {
|
hbaseMappingTmp.forEach((key, config) -> {
|
||||||
if ((mappingConfig.getOuterAdapterKey() == null && configuration.getKey() == null)
|
addConfig(key, config);
|
||||||
|| (mappingConfig.getOuterAdapterKey() != null
|
|
||||||
&& mappingConfig.getOuterAdapterKey().equalsIgnoreCase(configuration.getKey()))) {
|
|
||||||
hbaseMapping.put(key, mappingConfig);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
for (Map.Entry<String, MappingConfig> entry : hbaseMapping.entrySet()) {
|
|
||||||
String configName = entry.getKey();
|
|
||||||
MappingConfig mappingConfig = entry.getValue();
|
|
||||||
String k;
|
|
||||||
if (envProperties != null && !"tcp".equalsIgnoreCase(envProperties.getProperty("canal.conf.mode"))) {
|
|
||||||
k = StringUtils.trimToEmpty(mappingConfig.getDestination()) + "-"
|
|
||||||
+ StringUtils.trimToEmpty(mappingConfig.getGroupId()) + "_"
|
|
||||||
+ mappingConfig.getHbaseMapping().getDatabase() + "-"
|
|
||||||
+ mappingConfig.getHbaseMapping().getTable();
|
|
||||||
} else {
|
|
||||||
k = StringUtils.trimToEmpty(mappingConfig.getDestination()) + "_"
|
|
||||||
+ mappingConfig.getHbaseMapping().getDatabase() + "-"
|
|
||||||
+ mappingConfig.getHbaseMapping().getTable();
|
|
||||||
}
|
|
||||||
Map<String, MappingConfig> configMap = mappingConfigCache.computeIfAbsent(k,
|
|
||||||
k1 -> new ConcurrentHashMap<>());
|
|
||||||
configMap.put(configName, mappingConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, String> properties = configuration.getProperties();
|
Map<String, String> properties = configuration.getProperties();
|
||||||
|
|
||||||
@@ -223,4 +208,62 @@ public class HbaseAdapter implements OuterAdapter {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addSyncConfigToCache(String configName, MappingConfig mappingConfig) {
|
||||||
|
String k;
|
||||||
|
if (envProperties != null && !"tcp"
|
||||||
|
.equalsIgnoreCase(envProperties.getProperty("canal.conf.mode"))) {
|
||||||
|
k = StringUtils.trimToEmpty(mappingConfig.getDestination()) + "-" + StringUtils
|
||||||
|
.trimToEmpty(mappingConfig.getGroupId()) + "_" + mappingConfig.getHbaseMapping()
|
||||||
|
.getDatabase() + "-" + mappingConfig.getHbaseMapping().getTable();
|
||||||
|
} else {
|
||||||
|
k = StringUtils.trimToEmpty(mappingConfig.getDestination()) + "_" + mappingConfig
|
||||||
|
.getHbaseMapping().getDatabase() + "-" + mappingConfig.getHbaseMapping()
|
||||||
|
.getTable();
|
||||||
|
}
|
||||||
|
Map<String, MappingConfig> configMap = mappingConfigCache
|
||||||
|
.computeIfAbsent(k, k1 -> new ConcurrentHashMap<>());
|
||||||
|
configMap.put(configName, mappingConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean addConfig(String fileName, MappingConfig config) {
|
||||||
|
if (match(config)) {
|
||||||
|
hbaseMapping.put(fileName, config);
|
||||||
|
addSyncConfigToCache(fileName, config);
|
||||||
|
FileName2KeyMapping.register(getClass().getAnnotation(SPI.class).value(), fileName,
|
||||||
|
configuration.getKey());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateConfig(String fileName, MappingConfig config) {
|
||||||
|
if (config.getOuterAdapterKey() != null && !config.getOuterAdapterKey()
|
||||||
|
.equals(configuration.getKey())) {
|
||||||
|
// 理论上不允许改这个 因为本身就是通过这个关联起Adapter和Config的
|
||||||
|
throw new RuntimeException("not allow to change outAdapterKey");
|
||||||
|
}
|
||||||
|
hbaseMapping.put(fileName, config);
|
||||||
|
addSyncConfigToCache(fileName, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteConfig(String fileName) {
|
||||||
|
hbaseMapping.remove(fileName);
|
||||||
|
for (Map<String, MappingConfig> configMap : mappingConfigCache.values()) {
|
||||||
|
if (configMap != null) {
|
||||||
|
configMap.remove(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FileName2KeyMapping.unregister(getClass().getAnnotation(SPI.class).value(), fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean match(MappingConfig config) {
|
||||||
|
boolean sameMatch = config.getOuterAdapterKey() != null && config.getOuterAdapterKey()
|
||||||
|
.equalsIgnoreCase(configuration.getKey());
|
||||||
|
boolean prefixMatch = config.getOuterAdapterKey() == null && configuration.getKey()
|
||||||
|
.startsWith(StringUtils
|
||||||
|
.join(new String[]{Util.AUTO_GENERATED_PREFIX, config.getDestination(),
|
||||||
|
config.getGroupId()}, '-'));
|
||||||
|
return sameMatch || prefixMatch;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-43
@@ -1,23 +1,18 @@
|
|||||||
package com.alibaba.otter.canal.client.adapter.hbase.monitor;
|
package com.alibaba.otter.canal.client.adapter.hbase.monitor;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
import org.apache.commons.io.filefilter.FileFilterUtils;
|
|
||||||
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
|
|
||||||
import org.apache.commons.io.monitor.FileAlterationMonitor;
|
|
||||||
import org.apache.commons.io.monitor.FileAlterationObserver;
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import com.alibaba.otter.canal.client.adapter.config.YmlConfigBinder;
|
import com.alibaba.otter.canal.client.adapter.config.YmlConfigBinder;
|
||||||
import com.alibaba.otter.canal.client.adapter.hbase.HbaseAdapter;
|
import com.alibaba.otter.canal.client.adapter.hbase.HbaseAdapter;
|
||||||
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.support.MappingConfigsLoader;
|
import com.alibaba.otter.canal.client.adapter.support.MappingConfigsLoader;
|
||||||
import com.alibaba.otter.canal.client.adapter.support.Util;
|
import com.alibaba.otter.canal.client.adapter.support.Util;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Properties;
|
||||||
|
import org.apache.commons.io.filefilter.FileFilterUtils;
|
||||||
|
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
|
||||||
|
import org.apache.commons.io.monitor.FileAlterationMonitor;
|
||||||
|
import org.apache.commons.io.monitor.FileAlterationObserver;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class HbaseConfigMonitor {
|
public class HbaseConfigMonitor {
|
||||||
|
|
||||||
@@ -37,7 +32,7 @@ public class HbaseConfigMonitor {
|
|||||||
File confDir = Util.getConfDirPath(adapterName);
|
File confDir = Util.getConfDirPath(adapterName);
|
||||||
try {
|
try {
|
||||||
FileAlterationObserver observer = new FileAlterationObserver(confDir,
|
FileAlterationObserver observer = new FileAlterationObserver(confDir,
|
||||||
FileFilterUtils.and(FileFilterUtils.fileFileFilter(), FileFilterUtils.suffixFileFilter("yml")));
|
FileFilterUtils.and(FileFilterUtils.fileFileFilter(), FileFilterUtils.suffixFileFilter("yml")));
|
||||||
FileListener listener = new FileListener();
|
FileListener listener = new FileListener();
|
||||||
observer.addListener(listener);
|
observer.addListener(listener);
|
||||||
fileMonitor = new FileAlterationMonitor(3000, observer);
|
fileMonitor = new FileAlterationMonitor(3000, observer);
|
||||||
@@ -70,9 +65,11 @@ public class HbaseConfigMonitor {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
config.validate();
|
config.validate();
|
||||||
addConfigToCache(file, config);
|
boolean result = hbaseAdapter.addConfig(file.getName(), config);
|
||||||
|
if (result) {
|
||||||
logger.info("Add a new hbase mapping config: {} to canal adapter", file.getName());
|
logger.info("Add a new hbase mapping config: {} to canal adapter",
|
||||||
|
file.getName());
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error(e.getMessage(), e);
|
logger.error(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
@@ -97,10 +94,7 @@ public class HbaseConfigMonitor {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
config.validate();
|
config.validate();
|
||||||
if (hbaseAdapter.getHbaseMapping().containsKey(file.getName())) {
|
hbaseAdapter.updateConfig(file.getName(), config);
|
||||||
deleteConfigFromCache(file);
|
|
||||||
}
|
|
||||||
addConfigToCache(file, config);
|
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error(e.getMessage(), e);
|
logger.error(e.getMessage(), e);
|
||||||
@@ -113,33 +107,12 @@ public class HbaseConfigMonitor {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (hbaseAdapter.getHbaseMapping().containsKey(file.getName())) {
|
if (hbaseAdapter.getHbaseMapping().containsKey(file.getName())) {
|
||||||
deleteConfigFromCache(file);
|
hbaseAdapter.deleteConfig(file.getName());
|
||||||
|
|
||||||
logger.info("Delete a hbase mapping config: {} of canal adapter", file.getName());
|
logger.info("Delete a hbase mapping config: {} of canal adapter", file.getName());
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error(e.getMessage(), e);
|
logger.error(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addConfigToCache(File file, MappingConfig config) {
|
|
||||||
hbaseAdapter.getHbaseMapping().put(file.getName(), config);
|
|
||||||
Map<String, MappingConfig> configMap = hbaseAdapter.getMappingConfigCache()
|
|
||||||
.computeIfAbsent(StringUtils.trimToEmpty(config.getDestination()) + "."
|
|
||||||
+ config.getHbaseMapping().getDatabase() + "." + config.getHbaseMapping().getTable(),
|
|
||||||
k1 -> new HashMap<>());
|
|
||||||
configMap.put(file.getName(), config);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void deleteConfigFromCache(File file) {
|
|
||||||
|
|
||||||
hbaseAdapter.getHbaseMapping().remove(file.getName());
|
|
||||||
for (Map<String, MappingConfig> configMap : hbaseAdapter.getMappingConfigCache().values()) {
|
|
||||||
if (configMap != null) {
|
|
||||||
configMap.remove(file.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+143
-137
@@ -5,19 +5,24 @@ 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.ExecutorService;
|
|
||||||
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.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.*;
|
import com.alibaba.otter.canal.client.adapter.hbase.support.HRow;
|
||||||
|
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.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.JdbcTypeUtil;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.support.Util;
|
||||||
import com.google.common.base.Joiner;
|
import com.google.common.base.Joiner;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -79,7 +84,8 @@ public class HbaseEtlService extends AbstractEtlService {
|
|||||||
createTable();
|
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);
|
return super.importData(sql, params);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -120,146 +126,146 @@ public class HbaseEtlService extends AbstractEtlService {
|
|||||||
|
|
||||||
if (rowKeyColumns != null) {
|
if (rowKeyColumns != null) {
|
||||||
// 取rowKey字段拼接
|
// 取rowKey字段拼接
|
||||||
StringBuilder rowKeyVale = new StringBuilder();
|
StringBuilder rowKeyVale = new StringBuilder();
|
||||||
for (String rowKeyColumnName : rowKeyColumns) {
|
for (String rowKeyColumnName : rowKeyColumns) {
|
||||||
Object obj = rs.getObject(rowKeyColumnName);
|
Object obj = rs.getObject(rowKeyColumnName);
|
||||||
if (obj != null) {
|
if (obj != null) {
|
||||||
rowKeyVale.append(obj.toString());
|
rowKeyVale.append(obj.toString());
|
||||||
}
|
}
|
||||||
rowKeyVale.append("|");
|
rowKeyVale.append("|");
|
||||||
}
|
}
|
||||||
int len = rowKeyVale.length();
|
int len = rowKeyVale.length();
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
rowKeyVale.delete(len - 1, len);
|
rowKeyVale.delete(len - 1, len);
|
||||||
}
|
}
|
||||||
row.setRowKey(Bytes.toBytes(rowKeyVale.toString()));
|
row.setRowKey(Bytes.toBytes(rowKeyVale.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = 1; j <= cc; j++) {
|
||||||
|
String columnName = rs.getMetaData().getColumnName(j);
|
||||||
|
|
||||||
|
Object val = JdbcTypeUtil.getRSData(rs, columnName, jdbcTypes[j - 1]);
|
||||||
|
if (val == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
MappingConfig.ColumnItem columnItem = hbaseMapping.getColumnItems().get(columnName);
|
||||||
|
// 没有配置映射
|
||||||
|
if (columnItem == null) {
|
||||||
|
String family = hbaseMapping.getFamily();
|
||||||
|
String qualifile = columnName;
|
||||||
|
if (hbaseMapping.isUppercaseQualifier()) {
|
||||||
|
qualifile = qualifile.toUpperCase();
|
||||||
|
}
|
||||||
|
if (MappingConfig.Mode.STRING == hbaseMapping.getMode()) {
|
||||||
|
if (hbaseMapping.getRowKey() == null && j == 1) {
|
||||||
|
row.setRowKey(Bytes.toBytes(val.toString()));
|
||||||
|
} else {
|
||||||
|
row.addCell(family, qualifile, Bytes.toBytes(val.toString()));
|
||||||
|
}
|
||||||
|
} else if (MappingConfig.Mode.NATIVE == hbaseMapping.getMode()) {
|
||||||
|
Type type = Type.getType(classes[j - 1]);
|
||||||
|
if (hbaseMapping.getRowKey() == null && j == 1) {
|
||||||
|
row.setRowKey(TypeUtil.toBytes(val, type));
|
||||||
|
} else {
|
||||||
|
row.addCell(family, qualifile, TypeUtil.toBytes(val, type));
|
||||||
|
}
|
||||||
|
} else if (MappingConfig.Mode.PHOENIX == hbaseMapping.getMode()) {
|
||||||
|
PhType phType = PhType.getType(classes[j - 1]);
|
||||||
|
if (hbaseMapping.getRowKey() == null && j == 1) {
|
||||||
|
row.setRowKey(PhTypeUtil.toBytes(val, phType));
|
||||||
|
} else {
|
||||||
|
row.addCell(family, qualifile, PhTypeUtil.toBytes(val, phType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 如果不需要类型转换
|
||||||
|
if (columnItem.getType() == null || "".equals(columnItem.getType())) {
|
||||||
|
if (val instanceof java.sql.Date) {
|
||||||
|
SimpleDateFormat dateFmt = new SimpleDateFormat("yyyy-MM-dd");
|
||||||
|
val = dateFmt.format((Date) val);
|
||||||
|
} else if (val instanceof Timestamp) {
|
||||||
|
SimpleDateFormat datetimeFmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||||
|
val = datetimeFmt.format((Date) val);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int j = 1; j <= cc; j++) {
|
byte[] valBytes = Bytes.toBytes(val.toString());
|
||||||
String columnName = rs.getMetaData().getColumnName(j);
|
if (columnItem.isRowKey()) {
|
||||||
|
if (columnItem.getRowKeyLen() != null) {
|
||||||
Object val = JdbcTypeUtil.getRSData(rs, columnName, jdbcTypes[j - 1]);
|
valBytes = Bytes.toBytes(limitLenNum(columnItem.getRowKeyLen(), val));
|
||||||
if (val == null) {
|
row.setRowKey(valBytes);
|
||||||
continue;
|
} else {
|
||||||
|
row.setRowKey(valBytes);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
MappingConfig.ColumnItem columnItem = hbaseMapping.getColumnItems().get(columnName);
|
row.addCell(columnItem.getFamily(), columnItem.getQualifier(), valBytes);
|
||||||
// 没有配置映射
|
}
|
||||||
if (columnItem == null) {
|
} else {
|
||||||
String family = hbaseMapping.getFamily();
|
if (MappingConfig.Mode.STRING == hbaseMapping.getMode()) {
|
||||||
String qualifile = columnName;
|
byte[] valBytes = Bytes.toBytes(val.toString());
|
||||||
if (hbaseMapping.isUppercaseQualifier()) {
|
if (columnItem.isRowKey()) {
|
||||||
qualifile = qualifile.toUpperCase();
|
if (columnItem.getRowKeyLen() != null) {
|
||||||
|
valBytes = Bytes.toBytes(limitLenNum(columnItem.getRowKeyLen(), val));
|
||||||
}
|
}
|
||||||
if (MappingConfig.Mode.STRING == hbaseMapping.getMode()) {
|
row.setRowKey(valBytes);
|
||||||
if (hbaseMapping.getRowKey() == null && j == 1) {
|
} else {
|
||||||
row.setRowKey(Bytes.toBytes(val.toString()));
|
row.addCell(columnItem.getFamily(), columnItem.getQualifier(), valBytes);
|
||||||
} else {
|
}
|
||||||
row.addCell(family, qualifile, Bytes.toBytes(val.toString()));
|
} else if (MappingConfig.Mode.NATIVE == hbaseMapping.getMode()) {
|
||||||
}
|
Type type = Type.getType(columnItem.getType());
|
||||||
} else if (MappingConfig.Mode.NATIVE == hbaseMapping.getMode()) {
|
if (columnItem.isRowKey()) {
|
||||||
Type type = Type.getType(classes[j - 1]);
|
if (columnItem.getRowKeyLen() != null) {
|
||||||
if (hbaseMapping.getRowKey() == null && j == 1) {
|
String v = limitLenNum(columnItem.getRowKeyLen(), val);
|
||||||
row.setRowKey(TypeUtil.toBytes(val, type));
|
row.setRowKey(Bytes.toBytes(v));
|
||||||
} else {
|
} else {
|
||||||
row.addCell(family, qualifile, TypeUtil.toBytes(val, type));
|
row.setRowKey(TypeUtil.toBytes(val, type));
|
||||||
}
|
|
||||||
} else if (MappingConfig.Mode.PHOENIX == hbaseMapping.getMode()) {
|
|
||||||
PhType phType = PhType.getType(classes[j - 1]);
|
|
||||||
if (hbaseMapping.getRowKey() == null && j == 1) {
|
|
||||||
row.setRowKey(PhTypeUtil.toBytes(val, phType));
|
|
||||||
} else {
|
|
||||||
row.addCell(family, qualifile, PhTypeUtil.toBytes(val, phType));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 如果不需要类型转换
|
row.addCell(columnItem.getFamily(),
|
||||||
if (columnItem.getType() == null || "".equals(columnItem.getType())) {
|
columnItem.getQualifier(),
|
||||||
if (val instanceof java.sql.Date) {
|
TypeUtil.toBytes(val, type));
|
||||||
SimpleDateFormat dateFmt = new SimpleDateFormat("yyyy-MM-dd");
|
}
|
||||||
val = dateFmt.format((Date) val);
|
} else if (MappingConfig.Mode.PHOENIX == hbaseMapping.getMode()) {
|
||||||
} else if (val instanceof Timestamp) {
|
PhType phType = PhType.getType(columnItem.getType());
|
||||||
SimpleDateFormat datetimeFmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
if (columnItem.isRowKey()) {
|
||||||
val = datetimeFmt.format((Date) val);
|
row.setRowKey(PhTypeUtil.toBytes(val, phType));
|
||||||
}
|
} else {
|
||||||
|
row.addCell(columnItem.getFamily(),
|
||||||
byte[] valBytes = Bytes.toBytes(val.toString());
|
columnItem.getQualifier(),
|
||||||
if (columnItem.isRowKey()) {
|
PhTypeUtil.toBytes(val, phType));
|
||||||
if (columnItem.getRowKeyLen() != null) {
|
|
||||||
valBytes = Bytes.toBytes(limitLenNum(columnItem.getRowKeyLen(), val));
|
|
||||||
row.setRowKey(valBytes);
|
|
||||||
} else {
|
|
||||||
row.setRowKey(valBytes);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
row.addCell(columnItem.getFamily(), columnItem.getQualifier(), valBytes);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (MappingConfig.Mode.STRING == hbaseMapping.getMode()) {
|
|
||||||
byte[] valBytes = Bytes.toBytes(val.toString());
|
|
||||||
if (columnItem.isRowKey()) {
|
|
||||||
if (columnItem.getRowKeyLen() != null) {
|
|
||||||
valBytes = Bytes.toBytes(limitLenNum(columnItem.getRowKeyLen(), val));
|
|
||||||
}
|
|
||||||
row.setRowKey(valBytes);
|
|
||||||
} else {
|
|
||||||
row.addCell(columnItem.getFamily(), columnItem.getQualifier(), valBytes);
|
|
||||||
}
|
|
||||||
} else if (MappingConfig.Mode.NATIVE == hbaseMapping.getMode()) {
|
|
||||||
Type type = Type.getType(columnItem.getType());
|
|
||||||
if (columnItem.isRowKey()) {
|
|
||||||
if (columnItem.getRowKeyLen() != null) {
|
|
||||||
String v = limitLenNum(columnItem.getRowKeyLen(), val);
|
|
||||||
row.setRowKey(Bytes.toBytes(v));
|
|
||||||
} else {
|
|
||||||
row.setRowKey(TypeUtil.toBytes(val, type));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
row.addCell(columnItem.getFamily(),
|
|
||||||
columnItem.getQualifier(),
|
|
||||||
TypeUtil.toBytes(val, type));
|
|
||||||
}
|
|
||||||
} else if (MappingConfig.Mode.PHOENIX == hbaseMapping.getMode()) {
|
|
||||||
PhType phType = PhType.getType(columnItem.getType());
|
|
||||||
if (columnItem.isRowKey()) {
|
|
||||||
row.setRowKey(PhTypeUtil.toBytes(val, phType));
|
|
||||||
} else {
|
|
||||||
row.addCell(columnItem.getFamily(),
|
|
||||||
columnItem.getQualifier(),
|
|
||||||
PhTypeUtil.toBytes(val, phType));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (row.getRowKey() == null) throw new RuntimeException("RowKey 值为空");
|
|
||||||
|
|
||||||
rows.add(row);
|
|
||||||
complete = false;
|
|
||||||
if (i % hbaseMapping.getCommitBatch() == 0 && !rows.isEmpty()) {
|
|
||||||
hbaseTemplate.puts(hbaseMapping.getHbaseTable(), rows);
|
|
||||||
rows.clear();
|
|
||||||
complete = true;
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
impCount.incrementAndGet();
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug("successful import count:" + impCount.get());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!complete && !rows.isEmpty()) {
|
|
||||||
hbaseTemplate.puts(hbaseMapping.getHbaseTable(), rows);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(hbaseMapping.getHbaseTable() + " etl failed! ==>" + e.getMessage(), e);
|
|
||||||
errMsg.add(hbaseMapping.getHbaseTable() + " etl failed! ==>" + e.getMessage());
|
|
||||||
// throw new RuntimeException(e);
|
|
||||||
}
|
}
|
||||||
return i;
|
}
|
||||||
});
|
|
||||||
|
if (row.getRowKey() == null) throw new RuntimeException("RowKey 值为空");
|
||||||
|
|
||||||
|
rows.add(row);
|
||||||
|
complete = false;
|
||||||
|
if (i % hbaseMapping.getCommitBatch() == 0 && !rows.isEmpty()) {
|
||||||
|
hbaseTemplate.puts(hbaseMapping.getHbaseTable(), rows);
|
||||||
|
rows.clear();
|
||||||
|
complete = true;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
impCount.incrementAndGet();
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("successful import count:" + impCount.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!complete && !rows.isEmpty()) {
|
||||||
|
hbaseTemplate.puts(hbaseMapping.getHbaseTable(), rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error(hbaseMapping.getHbaseTable() + " etl failed! ==>" + e.getMessage(), e);
|
||||||
|
errMsg.add(hbaseMapping.getHbaseTable() + " etl failed! ==>" + e.getMessage());
|
||||||
|
// throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
} );
|
||||||
return true;
|
return true;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error(e.getMessage(), e);
|
logger.error(e.getMessage(), e);
|
||||||
|
|||||||
+3
-3
@@ -6,8 +6,8 @@ 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.fastjson.JSON;
|
import com.alibaba.fastjson2.JSON;
|
||||||
import com.alibaba.fastjson.serializer.SerializerFeature;
|
import com.alibaba.fastjson2.JSONWriter.Feature;
|
||||||
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.*;
|
import com.alibaba.otter.canal.client.adapter.hbase.support.*;
|
||||||
import com.alibaba.otter.canal.client.adapter.support.Dml;
|
import com.alibaba.otter.canal.client.adapter.support.Dml;
|
||||||
@@ -39,7 +39,7 @@ public class HbaseSyncService {
|
|||||||
delete(config, dml);
|
delete(config, dml);
|
||||||
}
|
}
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("DML: {}", JSON.toJSONString(dml, SerializerFeature.WriteMapNullValue));
|
logger.debug("DML: {}", JSON.toJSONString(dml, Feature.WriteNulls));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>canal.client-adapter</artifactId>
|
||||||
|
<groupId>com.alibaba.otter</groupId>
|
||||||
|
<version>1.1.7-SNAPSHOT</version>
|
||||||
|
<relativePath>../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>com.alibaba.otter</groupId>
|
||||||
|
<artifactId>client-adapter.kudu</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<name>canal client adapter kudu module for otter ${project.version}</name>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.otter</groupId>
|
||||||
|
<artifactId>client-adapter.common</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.kudu</groupId>
|
||||||
|
<artifactId>kudu-client</artifactId>
|
||||||
|
<version>1.6.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>4.12</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>mysql</groupId>
|
||||||
|
<artifactId>mysql-connector-java</artifactId>
|
||||||
|
<version>5.1.48</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
|
<version>2.4</version>
|
||||||
|
<configuration>
|
||||||
|
<descriptorRefs>
|
||||||
|
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||||
|
</descriptorRefs>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>make-assembly</id>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>single</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-antrun-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>run</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<tasks>
|
||||||
|
<copy todir="${project.basedir}/../launcher/target/classes/kudu" overwrite="true">
|
||||||
|
<fileset dir="${project.basedir}/target/classes/kudu" erroronmissingdir="true">
|
||||||
|
<include name="*.yml" />
|
||||||
|
</fileset>
|
||||||
|
</copy>
|
||||||
|
</tasks>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
||||||
+243
@@ -0,0 +1,243 @@
|
|||||||
|
package com.alibaba.otter.canal.client.adapter.kudu;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.client.adapter.OuterAdapter;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.kudu.config.KuduMappingConfig;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.kudu.config.KuduMappingConfigLoader;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.kudu.monitor.KuduConfigMonitor;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.kudu.service.KuduEtlService;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.kudu.service.KuduSyncService;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.kudu.support.KuduTemplate;
|
||||||
|
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.FileName2KeyMapping;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.support.OuterAdapterConfig;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.support.SPI;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.support.Util;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author liuyadong
|
||||||
|
* @description kudu适配器主类
|
||||||
|
*/
|
||||||
|
@SPI("kudu")
|
||||||
|
public class KuduAdapter implements OuterAdapter {
|
||||||
|
|
||||||
|
private static Logger logger = LoggerFactory.getLogger(KuduAdapter.class);
|
||||||
|
|
||||||
|
private Map<String, KuduMappingConfig> kuduMapping = new ConcurrentHashMap<>(); // 文件名对应配置
|
||||||
|
private Map<String, Map<String, KuduMappingConfig>> mappingConfigCache = new ConcurrentHashMap<>(); // 库名-表名对应配置
|
||||||
|
|
||||||
|
private KuduTemplate kuduTemplate;
|
||||||
|
|
||||||
|
private KuduSyncService kuduSyncService;
|
||||||
|
|
||||||
|
private KuduConfigMonitor kuduConfigMonitor;
|
||||||
|
|
||||||
|
private Properties envProperties;
|
||||||
|
|
||||||
|
private OuterAdapterConfig configuration;
|
||||||
|
|
||||||
|
public Map<String, KuduMappingConfig> getKuduMapping() {
|
||||||
|
return kuduMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Map<String, KuduMappingConfig>> getMappingConfigCache() {
|
||||||
|
return mappingConfigCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(OuterAdapterConfig configuration, Properties envProperties) {
|
||||||
|
this.envProperties = envProperties;
|
||||||
|
this.configuration = configuration;
|
||||||
|
Map<String, KuduMappingConfig> kuduMappingTmp = KuduMappingConfigLoader.load(envProperties);
|
||||||
|
// 过滤不匹配的key的配置,获取连接key,key为配置文件名称
|
||||||
|
kuduMappingTmp.forEach((key, config) -> {
|
||||||
|
addConfig(key, config);
|
||||||
|
});
|
||||||
|
// 判断目标字段是否为空
|
||||||
|
if (kuduMapping.isEmpty()) {
|
||||||
|
throw new RuntimeException("No kudu adapter found for config key: " + configuration.getKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> properties = configuration.getProperties();
|
||||||
|
|
||||||
|
String kudu_master = properties.get("kudu.master.address");
|
||||||
|
kuduTemplate = new KuduTemplate(kudu_master);
|
||||||
|
kuduSyncService = new KuduSyncService(kuduTemplate);
|
||||||
|
|
||||||
|
kuduConfigMonitor = new KuduConfigMonitor();
|
||||||
|
kuduConfigMonitor.init(this, envProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sync(List<Dml> dmls) {
|
||||||
|
if (dmls == null || dmls.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (Dml dml : dmls) {
|
||||||
|
if (dml == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String destination = StringUtils.trimToEmpty(dml.getDestination());
|
||||||
|
String groupId = StringUtils.trimToEmpty(dml.getGroupId());
|
||||||
|
String database = dml.getDatabase();
|
||||||
|
String table = dml.getTable();
|
||||||
|
Map<String, KuduMappingConfig> configMap;
|
||||||
|
if (envProperties != null && !"tcp".equalsIgnoreCase(envProperties.getProperty("canal.conf.mode"))) {
|
||||||
|
configMap = mappingConfigCache.get(destination + "-" + groupId + "_" + database + "-" + table);
|
||||||
|
} else {
|
||||||
|
configMap = mappingConfigCache.get(destination + "_" + database + "-" + table);
|
||||||
|
}
|
||||||
|
if (configMap != null) {
|
||||||
|
List<KuduMappingConfig> configs = new ArrayList<>();
|
||||||
|
configMap.values().forEach(config -> {
|
||||||
|
if (StringUtils.isNotEmpty(config.getGroupId())) {
|
||||||
|
if (config.getGroupId().equals(dml.getGroupId())) {
|
||||||
|
configs.add(config);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
configs.add(config);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!configs.isEmpty()) {
|
||||||
|
configs.forEach(config -> kuduSyncService.sync(config, dml));
|
||||||
|
} else {
|
||||||
|
logger.error("groupID didn't mach,please check your gruopId ");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.error("{} config didn't get,please check your map key ", destination + "_" + database + "-"
|
||||||
|
+ table);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
if (kuduConfigMonitor != null) {
|
||||||
|
kuduConfigMonitor.destroy();
|
||||||
|
}
|
||||||
|
// 加入kudu client 关闭钩子
|
||||||
|
kuduTemplate.closeKuduClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EtlResult etl(String task, List<String> params) {
|
||||||
|
EtlResult etlResult = new EtlResult();
|
||||||
|
KuduMappingConfig config = kuduMapping.get(task);
|
||||||
|
KuduEtlService hbaseEtlService = new KuduEtlService(kuduTemplate, config);
|
||||||
|
if (config != null) {
|
||||||
|
return hbaseEtlService.importData(params);
|
||||||
|
} else {
|
||||||
|
StringBuilder resultMsg = new StringBuilder();
|
||||||
|
boolean resSucc = true;
|
||||||
|
for (KuduMappingConfig configTmp : kuduMapping.values()) {
|
||||||
|
// 取所有的destination为task的配置
|
||||||
|
if (configTmp.getDestination().equals(task)) {
|
||||||
|
EtlResult etlRes = hbaseEtlService.importData(params);
|
||||||
|
if (!etlRes.getSucceeded()) {
|
||||||
|
resSucc = false;
|
||||||
|
resultMsg.append(etlRes.getErrorMessage()).append("\n");
|
||||||
|
} else {
|
||||||
|
resultMsg.append(etlRes.getResultMessage()).append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (resultMsg.length() > 0) {
|
||||||
|
etlResult.setSucceeded(resSucc);
|
||||||
|
if (resSucc) {
|
||||||
|
etlResult.setResultMessage(resultMsg.toString());
|
||||||
|
} else {
|
||||||
|
etlResult.setErrorMessage(resultMsg.toString());
|
||||||
|
}
|
||||||
|
return etlResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> count(String task) {
|
||||||
|
Map<String, Object> res = new LinkedHashMap<>();
|
||||||
|
KuduMappingConfig config = kuduMapping.get(task);
|
||||||
|
if (config != null && config.getKuduMapping() != null) {
|
||||||
|
String tableName = config.getKuduMapping().getTargetTable();
|
||||||
|
long rowCount = kuduTemplate.countRow(tableName);
|
||||||
|
res.put("kuduTable", tableName);
|
||||||
|
res.put("count", rowCount);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDestination(String task) {
|
||||||
|
KuduMappingConfig config = kuduMapping.get(task);
|
||||||
|
if (config != null && config.getKuduMapping() != null) {
|
||||||
|
return config.getDestination();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addSyncConfigToCache(String configName, KuduMappingConfig mappingConfig) {
|
||||||
|
String k;
|
||||||
|
if (envProperties != null && !"tcp".equalsIgnoreCase(envProperties.getProperty("canal.conf.mode"))) {
|
||||||
|
k = StringUtils.trimToEmpty(mappingConfig.getDestination()) + "-"
|
||||||
|
+ StringUtils.trimToEmpty(mappingConfig.getGroupId()) + "_"
|
||||||
|
+ mappingConfig.getKuduMapping().getDatabase() + "-" + mappingConfig.getKuduMapping().getTable();
|
||||||
|
} else {
|
||||||
|
k = StringUtils.trimToEmpty(mappingConfig.getDestination()) + "_"
|
||||||
|
+ mappingConfig.getKuduMapping().getDatabase() + "-" + mappingConfig.getKuduMapping().getTable();
|
||||||
|
}
|
||||||
|
Map<String, KuduMappingConfig> configMap = mappingConfigCache.computeIfAbsent(k,
|
||||||
|
k1 -> new ConcurrentHashMap<>());
|
||||||
|
configMap.put(configName, mappingConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean addConfig(String fileName, KuduMappingConfig config) {
|
||||||
|
if (match(config)) {
|
||||||
|
kuduMapping.put(fileName, config);
|
||||||
|
addSyncConfigToCache(fileName, config);
|
||||||
|
FileName2KeyMapping.register(getClass().getAnnotation(SPI.class).value(), fileName,
|
||||||
|
configuration.getKey());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateConfig(String fileName, KuduMappingConfig config) {
|
||||||
|
if (config.getOuterAdapterKey() != null && !config.getOuterAdapterKey()
|
||||||
|
.equals(configuration.getKey())) {
|
||||||
|
// 理论上不允许改这个 因为本身就是通过这个关联起Adapter和Config的
|
||||||
|
throw new RuntimeException("not allow to change outAdapterKey");
|
||||||
|
}
|
||||||
|
kuduMapping.put(fileName, config);
|
||||||
|
addSyncConfigToCache(fileName, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteConfig(String fileName) {
|
||||||
|
kuduMapping.remove(fileName);
|
||||||
|
for (Map<String, KuduMappingConfig> configMap : mappingConfigCache.values()) {
|
||||||
|
if (configMap != null) {
|
||||||
|
configMap.remove(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FileName2KeyMapping.unregister(getClass().getAnnotation(SPI.class).value(), fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean match(KuduMappingConfig config) {
|
||||||
|
boolean sameMatch = config.getOuterAdapterKey() != null && config.getOuterAdapterKey()
|
||||||
|
.equalsIgnoreCase(configuration.getKey());
|
||||||
|
boolean prefixMatch = config.getOuterAdapterKey() == null && configuration.getKey()
|
||||||
|
.startsWith(StringUtils
|
||||||
|
.join(new String[]{Util.AUTO_GENERATED_PREFIX, config.getDestination(),
|
||||||
|
config.getGroupId()}, '-'));
|
||||||
|
return sameMatch || prefixMatch;
|
||||||
|
}
|
||||||
|
}
|
||||||
+200
@@ -0,0 +1,200 @@
|
|||||||
|
package com.alibaba.otter.canal.client.adapter.kudu.config;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.client.adapter.support.AdapterConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author liuyadong
|
||||||
|
* @description kudu配置文件映射
|
||||||
|
*/
|
||||||
|
public class KuduMappingConfig implements AdapterConfig {
|
||||||
|
|
||||||
|
private String dataSourceKey; // 数据源key
|
||||||
|
|
||||||
|
private String destination; // canal实例或MQ的topic
|
||||||
|
|
||||||
|
private String groupId; // groupId
|
||||||
|
|
||||||
|
private String outerAdapterKey; // 对应适配器的key
|
||||||
|
|
||||||
|
private boolean concurrent = false; // 是否并行同步
|
||||||
|
|
||||||
|
private KuduMapping kuduMapping; // db映射配置
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDataSourceKey() {
|
||||||
|
return dataSourceKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AdapterMapping getMapping() {
|
||||||
|
return kuduMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDataSourceKey(String dataSourceKey) {
|
||||||
|
this.dataSourceKey = dataSourceKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGroupId() {
|
||||||
|
return groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGroupId(String groupId) {
|
||||||
|
this.groupId = groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOuterAdapterKey() {
|
||||||
|
return outerAdapterKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOuterAdapterKey(String outerAdapterKey) {
|
||||||
|
this.outerAdapterKey = outerAdapterKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getConcurrent() {
|
||||||
|
return concurrent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConcurrent(boolean concurrent) {
|
||||||
|
this.concurrent = concurrent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KuduMapping getKuduMapping() {
|
||||||
|
return kuduMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKuduMapping(KuduMapping kuduMapping) {
|
||||||
|
this.kuduMapping = kuduMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDestination() {
|
||||||
|
return destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDestination(String destination) {
|
||||||
|
this.destination = destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void validate() {
|
||||||
|
if (kuduMapping.database == null || kuduMapping.database.isEmpty()) {
|
||||||
|
throw new NullPointerException("KuduMapping.database");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class KuduMapping implements AdapterMapping {
|
||||||
|
|
||||||
|
private String database; // 数据库名或schema名
|
||||||
|
private String table; // 表名
|
||||||
|
private Map<String, String> targetPk = new LinkedHashMap<>(); // 目标表主键字段
|
||||||
|
private boolean mapAll = false; // 映射所有字段
|
||||||
|
private String targetDb; // 目标库名
|
||||||
|
private String targetTable; // 目标表名
|
||||||
|
private Map<String, String> targetColumns; // 目标表字段映射
|
||||||
|
|
||||||
|
private String etlCondition; // etl条件sql
|
||||||
|
|
||||||
|
private int readBatch = 5000;
|
||||||
|
private int commitBatch = 5000; // etl等批量提交大小
|
||||||
|
|
||||||
|
private Map<String, String> allMapColumns;
|
||||||
|
|
||||||
|
public String getDatabase() {
|
||||||
|
return database;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDatabase(String database) {
|
||||||
|
this.database = database;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTable() {
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTable(String table) {
|
||||||
|
this.table = table;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getTargetPk() {
|
||||||
|
return targetPk;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTargetPk(Map<String, String> targetPk) {
|
||||||
|
this.targetPk = targetPk;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getMapAll() {
|
||||||
|
return mapAll;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMapAll(Boolean mapAll) {
|
||||||
|
this.mapAll = mapAll;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTargetDb() {
|
||||||
|
return targetDb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTargetDb(String targetDb) {
|
||||||
|
this.targetDb = targetDb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTargetTable() {
|
||||||
|
return targetTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTargetTable(String targetTable) {
|
||||||
|
this.targetTable = targetTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getTargetColumns() {
|
||||||
|
if (targetColumns != null) {
|
||||||
|
targetColumns.forEach((key, value) -> {
|
||||||
|
if (StringUtils.isEmpty(value)) {
|
||||||
|
targetColumns.put(key, key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return targetColumns;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTargetColumns(Map<String, String> targetColumns) {
|
||||||
|
this.targetColumns = targetColumns;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEtlCondition() {
|
||||||
|
return etlCondition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEtlCondition(String etlCondition) {
|
||||||
|
this.etlCondition = etlCondition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getReadBatch() {
|
||||||
|
return readBatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReadBatch(int readBatch) {
|
||||||
|
this.readBatch = readBatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCommitBatch() {
|
||||||
|
return commitBatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCommitBatch(int commitBatch) {
|
||||||
|
this.commitBatch = commitBatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getAllMapColumns() {
|
||||||
|
return allMapColumns;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAllMapColumns(Map<String, String> allMapColumns) {
|
||||||
|
this.allMapColumns = allMapColumns;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+52
@@ -0,0 +1,52 @@
|
|||||||
|
package com.alibaba.otter.canal.client.adapter.kudu.config;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.client.adapter.config.YmlConfigBinder;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.support.MappingConfigsLoader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author liuyadong
|
||||||
|
* @description kudu表信息加载
|
||||||
|
*/
|
||||||
|
public class KuduMappingConfigLoader {
|
||||||
|
|
||||||
|
private static Logger logger = LoggerFactory.getLogger(KuduMappingConfigLoader.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载HBase表映射配置
|
||||||
|
*
|
||||||
|
* @return 配置名/配置文件名--对象
|
||||||
|
*/
|
||||||
|
public static Map<String, KuduMappingConfig> load(Properties envProperties) {
|
||||||
|
logger.info("## Start loading kudu mapping config ... ");
|
||||||
|
|
||||||
|
Map<String, KuduMappingConfig> result = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
Map<String, String> configContentMap = MappingConfigsLoader.loadConfigs("kudu");
|
||||||
|
configContentMap.forEach((fileName, content) -> {
|
||||||
|
KuduMappingConfig config = YmlConfigBinder.bindYmlToObj(null,
|
||||||
|
content,
|
||||||
|
KuduMappingConfig.class,
|
||||||
|
null,
|
||||||
|
envProperties);
|
||||||
|
if (config == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
config.validate();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("ERROR load Config: " + fileName + " " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
result.put(fileName, config);
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info("## kudu mapping config loaded");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
+134
@@ -0,0 +1,134 @@
|
|||||||
|
package com.alibaba.otter.canal.client.adapter.kudu.monitor;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.client.adapter.config.YmlConfigBinder;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.kudu.KuduAdapter;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.kudu.config.KuduMappingConfig;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.support.MappingConfigsLoader;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.support.Util;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Properties;
|
||||||
|
import org.apache.commons.io.filefilter.FileFilterUtils;
|
||||||
|
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
|
||||||
|
import org.apache.commons.io.monitor.FileAlterationMonitor;
|
||||||
|
import org.apache.commons.io.monitor.FileAlterationObserver;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author liuyadong
|
||||||
|
* @description 配置文件监听
|
||||||
|
*/
|
||||||
|
public class KuduConfigMonitor {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(KuduConfigMonitor.class);
|
||||||
|
|
||||||
|
private static final String adapterName = "kudu";
|
||||||
|
|
||||||
|
private KuduAdapter kuduAdapter;
|
||||||
|
|
||||||
|
private Properties envProperties;
|
||||||
|
|
||||||
|
private FileAlterationMonitor fileMonitor;
|
||||||
|
|
||||||
|
public void init(KuduAdapter kuduAdapter, Properties envProperties) {
|
||||||
|
this.kuduAdapter = kuduAdapter;
|
||||||
|
this.envProperties = envProperties;
|
||||||
|
File confDir = Util.getConfDirPath(adapterName);
|
||||||
|
try {
|
||||||
|
FileAlterationObserver observer = new FileAlterationObserver(confDir,
|
||||||
|
FileFilterUtils.and(FileFilterUtils.fileFileFilter(), FileFilterUtils.suffixFileFilter("yml")));
|
||||||
|
FileListener listener = new FileListener();
|
||||||
|
observer.addListener(listener);
|
||||||
|
fileMonitor = new FileAlterationMonitor(3000, observer);
|
||||||
|
fileMonitor.start();
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止监听配置文件
|
||||||
|
*/
|
||||||
|
public void destroy() {
|
||||||
|
try {
|
||||||
|
fileMonitor.stop();
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置文件监听
|
||||||
|
*/
|
||||||
|
private class FileListener extends FileAlterationListenerAdaptor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFileCreate(File file) {
|
||||||
|
super.onFileCreate(file);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 加载新增的配置文件
|
||||||
|
String configContent = MappingConfigsLoader.loadConfig(adapterName + File.separator + file.getName());
|
||||||
|
KuduMappingConfig config = YmlConfigBinder.bindYmlToObj(null,
|
||||||
|
configContent,
|
||||||
|
KuduMappingConfig.class,
|
||||||
|
null,
|
||||||
|
envProperties);
|
||||||
|
if (config == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
config.validate();
|
||||||
|
boolean result = kuduAdapter.addConfig(file.getName(), config);
|
||||||
|
if (result) {
|
||||||
|
logger.info("Add a new kudu mapping config: {} to canal adapter",
|
||||||
|
file.getName());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFileChange(File file) {
|
||||||
|
super.onFileChange(file);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (kuduAdapter.getKuduMapping().containsKey(file.getName())) {
|
||||||
|
// 加载配置文件
|
||||||
|
String configContent = MappingConfigsLoader.loadConfig(adapterName + File.separator
|
||||||
|
+ file.getName());
|
||||||
|
if (configContent == null) {
|
||||||
|
onFileDelete(file);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
KuduMappingConfig config = YmlConfigBinder.bindYmlToObj(null,
|
||||||
|
configContent,
|
||||||
|
KuduMappingConfig.class,
|
||||||
|
null,
|
||||||
|
envProperties);
|
||||||
|
if (config == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
config.validate();
|
||||||
|
kuduAdapter.updateConfig(file.getName(), config);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFileDelete(File file) {
|
||||||
|
super.onFileDelete(file);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (kuduAdapter.getKuduMapping().containsKey(file.getName())) {
|
||||||
|
kuduAdapter.deleteConfig(file.getName());
|
||||||
|
logger.info("Delete a hbase mapping config: {} of canal adapter", file.getName());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+132
@@ -0,0 +1,132 @@
|
|||||||
|
package com.alibaba.otter.canal.client.adapter.kudu.service;
|
||||||
|
|
||||||
|
import java.sql.ResultSetMetaData;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
|
import org.apache.kudu.client.KuduException;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.client.adapter.kudu.config.KuduMappingConfig;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.kudu.support.KuduTemplate;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.kudu.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.Util;
|
||||||
|
import com.google.common.base.Joiner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author liuyadong
|
||||||
|
* @description kudu 拉取历史数据
|
||||||
|
*/
|
||||||
|
public class KuduEtlService extends AbstractEtlService {
|
||||||
|
|
||||||
|
private KuduTemplate kuduTemplate;
|
||||||
|
private KuduMappingConfig config;
|
||||||
|
|
||||||
|
public KuduEtlService(KuduTemplate kuduTemplate, KuduMappingConfig config){
|
||||||
|
super("kudu", config);
|
||||||
|
this.kuduTemplate = kuduTemplate;
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EtlResult importData(List<String> params) {
|
||||||
|
EtlResult etlResult = new EtlResult();
|
||||||
|
List<String> errMsg = new ArrayList<>();
|
||||||
|
|
||||||
|
KuduMappingConfig.KuduMapping kuduMapping = config.getKuduMapping();
|
||||||
|
boolean flag = kuduTemplate.tableExists(kuduMapping.getTargetTable());
|
||||||
|
// 表不存在,停止导入
|
||||||
|
if (!flag) {
|
||||||
|
logger.info("{} is don't hava,please check your kudu table !", kuduMapping.getTargetTable());
|
||||||
|
errMsg.add(kuduMapping.getTargetTable() + " is don't hava,please check your kudu table !");
|
||||||
|
etlResult.setErrorMessage(Joiner.on("\n").join(errMsg));
|
||||||
|
return etlResult;
|
||||||
|
}
|
||||||
|
logger.info("{} etl is starting!", kuduMapping.getTargetTable());
|
||||||
|
String sql = "SELECT * FROM " + kuduMapping.getDatabase() + "." + kuduMapping.getTable();
|
||||||
|
return importData(sql, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean executeSqlImport(DataSource ds, String sql, List<Object> values,
|
||||||
|
AdapterConfig.AdapterMapping mapping, AtomicLong impCount, List<String> errMsg) {
|
||||||
|
KuduMappingConfig.KuduMapping kuduMapping = (KuduMappingConfig.KuduMapping) mapping;
|
||||||
|
// 获取字段元数据
|
||||||
|
Map<String, String> columnsMap = new LinkedHashMap<>();// 需要同步的字段
|
||||||
|
|
||||||
|
try {
|
||||||
|
Util.sqlRS(ds, "SELECT * FROM " + SyncUtil.getDbTableName(kuduMapping) + " LIMIT 1", rs -> {
|
||||||
|
try {
|
||||||
|
ResultSetMetaData rsd = rs.getMetaData();
|
||||||
|
int columnCount = rsd.getColumnCount();
|
||||||
|
List<String> columns = new ArrayList<>();
|
||||||
|
for (int i = 1; i <= columnCount; i++) {
|
||||||
|
columns.add(rsd.getColumnName(i).toLowerCase());
|
||||||
|
}
|
||||||
|
columnsMap.putAll(SyncUtil.getColumnsMap(kuduMapping, columns));
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error(e.getMessage(), e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 写入数据
|
||||||
|
logger.info("etl select data sql is :{}", sql);
|
||||||
|
Util.sqlRS(ds, sql, values, rs -> {
|
||||||
|
int idx = 1;
|
||||||
|
try {
|
||||||
|
List<Map<String, Object>> dataList = new ArrayList<>();
|
||||||
|
while (rs.next()) {
|
||||||
|
Map<String, Object> data = new HashMap<>();
|
||||||
|
for (Map.Entry<String, String> entry : columnsMap.entrySet()) {
|
||||||
|
String mysqlColumnName = entry.getKey();// mysql字段名
|
||||||
|
String kuduColumnName = entry.getValue();// kudu字段名
|
||||||
|
if (kuduColumnName == null) {
|
||||||
|
kuduColumnName = mysqlColumnName;
|
||||||
|
}
|
||||||
|
Object value = rs.getObject(kuduColumnName);
|
||||||
|
if (value != null) {
|
||||||
|
data.put(kuduColumnName, value);
|
||||||
|
} else {
|
||||||
|
data.put(kuduColumnName, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dataList.add(data);
|
||||||
|
idx++;
|
||||||
|
impCount.incrementAndGet();
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("successful import count:" + impCount.get());
|
||||||
|
}
|
||||||
|
if (idx % kuduMapping.getCommitBatch() == 0) {
|
||||||
|
kuduTemplate.upsert(kuduMapping.getTargetTable(), dataList);
|
||||||
|
dataList.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!dataList.isEmpty()) {
|
||||||
|
kuduTemplate.upsert(kuduMapping.getTargetTable(), dataList);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (SQLException | KuduException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
logger.error(kuduMapping.getTargetTable() + " etl failed! ==>" + e.getMessage(), e);
|
||||||
|
errMsg.add(kuduMapping.getTargetTable() + " etl failed! ==>" + e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error(e.getMessage(), e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
+209
@@ -0,0 +1,209 @@
|
|||||||
|
package com.alibaba.otter.canal.client.adapter.kudu.service;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.kudu.client.KuduException;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import com.alibaba.fastjson2.JSONWriter.Feature;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.kudu.config.KuduMappingConfig;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.kudu.support.KuduTemplate;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.support.Dml;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author liuyadong
|
||||||
|
* @description kudu实时同步
|
||||||
|
*/
|
||||||
|
public class KuduSyncService {
|
||||||
|
|
||||||
|
private static Logger logger = LoggerFactory.getLogger(KuduSyncService.class);
|
||||||
|
|
||||||
|
private KuduTemplate kuduTemplate;
|
||||||
|
|
||||||
|
// 源库表字段类型缓存: instance.schema.table -> <columnName, jdbcType>
|
||||||
|
// private Map<String, Map<String, Integer>> columnsTypeCache = new
|
||||||
|
// ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public KuduSyncService(KuduTemplate kuduTemplate){
|
||||||
|
this.kuduTemplate = kuduTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
// public Map<String, Map<String, Integer>> getColumnsTypeCache() {
|
||||||
|
// return columnsTypeCache;
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同步事件处理
|
||||||
|
*
|
||||||
|
* @param config
|
||||||
|
* @param dml
|
||||||
|
*/
|
||||||
|
public void sync(KuduMappingConfig config, Dml dml) {
|
||||||
|
if (config != null) {
|
||||||
|
String type = dml.getType();
|
||||||
|
if (type != null && type.equalsIgnoreCase("INSERT")) {
|
||||||
|
insert(config, dml);
|
||||||
|
} else if (type != null && type.equalsIgnoreCase("UPDATE")) {
|
||||||
|
upsert(config, dml);
|
||||||
|
} else if (type != null && type.equalsIgnoreCase("DELETE")) {
|
||||||
|
delete(config, dml);
|
||||||
|
}
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("DML: {}", JSON.toJSONString(dml, Feature.WriteNulls));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除事件
|
||||||
|
*
|
||||||
|
* @param config
|
||||||
|
* @param dml
|
||||||
|
*/
|
||||||
|
private void delete(KuduMappingConfig config, Dml dml) {
|
||||||
|
KuduMappingConfig.KuduMapping kuduMapping = config.getKuduMapping();
|
||||||
|
String configTable = kuduMapping.getTable();
|
||||||
|
String configDatabase = kuduMapping.getDatabase();
|
||||||
|
String table = dml.getTable();
|
||||||
|
String database = dml.getDatabase();
|
||||||
|
if (configTable.equals(table) && configDatabase.equals(database)) {
|
||||||
|
List<Map<String, Object>> data = dml.getData();
|
||||||
|
if (data == null || data.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 判定主键映射
|
||||||
|
String pkId = "";
|
||||||
|
Map<String, String> targetPk = kuduMapping.getTargetPk();
|
||||||
|
for (Map.Entry<String, String> entry : targetPk.entrySet()) {
|
||||||
|
String mysqlID = entry.getKey().toLowerCase();
|
||||||
|
String kuduID = entry.getValue();
|
||||||
|
if (kuduID == null) {
|
||||||
|
pkId = mysqlID;
|
||||||
|
} else {
|
||||||
|
pkId = kuduID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 切割联合主键
|
||||||
|
List<String> pkIds = Arrays.asList(pkId.split(","));
|
||||||
|
try {
|
||||||
|
int idx = 1;
|
||||||
|
boolean completed = false;
|
||||||
|
List<Map<String, Object>> dataList = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Map<String, Object> item : data) {
|
||||||
|
Map<String, Object> primaryKeyMap = new HashMap<>();
|
||||||
|
for (Map.Entry<String, Object> entry : item.entrySet()) {
|
||||||
|
String columnName = entry.getKey().toLowerCase();
|
||||||
|
Object value = entry.getValue();
|
||||||
|
if (pkIds.contains(columnName)) {
|
||||||
|
primaryKeyMap.put(columnName, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dataList.add(primaryKeyMap);
|
||||||
|
idx++;
|
||||||
|
if (idx % kuduMapping.getCommitBatch() == 0) {
|
||||||
|
kuduTemplate.delete(kuduMapping.getTargetTable(), dataList);
|
||||||
|
dataList.clear();
|
||||||
|
completed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!completed) {
|
||||||
|
kuduTemplate.delete(kuduMapping.getTargetTable(), dataList);
|
||||||
|
}
|
||||||
|
} catch (KuduException e) {
|
||||||
|
logger.error(e.getMessage());
|
||||||
|
logger.error("DML: {}", JSON.toJSONString(dml, Feature.WriteNulls));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新插入事件
|
||||||
|
*
|
||||||
|
* @param config
|
||||||
|
* @param dml
|
||||||
|
*/
|
||||||
|
private void upsert(KuduMappingConfig config, Dml dml) {
|
||||||
|
KuduMappingConfig.KuduMapping kuduMapping = config.getKuduMapping();
|
||||||
|
String configTable = kuduMapping.getTable();
|
||||||
|
String configDatabase = kuduMapping.getDatabase();
|
||||||
|
String table = dml.getTable();
|
||||||
|
String database = dml.getDatabase();
|
||||||
|
if (configTable.equals(table) && configDatabase.equals(database)) {
|
||||||
|
List<Map<String, Object>> data = dml.getData();
|
||||||
|
if (data == null || data.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
int idx = 1;
|
||||||
|
boolean completed = false;
|
||||||
|
List<Map<String, Object>> dataList = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Map<String, Object> entry : data) {
|
||||||
|
dataList.add(entry);
|
||||||
|
idx++;
|
||||||
|
if (idx % kuduMapping.getCommitBatch() == 0) {
|
||||||
|
kuduTemplate.upsert(kuduMapping.getTargetTable(), dataList);
|
||||||
|
dataList.clear();
|
||||||
|
completed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!completed) {
|
||||||
|
kuduTemplate.upsert(kuduMapping.getTargetTable(), dataList);
|
||||||
|
}
|
||||||
|
} catch (KuduException e) {
|
||||||
|
logger.error(e.getMessage());
|
||||||
|
logger.error("DML: {}", JSON.toJSONString(dml, Feature.WriteNulls));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插入事件
|
||||||
|
*
|
||||||
|
* @param config
|
||||||
|
* @param dml
|
||||||
|
*/
|
||||||
|
private void insert(KuduMappingConfig config, Dml dml) {
|
||||||
|
KuduMappingConfig.KuduMapping kuduMapping = config.getKuduMapping();
|
||||||
|
String configTable = kuduMapping.getTable();
|
||||||
|
String configDatabase = kuduMapping.getDatabase();
|
||||||
|
String table = dml.getTable();
|
||||||
|
String database = dml.getDatabase();
|
||||||
|
if (configTable.equals(table) && configDatabase.equals(database)) {
|
||||||
|
List<Map<String, Object>> data = dml.getData();
|
||||||
|
if (data == null || data.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
int idx = 1;
|
||||||
|
boolean completed = false;
|
||||||
|
List<Map<String, Object>> dataList = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Map<String, Object> entry : data) {
|
||||||
|
dataList.add(entry);
|
||||||
|
idx++;
|
||||||
|
if (idx % kuduMapping.getCommitBatch() == 0) {
|
||||||
|
kuduTemplate.insert(kuduMapping.getTargetTable(), dataList);
|
||||||
|
dataList.clear();
|
||||||
|
completed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!completed) {
|
||||||
|
kuduTemplate.insert(kuduMapping.getTargetTable(), dataList);
|
||||||
|
}
|
||||||
|
} catch (KuduException e) {
|
||||||
|
logger.error(e.getMessage());
|
||||||
|
logger.error("DML: {}", JSON.toJSONString(dml, Feature.WriteNulls));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+424
@@ -0,0 +1,424 @@
|
|||||||
|
package com.alibaba.otter.canal.client.adapter.kudu.support;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.kudu.ColumnSchema;
|
||||||
|
import org.apache.kudu.Schema;
|
||||||
|
import org.apache.kudu.Type;
|
||||||
|
import org.apache.kudu.client.Delete;
|
||||||
|
import org.apache.kudu.client.Insert;
|
||||||
|
import org.apache.kudu.client.KuduClient;
|
||||||
|
import org.apache.kudu.client.KuduException;
|
||||||
|
import org.apache.kudu.client.KuduScanner;
|
||||||
|
import org.apache.kudu.client.KuduSession;
|
||||||
|
import org.apache.kudu.client.KuduTable;
|
||||||
|
import org.apache.kudu.client.OperationResponse;
|
||||||
|
import org.apache.kudu.client.PartialRow;
|
||||||
|
import org.apache.kudu.client.SessionConfiguration;
|
||||||
|
import org.apache.kudu.client.Upsert;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author liuyadong
|
||||||
|
* @description kudu 操作工具类
|
||||||
|
*/
|
||||||
|
public class KuduTemplate {
|
||||||
|
|
||||||
|
private Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||||
|
|
||||||
|
private KuduClient kuduClient;
|
||||||
|
private String masters;
|
||||||
|
|
||||||
|
private final static int OPERATION_BATCH = 500;
|
||||||
|
|
||||||
|
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||||
|
|
||||||
|
public KuduTemplate(String master_str){
|
||||||
|
this.masters = master_str;
|
||||||
|
checkClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检车连接
|
||||||
|
*/
|
||||||
|
private void checkClient() {
|
||||||
|
if (kuduClient == null) {
|
||||||
|
// kudu master 以逗号分隔
|
||||||
|
List<String> masterList = Arrays.asList(masters.split(","));
|
||||||
|
kuduClient = new KuduClient.KuduClientBuilder(masterList).defaultOperationTimeoutMs(60000)
|
||||||
|
.defaultSocketReadTimeoutMs(60000)
|
||||||
|
.defaultAdminOperationTimeoutMs(60000)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询表是否存在
|
||||||
|
*
|
||||||
|
* @param tableName
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean tableExists(String tableName) {
|
||||||
|
this.checkClient();
|
||||||
|
try {
|
||||||
|
return kuduClient.tableExists(tableName);
|
||||||
|
} catch (KuduException e) {
|
||||||
|
logger.error("kudu table exists check fail,message :{}", e.getMessage());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除行
|
||||||
|
*
|
||||||
|
* @param tableName
|
||||||
|
* @param dataList
|
||||||
|
* @throws KuduException
|
||||||
|
*/
|
||||||
|
public void delete(String tableName, List<Map<String, Object>> dataList) throws KuduException {
|
||||||
|
this.checkClient();
|
||||||
|
KuduTable kuduTable = kuduClient.openTable(tableName);
|
||||||
|
KuduSession session = kuduClient.newSession();
|
||||||
|
try {
|
||||||
|
session.setFlushMode(SessionConfiguration.FlushMode.MANUAL_FLUSH);
|
||||||
|
session.setMutationBufferSpace(OPERATION_BATCH);
|
||||||
|
// 获取元数据结构
|
||||||
|
Map<String, Type> metaMap = new HashMap<>();
|
||||||
|
Schema schema = kuduTable.getSchema();
|
||||||
|
for (ColumnSchema columnSchema : schema.getColumns()) {
|
||||||
|
String colName = columnSchema.getName().toLowerCase();
|
||||||
|
Type type = columnSchema.getType();
|
||||||
|
metaMap.put(colName, type);
|
||||||
|
}
|
||||||
|
int uncommit = 0;
|
||||||
|
for (Map<String, Object> data : dataList) {
|
||||||
|
Delete delete = kuduTable.newDelete();
|
||||||
|
PartialRow row = delete.getRow();
|
||||||
|
for (Map.Entry<String, Object> entry : data.entrySet()) {
|
||||||
|
String name = entry.getKey().toLowerCase();
|
||||||
|
Type type = metaMap.get(name);
|
||||||
|
Object value = entry.getValue();
|
||||||
|
fillRow(row, name, value, type); // 填充行数据
|
||||||
|
}
|
||||||
|
session.apply(delete);
|
||||||
|
// 对于手工提交, 需要buffer在未满的时候flush,这里采用了buffer一半时即提交
|
||||||
|
uncommit = uncommit + 1;
|
||||||
|
if (uncommit > OPERATION_BATCH / 3 * 2) {
|
||||||
|
List<OperationResponse> delete_option = session.flush();
|
||||||
|
if (delete_option.size() > 0) {
|
||||||
|
OperationResponse response = delete_option.get(0);
|
||||||
|
if (response.hasRowError()) {
|
||||||
|
logger.error("delete row fail table name is :{} ", tableName);
|
||||||
|
logger.error("error list is :{}", response.getRowError().getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uncommit = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List<OperationResponse> delete_option = session.flush();
|
||||||
|
if (delete_option.size() > 0) {
|
||||||
|
OperationResponse response = delete_option.get(0);
|
||||||
|
if (response.hasRowError()) {
|
||||||
|
logger.error("delete row fail table name is :{} ", tableName);
|
||||||
|
logger.error("error list is :{}", response.getRowError().getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (KuduException e) {
|
||||||
|
logger.error("error message is :{}", dataList.toString());
|
||||||
|
throw e;
|
||||||
|
} finally {
|
||||||
|
if (!session.isClosed()) {
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新/插入字段
|
||||||
|
*
|
||||||
|
* @param tableName
|
||||||
|
* @param dataList
|
||||||
|
* @throws KuduException
|
||||||
|
*/
|
||||||
|
public void upsert(String tableName, List<Map<String, Object>> dataList) throws KuduException {
|
||||||
|
this.checkClient();
|
||||||
|
KuduTable kuduTable = kuduClient.openTable(tableName);
|
||||||
|
KuduSession session = kuduClient.newSession();
|
||||||
|
try {
|
||||||
|
session.setFlushMode(SessionConfiguration.FlushMode.MANUAL_FLUSH);
|
||||||
|
session.setMutationBufferSpace(OPERATION_BATCH);
|
||||||
|
// 获取元数据结构
|
||||||
|
Map<String, Type> metaMap = new HashMap<>();
|
||||||
|
Schema schema = kuduTable.getSchema();
|
||||||
|
for (ColumnSchema columnSchema : schema.getColumns()) {
|
||||||
|
String colName = columnSchema.getName().toLowerCase();
|
||||||
|
Type type = columnSchema.getType();
|
||||||
|
metaMap.put(colName, type);
|
||||||
|
}
|
||||||
|
int uncommit = 0;
|
||||||
|
for (Map<String, Object> data : dataList) {
|
||||||
|
Upsert upsert = kuduTable.newUpsert();
|
||||||
|
PartialRow row = upsert.getRow();
|
||||||
|
for (Map.Entry<String, Object> entry : data.entrySet()) {
|
||||||
|
String name = entry.getKey().toLowerCase();
|
||||||
|
Type type = metaMap.get(name);
|
||||||
|
Object value = entry.getValue();
|
||||||
|
fillRow(row, name, value, type); // 填充行数据
|
||||||
|
}
|
||||||
|
session.apply(upsert);
|
||||||
|
// 对于手工提交, 需要buffer在未满的时候flush,这里采用了buffer一半时即提交
|
||||||
|
uncommit = uncommit + 1;
|
||||||
|
if (uncommit > OPERATION_BATCH / 3 * 2) {
|
||||||
|
List<OperationResponse> update_option = session.flush();
|
||||||
|
if (update_option.size() > 0) {
|
||||||
|
OperationResponse response = update_option.get(0);
|
||||||
|
if (response.hasRowError()) {
|
||||||
|
logger.error("update row fail table name is :{} ", tableName);
|
||||||
|
logger.error("update list is :{}", response.getRowError().getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uncommit = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List<OperationResponse> update_option = session.flush();
|
||||||
|
if (update_option.size() > 0) {
|
||||||
|
OperationResponse response = update_option.get(0);
|
||||||
|
if (response.hasRowError()) {
|
||||||
|
logger.error("update row fail table name is :{} ", tableName);
|
||||||
|
logger.error("update list is :{}", response.getRowError().getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (KuduException e) {
|
||||||
|
logger.error("error message is :{}", dataList.toString());
|
||||||
|
throw e;
|
||||||
|
} finally {
|
||||||
|
if (!session.isClosed()) {
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插入数据
|
||||||
|
*
|
||||||
|
* @param tableName
|
||||||
|
* @param dataList
|
||||||
|
* @throws KuduException
|
||||||
|
*/
|
||||||
|
public void insert(String tableName, List<Map<String, Object>> dataList) throws KuduException {
|
||||||
|
this.checkClient();
|
||||||
|
KuduTable kuduTable = kuduClient.openTable(tableName);// 打开表
|
||||||
|
KuduSession session = kuduClient.newSession(); // 创建写session,kudu必须通过session写入
|
||||||
|
try {
|
||||||
|
session.setFlushMode(SessionConfiguration.FlushMode.MANUAL_FLUSH); // 采取Flush方式
|
||||||
|
// 手动刷新
|
||||||
|
session.setMutationBufferSpace(OPERATION_BATCH);
|
||||||
|
// 获取元数据结构
|
||||||
|
Map<String, Type> metaMap = new HashMap<>();
|
||||||
|
Schema schema = kuduTable.getSchema();
|
||||||
|
for (ColumnSchema columnSchema : schema.getColumns()) {
|
||||||
|
String colName = columnSchema.getName().toLowerCase();
|
||||||
|
Type type = columnSchema.getType();
|
||||||
|
metaMap.put(colName, type);
|
||||||
|
}
|
||||||
|
int uncommit = 0;
|
||||||
|
for (Map<String, Object> data : dataList) {
|
||||||
|
Insert insert = kuduTable.newInsert();
|
||||||
|
PartialRow row = insert.getRow();
|
||||||
|
for (Map.Entry<String, Object> entry : data.entrySet()) {
|
||||||
|
String name = entry.getKey().toLowerCase();
|
||||||
|
Type type = metaMap.get(name);
|
||||||
|
Object value = entry.getValue();
|
||||||
|
fillRow(row, name, value, type); // 填充行数据
|
||||||
|
}
|
||||||
|
session.apply(insert);
|
||||||
|
// 对于手工提交, 需要buffer在未满的时候flush,这里采用了buffer一半时即提交
|
||||||
|
uncommit = uncommit + 1;
|
||||||
|
if (uncommit > OPERATION_BATCH / 3 * 2) {
|
||||||
|
List<OperationResponse> insert_option = session.flush();
|
||||||
|
if (insert_option.size() > 0) {
|
||||||
|
OperationResponse response = insert_option.get(0);
|
||||||
|
if (response.hasRowError()) {
|
||||||
|
logger.error("insert row fail table name is :{} ", tableName);
|
||||||
|
logger.error("insert list is :{}", response.getRowError().getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uncommit = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List<OperationResponse> insert_option = session.flush();
|
||||||
|
if (insert_option.size() > 0) {
|
||||||
|
OperationResponse response = insert_option.get(0);
|
||||||
|
if (response.hasRowError()) {
|
||||||
|
logger.error("insert row fail table name is :{} ", tableName);
|
||||||
|
logger.error("insert list is :{}", response.getRowError().getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (KuduException e) {
|
||||||
|
logger.error("error message is :{}", dataList.toString());
|
||||||
|
throw e;
|
||||||
|
} finally {
|
||||||
|
if (!session.isClosed()) {
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计kudu表数据
|
||||||
|
*
|
||||||
|
* @param tableName
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public long countRow(String tableName) {
|
||||||
|
this.checkClient();
|
||||||
|
long rowCount = 0L;
|
||||||
|
try {
|
||||||
|
KuduTable kuduTable = kuduClient.openTable(tableName);
|
||||||
|
// 创建scanner扫描
|
||||||
|
KuduScanner scanner = kuduClient.newScannerBuilder(kuduTable).build();
|
||||||
|
// 遍历数据
|
||||||
|
while (scanner.hasMoreRows()) {
|
||||||
|
while (scanner.nextRows().hasNext()) {
|
||||||
|
rowCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (KuduException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return rowCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭钩子
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void closeKuduClient() {
|
||||||
|
if (kuduClient != null) {
|
||||||
|
try {
|
||||||
|
kuduClient.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("ShutdownHook Close KuduClient Error! error message {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 封装kudu行数据
|
||||||
|
*
|
||||||
|
* @param row
|
||||||
|
* @param rawVal
|
||||||
|
* @param type
|
||||||
|
*/
|
||||||
|
private void fillRow(PartialRow row, String colName, Object rawVal, Type type) {
|
||||||
|
String rowValue = "0";
|
||||||
|
if (!(rawVal == null || "".equals(rawVal))) {
|
||||||
|
rowValue = rawVal + "";
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (type == null) {
|
||||||
|
logger.warn("got unknown type {} for column '{}'-- ignoring this column", type, colName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
switch (type) {
|
||||||
|
case INT8:
|
||||||
|
row.addByte(colName, Byte.parseByte(rowValue));
|
||||||
|
break;
|
||||||
|
case INT16:
|
||||||
|
row.addShort(colName, Short.parseShort(rowValue));
|
||||||
|
break;
|
||||||
|
case INT32:
|
||||||
|
row.addInt(colName, Integer.parseInt(rowValue));
|
||||||
|
break;
|
||||||
|
case INT64:
|
||||||
|
row.addLong(colName, Long.parseLong(rowValue));
|
||||||
|
break;
|
||||||
|
case BINARY:
|
||||||
|
row.addBinary(colName, rowValue.getBytes());
|
||||||
|
break;
|
||||||
|
case STRING:
|
||||||
|
row.addString(colName, rowValue);
|
||||||
|
break;
|
||||||
|
case BOOL:
|
||||||
|
if (!("true".equalsIgnoreCase(rowValue) || "false".equalsIgnoreCase(rowValue))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
row.addBoolean(colName, Boolean.parseBoolean(rowValue));
|
||||||
|
break;
|
||||||
|
case FLOAT:
|
||||||
|
row.addFloat(colName, Float.parseFloat(rowValue));
|
||||||
|
break;
|
||||||
|
case DOUBLE:
|
||||||
|
row.addDouble(colName, Double.parseDouble(rowValue));
|
||||||
|
break;
|
||||||
|
case UNIXTIME_MICROS:
|
||||||
|
if ("0".equals(rowValue)) {
|
||||||
|
try {
|
||||||
|
Date parse = sdf.parse("2099-11-11 11:11:11");
|
||||||
|
row.addLong(colName, parse.getTime());
|
||||||
|
} catch (ParseException e) {
|
||||||
|
logger.warn("date column is null");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
Date parse = rowValue.length() > 19 ? sdf.parse(rowValue.substring(0, 19)) : sdf.parse(rowValue);
|
||||||
|
row.addLong(colName, parse.getTime());
|
||||||
|
} catch (ParseException e) {
|
||||||
|
logger.warn("date format error, error data is :{}", rowValue);
|
||||||
|
try {
|
||||||
|
Date parse = sdf.parse("2099-11-11 11:11:11");
|
||||||
|
row.addLong(colName, parse.getTime());
|
||||||
|
} catch (ParseException ie) {
|
||||||
|
logger.warn("date column is null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
logger.warn("got unknown type {} for column '{}'-- ignoring this column", type, colName);
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
logger.error(e.getMessage());
|
||||||
|
logger.error("column parse fail==> column name=>{},column type=>{},column value=>{}", colName, type, rawVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kudu数据类型映射
|
||||||
|
*
|
||||||
|
* @param
|
||||||
|
*/
|
||||||
|
private Type toKuduType(String mysqlType) throws IllegalArgumentException {
|
||||||
|
|
||||||
|
switch (mysqlType) {
|
||||||
|
case "varchar":
|
||||||
|
return Type.STRING;
|
||||||
|
case "int":
|
||||||
|
return Type.INT8;
|
||||||
|
case "decimal":
|
||||||
|
return Type.DOUBLE;
|
||||||
|
case "double":
|
||||||
|
return Type.DOUBLE;
|
||||||
|
case "datetime":
|
||||||
|
return Type.STRING;
|
||||||
|
case "timestamp":
|
||||||
|
return Type.STRING;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("The provided data type doesn't map to know any known one.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+68
@@ -0,0 +1,68 @@
|
|||||||
|
package com.alibaba.otter.canal.client.adapter.kudu.support;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.client.adapter.kudu.config.KuduMappingConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author liuyadong
|
||||||
|
* @description 工具
|
||||||
|
*/
|
||||||
|
public class SyncUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取源数据库 元数据结构
|
||||||
|
*
|
||||||
|
* @param kuduMapping
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String getDbTableName(KuduMappingConfig.KuduMapping kuduMapping) {
|
||||||
|
String result = "";
|
||||||
|
if (StringUtils.isNotEmpty(kuduMapping.getTable())) {
|
||||||
|
result += kuduMapping.getDatabase() + ".";
|
||||||
|
}
|
||||||
|
result += kuduMapping.getTable();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过滤转换字段
|
||||||
|
*
|
||||||
|
* @param kuduMapping
|
||||||
|
* @param columns
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Map<String, String> getColumnsMap(KuduMappingConfig.KuduMapping kuduMapping, List<String> columns) {
|
||||||
|
Map<String, String> columnsMap;
|
||||||
|
if (kuduMapping.getMapAll()) {
|
||||||
|
if (kuduMapping.getAllMapColumns() != null) {
|
||||||
|
return kuduMapping.getAllMapColumns();
|
||||||
|
}
|
||||||
|
columnsMap = new LinkedHashMap<>();
|
||||||
|
for (String srcColumn : columns) {
|
||||||
|
boolean flag = true;
|
||||||
|
if (kuduMapping.getTargetColumns() != null) {
|
||||||
|
for (Map.Entry<String, String> entry : kuduMapping.getTargetColumns().entrySet()) {
|
||||||
|
if (srcColumn.equals(entry.getValue())) {
|
||||||
|
columnsMap.put(entry.getKey(), srcColumn);
|
||||||
|
flag = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (flag) {
|
||||||
|
columnsMap.put(srcColumn, srcColumn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kuduMapping.setAllMapColumns(columnsMap);
|
||||||
|
} else {
|
||||||
|
columnsMap = kuduMapping.getTargetColumns();
|
||||||
|
}
|
||||||
|
return columnsMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
kudu=com.alibaba.otter.canal.client.adapter.kudu.KuduAdapter
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
dataSourceKey: defaultDS
|
||||||
|
destination: test_instance
|
||||||
|
outerAdapterKey: kudu
|
||||||
|
groupId: g1
|
||||||
|
kuduMapping:
|
||||||
|
database: test
|
||||||
|
table: test
|
||||||
|
targetTable: test
|
||||||
|
targetPk:
|
||||||
|
id: id
|
||||||
|
mapAll: true
|
||||||
|
targetColumns:
|
||||||
|
id:
|
||||||
|
name:
|
||||||
|
role_id:
|
||||||
|
c_time:
|
||||||
|
test1:
|
||||||
|
etlCondition:
|
||||||
|
commitBatch: 3000 # 批量提交的大小
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
package com.alibaba.otter.canal.client.adapter.kudu.test;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.kudu.client.KuduClient;
|
||||||
|
import org.apache.kudu.client.KuduException;
|
||||||
|
import org.apache.kudu.client.KuduTable;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class KuduConnectionTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test01() {
|
||||||
|
List<String> masterList = Arrays.asList("10.6.36.102:7051,10.6.36.187:7051,10.6.36.229:7051".split(","));
|
||||||
|
KuduClient kuduClient = new KuduClient.KuduClientBuilder(masterList).defaultOperationTimeoutMs(60000)
|
||||||
|
.defaultSocketReadTimeoutMs(30000)
|
||||||
|
.defaultAdminOperationTimeoutMs(60000)
|
||||||
|
.build();
|
||||||
|
try {
|
||||||
|
List<String> tablesList = kuduClient.getTablesList().getTablesList();
|
||||||
|
System.out.println(tablesList.toString());
|
||||||
|
KuduTable web_white_list = kuduClient.openTable("web_white_list");
|
||||||
|
|
||||||
|
} catch (KuduException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
package com.alibaba.otter.canal.client.adapter.kudu.test;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.client.adapter.kudu.config.KuduMappingConfig;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.kudu.config.KuduMappingConfigLoader;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.support.DatasourceConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description
|
||||||
|
*/
|
||||||
|
public class TestConfig {
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() {
|
||||||
|
// 加载数据源连接池
|
||||||
|
DatasourceConfig.DATA_SOURCES.put("defaultDS", TestConstant.dataSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLoad() {
|
||||||
|
Map<String, KuduMappingConfig> configMap = KuduMappingConfigLoader.load(null);
|
||||||
|
Assert.assertFalse(configMap.isEmpty());
|
||||||
|
}
|
||||||
|
}
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
package com.alibaba.otter.canal.client.adapter.kudu.test;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
import com.alibaba.druid.pool.DruidDataSource;
|
||||||
|
|
||||||
|
public class TestConstant {
|
||||||
|
|
||||||
|
public final static String jdbcUrl = "jdbc:mysql://10.0.9.5:3306/canal_manager?useUnicode=true";
|
||||||
|
public final static String jdbcUser = "axtest";
|
||||||
|
public final static String jdbcPassword = "axtest123";
|
||||||
|
|
||||||
|
public final static DruidDataSource dataSource;
|
||||||
|
|
||||||
|
static {
|
||||||
|
dataSource = new DruidDataSource();
|
||||||
|
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
|
||||||
|
dataSource.setUrl(jdbcUrl);
|
||||||
|
dataSource.setUsername(jdbcUser);
|
||||||
|
dataSource.setPassword(jdbcPassword);
|
||||||
|
dataSource.setInitialSize(1);
|
||||||
|
dataSource.setMinIdle(1);
|
||||||
|
dataSource.setMaxActive(1);
|
||||||
|
dataSource.setMaxWait(60000);
|
||||||
|
dataSource.setTimeBetweenEvictionRunsMillis(60000);
|
||||||
|
dataSource.setMinEvictableIdleTimeMillis(300000);
|
||||||
|
dataSource.setPoolPreparedStatements(false);
|
||||||
|
dataSource.setMaxPoolPreparedStatementPerConnectionSize(20);
|
||||||
|
dataSource.setValidationQuery("select 1");
|
||||||
|
try {
|
||||||
|
dataSource.init();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
package com.alibaba.otter.canal.client.adapter.kudu.test.sync;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.client.adapter.kudu.KuduAdapter;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.kudu.test.TestConstant;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.support.DatasourceConfig;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.support.OuterAdapterConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description
|
||||||
|
*/
|
||||||
|
public class Common {
|
||||||
|
|
||||||
|
public static KuduAdapter init() {
|
||||||
|
DatasourceConfig.DATA_SOURCES.put("defaultDS", TestConstant.dataSource);
|
||||||
|
|
||||||
|
OuterAdapterConfig outerAdapterConfig = new OuterAdapterConfig();
|
||||||
|
outerAdapterConfig.setName("kudu");
|
||||||
|
outerAdapterConfig.setKey("kudu");
|
||||||
|
Map<String, String> properties = new HashMap<>();
|
||||||
|
properties.put("kudu.master.address", "10.6.36.102,10.6.36.187,10.6.36.229");
|
||||||
|
outerAdapterConfig.setProperties(properties);
|
||||||
|
|
||||||
|
KuduAdapter adapter = new KuduAdapter();
|
||||||
|
adapter.init(outerAdapterConfig, null);
|
||||||
|
return adapter;
|
||||||
|
}
|
||||||
|
}
|
||||||
+59
@@ -0,0 +1,59 @@
|
|||||||
|
package com.alibaba.otter.canal.client.adapter.kudu.test.sync;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.client.adapter.kudu.KuduAdapter;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.support.Dml;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description
|
||||||
|
*/
|
||||||
|
public class TestSyncKudu {
|
||||||
|
|
||||||
|
private KuduAdapter kuduAdapter;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void init() {
|
||||||
|
kuduAdapter = Common.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEtl() {
|
||||||
|
List<String> param = new ArrayList<>();
|
||||||
|
kuduAdapter.etl("kudutest_user.yml", param);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCount() {
|
||||||
|
kuduAdapter.count("kudutest_user.yml");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSync() {
|
||||||
|
Dml dml = new Dml();
|
||||||
|
dml.setDestination("example");
|
||||||
|
dml.setGroupId("g1");
|
||||||
|
dml.setTs(new Date().getTime());
|
||||||
|
dml.setType("DELETE");
|
||||||
|
dml.setDatabase("test1");
|
||||||
|
dml.setTable("user");
|
||||||
|
List<Map<String, Object>> dataList = new ArrayList<>();
|
||||||
|
Map<String, Object> data = new LinkedHashMap<>();
|
||||||
|
dataList.add(data);
|
||||||
|
data.put("id", 1L);
|
||||||
|
data.put("name", "liuyadong");
|
||||||
|
data.put("role_id", 1L);
|
||||||
|
data.put("c_time", new Date());
|
||||||
|
dml.setData(dataList);
|
||||||
|
|
||||||
|
kuduAdapter.sync(Collections.singletonList(dml));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.5-SNAPSHOT</version>
|
<version>1.1.7-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
@@ -30,11 +30,6 @@
|
|||||||
<artifactId>client-adapter.common</artifactId>
|
<artifactId>client-adapter.common</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>com.alibaba.otter</groupId>
|
|
||||||
<artifactId>canal.client</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.yaml</groupId>
|
<groupId>org.yaml</groupId>
|
||||||
<artifactId>snakeyaml</artifactId>
|
<artifactId>snakeyaml</artifactId>
|
||||||
@@ -56,25 +51,6 @@
|
|||||||
<groupId>org.apache.curator</groupId>
|
<groupId>org.apache.curator</groupId>
|
||||||
<artifactId>curator-recipes</artifactId>
|
<artifactId>curator-recipes</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- 单独引入rocketmq依赖 -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.rocketmq</groupId>
|
|
||||||
<artifactId>rocketmq-client</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<!-- 单独引入kafka依赖 -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.kafka</groupId>
|
|
||||||
<artifactId>kafka-clients</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.rabbitmq</groupId>
|
|
||||||
<artifactId>amqp-client</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.alibaba.mq-amqp</groupId>
|
|
||||||
<artifactId>mq-amqp-client</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<!-- jdbc -->
|
<!-- jdbc -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>mysql</groupId>
|
<groupId>mysql</groupId>
|
||||||
@@ -85,7 +61,7 @@
|
|||||||
<artifactId>postgresql</artifactId>
|
<artifactId>postgresql</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.oracle</groupId>
|
<groupId>com.oracle.database.jdbc</groupId>
|
||||||
<artifactId>ojdbc6</artifactId>
|
<artifactId>ojdbc6</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -93,6 +69,12 @@
|
|||||||
<artifactId>mssql-jdbc</artifactId>
|
<artifactId>mssql-jdbc</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.hbase</groupId>
|
||||||
|
<artifactId>hbase-shaded-client</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- outer adapter jar with dependencies-->
|
<!-- outer adapter jar with dependencies-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.alibaba.otter</groupId>
|
<groupId>com.alibaba.otter</groupId>
|
||||||
@@ -159,6 +141,85 @@
|
|||||||
<classifier>jar-with-dependencies</classifier>
|
<classifier>jar-with-dependencies</classifier>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.otter</groupId>
|
||||||
|
<artifactId>client-adapter.tablestore</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>*</artifactId>
|
||||||
|
<groupId>*</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
<classifier>jar-with-dependencies</classifier>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<!-- connector plugin -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.otter</groupId>
|
||||||
|
<artifactId>connector.tcp</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>*</artifactId>
|
||||||
|
<groupId>*</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
<classifier>jar-with-dependencies</classifier>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.otter</groupId>
|
||||||
|
<artifactId>connector.kafka</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>*</artifactId>
|
||||||
|
<groupId>*</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
<classifier>jar-with-dependencies</classifier>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.otter</groupId>
|
||||||
|
<artifactId>connector.rocketmq</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>*</artifactId>
|
||||||
|
<groupId>*</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
<classifier>jar-with-dependencies</classifier>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.otter</groupId>
|
||||||
|
<artifactId>connector.rabbitmq</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>*</artifactId>
|
||||||
|
<groupId>*</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
<classifier>jar-with-dependencies</classifier>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.otter</groupId>
|
||||||
|
<artifactId>connector.pulsarmq</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>*</artifactId>
|
||||||
|
<groupId>*</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
<classifier>jar-with-dependencies</classifier>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@@ -50,6 +50,13 @@
|
|||||||
<include>**/*</include>
|
<include>**/*</include>
|
||||||
</includes>
|
</includes>
|
||||||
</fileSet>
|
</fileSet>
|
||||||
|
<fileSet>
|
||||||
|
<directory>../kudu/src/main/resources/kudu</directory>
|
||||||
|
<outputDirectory>/conf/kudu</outputDirectory>
|
||||||
|
<includes>
|
||||||
|
<include>**/*</include>
|
||||||
|
</includes>
|
||||||
|
</fileSet>
|
||||||
<fileSet>
|
<fileSet>
|
||||||
<directory>../rdb/src/main/resources/</directory>
|
<directory>../rdb/src/main/resources/</directory>
|
||||||
<outputDirectory>/conf</outputDirectory>
|
<outputDirectory>/conf</outputDirectory>
|
||||||
@@ -57,6 +64,13 @@
|
|||||||
<exclude>META-INF/**</exclude>
|
<exclude>META-INF/**</exclude>
|
||||||
</excludes>
|
</excludes>
|
||||||
</fileSet>
|
</fileSet>
|
||||||
|
<fileSet>
|
||||||
|
<directory>../tablestore/src/main/resources/</directory>
|
||||||
|
<outputDirectory>/conf</outputDirectory>
|
||||||
|
<excludes>
|
||||||
|
<exclude>META-INF/**</exclude>
|
||||||
|
</excludes>
|
||||||
|
</fileSet>
|
||||||
<fileSet>
|
<fileSet>
|
||||||
<directory>target</directory>
|
<directory>target</directory>
|
||||||
<outputDirectory>logs</outputDirectory>
|
<outputDirectory>logs</outputDirectory>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user