一. 开启MySQL binlog日志并注册账户
1、查看是否已经开启binlog
show variables like 'log_bin';
log_bin俩种状态(ON,OFF),如果为OFF,请先开启。
2、开启binlog日志
在mysql 下 etc/mysql/my.cnf 进行配置
因为我这里是docker 里面需要安装vim才可以打开文件 如下:
apt-get update
apt-get install vim
重启MySQL后显示binlog已开启
可以通过命令行==show master status\G;==查看当前binlog日志的信息(后面有用):
3、注册账户
新建用户用于canal订阅binlog使用,可以进入docker容器操作 也可以直接在Navicat中操作
# 新建用户 用户名:canal 密码:canal
CREATE USER canal IDENTIFIED by 'canal';
# 授权
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';
# 刷新MySQL的系统权限相关表
FLUSH PRIVILEGES;
这里注意mysql8.0 ,一定要用caching_sha2_password, 不能用mysql_native…,否则cacel将连接不了mysql。
二、docker安装canal
1、 先在Docker Hub中下载canal-server镜像
docker pull canal/canal-server:latest
2、 先启动Canal,用于复制properties配置文件
docker run -p 11111:11111 --name canal -d canal/canal-server:latest
3、 初次启动Canal镜像后,将instance.properties文件复制到宿主机,用于后续挂载使用
# de91f4226e27 容器ID
# /home/admin/canal-server/conf/example/instance.properties 容器中properties配置文件地址
# /mydata/canal/conf/ 宿主机的位置,按照个人配置即可
docker cp de91f4226e27:/home/admin/canal-server/conf/example/instance.properties /home/canal/conf/
4、 修改instance.properties
![在这里插入图片描述](https://img-blog.csdnimg.cn/b104e27e65b74e23a3c9055b786fafe7.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6ZqP5b2x6ZqP6KGM,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center注意Mysql 地址一定是docker容器外的实际ip地址+端口号
5、 删除旧的canal容器、再创建新容器(使用挂载)
# -v 本地的instance.properties:容器的instance.properties 将容器的instance.properties配置文件挂载到宿主机,方便后续变更
docker run -p 11111:11111 --name canal -v /home/canal/conf/instance.properties:/home/admin/canal-server/conf/example/instance.properties -d canal/canal-server:latest
至此,Canal就可以使用了,后续若需要更改instance.properties,只要在宿主机修改后重启docker容器即可。
启动完成canal 如下:
三.java 代码
依赖包
<dependency>
<groupId>com.alibaba.otter</groupId>
<artifactId>canal.client</artifactId>
<version>1.1.5</version>
</dependency>
<!-- Message、CanalEntry.Entry等来自此安装包 -->
<dependency>
<groupId>com.alibaba.otter</groupId>
<artifactId>canal.protocol</artifactId>
<version>1.1.5</version>
</dependency>
import com.alibaba.otter.canal.client.CanalConnector;
import com.alibaba.otter.canal.client.CanalConnectors;
import com.alibaba.otter.canal.protocol.CanalEntry;
import com.alibaba.otter.canal.protocol.Message;
import com.google.protobuf.InvalidProtocolBufferException;
import java.net.InetSocketAddress;
import java.util.List;
public class PbsAssetMapTest {
//Canal服务地址
private static final String SERVER_ADDRESS = "172.16.234.150";
//Canal Server 服务端口号
private static final Integer PORT = 11111;
//目的地,其实Canal Service内部有一个队列,和配置文件中一致即可,参考【修改instance.properties】图中
private static final String DESTINATION = "example";
//用户名和密码,但是目前不支持,只能为空
private static final String USERNAME = "";
//用户名和密码,但是目前不支持,只能为空
private static final String PASSWORD= "";
public static void main(String[] args){
CanalConnector canalConnector = CanalConnectors.newSingleConnector(new InetSocketAddress(SERVER_ADDRESS, PORT), DESTINATION, USERNAME, PASSWORD);
canalConnector.connect();
//订阅所有消息
canalConnector.subscribe(".*\\..*");
//恢复到之前同步的那个位置
canalConnector.rollback();
for(;;){
//获取指定数量的数据,但是不做确认标记,下一次取还会取到这些信息
Message message = canalConnector.getWithoutAck(100);
//获取消息id
long batchId = message.getId();
if(batchId != -1){
System.out.println("msgId -> " + batchId);
printEnity(message.getEntries());
//提交确认
//canalConnector.ack(batchId);
//处理失败,回滚数据
//canalConnector.rollback(batchId);
}
}
}
private static void printEnity(List<CanalEntry.Entry> entries) {
for (CanalEntry.Entry entry : entries) {
if(entry.getEntryType() != CanalEntry.EntryType.ROWDATA){
continue;
}
try{
CanalEntry.RowChange rowChange = CanalEntry.RowChange.parseFrom(entry.getStoreValue());
for (CanalEntry.RowData rowData : rowChange.getRowDatasList()) {
System.out.println(rowChange.getEventType());
switch (rowChange.getEventType()){
//如果希望监听多种事件,可以手动增加case
case INSERT:
String tableName = entry.getHeader().getTableName();
//测试users表进行映射处
List<CanalEntry.Column> afterColumnsList = rowData.getAfterColumnsList();
System.out.println(afterColumnsList);
break;
case UPDATE:
List<CanalEntry.Column> afterColumnsList2 = rowData.getAfterColumnsList();
System.out.println("新插入的数据是:" + afterColumnsList2);
break;
case DELETE:
List<CanalEntry.Column> beforeColumnsList = rowData.getBeforeColumnsList();
System.out.println("被删除的数据是:" + beforeColumnsList);
break;
default:
}
}
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
}
}
}
截止此处,整个canal的搭建和使用就结束了。
启动Java程序,修改数据库的值即可看到日志。
五.注意事项
运行Java程序后、修改数据库,并未发现有任何日志输出。只存在以下日志:
看到此日志说明Java程序已经连接上了canal,但是修改数据库后没有任何日志输出。
问题一: 是否为binlog问题
若按照文中配置,应该不会是binlog问题导致的
- 查询binlog是否开启
show variables like 'log_bin';
- 查询是否生成binlog日志
查看当前正在写入的binlog文件
show master status;
查看指定binlog文件的内容
show binlog events in 'mysql-bin.000002';
更新数据库再次查询【查看指定binlog文件的内容】,即可看到刚才生成的binlog日志明细
其他命令:
查询binlog文件列表
show binary logs;
只查看第一个binlog文件的内容
show binlog events;
问题二: canal连接数据库问题
1、查看canal容器启动日志 docker logs -f 容器ID或名称
发现启动日志并没发现有啥问题
2、进入canal容器,查询日志
进入docker容器
# 29326e3b6c5d canal容器ID
docker exec -it 29326e3b6c5d /bin/bash
# 进入文件夹
cd canal-server/logs/example/
# 查看历史记录 实时记录可以使用【tail -f example.log】
tail -100 example.log
发现日志存在连接数据库异常【No route to host (Host unreachable)】
经查看应该是防火墙没有关闭导致的
# 查询防火墙
systemctl status firewalld
# 临时关闭
systemctl stop firewalld
#永久关闭,即设置开机的时候不自动启动
systemctl disable firewalld
关闭防火墙后,发现日志请求就正常了,Java控制台也可以监听到日志了
问题三:
1.3 部署搭建——常见报错
① canal服务端数据库怎么都连不上
1.检查服务端的配置文件,将127.0.0.1:3306修改为实际ip地址+端口号,修改后回到bin目录重启程序
2.使用的canal用户是否已经授予并刷新了查看binlog的权限
② canal客户端数据库怎么都连不上
1.检查库和表对应的字符集,必须为相同的(1.1.3版本有碰到过这个问题)
2.是否启动了多个客户端程序
2.1 避坑指南——同步的中文乱码问题
这个原因是因为mysql初始化安装时默认的系统字符集为latin,所以需要修改源数据库和目标数据库的系统字符集
从docker里拷出配置文件
docker cp mysql:./etc/mysql/mysql.conf.d/mysqld.cnf \
/data/mysql/mysqld.cnf
追加配置 vim /data/mysql/mysqld.cnf
[mysqld]
character_set_server=utf8
[client]
default-character_set=utf8
[mysql]
default-character_set=utf8
拷回docker
docker cp /data/mysql/mysqld.cnf \ mysql:./etc/mysql/mysql.conf.d/mysqld.cnf
重启容器
注意:源库和目标库最好都修改下
2.2 避坑指南——字段大小写不自动区分的问题
这个在1.1.4还是1.1.5版本作者修复了这个bug,并将它设置为了可开关项,最好下1.1.5的版本
vim canal-adapter-1.5/conf/rdb/mytest_user.yml
dataSourceKey: defaultDS
destination: example
groupId: g1
outerAdapterKey: mysql1
concurrent: true
dbMapping:
database: over
table: pipe_gallery
targetTable: ex_zhgl_pipe_gallery
targetPk:
id: ID
mapAll: true
caseInsensitive: true #这一项默认是false,需要自己添加上去,是为了区分源库和目标库表字段大小写问题
targetColumns:
id: ID
2.3 Load canal adapter: rdb failed问题
这个问题好像非常的坑,问题出现的情况:①rdb中的key值加了特殊符号,②一个rdb的yml文件对应了多个key
所以解决的思路是:①将所有key值修改为默认的mysql+序号,如mysql1,mysql2,②创建多个rdb的yml文件,每个rdb中只包含一个源库和目标库的对应规则
1.修改application.yml文件
vim /data/canal-adapter/conf/application.yml
2.创建多个rdb下的yml文件
touch mytest_{1…30}.yml
3.修改每一个yml文件
4.注意事项:所属组id不用更改,只需修改所属组下的key值就行了