写点什么

mysql binlog 轻量同步工具 binlog portal

用户头像
dothetrick
关注
发布于: 2021 年 01 月 04 日
mysql binlog轻量同步工具binlog portal

使用 binlog 的原因

近期需要重构一个老系统,需要从几个服务中实时同步订单的修改到重构表里。

这里就面临两个选择,

  1. 在每个服务的 mysql 操作前埋点,发送修改信息到队列或服务上。这种方案需要修改多个服务的代码并且测试对原系统的影响,有额外开发和测试成本。

  2. 同步 mysql 的 binlog,根据表的 insert 和 update 更新新表。但是需要维护一个 binlog 同步的服务


本次选择了 binlog 同步的方式。搭建的 binlog 服务也可以用在之后新系统的缓存更新和同步 ES 索引上,相对于埋点这种只有一次性作用的方式,性价比较高。


工具调研:canal 和 mysql-binlog-connector-java


1.canal

要实现 binlog 同步服务,使用较多的开源方案是 canal,运行比较稳定,而且功能也很丰富。


但是在实际部署服务的时候,遇到了一些问题:

  • canal 采用了 client-server 的模式,至少需要部署两个集群。我们的项目都是使用私有云部署,为了稳定运行,就有额外的资源和维护开销。

- 后来发现 canal server 可以投递信息到 kafka。但是我们的消息队列是自研的,只能尝试去改源码。

  • canal 的 server 是完整独立的包,无法直接用 springboot 嵌套。而我们的基础组件都依赖于 springboot,比如监控,配置中心等。

  • canal 的 HA 部署使用的是 zookeeper,很可惜我们并没有可用的 zookeper 集群,也没有资源重新部署一个。

  • 单机部署的时候,已经处理的 binlog postion 是保存在文件里面的,我们用的私有云 docker,重启后全丢失。


2.mysql-binlog-connector-java


调研同时也发现了另一个 binlog 同步工具,mysql-binlog-connector-java


这是一个开源的 binlog 同步工具,功能很简单,就是接收 binlog 信息。作为一个依赖 jar 可以很容易在 springboot 中使用。


但是没有对 binlog 的内容做格式化处理,使用很不方便。当然更没有保存信息和分布式部署功能。


自研工具包 binlogportal


基于这些问题,我们需要一个具有以下特性的 binlog 同步工具:

  • 可以使用 springboot 加载运行,具有较好的扩展性

- 说白了就是作为一个 jar 包,开放出接口可以自定义处理 binlog 信息的方式

  • 可以使用 redis 实现 binlog position 的保存和分布式部署


为了满足这些条件,通过对mysql-binlog-connector-java封装后,实现了自研的工具 binlogportal。

  • 提供了 binlogportal-spring-boot-starter 包,可使用 spring boot 快速部署

  • 使用 redis 保存 binlog position 信息,重启后可从上次 position 位置开始

  • 当前支持 insert 和 update 的结构化

  • 提供默认的 http 事件处理器。可通过实现 IEventHandler 接口,自定义事件处理器

  • 使用 redis 作为分布式协调器,可多机部署实现高可用


使用说明


Mysql 配置

  • Mysql 需要开启 binlog 并设置为 row 模式

  • 同步 binlog 使用的 mysql 账号,需要添加 REPLICATION 权限,示例如下:

CREATE USER binlogportal IDENTIFIED BY '123456';GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'binlogportal'@'%';GRANT ALL PRIVILEGES ON *.* TO 'binlogportal'@'%';FLUSH PRIVILEGES;
复制代码

通过 spring boot 构建项目

  • 直接依赖 binlogportal-spring-boot-starter

<dependency>  <groupId>com.insistingon.binlogportal</groupId>  <artifactId>binlogportal-spring-boot-starter</artifactId>  <version>1.0.5</version></dependency>
复制代码
  • 通过 spring boot 的 application.yml 配置启动器

binlogportal:  enable: true # 是否启用autoconfig  distributed-enable: true # 是否启用分布式部署  distributed-redis: # distributed-enable为true时,要提供一个redis作为分布式协调器    host: 127.0.0.1    port: 6379    auth:  position-redis: # 保存binlog position的redis,必须配置    host: 127.0.0.1    port: 6379    auth:  db-config: # 数据库配置,可以有多个,key自定义即可    d1:      host: 0.0.0.0      port: 3306      user-name: binlogportal      password: 123456      handler-list: [logEventHandler] # 该数据库使用的事件处理器,名称为spring的bean name  http-handler: # 启用自带的http事件处理器,可发送请求    url-list: [http://127.0.0.1:8988/testit] # 要发送的url列表,http参数为统一的格式    result-callback: httpCallBack # 配置自定义的结果处理器,需要实现IHttpCallback接口,值为bean name
复制代码
  • Starter 启动

- spring boot autoconfig 启动成功后,会把 BinlogPortalStarter 的实例注入到 IOC 中

- 项目中通过注入的方式获取 binlogPortalStarter 使用

- binlogPortalStarter.start()会为每个 mysql 库创建一个线程处理 binlog

- 下面是使用 CommandLineRunner 启动 starter 的一个例子

@Slf4j@Componentpublic class BinlogSync implements CommandLineRunner {    @Resource    BinlogPortalStarter binlogPortalStarter;
public void run(String... args) throws Exception { try { binlogPortalStarter.start(); } catch (BinlogPortalException e) { log.error(e.getMessage(), e); } }}
复制代码


非 spring boot 项目

  • 非 spring boot 项目,可以使用基础包

<dependency>  <groupId>com.insistingon.binlogportal</groupId>  <artifactId>binlogportal</artifactId>  <version>1.0.5</version></dependency>
复制代码
  • 依赖后实现配置类BinlogPortalConfigSyncConfig,传入 Starter 中运行即可

public class TestClass{ public static void main(String[] args) {        SyncConfig syncConfig = new SyncConfig();        syncConfig.setHost("0.0.0.0");        syncConfig.setPort(3306);        syncConfig.setUserName("binlogportal");        syncConfig.setPassword("123456");
BinlogPortalConfig binlogPortalConfig = new BinlogPortalConfig(); binlogPortalConfig.addSyncConfig(syncConfig);
RedisConfig redisConfig = new RedisConfig("127.0.0.1", 6379); RedisPositionHandler redisPositionHandler = new RedisPositionHandler(redisConfig); binlogPortalConfig.setPositionHandler(redisPositionHandler);
binlogPortalConfig.setDistributedHandler(new RedisDistributedHandler(redisConfig));
BinlogPortalStarter binlogPortalStarter = new BinlogPortalStarter(); binlogPortalStarter.setBinlogPortalConfig(binlogPortalConfig); try { binlogPortalStarter.start(); } catch (BinlogPortalException e) { e.printStackTrace(); } }}
复制代码


2.分布式部署实现

项目中高可用实现是基于 redis 的分布式锁。


每个实例都会加载全部数据库的配置,在创建 binlog 连接之前,先要获取 redis 锁,获取锁后会定时刷新锁的过期时间。所有实例会定时重新抢锁。


同一个 mysql 库的 binlog 文件和 position 会保存在 redis 里,如果一个实例宕机。新抢到锁的实例在初始化时,会使用上个实例已保存的 binlog 信息继续获取。


项目源码可在 Git 上查看。

项目的 Git 地址:https://github.com/dothetrick/binlogportal


点击 阅读原文 可跳转到 git 项目地址


以上内容属个人学习总结,如有不当之处,欢迎在评论中指正


发布于: 2021 年 01 月 04 日阅读数: 58
用户头像

dothetrick

关注

程序员 2018.06.12 加入

某互联网公司高级开发,6年后端经验

评论

发布
暂无评论
mysql binlog轻量同步工具binlog portal