「从零单排canal 05」 server模块源码解析(一)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 「从零单排canal 05」 server模块源码解析(一)

本文将对canal的server模块进行分析,跟之前一样,我们带着几个问题来看源码:


  • CanalServer有几种使用方式?
  • 控制台Admin、客户端client是如何与CanalServer交互的?
  • CanalServerWithNetty和CanalServerWithEmbedded究竟有什么关系?
  • Canal事件消费的特色协议,异步流式api(get/ack/rollback协议)的设计是如何实现的?


server模块内的结构如下:


107.jpg


主要分为了三个包:


  • admin包:


这个包的CanalAdmin接口定义了canalServer上暴露给canal-admin控制台使用的一些服务接口。


上一篇deployer模块解析中提到的CanalAdminController就是实现了CanalAdmin接口(把这个接口的实现放在deployer模块是挺奇怪的)。Admin包中使用了netty作为服务端(CanalAdminWithNetty类中实现),接受控制台Admin的请求,返回当前canalServer的一些运行状态。


  • server包:


server模块的核心包,本文重点解析的部分,需要了解CanalServerWithEmbedded 和CanalServerWithNetty。


  • spi包:


定义了canalServer的监控内容 通过spi实现,比如项目中的Prometheus子模块实现了监控能力,我们不展开分析。


1. 从CanalServer的架构说起


CanalServer目前支持两种模式:


  • serverMode = tcp的Server-Client模式
  • serverMode = kafak 或 rocketMQ 的 Server-MQ-Client模式

为了大家能充分理解canalServer的结构,这里精心制作了一个canalServer的架构图(如果觉得这图不错,给本文点个赞吧)。


1.1 Server-Client模式


架构如图所示:

108.jpg


我们可以清楚的看到Server模块中各个模块的关系与能力:


  • CanalServerWithEmbedde维护了具体的instance任务,负责对binlog进行订阅、过滤、缓存,就是之前的文章介绍过的parser-sink-store的方式。
  • CanalServerWithNetty作为服务端,接收CanalClient的请求,将binlog的消息发送给client。
  • CanalAdminWithNetty作为admin的服务器,接收控制台Admin的控制操作、查询状态操作等,启停或显示当前CanalServer以及instance的状态。

1.2 Server-MQ-Client模式


架构如图所示:

109.jpg


主体部分与Server-client模式一致,主要区别如下:


  • 不需要CanalServerWithNetty,改为CanalMQProducer投递消息给消息队列。
  • 不使用CanalClient,改为MqClient获取消息队列的消息进行消费。

这种模式相比于Server-client模式

  • 下游解耦,利用消息队列的特性,可以支持多个客户端广播消费、集群消费、重复消费等。
  • 会增加系统的复杂度,增加一些延迟。

具体模式的选择,需要根据具体的使用场景来决定。


2.server包


admin包和spi包都不属于核心逻辑,因此我们重点关注server包的代码。

110.jpg


我们看到,server包下面分为了embedded包、exception包、netty包和几个接口类。


其中,最顶层的设计就要从CanalServer接口入手。

111.jpg


它的实现类有两个,CanalServerWithEmbedded 和 CanalServerWithNetty。


它们之间的区别官方文档给了一些说明。

112.jpg


那么,对于官方文档中提到的Embedded(嵌入式)的自主开发是怎么使用呢?


跟我们上面提到的Server-Client模式和Server-MQ-Client模式完全不同,采用了一种无server的架构,如下图所示。

113.jpg


我们可以看到,这种模式没有了Canal-Server,直接在自己的应用中引入canal,然后使用CanalServerWithEmbedded进行数据抓取和订阅。


当然,这种方式开发成本有点高,一般也不会去这样使用。


对于CanalServerWithEmbedded 和 CanalServerWithNetty,官方文档里面实际上没有解释的特别到位,只讲了区别,没有讲联系。


这两个实现类除了官方文档中说明的区别之外,还有很大的联系。


可以看看我们上文介绍的架构图,对于Server-Client模式下的模块联系

114.jpg


实际上,真正的执行逻辑是在CanalServerWithEmbedded中的,CanalServerWithNetty中持有了CanalServerWithEmbedded对象,委托embedded进行相关逻辑处理,CanalServerWithNetty更多的作用是充当服务端与CanalClient进行交互。


3. CanalServerWithNetty类


下面,我们先看看CanalServerWithNetty类。


3.1 单例构建

使用 private构造器 + 静态内部类 来实现一个单例模式,保证了一个CanalServer内部只有一个CanalServerWithNetty。


同时,我们能看到内部持有一个CanalServerWithEmbedded对象,用来处理相关请求,验证了我们上面的说明。

115.jpg


3.2 启动逻辑 start()


源码如下:

116.jpg


主要流程如下:


  • 启动embeddedServer。
  • 创建bootstrap实例,设置netty相关配置。


参数NioServerSocketChannelFactory也是Netty的API,接受2个线程池参数,第一个线程池是Accept线程池,第二个线程池是woker线程池,Accept线程池接收到client连接请求后,会将代表client的对象转发给worker线程池处理。这里属于netty的知识,不熟悉的可以暂时不必深究,简单认为netty使用线程来处理客户端的高并发请求即可。


  • 构造对应的pipeline,包括解码处理、身份验证、创建netty的 seesionHandler(真正处理客户端请求,seesionHandler的实现是核心逻辑)。


pipeline实际上就是netty对客户端请求的处理器链,可以类比JAVA EE编程中Filter的责任链模式,上一个filter处理完成之后交给下一个filter处理,只不过在netty中,不再是filter,而是ChannelHandler。


  • 启动netty,监听port端口,然后客户端对 这个端口的请求可以被接收到

对于 netty的相关知识 ,本文 不深入展开,简单理解 为一个高性能服务器即可,可以监听 端口请求,并 进行相应的处理。

重点在于sessionHandler的处理。

3.3 逻辑分发SessionHandler类

canalServer的处理逻辑显然都在sessionHandler里面,而这个handler在构建时,传入了embeddedServer。

前面我们提过,serverWithNetty的处理逻辑是委派给embeddedServer的,所以这里就非常顺理成章了,让handler维护embeddedServer实例,进行逻辑处理。

sessionHandler继承了netty的SimpleChannelHandler类,重写了messageReceived方法,接收到不同请求后,委托embeddedServer用不同方法进行处理 。

这个方法里面的代码非常冗长,而本质都是委托给embeddedServer去处理,因此,我们看下主干逻辑即可。

117.jpg


可以看到,根据不同的packet类型,最终都是委托给embeddedServer进行处理,这里只是做一个逻辑的判断和分发。


3.4 CanalServerWithNetty小结


到此,我们已经了解了CanalServerWithNetty是如何启动的。


并且,它的主要定位就是充当服务器,接收客户端的请求,然后做消息分发,委托给CanalServerEmbedded进行处理。


下面,我们来看下CanalServerEmbedded的相关实现。

目录
相关文章
|
10天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
39 2
|
11天前
|
存储 安全 Linux
Golang的GMP调度模型与源码解析
【11月更文挑战第11天】GMP 调度模型是 Go 语言运行时系统的核心部分,用于高效管理和调度大量协程(goroutine)。它通过少量的操作系统线程(M)和逻辑处理器(P)来调度大量的轻量级协程(G),从而实现高性能的并发处理。GMP 模型通过本地队列和全局队列来减少锁竞争,提高调度效率。在 Go 源码中,`runtime.h` 文件定义了关键数据结构,`schedule()` 和 `findrunnable()` 函数实现了核心调度逻辑。通过深入研究 GMP 模型,可以更好地理解 Go 语言的并发机制。
|
23天前
|
消息中间件 缓存 安全
Future与FutureTask源码解析,接口阻塞问题及解决方案
【11月更文挑战第5天】在Java开发中,多线程编程是提高系统并发性能和资源利用率的重要手段。然而,多线程编程也带来了诸如线程安全、死锁、接口阻塞等一系列复杂问题。本文将深度剖析多线程优化技巧、Future与FutureTask的源码、接口阻塞问题及解决方案,并通过具体业务场景和Java代码示例进行实战演示。
40 3
|
1月前
|
存储
让星星⭐月亮告诉你,HashMap的put方法源码解析及其中两种会触发扩容的场景(足够详尽,有问题欢迎指正~)
`HashMap`的`put`方法通过调用`putVal`实现,主要涉及两个场景下的扩容操作:1. 初始化时,链表数组的初始容量设为16,阈值设为12;2. 当存储的元素个数超过阈值时,链表数组的容量和阈值均翻倍。`putVal`方法处理键值对的插入,包括链表和红黑树的转换,确保高效的数据存取。
56 5
|
1月前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
113 5
|
1月前
|
XML Java 数据格式
Spring底层架构源码解析(二)
Spring底层架构源码解析(二)
|
1月前
|
网络协议 定位技术 Windows
Windows Server 2019 DNS服务器搭建
Windows Server 2019 DNS服务器搭建
|
1月前
|
JSON 前端开发 JavaScript
前端模块打包器的深度解析
【10月更文挑战第13天】前端模块打包器的深度解析
|
1月前
|
缓存 前端开发 JavaScript
Webpack技术深度解析:模块打包与性能优化
【10月更文挑战第13天】Webpack技术深度解析:模块打包与性能优化
|
1月前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
70 0
下一篇
无影云桌面