mirror of
https://github.com/alibaba/canal.git
synced 2026-11-04 00:30:15 +08:00
251
client-adapter/README.md
Normal file
251
client-adapter/README.md
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
## 基本说明
|
||||||
|
canal 1.1.1版本之后, 增加客户端数据落地的适配及启动功能, 目前支持功能:
|
||||||
|
* 客户端启动器
|
||||||
|
* 同步管理REST接口
|
||||||
|
* 日志适配器, 作为DEMO
|
||||||
|
* HBase的数据同步(表对表同步), ETL功能
|
||||||
|
* (后续支持) ElasticSearch多表数据同步,ETL功能
|
||||||
|
|
||||||
|
## 环境版本
|
||||||
|
* 操作系统:无要求
|
||||||
|
* java版本: jdk1.8 以上
|
||||||
|
* canal 版本: 请下载最新的安装包,本文以当前v1.1.1 的canal.deployer-1.1.1.tar.gz为例
|
||||||
|
* MySQL版本 :5.7.18
|
||||||
|
* HBase版本: Apache HBase 1.1.2, 若和服务端版本不一致可自行替换客户端HBase依赖
|
||||||
|
|
||||||
|
## 一、适配器启动器
|
||||||
|
client-adapter分为适配器和启动器两部分, 适配器为多个fat jar, 每个适配器会将自己所需的依赖打成一个包, 以SPI的方式让启动器动态加载
|
||||||
|
|
||||||
|
|
||||||
|
启动器为 SpringBoot 项目, 支持canal-client启动的同时提供相关REST管理接口, 运行目录结构为:
|
||||||
|
```
|
||||||
|
canal-adapter-launcher.jar
|
||||||
|
- lib
|
||||||
|
client-adapter.logger-1.1.1-jar-with-dependencies.jar
|
||||||
|
client-adapter.hbase-1.1.1-jar-with-dependencies.jar
|
||||||
|
- config
|
||||||
|
application.yml
|
||||||
|
- hbase
|
||||||
|
mytest_person2.yml
|
||||||
|
```
|
||||||
|
以上目录结构最终会打包成 canal-adapter-launcher.tar.gz 压缩包
|
||||||
|
|
||||||
|
## 二、启动器
|
||||||
|
### 2.1 启动器配置 application.yml
|
||||||
|
#### canal相关配置部分说明
|
||||||
|
```
|
||||||
|
canal.conf:
|
||||||
|
canalServerHost: 127.0.0.1:11111 # 对应单机模式下的canal server的ip:port
|
||||||
|
zookeeperHosts: slave1:2181 # 对应集群模式下的zk地址, 如果配置了canalServerHost, 则以canalServerHost为准
|
||||||
|
bootstrapServers: slave1:6667 # kafka或rocketMQ地址, 与canalServerHost不能并存
|
||||||
|
flatMessage: true # 扁平message开关, 是否以json字符串形式投递数据, 仅在kafka/rocketMQ模式下有效
|
||||||
|
canalInstances: # canal实例组, 如果是tcp模式可配置此项
|
||||||
|
- instance: example # 对应canal destination
|
||||||
|
adapterGroups: # 对应适配器分组, 分组间的适配器并行运行
|
||||||
|
- outAdapters: # 适配器列表, 分组内的适配串行运行
|
||||||
|
- name: logger # 适配器SPI名
|
||||||
|
- name: hbase
|
||||||
|
properties: # HBase相关连接参数
|
||||||
|
hbase.zookeeper.quorum: slave1
|
||||||
|
hbase.zookeeper.property.clientPort: 2181
|
||||||
|
zookeeper.znode.parent: /hbase
|
||||||
|
mqTopics: # MQ topic租, 如果是kafka或者rockeMQ模式可配置此项, 与canalInstances不能并存
|
||||||
|
- mqMode: kafka # MQ的模式: kafak/rocketMQ
|
||||||
|
topic: example # MQ topic
|
||||||
|
groups: # group组
|
||||||
|
- groupId: g2 # group id
|
||||||
|
outAdapters: # 适配器列表, 以下配置和canalInstances中一样
|
||||||
|
- name: logger
|
||||||
|
```
|
||||||
|
#### 适配器相关配置部分说明
|
||||||
|
```
|
||||||
|
adapter.conf:
|
||||||
|
datasourceConfigs: # 数据源配置列表, 数据源将在适配器中用于ETL、数据同步回查等使用
|
||||||
|
defaultDS: # 数据源 dataSourceKey, 适配器中通过该值获取指定数据源
|
||||||
|
url: jdbc:mysql://127.0.0.1:3306/mytest?useUnicode=true
|
||||||
|
username: root
|
||||||
|
password: 121212
|
||||||
|
adapterConfigs: # 适配器内部配置列表
|
||||||
|
- hbase/mytest_person2.yml # 类型/配置文件名, 这里示例就是对应HBase适配器hbase目录下的mytest_person2.yml文件
|
||||||
|
```
|
||||||
|
## 2.2 适配器管理REST接口
|
||||||
|
#### 2.2.1 查询所有订阅同步的canal destination或MQ topic
|
||||||
|
```
|
||||||
|
curl http://127.0.0.1:8081/destinations
|
||||||
|
```
|
||||||
|
#### 2.2.2 数据同步开关
|
||||||
|
```
|
||||||
|
curl http://127.0.0.1:8081/syncSwitch/example/off -X PUT
|
||||||
|
```
|
||||||
|
针对 example 这个canal destination/MQ topic 进行开关操作. off代表关闭, 该destination/topic下的同步将阻塞或者断开连接不再接收数据, on代表开启
|
||||||
|
|
||||||
|
注: 如果在配置文件中配置了 zookeeperHosts 项, 则会使用分布式锁来控制HA中的数据同步开关, 如果是单机模式则使用本地锁来控制开关
|
||||||
|
#### 2.2.3 数据同步开关状态
|
||||||
|
```
|
||||||
|
curl http://127.0.0.1:8081/syncSwitch/example
|
||||||
|
```
|
||||||
|
查看指定 canal destination/MQ topic 的数据同步开关状态
|
||||||
|
#### 2.2.4 手动ETL
|
||||||
|
```
|
||||||
|
curl http://127.0.0.1:8081/etl/hbase/mytest_person2.yml -X POST -d "params=2018-10-21 00:00:00"
|
||||||
|
```
|
||||||
|
导入数据到指定类型的库
|
||||||
|
#### 2.2.5 查看相关库总数据
|
||||||
|
```
|
||||||
|
curl http://127.0.0.1:8081/count/hbase/mytest_person2.yml
|
||||||
|
```
|
||||||
|
### 2.3 启动canal-adapter示例
|
||||||
|
#### 2.3.1 启动canal server (单机模式), 参考: [Canal QuickStart](https://github.com/alibaba/canal/wiki/QuickStart)
|
||||||
|
#### 2.3.2 修改config/application.yml为:
|
||||||
|
```
|
||||||
|
server:
|
||||||
|
port: 8081
|
||||||
|
logging:
|
||||||
|
level:
|
||||||
|
com.alibaba.otter.canal.client.adapter.hbase: DEBUG
|
||||||
|
spring:
|
||||||
|
jackson:
|
||||||
|
date-format: yyyy-MM-dd HH:mm:ss
|
||||||
|
time-zone: GMT+8
|
||||||
|
default-property-inclusion: non_null
|
||||||
|
|
||||||
|
canal.conf:
|
||||||
|
canalServerHost: 127.0.0.1:11111
|
||||||
|
flatMessage: true
|
||||||
|
canalInstances:
|
||||||
|
- instance: example
|
||||||
|
adapterGroups:
|
||||||
|
- outAdapters:
|
||||||
|
- name: logger
|
||||||
|
```
|
||||||
|
启动 canal-adapter-launcher.jar
|
||||||
|
```
|
||||||
|
java -jar canal-adapter-launcher.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
## 三、HBase适配器
|
||||||
|
### 3.1 修改启动器配置: application.yml
|
||||||
|
```
|
||||||
|
server:
|
||||||
|
port: 8081
|
||||||
|
logging:
|
||||||
|
level:
|
||||||
|
com.alibaba.otter.canal.client.adapter.hbase: DEBUG
|
||||||
|
spring:
|
||||||
|
jackson:
|
||||||
|
date-format: yyyy-MM-dd HH:mm:ss
|
||||||
|
time-zone: GMT+8
|
||||||
|
default-property-inclusion: non_null
|
||||||
|
|
||||||
|
canal.conf:
|
||||||
|
canalServerHost: 127.0.0.1:11111
|
||||||
|
flatMessage: true
|
||||||
|
canalInstances:
|
||||||
|
- instance: example
|
||||||
|
adapterGroups:
|
||||||
|
- outAdapters:
|
||||||
|
- name: hbase
|
||||||
|
properties:
|
||||||
|
hbase.zookeeper.quorum: slave1
|
||||||
|
hbase.zookeeper.property.clientPort: 2181
|
||||||
|
zookeeper.znode.parent: /hbase
|
||||||
|
|
||||||
|
adapter.conf:
|
||||||
|
datasourceConfigs:
|
||||||
|
defaultDS:
|
||||||
|
url: jdbc:mysql://127.0.0.1:3306/mytest?useUnicode=true
|
||||||
|
username: root
|
||||||
|
password: 121212
|
||||||
|
adapterConfigs:
|
||||||
|
- hbase/mytest_person.yml
|
||||||
|
```
|
||||||
|
其中指定了一个HBase表映射文件: mytest_person.yml
|
||||||
|
### 3.2 适配器表映射文件
|
||||||
|
修改 config/hbase/mytest_person.yml文件:
|
||||||
|
```
|
||||||
|
dataSourceKey: defaultDS # 对应application.yml中的datasourceConfigs下的配置
|
||||||
|
hbaseOrm: # mysql--HBase的单表映射配置
|
||||||
|
mode: STRING # HBase中的存储类型, 默认统一存为String, 可选: #PHOENIX #NATIVE #STRING
|
||||||
|
# NATIVE: 以java类型为主, PHOENIX: 将类型转换为Phoenix对应的类型
|
||||||
|
destination: example # 对应 canal destination/MQ topic 名称
|
||||||
|
database: mytest # 数据库名/schema名
|
||||||
|
table: person # 表名
|
||||||
|
hbaseTable: MYTEST.PERSON # HBase表名
|
||||||
|
family: CF # 默认统一Column Family名称
|
||||||
|
uppercaseQualifier: true # 字段名转大写, 默认为true
|
||||||
|
commitBatch: 3000 # 批量提交的大小, ETL中用到
|
||||||
|
#rowKey: id,type # 复合字段rowKey不能和columns中的rowKey并存
|
||||||
|
# 复合rowKey会以 '|' 分隔
|
||||||
|
columns: # 字段映射, 如果不配置将自动映射所有字段,
|
||||||
|
# 并取第一个字段为rowKey, HBase字段名以mysql字段名为主
|
||||||
|
id: ROWKE
|
||||||
|
name: CF:NAME
|
||||||
|
email: EMAIL # 如果column family为默认CF, 则可以省略
|
||||||
|
type: # 如果HBase字段和mysql字段名一致, 则可以省略
|
||||||
|
c_time:
|
||||||
|
birthday:
|
||||||
|
```
|
||||||
|
如果涉及到类型转换,可以如下形式:
|
||||||
|
```
|
||||||
|
...
|
||||||
|
columns:
|
||||||
|
id: ROWKE$STRING
|
||||||
|
...
|
||||||
|
type: TYPE$BYTE
|
||||||
|
...
|
||||||
|
```
|
||||||
|
类型转换涉及到Java类型和Phoenix类型两种, 分别定义如下:
|
||||||
|
```
|
||||||
|
#Java 类型转换, 对应配置 mode: NATIVE
|
||||||
|
$DEFAULT
|
||||||
|
$STRING
|
||||||
|
$INTEGER
|
||||||
|
$LONG
|
||||||
|
$SHORT
|
||||||
|
$BOOLEAN
|
||||||
|
$FLOAT
|
||||||
|
$DOUBLE
|
||||||
|
$BIGDECIMAL
|
||||||
|
$DATE
|
||||||
|
$BYTE
|
||||||
|
$BYTES
|
||||||
|
```
|
||||||
|
```
|
||||||
|
#Phoenix 类型转换, 对应配置 mode: PHOENIX
|
||||||
|
$DEFAULT 对应PHOENIX里的VARCHAR
|
||||||
|
$UNSIGNED_INT 对应PHOENIX里的UNSIGNED_INT 4字节
|
||||||
|
$UNSIGNED_LONG 对应PHOENIX里的UNSIGNED_LONG 8字节
|
||||||
|
$UNSIGNED_TINYINT 对应PHOENIX里的UNSIGNED_TINYINT 1字节
|
||||||
|
$UNSIGNED_SMALLINT 对应PHOENIX里的UNSIGNED_SMALLINT 2字节
|
||||||
|
$UNSIGNED_FLOAT 对应PHOENIX里的UNSIGNED_FLOAT 4字节
|
||||||
|
$UNSIGNED_DOUBLE 对应PHOENIX里的UNSIGNED_DOUBLE 8字节
|
||||||
|
$INTEGER 对应PHOENIX里的INTEGER 4字节
|
||||||
|
$BIGINT 对应PHOENIX里的BIGINT 8字节
|
||||||
|
$TINYINT 对应PHOENIX里的TINYINT 1字节
|
||||||
|
$SMALLINT 对应PHOENIX里的SMALLINT 2字节
|
||||||
|
$FLOAT 对应PHOENIX里的FLOAT 4字节
|
||||||
|
$DOUBLE 对应PHOENIX里的DOUBLE 8字节
|
||||||
|
$BOOLEAN 对应PHOENIX里的BOOLEAN 1字节
|
||||||
|
$TIME 对应PHOENIX里的TIME 8字节
|
||||||
|
$DATE 对应PHOENIX里的DATE 8字节
|
||||||
|
$TIMESTAMP 对应PHOENIX里的TIMESTAMP 12字节
|
||||||
|
$UNSIGNED_TIME 对应PHOENIX里的UNSIGNED_TIME 8字节
|
||||||
|
$UNSIGNED_DATE 对应PHOENIX里的UNSIGNED_DATE 8字节
|
||||||
|
$UNSIGNED_TIMESTAMP 对应PHOENIX里的UNSIGNED_TIMESTAMP 12字节
|
||||||
|
$VARCHAR 对应PHOENIX里的VARCHAR 动态长度
|
||||||
|
$VARBINARY 对应PHOENIX里的VARBINARY 动态长度
|
||||||
|
$DECIMAL 对应PHOENIX里的DECIMAL 动态长度
|
||||||
|
```
|
||||||
|
如果不配置将以java对象原生类型默认映射转换
|
||||||
|
### 3.3 启动HBase数据同步
|
||||||
|
#### 创建HBase表
|
||||||
|
在HBase shell中运行:
|
||||||
|
```
|
||||||
|
create 'MYTEST.PERSON', {NAME=>'CF'}
|
||||||
|
```
|
||||||
|
#### 启动canal-adapter启动器
|
||||||
|
```
|
||||||
|
java -jar canal-adapter-launcher.jar
|
||||||
|
```
|
||||||
|
#### 验证
|
||||||
|
修改mysql mytest.person表的数据, 将会自动同步到HBase的MYTEST.PERSON表下面, 并会打出DML的log
|
||||||
@@ -45,7 +45,7 @@ hbaseOrm:
|
|||||||
# $TINYINT 对应PHOENIX里的TINYINT 1字节
|
# $TINYINT 对应PHOENIX里的TINYINT 1字节
|
||||||
# $SMALLINT 对应PHOENIX里的SMALLINT 2字节
|
# $SMALLINT 对应PHOENIX里的SMALLINT 2字节
|
||||||
# $FLOAT 对应PHOENIX里的FLOAT 4字节
|
# $FLOAT 对应PHOENIX里的FLOAT 4字节
|
||||||
# DOUBLE 对应PHOENIX里的DOUBLE 8字节
|
# $DOUBLE 对应PHOENIX里的DOUBLE 8字节
|
||||||
# $BOOLEAN 对应PHOENIX里的BOOLEAN 1字节
|
# $BOOLEAN 对应PHOENIX里的BOOLEAN 1字节
|
||||||
# $TIME 对应PHOENIX里的TIME 8字节
|
# $TIME 对应PHOENIX里的TIME 8字节
|
||||||
# $DATE 对应PHOENIX里的DATE 8字节
|
# $DATE 对应PHOENIX里的DATE 8字节
|
||||||
|
|||||||
@@ -2,20 +2,19 @@ package com.alibaba.otter.canal.adapter.launcher.loader;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Callable;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
import com.alibaba.otter.canal.adapter.launcher.common.SyncSwitch;
|
|
||||||
import com.alibaba.otter.canal.adapter.launcher.config.SpringContext;
|
|
||||||
import com.alibaba.otter.canal.client.adapter.support.Dml;
|
|
||||||
import com.alibaba.otter.canal.protocol.FlatMessage;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.adapter.launcher.common.SyncSwitch;
|
||||||
|
import com.alibaba.otter.canal.adapter.launcher.config.SpringContext;
|
||||||
import com.alibaba.otter.canal.client.adapter.OuterAdapter;
|
import com.alibaba.otter.canal.client.adapter.OuterAdapter;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.support.Dml;
|
||||||
import com.alibaba.otter.canal.client.adapter.support.MessageUtil;
|
import com.alibaba.otter.canal.client.adapter.support.MessageUtil;
|
||||||
|
import com.alibaba.otter.canal.protocol.FlatMessage;
|
||||||
import com.alibaba.otter.canal.protocol.Message;
|
import com.alibaba.otter.canal.protocol.Message;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -44,21 +43,21 @@ public abstract class AbstractCanalAdapterWorker {
|
|||||||
protected void writeOut(final Message message) {
|
protected void writeOut(final Message message) {
|
||||||
List<Future<Boolean>> futures = new ArrayList<>();
|
List<Future<Boolean>> futures = new ArrayList<>();
|
||||||
// 组间适配器并行运行
|
// 组间适配器并行运行
|
||||||
for (List<OuterAdapter> outerAdapters : canalOuterAdapters) {
|
canalOuterAdapters.forEach(outerAdapters -> {
|
||||||
final List<OuterAdapter> adapters = outerAdapters;
|
final List<OuterAdapter> adapters = outerAdapters;
|
||||||
futures.add(groupInnerExecutorService.submit(() -> {
|
futures.add(groupInnerExecutorService.submit(() -> {
|
||||||
try {
|
try {
|
||||||
// 组内适配器穿行运行,尽量不要配置组内适配器
|
// 组内适配器穿行运行,尽量不要配置组内适配器
|
||||||
for (final OuterAdapter c : adapters) {
|
adapters.forEach(adapter -> {
|
||||||
long begin = System.currentTimeMillis();
|
long begin = System.currentTimeMillis();
|
||||||
MessageUtil.parse4Dml(canalDestination, message, c::sync);
|
MessageUtil.parse4Dml(canalDestination, message, adapter::sync);
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("{} elapsed time: {}",
|
logger.debug("{} elapsed time: {}",
|
||||||
c.getClass().getName(),
|
adapter.getClass().getName(),
|
||||||
(System.currentTimeMillis() - begin));
|
(System.currentTimeMillis() - begin));
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
return true;
|
return true;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return false;
|
return false;
|
||||||
@@ -67,79 +66,94 @@ public abstract class AbstractCanalAdapterWorker {
|
|||||||
|
|
||||||
// 等待所有适配器写入完成
|
// 等待所有适配器写入完成
|
||||||
// 由于是组间并发操作,所以将阻塞直到耗时最久的工作组操作完成
|
// 由于是组间并发操作,所以将阻塞直到耗时最久的工作组操作完成
|
||||||
for (Future<Boolean> f : futures) {
|
futures.forEach(future -> {
|
||||||
try {
|
try {
|
||||||
if (!f.get()) {
|
if (!future.get()) {
|
||||||
logger.error("Outer adapter write failed");
|
logger.error("Outer adapter write failed");
|
||||||
}
|
}
|
||||||
} catch (InterruptedException | ExecutionException e) {
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void writeOut(final FlatMessage flatMessage) {
|
protected void writeOut(final FlatMessage flatMessage) {
|
||||||
List<Future<Boolean>> futures = new ArrayList<>();
|
List<Future<Boolean>> futures = new ArrayList<>();
|
||||||
// 组间适配器并行运行
|
// 组间适配器并行运行
|
||||||
for (List<OuterAdapter> outerAdapters : canalOuterAdapters) {
|
canalOuterAdapters.forEach(outerAdapters -> {
|
||||||
final List<OuterAdapter> adapters = outerAdapters;
|
futures.add(groupInnerExecutorService.submit(() -> {
|
||||||
futures.add(groupInnerExecutorService.submit(new Callable<Boolean>() {
|
try {
|
||||||
|
// 组内适配器穿行运行,尽量不要配置组内适配器
|
||||||
@Override
|
outerAdapters.forEach(adapter -> {
|
||||||
public Boolean call() {
|
long begin = System.currentTimeMillis();
|
||||||
try {
|
Dml dml = MessageUtil.flatMessage2Dml(canalDestination, flatMessage);
|
||||||
// 组内适配器穿行运行,尽量不要配置组内适配器
|
adapter.sync(dml);
|
||||||
for (OuterAdapter c : adapters) {
|
if (logger.isDebugEnabled()) {
|
||||||
long begin = System.currentTimeMillis();
|
logger.debug("{} elapsed time: {}",
|
||||||
Dml dml = MessageUtil.flatMessage2Dml(canalDestination, flatMessage);
|
adapter.getClass().getName(),
|
||||||
c.sync(dml);
|
(System.currentTimeMillis() - begin));
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug("{} elapsed time: {}",
|
|
||||||
c.getClass().getName(),
|
|
||||||
(System.currentTimeMillis() - begin));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
});
|
||||||
} catch (Exception e) {
|
return true;
|
||||||
return false;
|
} catch (Exception e) {
|
||||||
}
|
return false;
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// 等待所有适配器写入完成
|
// 等待所有适配器写入完成
|
||||||
// 由于是组间并发操作,所以将阻塞直到耗时最久的工作组操作完成
|
// 由于是组间并发操作,所以将阻塞直到耗时最久的工作组操作完成
|
||||||
for (Future<Boolean> f : futures) {
|
futures.forEach(future -> {
|
||||||
try {
|
try {
|
||||||
if (!f.get()) {
|
if (!future.get()) {
|
||||||
logger.error("Outer adapter write failed");
|
logger.error("Outer adapter write failed");
|
||||||
}
|
}
|
||||||
} catch (InterruptedException | ExecutionException e) {
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
if (!running) {
|
||||||
|
thread = new Thread(this::process);
|
||||||
|
thread.setUncaughtExceptionHandler(handler);
|
||||||
|
thread.start();
|
||||||
|
running = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void stopOutAdapters() {
|
protected abstract void process();
|
||||||
if (thread != null) {
|
|
||||||
try {
|
public void stop() {
|
||||||
thread.join();
|
try {
|
||||||
} catch (InterruptedException e) {
|
if (!running) {
|
||||||
// ignore
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
groupInnerExecutorService.shutdown();
|
closeConnection();
|
||||||
logger.info("topic connectors' worker thread dead!");
|
running = false;
|
||||||
for (List<OuterAdapter> outerAdapters : canalOuterAdapters) {
|
|
||||||
for (OuterAdapter adapter : outerAdapters) {
|
syncSwitch.release(canalDestination);
|
||||||
adapter.destroy();
|
|
||||||
|
logger.info("destination {} is waiting for adapters' worker thread die!", canalDestination);
|
||||||
|
if (thread != null) {
|
||||||
|
try {
|
||||||
|
thread.join();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
groupInnerExecutorService.shutdown();
|
||||||
|
logger.info("destination {} adapters worker thread dead!", canalDestination);
|
||||||
|
canalOuterAdapters.forEach(outerAdapters -> outerAdapters.forEach(OuterAdapter::destroy));
|
||||||
|
logger.info("destination {} all adapters destroyed!", canalDestination);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
logger.info("topic all connectors destroyed!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void start();
|
protected abstract void closeConnection();
|
||||||
|
|
||||||
public abstract void stop();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,59 +37,18 @@ public class CanalAdapterKafkaWorker extends AbstractCanalAdapterWorker {
|
|||||||
this.flatMessage = flatMessage;
|
this.flatMessage = flatMessage;
|
||||||
connector = KafkaCanalConnectors.newKafkaConnector(bootstrapServers, topic, null, groupId, flatMessage);
|
connector = KafkaCanalConnectors.newKafkaConnector(bootstrapServers, topic, null, groupId, flatMessage);
|
||||||
// connector.setSessionTimeout(1L, TimeUnit.MINUTES);
|
// connector.setSessionTimeout(1L, TimeUnit.MINUTES);
|
||||||
|
|
||||||
// super.initSwitcher(topic);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start() {
|
protected void closeConnection(){
|
||||||
if (!running) {
|
connector.stopRunning();
|
||||||
thread = new Thread(() -> process());
|
|
||||||
thread.setUncaughtExceptionHandler(handler);
|
|
||||||
running = true;
|
|
||||||
thread.start();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() {
|
protected void process() {
|
||||||
try {
|
|
||||||
if (!running) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
connector.stopRunning();
|
|
||||||
running = false;
|
|
||||||
|
|
||||||
// if (switcher != null && !switcher.state()) {
|
|
||||||
// switcher.set(true);
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (thread != null) {
|
|
||||||
try {
|
|
||||||
thread.join();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
groupInnerExecutorService.shutdown();
|
|
||||||
logger.info("topic {} connectors' worker thread dead!", this.topic);
|
|
||||||
for (List<OuterAdapter> outerAdapters : canalOuterAdapters) {
|
|
||||||
for (OuterAdapter adapter : outerAdapters) {
|
|
||||||
adapter.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.info("topic {} all connectors destroyed!", this.topic);
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void process() {
|
|
||||||
while (!running)
|
while (!running)
|
||||||
;
|
;
|
||||||
ExecutorService executor = Executors.newSingleThreadExecutor();
|
ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||||
// final AtomicBoolean executing = new AtomicBoolean(true);
|
|
||||||
while (running) {
|
while (running) {
|
||||||
try {
|
try {
|
||||||
logger.info("=============> Start to connect topic: {} <=============", this.topic);
|
logger.info("=============> Start to connect topic: {} <=============", this.topic);
|
||||||
@@ -99,7 +58,7 @@ public class CanalAdapterKafkaWorker extends AbstractCanalAdapterWorker {
|
|||||||
logger.info("=============> Subscribe topic: {} succeed <=============", this.topic);
|
logger.info("=============> Subscribe topic: {} succeed <=============", this.topic);
|
||||||
while (running) {
|
while (running) {
|
||||||
try {
|
try {
|
||||||
// switcher.get(); //等待开关开启
|
syncSwitch.get(canalDestination);
|
||||||
|
|
||||||
List<?> messages;
|
List<?> messages;
|
||||||
if (!flatMessage) {
|
if (!flatMessage) {
|
||||||
|
|||||||
@@ -135,20 +135,18 @@ public class CanalAdapterLoader {
|
|||||||
public void destroy() {
|
public void destroy() {
|
||||||
if (canalWorkers.size() > 0) {
|
if (canalWorkers.size() > 0) {
|
||||||
ExecutorService stopExecutorService = Executors.newFixedThreadPool(canalWorkers.size());
|
ExecutorService stopExecutorService = Executors.newFixedThreadPool(canalWorkers.size());
|
||||||
for (CanalAdapterWorker v : canalWorkers.values()) {
|
for (CanalAdapterWorker canalAdapterWorker : canalWorkers.values()) {
|
||||||
final CanalAdapterWorker caw = v;
|
stopExecutorService.submit(canalAdapterWorker::stop);
|
||||||
stopExecutorService.submit(caw::stop);
|
|
||||||
}
|
}
|
||||||
stopExecutorService.shutdown();
|
stopExecutorService.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canalMQWorker.size() > 0) {
|
if (canalMQWorker.size() > 0) {
|
||||||
ExecutorService stopMQWokerService = Executors.newFixedThreadPool(canalMQWorker.size());
|
ExecutorService stopMQWorkerService = Executors.newFixedThreadPool(canalMQWorker.size());
|
||||||
for (AbstractCanalAdapterWorker tmp : canalMQWorker.values()) {
|
for (AbstractCanalAdapterWorker canalAdapterMQWorker : canalMQWorker.values()) {
|
||||||
final AbstractCanalAdapterWorker worker = tmp;
|
stopMQWorkerService.submit(canalAdapterMQWorker::stop);
|
||||||
stopMQWokerService.submit(worker::stop);
|
|
||||||
}
|
}
|
||||||
stopMQWokerService.shutdown();
|
stopMQWorkerService.shutdown();
|
||||||
}
|
}
|
||||||
logger.info("All canal adapters destroyed");
|
logger.info("All canal adapters destroyed");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,38 +36,12 @@ public class CanalAdapterRocketMQWorker extends AbstractCanalAdapterWorker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start() {
|
protected void closeConnection() {
|
||||||
if (!running) {
|
connector.stopRunning();
|
||||||
thread = new Thread(new Runnable() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
process();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
thread.setUncaughtExceptionHandler(handler);
|
|
||||||
running = true;
|
|
||||||
thread.start();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() {
|
protected void process() {
|
||||||
try {
|
|
||||||
if (!running) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
connector.stopRunning();
|
|
||||||
running = false;
|
|
||||||
logger.info("Stop topic {} out adapters begin", this.topic);
|
|
||||||
stopOutAdapters();
|
|
||||||
logger.info("Stop topic {} out adapters end", this.topic);
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void process() {
|
|
||||||
while (!running)
|
while (!running)
|
||||||
;
|
;
|
||||||
ExecutorService executor = Executors.newSingleThreadExecutor();
|
ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||||
@@ -84,25 +58,27 @@ public class CanalAdapterRocketMQWorker extends AbstractCanalAdapterWorker {
|
|||||||
|
|
||||||
final Message message = connector.getWithoutAck(1);
|
final Message message = connector.getWithoutAck(1);
|
||||||
if (message != null) {
|
if (message != null) {
|
||||||
executor.submit(new Runnable() {
|
executor.submit(() -> {
|
||||||
|
try {
|
||||||
@Override
|
if (logger.isDebugEnabled()) {
|
||||||
public void run() {
|
logger.debug("topic: {} batchId: {} batchSize: {} ",
|
||||||
try {
|
topic,
|
||||||
if (logger.isDebugEnabled()) {
|
message.getId(),
|
||||||
logger.debug("topic: {} batchId: {} batchSize: {} ", topic, message.getId(), message.getEntries().size());
|
message.getEntries().size());
|
||||||
}
|
|
||||||
long begin = System.currentTimeMillis();
|
|
||||||
writeOut(message);
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
if ((System.currentTimeMillis() - begin) > 5 * 60 * 1000) {
|
|
||||||
logger.error("topic: {} batchId {} elapsed time: {} ms", topic, message.getId(), now - begin);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e.getMessage(), e);
|
|
||||||
}
|
}
|
||||||
connector.ack(message.getId());
|
long begin = System.currentTimeMillis();
|
||||||
|
writeOut(message);
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
if ((System.currentTimeMillis() - begin) > 5 * 60 * 1000) {
|
||||||
|
logger.error("topic: {} batchId {} elapsed time: {} ms",
|
||||||
|
topic,
|
||||||
|
message.getId(),
|
||||||
|
now - begin);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
|
connector.ack(message.getId());
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
logger.debug("Message is null");
|
logger.debug("Message is null");
|
||||||
|
|||||||
@@ -57,49 +57,12 @@ public class CanalAdapterWorker extends AbstractCanalAdapterWorker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start() {
|
protected void closeConnection() {
|
||||||
if (!running) {
|
connector.stopRunning();
|
||||||
thread = new Thread(this::process);
|
|
||||||
thread.setUncaughtExceptionHandler(handler);
|
|
||||||
thread.start();
|
|
||||||
running = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() {
|
protected void process() {
|
||||||
try {
|
|
||||||
if (!running) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
connector.stopRunning();
|
|
||||||
running = false;
|
|
||||||
|
|
||||||
syncSwitch.release(canalDestination);
|
|
||||||
|
|
||||||
logger.info("destination {} is waiting for adapters' worker thread die!", canalDestination);
|
|
||||||
if (thread != null) {
|
|
||||||
try {
|
|
||||||
thread.join();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
groupInnerExecutorService.shutdown();
|
|
||||||
logger.info("destination {} adapters' worker thread dead!", canalDestination);
|
|
||||||
for (List<OuterAdapter> outerAdapters : canalOuterAdapters) {
|
|
||||||
for (OuterAdapter adapter : outerAdapters) {
|
|
||||||
adapter.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.info("destination {} all adapters destroyed!", canalDestination);
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void process() {
|
|
||||||
while (!running)
|
while (!running)
|
||||||
; // waiting until running == true
|
; // waiting until running == true
|
||||||
while (running) {
|
while (running) {
|
||||||
@@ -129,11 +92,7 @@ public class CanalAdapterWorker extends AbstractCanalAdapterWorker {
|
|||||||
int size = message.getEntries().size();
|
int size = message.getEntries().size();
|
||||||
|
|
||||||
if (batchId == -1 || size == 0) {
|
if (batchId == -1 || size == 0) {
|
||||||
try {
|
Thread.sleep(1000);
|
||||||
Thread.sleep(1000);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("destination: {} batchId: {} batchSize: {} ",
|
logger.debug("destination: {} batchId: {} batchSize: {} ",
|
||||||
@@ -143,18 +102,18 @@ public class CanalAdapterWorker extends AbstractCanalAdapterWorker {
|
|||||||
}
|
}
|
||||||
long begin = System.currentTimeMillis();
|
long begin = System.currentTimeMillis();
|
||||||
writeOut(message);
|
writeOut(message);
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("destination: {} batchId: {} elapsed time: {} ms",
|
logger.debug("destination: {} batchId: {} elapsed time: {} ms",
|
||||||
this.canalDestination,
|
this.canalDestination,
|
||||||
batchId,
|
batchId,
|
||||||
now - begin);
|
System.currentTimeMillis() - begin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
connector.ack(batchId); // 提交确认
|
connector.ack(batchId); // 提交确认
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
connector.rollback(batchId); // 处理失败, 回滚数据
|
connector.rollback(batchId); // 处理失败, 回滚数据
|
||||||
throw e;
|
logger.error("sync error!", e);
|
||||||
|
Thread.sleep(500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user