MyCat通信模块

开始的地方

这是我了解MyCat开始的地方,当然现在也要回过头来再看一下,同样的,也从这里开始吧
之前看了很多资料,十分感谢前者的分享,正是他们的存在与热心分享,我才有机会去接触和学习,这里也挂一下参考资料:
一篇文章让你成为 NIO 大师 - MyCAT通信模型
数据库分库分表(Mycat等)-张哈希
本文以总结自己的理解与收获为主,难免引荐上面大佬的博文

通信执行过程

首先我要贴一张图
这里写图片描述
MyCat前端与后端都可以使用NIO和AIO进行通信,但是现在的服务器大都是使用Linux系统,而Linux不支持AIO,使用起来性能甚至远比不上NIO,所以就只从NIO通信基础出发来理解整个MyCat通信模块

这里先不讲MyCat启动的过程,后面的文章再继续谈,我们先来看看MyCat通信模块中的前端连接模块。

前端连接模块

注册连接

首先是NIOAcceptor负责处理整个前端的连接请求,这里的前端指的是应用与MyCat之间的连接请求。NIOAcceptor初始化时,会新建新的selector和serverSocketChannel,并将其绑定端口、IP等信息,然后向selector注册这个serverSocketChannel,感兴趣的事件是OP_ACCEPT;接下来NIOAcceptor由于继承了Thread类,在run方法中获取了selector的拷贝tSelector,然后调用select()方法阻塞式轮询是否有连接请求
当有连接请求时,NIOAcceptor会调用accept()方法,获取socketChannel,并设置为非阻塞,使用前端连接工厂FrontendConnection获取一个封装了的前端连接,然后将连接与socketChannel绑定,从NIOProcessor池中获取NIOProcessor,用来管理前端连接,并将从NIOReactor池中获取的NIOReactor实例与前端连接绑定,然后将连接请求交给NIOReactor去处理

前端认证

接下来看NIOReactor,NIOAcceptor将连接传递过来以后,连接实例被放到了一个ConcurrentLinkdQueue等待队列中,同时唤醒内部线程类RW,用来从队列中取出连接实例;每一个RW线程都有一个专属的selector,在run()方法中循环调用select()方法轮询连接事件;当有连接请求时,RW线程会调用register()方法去从连接队列中去除连接请求实例,再将连接中取出的NIOSocketWR的通道注册到selector上,注册的感兴趣的事件是OP_READ,随后,调用FrontendConnection中的register()方法生成握手包,MyCat与应用建立TCP连接后,发送该握手包。接着就是等待应用发回认证包,这里会调用NIOSocketWR的asyRead()f方法进行异步读;从缓冲池中获取到buffer准备接收数据,实际上还是AbstractConnection中的onReadData()方法去读连接中来自应用的数据,将应用发来的认证包读取到buffer中,再交予不同功能的handler去处理这些数据,在这里我们进行的是前端认证,所以使用FrontendAuthenticator类对象进行认证包的解析与认证,这里认证的信息是配置在server.xml中的MyCat-Server的用户信息,认证成功后,将AbstractConnection中的handler替换成FrontendCommandHandler类的对象,准备处理来自应用的命令行SQL语句;然后向应用写回OK,正式建立了前端连接;
这里写图片描述

读取前端SQL语句

接下来再回到NIOReactor中的RW线程中,run()方法注册了连接后,前面说了selector注册的是OP_READ事件,这里就会在当有数据写入到channel中且读事件就绪后,RW线程会判断一下究竟是写事件还是读事件(因为RW线程读写复用),然后调用连接实例接着AbstractionConnection真正开始读取数据,然后将数据交给FrontendCommandHandler实例来处理这些数据(命令或SQL语句)。在这里我想引入一段别人的话,我觉得说的很好,当时清除的让我认识到了一些模糊的地方:

前面反复提到一个词:连接实例,究竟什么是连接实例?客户端发往MyCAT的每一次请求,以及MyCAT发往MySQL的每一次请求,都是一个连接实例。可以把连接实例看作是一次请求事件的主干,我们都知道,NIO是一个同步非阻塞的 I/O 模型。阻塞的线程没有做任何有意义的事情,却依然消耗系统资源,这是我们不能接受的,所谓非阻塞,就是不断的在这条主干上衍生分支,来处理复杂的业务请求,这样主干就不会阻塞。而同步,是指线程不断轮询 IO 事件是否就绪,主干上衍生的这些分支,都维护了一个Selector对象,Selector代替了主干线程来执行这种轮询,包括前面讲到的acceptor和reactor;这些分支线程是以线程池的形式存在的,是可以复用的,从而减少了频繁创建、启动、挂起、析构新线程的开销,大大提升系统的并发效率。

另外这里会有一个很有意思的地方,就是MyCat会根据不同的情况,调用不同的handler来处理读取到的数据;首先从channel中读取字节流形式的数据,按照MySQL协议进行数据的拆包,判断是什么类型的信息,如果是认证信息,就让负责认证的handler来处理,如果是SQL语句,就让负责解析SQL的handler来处理,后面还会说到有不同的处理后端信息的handler,它们都来自同一个接口NIOHandler,MyCat的AbstractionConnection连接实例是前后端连接实例的抽象,所有的数据处理都是在这里读取并交给不同的handler处理,感觉这种用法很是精髓

解析SQL

当准备开始解析SQL的时候,具体的handler会开始进行一次浅解析,根据SQL语句的CURD关键词,并判断语句是否需要发送到后端,来再次精确到不同操作的handler,这里先不讲,等日后分析到MyCat的路由解析模块时再添上链接,假设现在已经选取了合适的handler,执行的细节也先不提,我们接下来看后端连接模块

后端连接模块

后端连接与前端连接绑定

后端连接实例也是通过工厂生成的,当处理SQL语句的handler需要操作后端连接实例的时候,例如查询,ServerQueryHandler中的ServerConnection会调用execute()方法,进行SQL语句的路由计算,得到RouteResultSet路由计算结果,里面包含了路由信息,接着ServerConnection中的NonBlockingSession实例会调用execute()方法,判断路由信息单节点还是多节点,然后合适的NodeHandler会判断是否在NonBlockingSession中存在后端连接,其实这个session早在前端连接由工厂方法创建的时候就已经唯一的绑定了,这个会话里保存了所有的前后端连接涉及的信息;如果session中没有后端连接或者连接已经过期,需要调用PhysicalDBPoolPhySicalDataSource到具体的某个数据源中去获取连接,这里会涉及到负载均衡和writeHost以及readHost,后端连接MySQLConnection是由MySQLConnectionFactory的make()方法创建,这里会配置连接的端口、IP、用户、密码等,然后设置AbstractConnecion中的handler为MySQLConnectionAuthenticator,准备后面的后端认证;然后将该连接实例交给NIOConnector去注册和认证

后端认证

在继承了Thread类的NIOConnector中,同样会将连接放到一个缓冲队列中,然后唤醒selector,注册OP_CONNECT事件,然后连接中的socketChannel主动向MySQL发起连接,然后在run()方法中将后端连接实例与NIOProcessor实例绑定在一起(NIOProcessor负责管理前后端连接),然后将socketChannel交给NIOReactor去注册,像前端连接注册一样;放到连接队列中,唤醒selector,监听读事件,然后调用AbstractConnection的异步读,这是因为连接到MySQL实例时,MySQL会主动发回握手包;接下来会调用后端验证的handler去处理读取的数据;握手成功后MyCat发送认证数据,这些也配置在schame.xml中数据源实例中,发送数据是将数据存储在buffer中,然后放到写缓冲队列中,调用NIOSoketWR的doNextWriteCheck()方法,使用CAS获取写锁,然后将缓冲队列中的数据一次性全部写入到socketChannel中;如果没有写完或者是又有新的buffer放入到写缓冲队列中,则继续注册写事件,否则取消注册写事件;然后再回到NIOReactor中,因为已经注册了读事件且需要读取MySQL实例返回的OK_Packet,这时AbstractConnection中的handler已经又换成了MySQLConnectionHandler,用来处理MySQL返回的结果集

后端发送SQL语句

得到合适的认证过的后端连接后会调用PhysicalDataSource中的takeCon()方法,然后会将之前传入session设置为后端连接实例的回调,将前端连接和后端连接绑定在了一起,这一步很重要,后短链接需要来自前端连接的SQL,前端连接需要来自后端的结果集,当然了这里的前后端连接都是不可以复用的,长时间不使用会被特定的线程清除;
session获取了具体的经过认证的后端连接实例后,实际会由MySQLConnection调用execute()方法,接下来是真正处理SQL语句的地方,已经经过路由计算的SQL语句是被加工过的,会按照MySQL协议封装成数据包,然后放到写缓冲队列中,接下来像验证时一样,写完数据,便等待MySQL返回结果,并异步读取结果交给响应的MySQLConnectionHandler处理;

后端接收MySQL返回的结果集

MyCat接收到返回的结果集之后,根据结果集的内容分为三个阶段进行解析,具体的过程这里不细说,处理之后的数据交给了SingleNodeHandler,准备将其交给前端连接;这里并没有直接将数据传给前端连接,而是通过以二进制包的形式写入到buffer中,再放入到写缓冲队列,由前端连接将结果的二进制包写到socketChannel中,返回给应用;至此,整个MyCat的通信模块流程已经走了一遍

MyCat的NIO通信模型

查资料时,看到了一张图,感觉很是到位
这里写图片描述
此之谓Reactor模型,和AIO对应的是Proactor模型,这种实现了真正的异步模式,但是我们在这里只分析NIO的情况

一点小的感想

分布式数据库中间件MyCat作为一款开源的分布式组件,具有高性能、高可用、高并发的特点,基于Cobar但是更胜于闭源前的Cobar,和HotDB一样都是Cobar的衍生产品。它是我学习分布式数据库的入门,更是我了解分布式技术的入门,从这里开始我仿佛也进入了新世界,不再将眼光局限于Java的后台开发,因为有更好玩更有意思的东西,更新潮更牛逼的技术。

有时想想,原理很简单却又很巧妙,是这些精心雕琢的产品才造就了Java开发的常青以及盛世,虽然近年来AI是热门,但是我还是更倾心于这种基础组件,就是因为它们是基础,是可以作为任何技术和应用的核心。也正是因为它们的不可替代性,让我更加对阿里巴巴这家公司的技术充满了向往。

之前看过的一本书《淘宝技术这十年》,当时对一些技术不够了解,并没有觉得多么称奇,现在看来,那些技术无愧于伟大二字,他们那些大牛承担了从无到有的工作,所以才有了支撑淘宝海量业务的强大组件,有了Cobar,有了MyCat,有了分布式数据库现在百花盛开的场面。今天刚看到的消息,华为也推出了他们的分布式数据库中间件DDM,可以遇见的是在未来,分布式技术必将长盛不衰。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值