canal源码分析系列——ErosaConnection分析

99封情书 提交于 2019-12-04 22:33:45

类结构

ErosaConnection

|

|--------------------------------------

|                                                    |

MysqlConnection                            LocalBinLogConnection

ErosaConnection是一个连接的接口,定义了一些通用的方法。目前它有两个实现类,MysqlConnection是与MySQL服务器连接的实现类,LocalBinLogConnection是与本地的binlog文件进行连接的实现类。从类中可以看出,目前canal还不支持oracle的实现。

接口定义

package com.alibaba.otter.canal.parse.inbound;

import java.io.IOException;

/**
 * 通用的Erosa的链接接口, 用于一般化处理mysql/oracle的解析过程
 * 
 * @author: yuanzu Date: 12-9-20 Time: 下午2:47
 */
public interface ErosaConnection {

	/**
	 * 建立连接
	 * @throws IOException
	 */
    public void connect() throws IOException;

    /**
     * 重新建立连接,会断开已有连接
     * @throws IOException
     */
    public void reconnect() throws IOException;

    /**
     * 断开连接。
     * @throws IOException
     */
    public void disconnect() throws IOException;

    /**
     * 是否建立连接。
     * @return
     */
    public boolean isConnected();

    /**
     * 用于快速数据查找,和dump的区别在于,seek会只给出部分的数据
     * @param binlogfilename biglog文件名
     * @param binlogPosition binlog起始位置。
     * @param func 事件解析处理器。
     */
    public void seek(String binlogfilename, Long binlogPosition, SinkFunction func) throws IOException;

    /**
     * 获取binlog事件,如果没有数据会阻塞,等待数据的到达。
     * @param binlogfilename biglog文件名
     * @param binlogPosition binlog起始位置。
     * @param func 事件解析处理器。
     */
    public void dump(String binlogfilename, Long binlogPosition, SinkFunction func) throws IOException;

    /**
     * 获取binlog事件,如果没有数据会阻塞,等待数据的到达。
     * @param timestamp 起始时间,只同步该时间之后的产生的新事件。
     * @param func 事件解析处理器。
     */
    public void dump(long timestamp, SinkFunction func) throws IOException;

    /**
     * 产生一个新的连接。
     */
    ErosaConnection fork();
}

其中实现类MysqlConnection是我们经常会使用到的一个类,先看看这个类是如何实现的。

MysqlConnection

属性和构造函数

private MysqlConnector      connector;
    private long                slaveId;
    private Charset             charset = Charset.forName("UTF-8");
    private BinlogFormat        binlogFormat;
    private BinlogImage         binlogImage;

    public MysqlConnection(){
    }

    public MysqlConnection(InetSocketAddress address, String username, String password){

        connector = new MysqlConnector(address, username, password);
    }

    public MysqlConnection(InetSocketAddress address, String username, String password, byte charsetNumber,
                           String defaultSchema){
        connector = new MysqlConnector(address, username, password, charsetNumber, defaultSchema);
    }

从代码可以看出,它大部分依赖一个MysqlConnector组件来实现与MySQL的连接。我们稍后看看该代码的实现。

构造函数需要的是MySql服务器的地址,用户名和密码,该用户必须具备了replication slave权限才可以。slaveId是当前解析器的slaveId,它不能与其它的slaveId冲突。

连接方法实现

public void connect() throws IOException {
        connector.connect();
    }

    public void reconnect() throws IOException {
        connector.reconnect();
    }

    public void disconnect() throws IOException {
        connector.disconnect();
    }

    public boolean isConnected() {
        return connector.isConnected();
    }


基本上都是调用了MysqlConnection的方法实现的,还需要进入该类查看实现。

dump方法


public void dump(String binlogfilename, Long binlogPosition, SinkFunction func) throws IOException {
        updateSettings();
        sendBinlogDump(binlogfilename, binlogPosition);
        DirectLogFetcher fetcher = new DirectLogFetcher(connector.getReceiveBufferSize());
        fetcher.start(connector.getChannel());
        LogDecoder decoder = new LogDecoder(LogEvent.UNKNOWN_EVENT, LogEvent.ENUM_END_EVENT);
        LogContext context = new LogContext();
        while (fetcher.fetch()) {
            LogEvent event = null;
            event = decoder.decode(fetcher, context);

            if (event == null) {
                throw new CanalParseException("parse failed");
            }

            if (!func.sink(event)) {
                break;
            }
        }
    }  
/**
     * the settings that will need to be checked or set:<br>
     * <ol>
     * <li>wait_timeout</li>
     * <li>net_write_timeout</li>
     * <li>net_read_timeout</li>
     * </ol>
     * 
     * @param channel
     * @throws IOException
     */
    private void updateSettings() throws IOException {
        try {
            update("set wait_timeout=9999999");
        } catch (Exception e) {
            logger.warn(ExceptionUtils.getFullStackTrace(e));
        }
        try {
            update("set net_write_timeout=1800");
        } catch (Exception e) {
            logger.warn(ExceptionUtils.getFullStackTrace(e));
        }


        try {
            update("set net_read_timeout=1800");
        } catch (Exception e) {
            logger.warn(ExceptionUtils.getFullStackTrace(e));
        }


        try {
            // 设置服务端返回结果时不做编码转化,直接按照数据库的二进制编码进行发送,由客户端自己根据需求进行编码转化
            update("set names 'binary'");
        } catch (Exception e) {
            logger.warn(ExceptionUtils.getFullStackTrace(e));
        }


        try {
            // mysql5.6针对checksum支持需要设置session变量
            // 如果不设置会出现错误: Slave can not handle replication events with the
            // checksum that master is configured to log
            // 但也不能乱设置,需要和mysql server的checksum配置一致,不然RotateLogEvent会出现乱码
            update("set @master_binlog_checksum= '@@global.binlog_checksum'");
        } catch (Exception e) {
            logger.warn(ExceptionUtils.getFullStackTrace(e));
        }


        try {
            // mariadb针对特殊的类型,需要设置session变量
            update("SET @mariadb_slave_capability='" + LogEvent.MARIA_SLAVE_CAPABILITY_MINE + "'");
        } catch (Exception e) {
            logger.warn(ExceptionUtils.getFullStackTrace(e));
        }
    }


dump方法的实现流程是这样的。

1. 更新MySQL配置信息。调用方法updateSettings();主要包括设置超时时间、设置数据库直接发送二进制数据,设置master_binlog_checksum和mariadb_slave_capability等变量值。

2.发送binlogdump命令。发送COM_BINLOG_DUMP命令,携带binlogFileName、binlogPosition和slaveServerId等关键信息。

3.构建一个binlog获取器组件DirectLogFetcher。使用它获得binlog数据。

4.循环从DirectLogFetcher获取内容,将获取到的数据转化为event。

5.调用SinkFunction处理获取到的event,若处理失败则会中断循环,否则继续。

接下来要看懂他们就需要了解MySQL的binlog协议及数据格式定义了。






易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!