MyCat在逻辑上由几个模块组成: 通信协议、路由解析、结果集处理、数据库连接、监控等模块。如图所示:
BIO(同步阻塞I/O) 通常由一个单独的Acceptor线程负责监听客户端的连接, 接收到客户端的连接请求后, 会为每个客户端创建一个新的线程进行处理, 处理完成之后, 再给客户端返回结果, 销毁线程 。
每个客户端请求接入时, 都需要开启一个线程进行处理, 一个线程只能处理一个客户端连接。 当客户端变多时,会创建大量的处理线程, 每个线程都需要分配栈空间和CPU, 并且频繁的线程上下文切换也会造成性能的浪费。所以该模式, 无法满足高性能、高并发接入的需求。
NIO(同步非阻塞I/O)基于Reactor模式作为底层通信模型,Reactor模式可以将事件驱动的应用进行事件分派, 将客户端发送过来的服务请求分派给合适的处理类(handler)。当Socket有流可读或可写入Socket时, 操作系统会通知相应的应用程序进行处理, 应用程序再将流读取到缓冲区或写入操作系统。 这时已经不是一个连接对应一个处理线程了, 而是一个有效的请求对应一个线程, 当没有数据时, 就没有工作线程来处理。
NIO 的最大优点体现在线程轮询访问Selector
, 当read
或write
到达时则处理, 未到达时则继续轮询。
AIO,全程 Asynchronous IO(异步非阻塞的IO), 是一种非阻塞异步的通信模式。在NIO的基础上引入了新的异步通道的概念,并提供了异步文件通道和异步套接字通道的实现。AIO中客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。
AIO与NIO的主要区别在于回调与轮询, 客户端不需要关注服务处理事件是否完成, 也不需要轮询,只需要关注自己的回调函数。
在MyCat
中实现了NIO
与AIO
两种I/O模式
, 可以通过配置文件server.xml
进行指定 :
<property name="usingAIO">1</property>
usingAIO
为1代表使用AIO模型 , 为0表示使用NIO模型;
MyCatStartUp
是整个MyCat服务启动的入口;MyCat
的home目录后, 把主要的任务交给MyCatServer
, 并调用其startup方法;usingAIO
的配置 , 如果配置为1, 说明使用AIO模型 , 进入到AIO的分支, 并创建两个连接, 一个是管理后台连接(9066), 一个server的连接(8066);AIOAcceptor
接收客户端请求, 绑定端口, 创建服务端的异步Socket
;在accept方法中完成两件事: ①. FrontedConnection
的创建, 这是前段连接的关键; ②.register
注册事件, MySQL协议握手包就在此时发送;如果设置的usingAIO
为0 ,那么将走NIOAcceptor
通道 , 流程如下:
NIOAcceptor
负责处理Accept事件, 服务端接收客户端的连接事件, 就是MyCat作为服务端去处理前端业务程序发过来的连接请求, 建立链接后, 调用NIOAcceptor
的NIOReactor.postRegister方法
进行注册(并没有注解注册, 而是放入缓冲队列, 避免加锁的竞争)。MySQL协议处于应用层之下、TCP/IP之上, 在MySQL客户端和服务端之间使用。包含了链接器、MySQL代理、主从复制服务器之间通信,并支持SSL加密、传输数据的压缩、连接和身份验证及数据交互等。其中,握手认证阶段和命令执行阶段是MySQL协议中的两个重要阶段。
AuthPacket
), 该包中包含用户名、密码等信息。OKPacket
,表示登录成功,否则返回ERR_Packet
,表示拒绝。报文分析如下:
Packet Length
: 包的长度;
Packet Number
: 包的序号;
Server Greeting
: 消息体, 包含了协议版本、MySQLServer版本、线程ID和字符集等信息。
客户端在接收到服务端发来的初始握手包之后, 向服务端发出认证请求, 该请求包含以下信息(由Wireshark抓获) :
服务端接收到客户端的登录认证包之后,如果通过认证,则返回一个OKPacket,如果未通过认证,则返回一个ERROR包。
OK报文如下:
在握手认证阶段通过并完成以后, 客户端可以向服务端发送各种命令来请求数据, 此阶段的流程是:命令请求->返回结果集。
在MyCat
中同时实现了NIO
和AIO
, 通过配置可以选择NIO
和AIO
。MyCat Server
在启动阶段已经选择好采用NIO
还是AIO
,因此建立I/O
通道后,MyCat
服务端一直等待客户端端的连接,当有连接到来的时候,MyCat
首先发送握手包。
MyCat中的源码中io.mycat.net.FrontendConnection类
的实现如下:
握手包信息组装完毕后, 通过FrontedConnection
写回客户端。
客户端接收到握手包后, 紧接着向服务端发起一个认证包, MyCat封装为类 AuthPacket:
客户端发送的认证包转由 FrontendAuthenticator
的Handler
来处理, 主要操作就是拆包, 检查用户名、密码合法性, 检查连接数是够超出限制。源码实现如下:
认证失败, 调用failure方法, 认证成功调用success方法。
命令执行阶段就是SQL命令和SQL语句执行阶段, 在该阶段MyCat主要需要做的事情, 就是对客户端发来的数据包进行拆包, 并判断命令的类型, 并解析SQL语句, 执行响应的SQL语句, 最后把执行结果封装在结果集包中, 返回给客户端。
从客户端发来的命令交给 FrontendCommandHandler
中的handle方法处理:
处理具体的请求, 返回客户端结果集数据包: