canal-server、client部署

一 canal应用架构设计

组件说明:

1.linux版本(CentOS Linux 7)
2.mysql版本:5.7
3.canal版本:canal-1.1.4
4.JDK版本: 1.8
在这里插入图片描述

canal工作原理:

1.模拟mysql slave的交互协议,伪装自己为mysql slave,向mysql master发送dump协议;
2.mysql master收到dump请求,开始推送binary log给slave(也就是canal);
3.解析binary log对象(原始为byte流)
了解更多详细更新可以查看文章:【了解canal,看这个就够了】

二 架构落地实现流程

2.1 mysql配置与安装

1. 下载安装
安装mysql,具体安装流程可参考文章:Linux-安装MySQL
2. 创建canal账户
在创建root账号并设置远程访问之后,接着创建canal账号并设置远程访问和权限:

mysql> CREATE USER 'canal'@'%' IDENTIFIED BY 'canal';
mysql> GRANT ALL ON canal.* TO 'canal'@'%';
mysql> GRANT SELECT, REPLICATION CLIENT, REPLICATION SLAVE ON *.* TO 'canal'@'%';//赋予
mysql>FLUSH PRIVILEGES;//刷新权限

3. 验证登录

#远程登录
mysql -h 192.168.175.22 -P 3306 -u canal -pcanal

#本地登录
mysql -ucanal -pcanal

4. 修改my.cnf配置
修改my.conf配置,查找my.cnf配置位置命令:whereis my.conf

my.cnf配置新增如下内容:

log_bin=mysql-bin  #指定bin-log的名称,尽量可以标识业务含义
binlog_format=row  #选择row模式,必须!!!
server_id=1  #mysql服务器id

2.2 canal server配置与启动

1. 下载canal
下载地址: https://github.com/alibaba/canal/releases/download/canal-1.1.3/canal.deployer-1.1.4.tar.gz上传服务器

wget https://raw.github.com/alibaba/canal/gh-pages/download/canal.deployer-1.1.4.tar.gz

2.上传并解压
使用如下命令进行解压至目标目录:

tar xzvf canal.deployer-1.1.4.tar.gz -C canal

3. 修改配置
新解压的文件夹/canal/conf/有一个example文件夹,一个example就代表一个instance实例.而一个instance实例就是一个消息队列,所以这里可以将文件名改为example1,同时再复制出来一个叫example2.(命名可以使用监听的数据库名)

修改/canal/conf/example1/instance.properties配置文件:

canal.instance.master.address=127.0.0.1:3306
canal.instance.dbUsername=canal
canal.instance.dbPassword=canal
canal.instance.connectionCharset = UTF-8
canal.mq.topic=example1

修改/canal/conf/example2/instance.properties配置文件:

canal.instance.master.address=127.0.0.2:3306
canal.instance.dbUsername=canal
canal.instance.dbPassword=canal
canal.instance.connectionCharset = UTF-8
canal.mq.topic=example2

配置文件参数说明
4. 启动canal server
进入文件夹/canal/bin执行如下命令:

./startup.sh

1
查看日志/usr/local/hadoop/app/canal/logs/canal/canal.log,出现如下内容,即表示启动成功:

2019-06-07 21:15:03.372 [main] INFO  com.alibaba.otter.canal.deployer.CanalLauncher - ## load canal configurations
2019-06-07 21:15:03.427 [main] INFO  c.a.o.c.d.monitor.remote.RemoteConfigLoaderFactory - ## load local canal configurations
2019-06-07 21:15:03.529 [main] INFO  com.alibaba.otter.canal.deployer.CanalStater - ## start the canal server.
2019-06-07 21:15:06.251 [main] INFO  com.alibaba.otter.canal.deployer.CanalController - ## start the canal server[192.168.175.22:11111]
2019-06-07 21:15:22.245 [main] INFO  com.alibaba.otter.canal.deployer.CanalStater - ## the canal server is running now ......

5. 启动canal client
注意运行canal客户端代码时,一定要先启动canal server!!!
(1) 添加pom依赖

 <!--canal-->
 <dependency>
    <groupId>com.alibaba.otter</groupId>
    <artifactId>canal.client</artifactId>
    <version>1.1.3</version>
 </dependency>

(2) canal client代码:

// An highlighted block
var foo = 'bar';


package com.xgh.canal;


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

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.EventType;
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 CanalClientTest {

    public static void main(String args[]) {
        // 创建链接
        CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress("192.168.175.20", 11111),
                "example1", "", "");//或者example2
        int batchSize = 1000;
        int emptyCount = 0;
        try {
            connector.connect();
            connector.subscribe(".*\\..*");//订阅所有库下面的所有表
            //connector.subscribe("canal.t_canal");//订阅库canal库下的表t_canal
            connector.rollback();
            int totalEmtryCount = 1200;
            while (emptyCount < totalEmtryCount) {//实际生产中需要设置为true,死循环
                Message message = connector.getWithoutAck(batchSize); // 获取指定数量的数据
                long batchId = message.getId();
                int size = message.getEntries().size();
                if (batchId == -1 || size == 0) {
                    emptyCount++;
                    System.out.println("empty count : " + emptyCount);//此時代表當前數據庫無遍更數據
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    emptyCount = 0;
                    System.out.printf("message[batchId=%s,size=%s] \n", batchId, size);
                    printEntry(message.getEntries());
                }

                connector.ack(batchId); // 提交确认
                // connector.rollback(batchId); // 处理失败, 回滚数据
            }

            System.out.println("empty too many times, exit");
        } finally {
            connector.disconnect();
        }
    }

    private static void printEntry(List<Entry> entrys) {
        for (Entry entry : entrys) {
            if (entry.getEntryType() == EntryType.TRANSACTIONBEGIN
                    || entry.getEntryType() == EntryType.TRANSACTIONEND) {
                continue;
            }

            RowChange rowChage = null;
            try {
                rowChage = RowChange.parseFrom(entry.getStoreValue());
            } catch (Exception e) {
                throw new RuntimeException("ERROR ## parser of eromanga-event has an error , data:" + entry.toString(),
                        e);
            }
            System.out.println("rowChare ======>"+rowChage.toString());

            EventType eventType = rowChage.getEventType(); //事件類型,比如insert,update,delete
            System.out.println(String.format("================> binlog[%s:%s] , name[%s,%s] , eventType : %s",
                    entry.getHeader().getLogfileName(),//mysql的my.cnf配置中的log-bin名稱
                    entry.getHeader().getLogfileOffset(), //偏移量
                    entry.getHeader().getSchemaName(),//庫名
                    entry.getHeader().getTableName(), //表名
                    eventType));//事件名

            for (RowData rowData : rowChage.getRowDatasList()) {
                if (eventType == EventType.DELETE) {
                    printColumn(rowData.getBeforeColumnsList());
                } else if (eventType == EventType.INSERT) {
                    printColumn(rowData.getAfterColumnsList());
                } else {
                    System.out.println("-------> before");
                    printColumn(rowData.getBeforeColumnsList());
                    System.out.println("-------> after");
                    printColumn(rowData.getAfterColumnsList());
                }
            }
        }
    }

    private static void printColumn(List<Column> columns) {
        for (Column column : columns) {
            System.out.println(column.getName() + " : " + column.getValue() + "    update=" + column.getUpdated());
        }
    }

}

检查下CanalConnector是否调用subscribe(filter)方法;有的话,filter需要和instance.properties的canal.instance.filter.regex一致,否则subscribe的filter会覆盖instance的配置

6. 触发数据库变更
改变数据库数据,canal client输出日志:

================> binlog[mysql-bin.000001:6764] , name[canal,t_canal] , eventType : INSERT
id : 10    update=true
name : hello    update=true
status : 1    update=true

2.3 Mysql的Binary log日志

a、它包含的内容及作用如下:
包含了所有更新了数据或者已经潜在更新了数据(比如没有匹配任何行的一个DELETE)
包含关于每个更新数据库(DML)的语句的执行时间信息
不包含没有修改任何数据的语句,如果需要启用该选项,需要开启通用日志功能
主要目的是尽可能的将数据库恢复到数据库故障点,因为二进制日志包含备份后进行的所有更新
用于在主复制服务器上记录所有将发送给从服务器的语句
启用该选项数据库性能降低1%,但保障数据库完整性,对于重要数据库值得以性能换完整。有些类似于Oracle开启归档模式。

b、开启二进制日志的方法及属性
使用–log-bin[=file_name]选项或在配置文件中指定log-bin启动时,mysqld写入包含所有更新数据的SQL命令的日志文件。
对于未给出file_name值, 默认名为-bin后面所跟的主机名。
在未指定绝对路径的情形下,缺省位置保存在数据目录下。
每个二进制日志名会添加一个数字扩展名用于日志老化,因此不支持自定义的扩展名,会被mysql数字扩展名动态替换。
若当前的日志大小达到max_binlog_size,则自动创建新的二进制日志。
对于大的事务,二进制日志会超过max_binlog_size设定的值。也即是事务仅仅写入一个二进制日志。
由是可知,二进制日志文件大小接近,其size不是完全相等,这点不同于oracle。
二进制日志文件会有一个对应二进制日志索引文件,该文件包含所有的二进制日志,其文件名与二进制日志相同,扩展名为.index
二进制索引文件通过–log-bin-index[=file_name]选项来指定
RESET MASTER语句将删除所有二进制日志文件,这将影响到从库。也可以用PURGE MASTER LOGS只删除部分二进制文件。

可以使用 Docker Compose 来部署 Otter-Server 并将数据入 ClickHouse。具体步骤如下: 1. 创建 Docker Compose 配置文件: 在任意目录下创建一个名为 `docker-compose.yml` 的文件,并添加以下内容: ``` version: '3' services: otter-server: image: alibaba/otter ports: - "8080:8080" - "1099:1099" - "2088:2088" environment: JAVA_OPTS: "-Dfile.encoding=UTF-8 -server -Xmx2g -Xms2g -Xmn512m -Xss256k -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/heapdump" volumes: - ./otter/data:/data - ./otter/logs:/logs command: ["/data/otter/bin/startup.sh"] depends_on: - clickhouse networks: - otter clickhouse: image: yandex/clickhouse-server ports: - "8123:8123" volumes: - ./clickhouse/data:/var/lib/clickhouse - ./clickhouse/config:/etc/clickhouse-server networks: - otter networks: otter: ``` 其中,`./otter/data` 和 `./otter/logs` 是宿主机器上存储 Otter-Server 数据和日志的路径,`./clickhouse/data` 和 `./clickhouse/config` 是宿主机器上存储 ClickHouse 数据和配置文件的路径。你可以自行修改路径。 2. 创建 ClickHouse 表: 在 `./clickhouse/config/users.xml` 文件中添加以下内容: ``` <profiles> <default> <quota>default</quota> <max_memory_usage>5000000000</max_memory_usage> </default> </profiles> <users> <default> <password></password> <networks incl="networks" replace="replace"> <ip>::/0</ip> </networks> <profiles> <default></default> </profiles> </default> </users> <quotas> <default> <interval>60</interval> <queries>0</queries> <errors>0</errors> <result_rows>0</result_rows> <read_rows>0</read_rows> <execution_time>0</execution_time> <memory>0</memory> </default> </quotas> <macros> <shard>1</shard> </macros> <clickhouse_remote_servers> </clickhouse_remote_servers> <clickhouse_dictionaries> </clickhouse_dictionaries> <clickhouse_table_engines> <odbc> <read_only>0</read_only> <database>default</database> <table>otter_test</table> <connection_string>DSN=clickhouse;DATABASE=default;UID=default;PWD=</connection_string> <data_source_name>clickhouse</data_source_name> <odbc_driver>clickhouse-odbc</odbc_driver> </odbc> </clickhouse_table_engines> ``` 其中,`otter_test` 是 ClickHouse 中的表名,可以自行修改。然后,执行以下命令启动 ClickHouse: ``` docker-compose up clickhouse ``` 在启动完成后,可以使用以下命令进入 ClickHouse 控制台: ``` docker-compose exec clickhouse clickhouse-client ``` 然后,可以使用以下命令创建表: ``` CREATE TABLE otter_test ( id UInt32, name String, age UInt8, PRIMARY KEY (id) ) ENGINE = ODBC('dsn=clickhouse;database=default;table=otter_test;uid=default;pwd='); ``` 3. 配置 Canal: 在 `./otter/data/conf/otter.properties` 文件中添加以下配置: ``` otter.canal.zookeeper.cluster=zookeeper:2181 ``` 然后,在 `./otter/data/bin/otter-admin.sh` 文件中添加以下配置: ``` set OTTER_OPTS="$OTTER_OPTS -Dotter.canal.zookeeper.cluster=zookeeper:2181" ``` 4. 启动 Otter-Server: 使用以下命令启动 Otter-Server: ``` docker-compose up otter-server ``` 至此,Otter-Server 就可以将数据入 ClickHouse 中了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值