文章目录

写在前面

本文用到了docker,安装docker请移步:​​centos7安装docker-简单而详细无坑​

一、初始化mysql

1、安装mysql

​docker安装mysql-简单无坑​

2、创建数据库

在这里创建了一个名为【mytest】的数据库。

3、修改mysql配置文件

修改my.cnf(默认在/etc/mysql目录),添加以下两行

server_id=1
#开启二进制
log-bin=mysql-bin
#表示需要同步的库
binlog-do-db=mytest

改完记得重启mysql。(/mysql/data目录下会出现mysql-bin.000001)

4、创建一个同步用户

在mysql中创建一个专门用来同步的用户

create user canal@'%' IDENTIFIED by 'canal';
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT,SUPER ON *.* TO 'canal'@'%';
FLUSH PRIVILEGES;

5、查看主库的状态

使用canal订阅mysql的binlog,springboot使用canal订阅mysql的binlog_mysql

二、初始化canal

1、创建网络,让mysql与canal通信

docker

1、使用docker启动canal

# 下载canal
docker pull canal/canal-server:v1.1.5

# 启动canal
docker run -p 11111:11111 --name canal --network cxf -d canal/canal-server:v1.1.5

2、查看mysql地址

# 这里是172.17.0.2
docker

3、修改canal配置文件

修改canal.properties配置文件(/home/admin/canal-server/conf目录)(不需要改)

# 默认端口 11111
# 默认输出model为tcp, mysql就使用tcp
# tcp, kafka, RocketMQ
canal.serverMode = tcp

#################################################
######### destinations #############
#################################################
# canal可以有多个instance,每个实例有独立的配置文件,默认只 有一个example实例。
# 如果需要处理多个mysql数据的话,可以复制出多个example,对其重新命名,
# 命令和配置文件中指定的名称一致。然后修改canal.properties 中的 canal.destinations
# canal.destinations=实例 1,实例 2,实例 3
canal.destinations =

修改instance.properties配置文件(/home/admin/canal-server/conf/example目录)(只需要修改mysql地址即可)

# 不能和mysql重复
canal.instance.mysql.slaveId=2
# 使用mysql的虚拟ip和端口
canal.instance.master.address=172.17.0.3:3306
# 使用已创建的canal用户
canal.instance.dbUsername=canal
canal.instance.dbPassword=canal
canal.instance.connectionCharset = UTF-8
# canal.instance.defaultDatabaseName =test

# 表示匹配所有的库所有的表
canal.instance.filter.regex =.*\\..*

4、重启canal

当canal监听到binlog发生变化,会通知canal客户端。

三、java使用canal客户端

1、依赖

<dependency>
<groupId>com.alibaba.otter</groupId>
<artifactId>canal.client</artifactId>
<version>1.1.2</version>
</dependency>

2、核心代码

import com.alibaba.fastjson.JSONObject;
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.ByteString;

import java.net.InetSocketAddress;
import java.util.List;

public class CanalClient {

public static void main(String[] args) throws Exception{

//1.获取 canal 连接对象
CanalConnector canalConnector =
CanalConnectors.newSingleConnector(new
InetSocketAddress("canal所在服务器IP", 11111), "example", "", "");

System.out.println("canal启动并开始监听数据 ...... ");
while (true){
canalConnector.connect();
//订阅表
canalConnector.subscribe("shop001.*");
//获取数据
Message message = canalConnector.get(100);
//解析message
List<CanalEntry.Entry> entries = message.getEntries();
if(entries.size() <=0){
System.out.println("未检测到数据");
Thread.sleep(1000);
}
for(CanalEntry.Entry entry : entries){
//1、获取表名
String tableName = entry.getHeader().getTableName();
//2、获取类型
CanalEntry.EntryType entryType = entry.getEntryType();
//3、获取序列化后的数据
ByteString storeValue = entry.getStoreValue();

//判断是否rowdata类型数据
if(CanalEntry.EntryType.ROWDATA.equals(entryType)){
//对第三步中的数据进行解析
CanalEntry.RowChange rowChange = CanalEntry.RowChange.parseFrom(storeValue);
//获取当前事件的操作类型
CanalEntry.EventType eventType = rowChange.getEventType();
//获取数据集
List<CanalEntry.RowData> rowDatasList = rowChange.getRowDatasList();
//便利数据
for(CanalEntry.RowData rowData : rowDatasList){
//数据变更之前的内容
JSONObject beforeData = new JSONObject();
List<CanalEntry.Column> beforeColumnsList = rowData.getAfterColumnsList();
for(CanalEntry.Column column : beforeColumnsList){
beforeData.put(column.getName(),column.getValue());
}
//数据变更之后的内容
List<CanalEntry.Column> afterColumnsList = rowData.getAfterColumnsList();
JSONObject afterData = new JSONObject();
for(CanalEntry.Column column : afterColumnsList){
afterData.put(column.getName(),column.getValue());
}
System.out.println("Table :" + tableName +
",eventType :" + eventType +
",beforeData :" + beforeData +
",afterData : " + afterData);
}
}else {
System.out.println("当前操作类型为:" + entryType);
}
}
}
}
}

四、springboot使用canal客户端(亲测该方式并不是很友好。。)

1、引入依赖

<dependency>
<groupId>top.javatool</groupId>
<artifactId>canal-spring-boot-starter</artifactId>
<version>1.2.1-RELEASE</version>
</dependency>

2、编写配置

canal:
destination: example # canal的集群名字,要与安装canal时设置的名称一致
server: 192.168.56.10:11111 # canal服务地址

3、监听类

import org.springframework.stereotype.Component;
import top.javatool.canal.client.annotation.CanalTable;
import top.javatool.canal.client.handler.EntryHandler;

@CanalTable("tb_item") //监听的表
@Component
public class ItemHandler implements EntryHandler<Testb> {


@Override
public void insert(Testb item) {
System.out.println("insert");
System.out.println(item);
}

@Override
public void update(Testb before, Testb after) {
System.out.println("update");
System.out.println(before);
System.out.println(after);

}

@Override
public void delete(Testb item) {
System.out.println("delete");
System.out.println(item);

}
}

class Testb{
private Integer id;
private String name;

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Override
public String toString() {
return "Testb{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}