Giter Site home page Giter Site logo

binlogportal's Introduction

binlogportal

A simple mysql binlog sync tool

It has following features:

1.Store binlog postion use redis

2.Support springboot starter

3.Easy to use and deploy


项目简介

一个轻量级的mysql binlog同步工具。可以单机部署,同时支持分布式高可用。

项目主要目标是提供可基于spring boot快速部署的同步工具,外部依赖只有redis。

当前版本特性:

  • 提供了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;

1.快速开始

1.1 通过spring boot构建项目

  • 直接依赖binlogportal-spring-boot-starter
<dependency>
  <groupId>com.insistingon.binlogportal</groupId>
  <artifactId>binlogportal-spring-boot-starter</artifactId>
  <version>1.1.0</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
@Component
public 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);
        }
    }
}

1.2 非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信息继续获取。

binlogportal's People

Contributors

dothetrick avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

binlogportal's Issues

大佬运行报错 这是啥问题呢

2021-11-10 14:18:04.262 WARN 25352 --- [iyuncs.com:3306] c.g.shyiko.mysql.binlog.BinaryLogClient : com.insistingon.binlogportal.event.MultiEventHandlerListener@4a7c22b1 choked on Event{header=EventHeaderV4{timestamp=1636524980000, eventType=UPDATE_ROWS, serverId=19404926, headerLength=19, dataLength=4385, nextPosition=280448513, flags=0}, data=UpdateRowsEventData{tableId=327, includedColumnsBeforeUpdate={0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27}, includedColumns={0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27}, rows=[
{before=[2411, [B@491e63ae, -1.00, 0.00, 299.00, 4.00, 0.00, 13, 4, 0, 0, 100, 5, 0, 2, 4, 6, 0, [B@198954f2, [B@644f887d, [B@262dacf6, [B@1c94b88e, [B@4fe9412a, [B@577d34ca, [B@1c7c68cf, 1613460000, 1629539443000, 1629539443000], after=[2411, [B@2515f306, -1.00, 0.00, 299.00, 4.00, 0.00, 13, 4, 0, 0, 100, 5, 5, 2, 4, 6, 0, [B@2070069f, [B@1e15ad3f, [B@61d08422, [B@6bd7d6dd, [B@4bb361c6, [B@787c41da, [B@1de83b63, 1613460000, 1629539443000, 1629539443000]}
]}}

java.lang.NullPointerException: null
at com.insistingon.binlogportal.event.MultiEventHandlerListener.lambda$null$0(MultiEventHandlerListener.java:113) ~[binlogportal-1.0.11.jar:na]
at java.util.ArrayList.forEach(ArrayList.java:1259) ~[na:1.8.0_291]
at com.insistingon.binlogportal.event.MultiEventHandlerListener.lambda$onEvent$1(MultiEventHandlerListener.java:111) ~[binlogportal-1.0.11.jar:na]
at java.util.ArrayList.forEach(ArrayList.java:1259) ~[na:1.8.0_291]
at com.insistingon.binlogportal.event.MultiEventHandlerListener.onEvent(MultiEventHandlerListener.java:110) ~[binlogportal-1.0.11.jar:na]
at com.github.shyiko.mysql.binlog.BinaryLogClient.notifyEventListeners(BinaryLogClient.java:1158) [mysql-binlog-connector-java-0.21.0.jar:0.21.0]
at com.github.shyiko.mysql.binlog.BinaryLogClient.listenForEventPackets(BinaryLogClient.java:1005) [mysql-binlog-connector-java-0.21.0.jar:0.21.0]
at com.github.shyiko.mysql.binlog.BinaryLogClient.connectWithTimeout(BinaryLogClient.java:517) [mysql-binlog-connector-java-0.21.0.jar:0.21.0]
at com.github.shyiko.mysql.binlog.BinaryLogClient.access$1100(BinaryLogClient.java:90) [mysql-binlog-connector-java-0.21.0.jar:0.21.0]
at com.github.shyiko.mysql.binlog.BinaryLogClient$7.run(BinaryLogClient.java:881) [mysql-binlog-connector-java-0.21.0.jar:0.21.0]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_291]

position相关疑问

建议将更新redis中position的逻辑放到业务处理完消息之后,这样能尽可能减少因为服务宕机或者其他异常导致的消息丢失

连接不上mysql的binlog

mysql版本:5.7.39
binlogportal版本:1.0.3
在自己电脑上连接是正常的,日志如下:
2023-05-06 13:51:45.073 [redisson-netty-4-18] INFO o.r.connection.pool.MasterPubSubConnectionPool-1 connections initialized for /127.0.0.1:63790 2023-05-06 13:51:45.101 [redisson-netty-4-19] INFO org.redisson.connection.pool.MasterConnectionPool-24 connections initialized for /127.0.0.1:63790 2023-05-06 13:51:45.592 [Timer-0] INFO com.github.shyiko.mysql.binlog.BinaryLogClient-Connected to 127.0.0.1:3306 at mysql-bin.000003/56376353 (sid:-1266629332431288544, cid:5355)
在生产环境部署只有两个redis连接的日志,没有mysql连接binlog的日志。
生产环境是一主二从,binlogportal连的是master的ip和端口,binlog已开启,模式是ROW,还有可能是哪里配置有问题吗?

position位点不更新

binlog初始化有的位点,位点不进行更新,在onEvent方法一直在走ROTATE的逻辑,位点还是初始化的位点

代码疑惑

我看只创建了list,添加了columns,但是后续未使用
image

在活动不频繁的linux服务器上会出现 JVM 阻塞

BinaryLogClientFactory中生成ServerId的的方法

   private long getRandomServerId() {
        try {
            return SecureRandom.getInstanceStrong().nextLong();
        } catch (NoSuchAlgorithmException e) {
            return RandomUtils.nextLong();
        }
    }

SecureRandom.getInstanceStrong()方法在Linux环境下使用/dev/random生成随机数的种子。但是/dev/random是一个阻塞数字生成器,如果没有足够的随机数据,那么就会一直等待,迫使 JVM 阻塞。

集群部署问题

image
这里应该有bug啊,没法实现分布式吧,binaryLogClientFactory.getClient(syncConfig).connect(); 这里会阻塞,这时候锁被占用,另一台服务这时候获取锁,没有拿到,但是最后finally释放了锁,然后定时任务再次执行就会获取到,最后每一台机器都有一个binlog监听

对象反序列化时候,抛出EOFException

项目启动一段时间之后,idea 控制台就会报如下错误:
Failed to deserialize data of EventHeaderV4{timestamp=1650557316000, eventType=EXT_WRITE_ROWS, serverId=2, headerLength=19, dataLength=301, nextPosition=53044880, flags=0}.mysql-bin.000001/53044478
com.github.shyiko.mysql.binlog.event.deserialization.EventDataDeserializationException:### Failed to deserialize data of EventHeaderV4{timestamp=1650557316000, eventType=EXT_WRITE_ROWS, serverId=2, headerLength=19, dataLength=301, nextPosition=53044880, flags=0}
at com.github.shyiko.mysql.binlog.event.deserialization.EventDeserializer.deserializeEventData(EventDeserializer.java:300)
at com.github.shyiko.mysql.binlog.event.deserialization.EventDeserializer.nextEvent(EventDeserializer.java:223)
at com.github.shyiko.mysql.binlog.BinaryLogClient.listenForEventPackets(BinaryLogClient.java:984)
at com.github.shyiko.mysql.binlog.BinaryLogClient.connectWithTimeout(BinaryLogClient.java:517)
at com.github.shyiko.mysql.binlog.BinaryLogClient.access$1100(BinaryLogClient.java:90)
at com.github.shyiko.mysql.binlog.BinaryLogClient$7.run(BinaryLogClient.java:881)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.io.EOFException: null
at com.github.shyiko.mysql.binlog.io.ByteArrayInputStream.read(ByteArrayInputStream.java:190)
at java.io.InputStream.read(InputStream.java:170)
at com.github.shyiko.mysql.binlog.io.ByteArrayInputStream.fill(ByteArrayInputStream.java:96)
at com.github.shyiko.mysql.binlog.io.ByteArrayInputStream.read(ByteArrayInputStream.java:89)
at com.github.shyiko.mysql.binlog.event.deserialization.AbstractRowsEventDataDeserializer.deserializeVarString(AbstractRowsEventDataDeserializer.java:376)
at com.github.shyiko.mysql.binlog.event.deserialization.AbstractRowsEventDataDeserializer.deserializeCell(AbstractRowsEventDataDeserializer.java:184)
at com.github.shyiko.mysql.binlog.event.deserialization.AbstractRowsEventDataDeserializer.deserializeRow(AbstractRowsEventDataDeserializer.java:138)
at com.github.shyiko.mysql.binlog.event.deserialization.WriteRowsEventDataDeserializer.deserializeRows(WriteRowsEventDataDeserializer.java:64)
at com.github.shyiko.mysql.binlog.event.deserialization.WriteRowsEventDataDeserializer.deserialize(WriteRowsEventDataDeserializer.java:56)
at com.github.shyiko.mysql.binlog.event.deserialization.WriteRowsEventDataDeserializer.deserialize(WriteRowsEventDataDeserializer.java:32)
at com.github.shyiko.mysql.binlog.event.deserialization.EventDeserializer.deserializeEventData(EventDeserializer.java:294)
... 6 common frames omitted

查了一些资料,感觉应该在
image
捕捉 IOEception 前面增加对 EOFException 处理逻辑。
catch (EOFException e) {
System.out.println("");
System.out.println("End of file reached");
break;
} catch (IOException e) {}

连接MySQL异常

MySQL版本:8.0
连接错误信息:Client does not support authentication protocol requested by server; consider upgrading MySQL client

中文乱码问题

做了编码转换,中文会乱码,但是不知道为什么,并不是全部中文会乱码,一部分中文乱码

项目启动后会报 java.net.SocketException: Connection reset

Failed to deserialize data of EventHeaderV4{timestamp=1650557453000, eventType=TABLE_MAP, serverId=2, headerLength=19, dataLength=62, nextPosition=86626369, flags=0}.mysql-bin.000001/86626288
com.github.shyiko.mysql.binlog.event.deserialization.EventDataDeserializationException: Failed to deserialize data of EventHeaderV4{timestamp=1650557453000, eventType=TABLE_MAP, serverId=2, headerLength=19, dataLength=62, nextPosition=86626369, flags=0}
at com.github.shyiko.mysql.binlog.event.deserialization.EventDeserializer.deserializeEventData(EventDeserializer.java:300)
at com.github.shyiko.mysql.binlog.event.deserialization.EventDeserializer.deserializeTableMapEventData(EventDeserializer.java:272)
at com.github.shyiko.mysql.binlog.event.deserialization.EventDeserializer.nextEvent(EventDeserializer.java:219)
at com.github.shyiko.mysql.binlog.BinaryLogClient.listenForEventPackets(BinaryLogClient.java:984)
at com.github.shyiko.mysql.binlog.BinaryLogClient.connectWithTimeout(BinaryLogClient.java:517)
at com.github.shyiko.mysql.binlog.BinaryLogClient.connect(BinaryLogClient.java:490)
at com.insistingon.binlogportal.distributed.RedisDistributedHandler$1.lambda$run$0(RedisDistributedHandler.java:61)
at java.util.HashMap.forEach(HashMap.java:1289)
at com.insistingon.binlogportal.distributed.RedisDistributedHandler$1.run(RedisDistributedHandler.java:56)
at java.util.TimerThread.mainLoop(Timer.java:555)
at java.util.TimerThread.run(Timer.java:505)
Caused by: java.net.SocketException: Connection reset
at java.net.SocketInputStream.read(SocketInputStream.java:210)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at com.github.shyiko.mysql.binlog.io.BufferedSocketInputStream.read(BufferedSocketInputStream.java:51)
at com.github.shyiko.mysql.binlog.io.ByteArrayInputStream.readWithinBlockBoundaries(ByteArrayInputStream.java:202)
at com.github.shyiko.mysql.binlog.io.ByteArrayInputStream.read(ByteArrayInputStream.java:184)
at com.github.shyiko.mysql.binlog.io.ByteArrayInputStream.readZeroTerminatedString(ByteArrayInputStream.java:81)
at com.github.shyiko.mysql.binlog.event.deserialization.TableMapEventDataDeserializer.deserialize(TableMapEventDataDeserializer.java:36)
at com.github.shyiko.mysql.binlog.event.deserialization.TableMapEventDataDeserializer.deserialize(TableMapEventDataDeserializer.java:27)
at com.github.shyiko.mysql.binlog.event.deserialization.EventDeserializer.deserializeEventData(EventDeserializer.java:294)
... 10 common frames omitted

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.