使用canal-adapter1.1.4同步mariaDB10.0.25 到elasticsearch7.9.3
canal 工作原理
- canal 模拟 MySQL slave 的交互协议,伪装自己为 MySQL slave ,向 MySQL master 发送dump 协议
- MySQL master 收到 dump 请求,开始推送 binary log 给 slave (即 canal )
- canal 解析 binary log 对象(原始为 byte 流)
主要步骤: canal-server读取binlog —> canal-adapter读取canal-server 处理数据到es
相关版本
- mariaDB10.0.25
- canal-server1.1.4
- canal-adapter1.1.4
- elasticsearch7.9.3
前提条件
mysql配置 binlog_format 必须为ROW
[mysqld]
log_bin=mysql-bin #[必须]启用二进制日志
server-id=222 #[必须]服务器唯一ID 2^32,默认是1,一般取IP最后一段
binlog_format=row #STATEMENT只包含修改信息 ROW 任何情况都可以被复制 MIXED 前两者的混合使用
创建测试数据库和表 后面都是基于这个表测试
-- 创建数据库
create database `log` DEFAULT CHARACTER SET utf8;
-- 进入数据库
use log;
-- 创建表
CREATE TABLE `test` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=367 DEFAULT CHARSET=latin1;
索引
PUT /canal-test
{
"mappings": {
"properties": {
"name":{
"type": "text"
},
"age":{
"type": "integer"
},
"updateTime":{
"type": "date"
}
}
}
}
canal-server
启动canal-server
docker-compose方式启动
version: '3'
services:
canal-server:
image: canal/canal-server:v1.1.4
container_name: canal-server
ports:
- 11111:11111
environment:
- canal.instance.mysql.slaveId=12
- canal.auto.scan=false
- canal.destinations=test
- canal.instance.master.address=192.168.0.201:13306
- canal.instance.dbUsername=root
- canal.instance.dbPassword=root
- canal.instance.connectionCharset=UTF-8
- canal.mq.topic=test
#- canal.instance.filter.regex=esen_approval.apt_approval
volumes:
- /data/local/canal-server/11111/conf/:/admin/canal-server/conf/
- /data/local/canal-server/11111/logs/:/admin/canal-server/logs/
canal-client 测试代码
pom.xml
<dependency>
<groupId>com.alibaba.otter</groupId>
<artifactId>canal.client</artifactId>
<version>1.1.4</version>
</dependency>
SimpleClient.java
package com.example.canalclienttest;
/**
* 功能描述:
*
* @author Administrator
* @date 2020/12/19
*/
import java.net.InetSocketAddress;
import java.util.List;
import java.util.stream.Collectors;
import com.alibaba.otter.canal.client.CanalConnector;
import com.alibaba.otter.canal.client.CanalConnectors;
import com.alibaba.otter.canal.protocol.CanalEntry.Column;
import com.alibaba.otter.canal.protocol.CanalEntry.Entry;
import com.alibaba.otter.canal.protocol.CanalEntry.EntryType;
import com.alibaba.otter.canal.protocol.CanalEntry.RowChange;
import com.alibaba.otter.canal.protocol.CanalEntry.RowData;
import com.alibaba.otter.canal.protocol.Message;
public class SimpleClient {
static int count=1;
public static void main(String[] args) throws Exception {
CanalConnector connector = CanalConnectors.newSingleConnector(
new InetSocketAddress("192.168.0.202", 11111), "test", "", "");
connector.connect();
connector.subscribe(".*\\..*");
connector.rollback();
while (true) {
Message message = connector.getWithoutAck(100);
long batchId = message.getId();
if (batchId == -1 || message.getEntries().isEmpty()) {
Thread.sleep(100);
continue;
}
System.out.println(count++);
printEntries(message.getEntries());
connector.ack(batchId);
}
}
private static void printEntries(List<Entry> entries) throws Exception {
for (Entry entry : entries) {
if (entry.getEntryType() != EntryType.ROWDATA) {
continue;
}
RowChange rowChange = RowChange.parseFrom(entry.getStoreValue());
for (RowData rowData : rowChange.getRowDatasList()) {
switch (rowChange.getEventType()) {
case INSERT:
case UPDATE:
System.out.print("UPSERT ");
printColumns(rowData.getAfterColumnsList());
if ("retl_buffer".equals(entry.getHeader().getTableName())) {
String tableName = rowData.getAfterColumns(1).getValue();
String pkValue = rowData.getAfterColumns(2).getValue();
System.out.println("SELECT * FROM " + tableName + " WHERE id = " + pkValue);
}
break;
case DELETE:
System.out.print("DELETE ");
printColumns(rowData.getBeforeColumnsList());
break;
default:
break;
}
}
}
}
private static void printColumns(List<Column> columns) {
String line = columns.stream()
.map(column -> column.getName() + "=" + column.getValue())
.collect(Collectors.joining(","));
System.out.println(line);
}
}
canal-adapter
安装canal-adapter
百度云canal.adapter-1.1.4.tar.gz,提取码:i3be
解压
mkdir /usr/local/canal.adapter-1.1.4
tar zxvf canal.adapter-1.1.4.tar.gz -C /usr/local/canal.adapter-1.1.4
得到目录
重新编译canal-adapter es源码 复制到plugin目录
- 下载canal1.1.4源码
git clone [email protected]:alibaba/canal.git
- 修改canal-canal-1.1.4\client-adapter\elasticsearch\pom.xml
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.9.3</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>transport</artifactId>
<version>7.9.3</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>7.9.3</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.9.3</version>
</dependency>
- 修改canal-canal-1.1.4\client-adapter\elasticsearch\src\main\resources\META-INF\canal\com.alibaba.otter.canal.client.adapter.OuterAdapter
es7.9.3=com.alibaba.otter.canal.client.adapter.es.ESAdapter
- mvn 打包
在canal的根目录运行
cd mvn clean package -pl com.alibaba.otter:client-adapter.elasticsearch -am
旧方法和新方法参考 改一下再打包
6.4.3
import org.elasticsearch.cluster.metadata.MappingMetaData;
restHighLevelClient.bulk(bulkRequest);
7.9.3
import org.elasticsearch.cluster.metadata.MappingMetadata;
restHighLevelClient.bulk(bulkRequest,RequestOptions.DEFAULT);
- 把打包的client-adapter.elasticsearch-1.1.4-jar-with-dependencies.jar复制到plugin目录
注意:重名可以随便给jar取个名字
例如client-adapter.elasticsearch-1.1.4-jar-with-dependencies7.9.3.jar
配置canal-adapter
参考文档canalSync-E
./conf/application.yml
server:
port: 8081
spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
default-property-inclusion: non_null
canal.conf:
mode: tcp # kafka rocketMQ
canalServerHost: 192.168.0.202:11111 #canal 地址和端口
batchSize: 500
syncBatchSize: 1000
retries: 0
timeout:
accessKey:
secretKey:
srcDataSources:
defaultDS:
url: jdbc:mysql://192.168.0.201:13306/log?useUnicode=true #数据库
username: root
password: root
canalAdapters:
- instance: test # canal instance Name or mq topic name 对应canal创建的文件夹
groups:
- groupId: g1
outerAdapters:
- name: es7.9.3 #适配器名称
hosts: 192.168.0.202:9200 # es 集群地址, 逗号分隔
properties:
mode: rest # transport # or rest
./conf/es/test.yml
dataSourceKey: defaultDS # 源数据源的key, 对应上面配置的srcDataSources中的值
#outerAdapterKey: exampleKey # 对应application.yml中es配置的key
destination: test # cannal的instance或者MQ的topic
groupId: g1 # 对应MQ模式下的groupId, 只会同步对应groupId的数据
esMapping:
_index: canal-test
_type: _doc
_id: _id
sql: "
select a.id as _id,a.name as name,a.age as age,a.update_time as updateTime
from test as a
"
commitBatch: 3000
启动canal-adapte
adapter会自动加载 conf/es 下的所有.yml结尾的配置文件
#第一个窗口启动canal-adapte
chmod +x ./bin/* && ./bin/restart.sh
#第二个窗口输出日志
tail -f ./logs/adapter/adapter.log
对数据表进行curd
测试语句
#
insert into test values(0,RAND(),FLOOR(RAND()*100),now())
update set name='update_namexxx' where id=
deleet from test where id=xxx
报错解决
日志报错Not found the mapping info of index
2020-12-22 16:49:02.519 [pool-3-thread-1] ERROR c.a.o.canal.adapter.launcher.loader.CanalAdapterWorker - java.lang.IllegalArgumentException: Not found the mapping info of index: canal-test
java.lang.RuntimeException: java.lang.IllegalArgumentException: Not found the mapping info of index: canal-test
at com.alibaba.otter.canal.client.adapter.es.service.ESSyncService.sync(ESSyncService.java:111)
at com.alibaba.otter.canal.client.adapter.es.service.ESSyncService.sync(ESSyncService.java:58)
at com.alibaba.otter.canal.client.adapter.es.ESAdapter.sync(ESAdapter.java:169)
at com.alibaba.otter.canal.client.adapter.es.ESAdapter.sync(ESAdapter.java:148)
at com.alibaba.otter.canal.adapter.launcher.loader.AbstractCanalAdapterWorker.batchSync(AbstractCanalAdapterWorker.java:201)
at com.alibaba.otter.canal.adapter.launcher.loader.AbstractCanalAdapterWorker.lambda$null$1(AbstractCanalAdapterWorker.java:62)
at java.util.ArrayList.forEach(ArrayList.java:1257)
at com.alibaba.otter.canal.adapter.launcher.loader.AbstractCanalAdapterWorker.lambda$null$2(AbstractCanalAdapterWorker.java:58)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.IllegalArgumentException: Not found the mapping info of index: canal-test
at com.alibaba.otter.canal.client.adapter.es.support.ESTemplate.getEsType(ESTemplate.java:498)
at com.alibaba.otter.canal.client.adapter.es.support.ESTemplate.getValFromData(ESTemplate.java:345)
at com.alibaba.otter.canal.client.adapter.es.support.ESTemplate.getESDataFromDmlData(ESTemplate.java:376)
at com.alibaba.otter.canal.client.adapter.es.service.ESSyncService.singleTableSimpleFiledInsert(ESSyncService.java:434)
at com.alibaba.otter.canal.client.adapter.es.service.ESSyncService.insert(ESSyncService.java:134)
at com.alibaba.otter.canal.client.adapter.es.service.ESSyncService.sync(ESSyncService.java:94)
... 11 common frames omitted
修改 com.alibaba.otter.canal.client.adapter.es.support.ESConnection
mappingMetaData = mappings.get(index).get(type);
150行改为
mappingMetaData = mappings.get(index).get(“properties”);
日志报错c.a.o.canal.adapter.launcher.loader.CanalAdapterWorker - java.lang.NullPointerException
2020-12-22 17:06:27.092 [pool-3-thread-1] ERROR c.a.o.canal.adapter.launcher.loader.CanalAdapterWorker - java.lang.NullPointerException
java.lang.RuntimeException: java.lang.NullPointerException
at com.alibaba.otter.canal.client.adapter.es.service.ESSyncService.sync(ESSyncService.java:111)
at com.alibaba.otter.canal.client.adapter.es.service.ESSyncService.sync(ESSyncService.java:58)
at com.alibaba.otter.canal.client.adapter.es.ESAdapter.sync(ESAdapter.java:169)
at com.alibaba.otter.canal.client.adapter.es.ESAdapter.sync(ESAdapter.java:148)
at com.alibaba.otter.canal.adapter.launcher.loader.AbstractCanalAdapterWorker.batchSync(AbstractCanalAdapterWorker.java:201)
at com.alibaba.otter.canal.adapter.launcher.loader.AbstractCanalAdapterWorker.lambda$null$1(AbstractCanalAdapterWorker.java:62)
at java.util.ArrayList.forEach(ArrayList.java:1257)
at com.alibaba.otter.canal.adapter.launcher.loader.AbstractCanalAdapterWorker.lambda$null$2(AbstractCanalAdapterWorker.java:58)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NullPointerException: null
at com.alibaba.otter.canal.client.adapter.es.support.ESTemplate.getEsType(ESTemplate.java:504)
at com.alibaba.otter.canal.client.adapter.es.support.ESTemplate.getValFromData(ESTemplate.java:345)
at com.alibaba.otter.canal.client.adapter.es.support.ESTemplate.getESDataFromDmlData(ESTemplate.java:376)
at com.alibaba.otter.canal.client.adapter.es.service.ESSyncService.singleTableSimpleFiledInsert(ESSyncService.java:434)
at com.alibaba.otter.canal.client.adapter.es.service.ESSyncService.insert(ESSyncService.java:134)
at com.alibaba.otter.canal.client.adapter.es.service.ESSyncService.sync(ESSyncService.java:94)
... 11 common frames omitted
修改com.alibaba.otter.canal.client.adapter.es.support.ESTemplate
Map<String, Object> esMapping = mappingMetaData.getSourceAsMap();
Map<String, Object> esMapping = (Map<String, Object>) sourceMap.get("properties");
502和503行改成
Map<String, Object> esMapping = mappingMetaData.getSourceAsMap();
//Map<String, Object> esMapping = (Map<String, Object>) sourceMap.get("properties");
日志报错options cannot be null
2020-12-22 17:13:59.639 [pool-3-thread-1] ERROR c.a.o.canal.adapter.launcher.loader.CanalAdapterWorker - options cannot be null
java.lang.NullPointerException: options cannot be null
at java.util.Objects.requireNonNull(Objects.java:228)
at org.elasticsearch.client.Request.setOptions(Request.java:129)
at org.elasticsearch.client.RestHighLevelClient.internalPerformRequest(RestHighLevelClient.java:1610)
at org.elasticsearch.client.RestHighLevelClient.performRequest(RestHighLevelClient.java:1583)
at org.elasticsearch.client.RestHighLevelClient.performRequestAndParseEntity(RestHighLevelClient.java:1553)
at org.elasticsearch.client.RestHighLevelClient.bulk(RestHighLevelClient.java:533)
at com.alibaba.otter.canal.client.adapter.es.support.ESConnection$ESBulkRequest.bulk(ESConnection.java:422)
at com.alibaba.otter.canal.client.adapter.es.support.ESTemplate.commit(ESTemplate.java:198)
at com.alibaba.otter.canal.client.adapter.es.service.ESSyncService.commit(ESSyncService.java:853)
at com.alibaba.otter.canal.client.adapter.es.ESAdapter.sync(ESAdapter.java:151)
at com.alibaba.otter.canal.adapter.launcher.loader.AbstractCanalAdapterWorker.batchSync(AbstractCanalAdapterWorker.java:201)
at com.alibaba.otter.canal.adapter.launcher.loader.AbstractCanalAdapterWorker.lambda$null$1(AbstractCanalAdapterWorker.java:62)
at java.util.ArrayList.forEach(ArrayList.java:1257)
at com.alibaba.otter.canal.adapter.launcher.loader.AbstractCanalAdapterWorker.lambda$null$2(AbstractCanalAdapterWorker.java:58)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
2020-12-22 17:13:59.640 [Thread-4] ERROR c.a.o.canal.adapter.launcher.loader.CanalAdapterWorker - Outer adapter sync failed! Error sync but ACK!
修改com.alibaba.otter.canal.client.adapter.es.support.ESConnection
return restHighLevelClient.bulk(bulkRequest,null);
422行改为
return restHighLevelClient.bulk(bulkRequest,RequestOptions.DEFAULT);
再次重启测试,同时检查增删改查数据是否同步 问题已解决
2020-12-22 17:27:23.504 [pool-3-thread-1] INFO c.a.otter.canal.client.adapter.es.support.ESTemplate - index=canal-test,type=_doc
2020-12-22 17:27:23.788 [pool-3-thread-1] DEBUG c.a.otter.canal.client.adapter.es.service.ESSyncService - DML: {
"data":[{
"id":272,"name":"a","age":1,"update_time":1608600443000}],"database":"log","destination":"test","es":1608629243000,"groupId":null,"isDdl":false,"old":null,"pkNames":["id"],"sql":"","table":"test","ts":1608629243464,"type":"INSERT"}
Affected indexes: canal-test
2020-12-22 17:27:23.789 [pool-3-thread-1] DEBUG c.a.otter.canal.client.adapter.es.service.ESSyncService - DML: {
"data":[{
"id":273,"name":"a","age":1,"update_time":1608600443000}],"database":"log","destination":"test","es":1608629243000,"groupId":null,"isDdl":false,"old":null,"pkNames":["id"],"sql":"","table":"test","ts":1608629243503,"type":"INSERT"}
Affected indexes: canal-test
2020-12-22 17:27:23.852 [pool-3-thread-1] DEBUG c.a.otter.canal.client.adapter.es.service.ESSyncService - DML: {
"data":[{
"id":274,"name":"a","age":1,"update_time":1608600443000}],"database":"log","destination":"test","es":1608629243000,"groupId":null,"isDdl":false,"old":null,"pkNames":["id"],"sql":"","table":"test","ts":1608629243852,"type":"INSERT"}
Affected indexes: canal-test
2020-12-22 17:27:29.330 [pool-3-thread-1] DEBUG c.a.otter.canal.client.adapter.es.service.ESSyncService - DML: {
"data":[{
"id":276,"name":"a","age":1,"update_time":1608600449000}],"database":"log","destination":"test","es":1608629249000,"groupId":null,"isDdl":false,"old":null,"pkNames":["id"],"sql":"","table":"test","ts":1608629249328,"type":"INSERT"}
Affected indexes: canal-test
2020-12-22 17:27:29.332 [pool-3-thread-1] DEBUG c.a.otter.canal.client.adapter.es.service.ESSyncService - DML: {
"data":[{
"id":277,"name":"a","age":1,"update_time":1608600449000}],"database":"log","destination":"test","es":1608629249000,"groupId":null,"isDdl":false,"old":null,"pkNames":["id"],"sql":"","table":"test","ts":1608629249329,"type":"INSERT"}
Affected indexes: canal-test
相关资源下载
canal.adapter-1.1.4.tar.gz,提取码:i3be
client-adapter.elasticsearch-1.1.4-jar-with-dependencies7.9.3.jar,提取码:3wa8