erosa mysql_MySQL协议和canal实现

前言

前面的文章里,我们了解到 canal 可以从 MySQL 中感知数据的变化。这是因为它模拟 MySQL slave 的交互协议,伪装自己为 MySQL slave ,从而实现了主从复制。

正是了解到这一点,笔者有两个问题便一直萦绕于心:它是如何模拟 MySQL slave 交互协议的?

它又是怎么解析 binlog 日志的呢?

今天,笔者准备就着这两个问题,扒拉扒拉 canal 的代码,一探究竟。

一、MySQL 主从复制

在谈 canal 之前,我们有必要再重温下 MySQL 主从复制的原理。

64cf71a74b21fa25d9aa319e99355ce0.png

总结上图的流程如下:MySQL master 将数据变更写入二进制日志 (binary log , 其中记录叫做二进制日志事件binary log events);

MySQL slave 将 master 的 binary log events 拷贝到它的中继日志 (relay log);

MySQL slave 重放 relay log 中的事件,将数据变更反映到自己的数据库。

二、canal 原理

20ccf019476610c1d380dd60d2b2c141.png

上图就很形象的描述了 canal 的角色。它的原理也很简单:canal模拟mysql slave的交互协议,伪装自己为mysql slave,向mysql master发送dump协议;

mysql master收到dump请求,开始推送binary log给slave(也就是canal);

canal解析binary log对象(原始为byte流);

canal将解析后的对象,根据业务场景,分发到比如 MySQL 、RocketMQ 或者 ES 中。

三、源码启动

看完了 MySQL 主从复制和 canal 原理之后,为了方便 debug ,笔者已经在 GitHub Fork 了源码,并导入本地。

可以找到 com.alibaba.otter.canal.deployer.CanalLauncher 类,它就是 canal 独立版本启动的入口类。

在这里,直接运行 main 方法即可运行 canal ,和在 /canal/bin/startup.sh 中效果一样。

事实上,canal 的代码比较多,在架构上又分了很多模块设计,比如事件解析器、事件消费、内存存储、服务实例、元数据、高可用等。

本文不打算面面俱到介绍每一个的实现,那就得正儿八经写一个 canal 系列才行。主要还是为了开头我们提出的那两个问题。

四、如何模拟slave ?

上面我们已经说到, CanalLauncher是canal 启动的入口类。

运行 main 方法之后, canal 会先做很多准备工作。比如加载配置文件、初始化消息队列、启动 canal Admin、加载Spring配置、注册钩子程序等。

canal 模拟 slave 协议,是在 EventParser模块中开始进行的。

4afbaeab51935283bbf647f64ae2cb6e.png

在 canal 代码中,整个流程简化如下:// 开始执行replication// 1. 构造Erosa连接ErosaConnection erosaConnection = buildErosaConnection();// 2. 启动一个心跳线程startHeartBeat(erosaConnection);// 3. 执行dump前的准备工作preDump(erosaConnection);erosaConnection.connect();// 链接// 查询master serverIdlong queryServerId = erosaConnection.queryServerId();if (queryServerId != 0) {    serverId = queryServerId;}// 4. 获取binlog最后的位置信息EntryPosition position = findStartPosition(erosaConnection);final EntryPosition startPosition = position;// 加载元数据processTableMeta(startPosition);// 重新链接,因为在找position过程中可能有状态,需要断开后重建erosaConnection.reconnect();// 4. 开始dump数据erosaConnection.dump(startPosition.getJournalName(),startPosition.getPosition(),sinkHandler);

1、握手、验证

在开始之前,canal 必须先要和 MySQL 服务器建立连接,并完成客户端身份验证。

在 MySQL 中,连接过程协议如下:

fa240af23981432b1e9314eb803295d9.png

在代码中,我们看一下它的连接方法:

21e24aeef998923d8789a6165ebbe174.png

其中, negotiate方法是握手协议和客户端验证的具体实现。就是按照 MySQL 的协议规范,通过上面创建的 Socketchannel来读写网络数据。

2、dump前的准备

正确连接到 MySQL 后,在开始执行 dump 指令之前,还要初始化一些配置信息。

思路就是通过 MySQL 执行器,执行 SQL 语句,获取信息。

代码就不粘了,不过它们执行的语句如下:show variables like 'binlog_format'      #获取binlog format格式show variables like 'binlog_row_image'   #获取binlog image格式show variables like 'server_id'          #获取matser serverIdshow master status                       #获取binlog名称和position

3、注册slave

现在开始调用 erosaConnection.dump(binlogfilename,binlogPosition,func)方法,来注册slave和发送dump命令。

在使用 COM_BINLOG_DUMP请求binlog事件之前发送,在主服务器上注册一个从服务器,它的指令是 COM_REGISTER_SLAVE。

88b5feef22e7fb63cc1b3176f1431bdf.png

注册完之后,就是发送dump请求,它的指令是 COM_BINLOG_DUMP。

145fd2b777e50c7c3bebc7addad4db1f.png

在执行完这段代码后,我们通过 show processlist;查看进程,就可以看到这个dump线程的状态。

其中 command = Binlog Dump

state = Master has sent all binlog to slave; waiting for more updates。

五、如何解析binlog数据 ?

在上面章节中,我们已经看到,MySQL主服务器已经接受了 canal 这个从服务器,那么当canal拿到binlog内容后, 又是怎么解析它的呢?

首先,还记得在配置MySQL服务器的时候,我们将 binlog-format设置为ROW模式,它是基于行的复制。

binlog中每一个数据变更可以叫做事件,在ROW模式下,有几个主要的事件类型:事件SQL命令rows 内容TABLEMAPEVENTnull定义将要更改的表。

WRITEROWSEVENT插入要插入的行数据

DELETEROWSEVENT删除被删除的数据

UPDATEROWSEVENT更新原数据+要更改的数据

每一次数据的变更,都会触发2个事件,先把要更改的表信息告诉你,然后再告诉你更改的row内容。

比如 TABLE_MAP_EVENT+WRITE_ROWS_EVENT。

canal在接收到binlog数据后,并不会马上把它解析成我们熟悉的JSON数据,而是在发送的时候才开始。

比如我们选择使用 RocketMQ,那么在发送之前才开始将binlog里面的byte数组转化为对象。// 并发构造EntryRowData[] datas = MQMessageUtils.buildMessageData(message, executor);// 串行分区List flatMessages = MQMessageUtils.messageConverter(datas, message.getId());

在这两个方法里,就完成了byte数组到对象的转化。转化成的 FlatMessage对象,就成了我们在消息队列中消费到的数据结构。public class FlatMessage implements Serializable {    private long                      id;    private String                    database;    private String                    table;    private List              pkNames;    private Boolean                   isDdl;    private String                    type;    // binlog executeTime    private Long                      es;    // dml build timeStamp    private Long                      ts;    private String                    sql;    private Map      sqlType;    private Map       mysqlType;    private List> data;    private List> old;}

总结

正如本文开头所言,笔者在刚了解到canal机制的时候,确实觉得很不可思议。

咦,它是怎么模拟MySQL slave的呢 ?总觉得是不是有啥黑科技在里面。。。

事实上,这是源于笔者对MySQL的无知。

MySQL早就制定好了各种接口协议,怎么连接、验证、注册和dump都明明白白的写在那儿啦。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/335595.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HH SaaS电商系统的商品关联版式功能模块设计

关联版式 1、关联版式跟着商品走,关联spu_ext_id,一对多关系 2、商城和店铺有各自的关联版式

spring nosql_使用Spring Security和NoSQL的Spring Boot

spring nosql在前面的文章中,我们从一个SQL数据库提供用户和权威检索自定义查询设置弹簧安全配置。 如今,许多现代应用程序都使用NoSQL数据库。 Spring安全性不是NoSQL数据库的现成解决方案。 在这种情况下,我们需要通过实现自定义UserDeta…

ofbiz修改mysql_如何将OFBIZG的默认数据库更改mysql的方法(只求参考 )

ofbiz自带的数据库是Derby,这是一种小型的适合与测试系统的数据库,但不适合在产品级系统中使用,所以通常我们需要将ofbiz迁移到其它数据库上,下面我就以如何迁移至mysql为例,向大家讲述数据库迁移过程,迁移至其它数据库的过程类似.第一步:修改entityengine.xml文件.该文件的位置…

HH SaaS电商系统的供应商系统设计

供应商信息结构图 供应商类型 商城的供应商划分为专享型、共享型两种,但是租户和店铺供应商则都是“专享型”的。 共享型供应商发布的商品归属供应商自己的,商品档案供应商才有资格管理,所以spu_base需要保存供应商id,有供应商id…

c mysql 地址池_FreeRadius 根据mysql 下发指定地址池的地址...

一、使用radius本地文件存储IP地址。修改modules/ippoolippool main_pool {range-start 192.168.111.1range-stop 192.168.113.254netmask 255.255.255.0cache-size 800session-db ${db_dir}/db.ippoolip-index ${db_dir}/db.ipindexoverride nomaximum-timeout 0}在si…

aws faas_带有AWS Lambda和Java的无服务器FaaS

aws faas什么是无服务器架构? 无服务器架构在由第三方完全管理的临时容器中运行自定义代码。 自定义代码通常只是完整应用程序的一小部分。 也称为函数 。 这为无服务器架构提供了另一个名称,即功能即服务 (FaaS)。 该容器是短暂的…

跨境商品的进口税额显示

跨境商品的采购类型有三种:直邮、保税、一般贸易,而一般贸易的商品已经清关入境了,虽然是跨境商品,但是无需再清关,所以商品详情页无需显示进口税相关信息。 直邮跨境商品显示的进口税信息如下图所示: 保税…

HH SaaS电商系统的跨境商品展示、下单、清关、出库全流程设计

跨境商品的展示 后补 跨境商品的下单 在订单确认页面就要按SKU拆单,所以跨境销售订单的主单和子单是一对一的关系 多种进口渠道的商品在同个销售主单中,在进口清关、收货、货款结算时会出现问题,假设销售订单中有2种商品,一种…

coreldraw x8段落_CDR X8设置自定义文字为默认字体(二)

通过上一篇文章的介绍,我们已经了解到了在CorelDRAW中如何自定义设置默认字体,相关阅读可参阅:CDR X8设置文字为默认字体。其实在CorelDRAW软件中给用户提供方式不止是一种,本文将介绍更多关于设置默认字体的方法。1. 打开CorelDR…

javaone_代理的JavaOne 2016观察

javaone我无法参加JavaOne 2016,因此很高兴看到在线资源众多,使我能够基于JavaOne 2016内容进行观察。 我在本文中引用并简要描述了其中的一些JavaOne 2016资源,并根据这些资源的使用添加了一些我自己的观察结果。 正如Katharine在JavaOne综述…

组合商品和商品套餐(套装)的设计

文章目录商品套餐创建商品套餐活动的交互设计组合商品创建组合商品的交互设计商品套餐 商品套餐设计成一种促销活动,活动结束时间不设置表示“长期有效”商品套餐是指SKU和SKU的组合套餐,例如:iphone 6s 数据线 白色iphone 6s 充电器 白色&a…

antlr 4.7.1_新ANTLR 4.6的重要更改

antlr 4.7.1自上一个主要版本发布以来,已经过去了将近一年的时间,推出了新的ANTLR版本: 4.6 。 有很多新闻:新的目标,更好的性能,更好的错误处理以及ANTLR本身开发中的一些改进。 新目标 影响最大的新闻可…

电商系统下单时商品库存和销售状态如何处理

文章目录查看购物车时商品库存问题商品被下架了如何处理价格变动了如何处理促销活动到期了如何处理提交订单时库存问题商品被下架了如何处理价格变动了如何处理促销活动到期了如何处理使用的优惠券到期了如何处理选择商品时库存问题商品被下架了如何处理价格变动了如何处理促销…

json怎么读取数据库_如何:使用Json插入数据库并从中读取

json怎么读取数据库在本文中,我们将为Speedment创建一个插件,该插件使用Gson生成序列化和反序列化逻辑,从而使其在数据库实体和JSON字符串之间进行映射非常容易。 这将有助于展示Speedment代码生成的可扩展性,同时探索Gson库的一些…

django连接mysql步骤_使用Django连接Mysql数据库步骤

链接mysql步骤第一步:在终端下载pymysql文件–pip install pymysql第二步:在gjango项目的__init__文件中添加代码import pymysqlpymysql .install_as_MySQLdb()第三步:找到mysql的连接源,然后填信息,如果没有mysql驱动…

HH SaaS电商系统的商品发货策略设计

什么是发货策略 用来指定商品的发货仓库的策略方案,我们在实际的业务开展过程中,同种商品不同的销售渠道也许发货仓库不同,同种商品同个销售渠道在不同的业务开展阶段中也许发货仓库不同,所以需要通过灵活配置发货策略来满足此需…

mysql 上一篇_mysql取上一篇和下一篇的查询

$id 为当前文章 ID获取文章上一篇文章:SELECT id FROM table WHERE id>$id ORDER BY id ASC LIMIT 1获取文章下一篇文章:SELECT id FROM table WHERE id查询思路:获取与该文章同表相连的文章,如果根据分类获取相应的关联文章&a…

jhipster_JHipster入门,第3部分

jhipster欢迎回到本JHipster教程系列! 在第一部分中,我们介绍了如何创建整体应用程序。 在第二部分中 ,我们逐步创建了一个微服务应用程序(这有点复杂)。 对于那些正在努力使JHipster正常运转的人,我想着重…

什么是UID、UED、UXD、IXD、UCD、IAD,看这篇就足够了

文章目录UID: User Interface Design 用户界面设计UI:User Interface(用户界面)UID(用户界面设计师)UID认知现状UED: User Experience Design 用户体验设计UE or UX: User Experienc…

jhipster_JHipster入门,第2部分

jhipster所以你回来了! 在本系列的最后一部分中 ,我们采用了单片路线创建了一个JHipster应用程序。 这是红色药丸路线; 生活几乎与您习惯的一样。 但是也许您喜欢挑战。 也许您想超越红色药丸并尝试蓝色药丸。 在这种情况下,Blue…