开发者学堂课程【全面讲解开源数据库中间件MyCat使用及原理(三):MyCat - 架构剖析 - 网络 IO 架构 】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/757/detail/13294
MyCat - 架构剖析 - 网络 IO 架构
内容介绍:
一、BIO、NIO 与 AIO 的概念
二、实现
一、回顾 BIO、NIO 与 AIO 的概念
1) . BIO
BIO (同步阻塞 I/O )是我们比较熟悉的一个模型,在讲解 TCP/IP 以及网络编程时使用 BIO,由每一个客户端建立连接,然后服务端为客户端创建一个新的线程来处理请求。
当请求处理完后该线程就会销毁。意味着一个线程只能处理一个客户端的连接,处理完后就会销毁。此时系统中会创建大量的处理线程,每个线程又需要分配一定的空间以及 CPU,而且多个线程在进行切换时线程的上下文切换会造成性能浪费。
总结:通常由一个单独的 Acceptor 线程负责监听客户端的连接,接收到客户端的连接请求后,然后会为每个客户端创建一个新的线程进行处理,处理完成之后,再给客户端返回结果,销毁线程。
每个客户端请求接入时,都需要开启一个线程进行处理,一个线程只能处理一个客户端连接。当客户端变多时,会创建大量的处理线程,每个线程都需要分配栈空间和 CPU,并且频繁的线程上下文切换也会造成性能的浪费。所以该模式,无法满足高性能、高并发接入的需求。
2) . NIO
NIO (同步非阻塞 I/O )基于 Reactor 模式作为底层通信模型,机制基于事件驱动,当触发了服务器端的某一个 I/O 事件后就会对该事件进行处理,将客户端发送过来的请求再派发给合适的处理类 handler,通过 handler 进行处理,此时有一个特点:对于 NIO 来说,这并不是一个客户端连接后就进行分配一个线程,而是一个有效的请求对应一个线程,当没有数据时,就没有工作线程。
总结:Reactor 模式可以将事件驱动的应用进行事件分派,将客户端发送过来的服务请求分派给合适的处理类( handler )。当 socket 有流可读或可写入 socket 时,操作系统会通知相应的应用程序进行处理,应用程序再将流读取到缓冲区或写入操作系统。这时已经不是一个连接对应一个处理线程了,而是一个有效的请求对应一个线程,当没有数据时,就没有工作线程来处理。
NIO 的最大优点体现在线程轮询访问 Selector,当 read 或 write 到达时则处理,未到达时则继续轮询。
NIO 相对于 BIO 工作性能大大提升,所以 NIO(同步非阻塞 I/O )在很多通信框架中都会使用到,比如阿里巴巴的框架。
3) . AIO
AIO,全程 Asynchronous IO (异步非阻塞的 IO ),是一种非阻塞异步的通信模式。在 NIO 的基础上引入了新的异步通道的概念,并提供了异步文件通道和异步套接字通道的实现。AIO 中客户端的 I/O 请求都是由 os 先完成了再通知服务器应用去启动线程进行处理。
AIO 与 NIO 的主要区别在于回调与轮询,AIO 的机制客户端不需要关注服务处理事件是否完成,也不需要轮询,只需要关注自己的回调函数。
二、实现
以上简单介绍 BIO、NIO、AIO 三种模型,也进行了简单的性能对比,那么在mycat 中选用哪种模型呢?
在 Mycat 中实现了 NIO 与 AIO 两种 I/O 模式,在使用 mycat 时可以通过配置文件 server.xml 进行指定选用 NIO 还是 AIO:
<property name="usingAIO">1</property>
加上如上配置,usingAIO 为1 代表使用 AIO 模型,为 0 表示使用 NIO 模型; mycat 在启动时就会读取该值来选择到底使用哪个模型。
接下来通过跟踪源码的方式来了解 mycat 的 AIO 以及 NIO 架构和流程。
先打开我们的源码,导入进的源码中有 src 源码目录,src 下的 main 下的 java 就是核心源码
其中存在一个入口类 MycatStartup,打开 MycatStartup 会发现存在一个 main 方法:
public static void main(String[] args){
zkConfig-getInstance ( ) .initZk ( ) ;
try {
String home = SystemConfig . getHomePath();
if( home == null) {
system.out.println (SystemConfig.SYS_HOME + " is not set." );
system.exit( status: -1) ;
}
Mycatserver server = Mycatserver.getInstance ( ) ;
//这个方法执行的代码,上面 systemConfig. getHomePath()
已经执行过了,建议注释掉。/ / server.beforeStart();
server.startup();
System.out.println(“MyCAT Server startup successfully.see logs in logs/mycat.log”);
}
对于 java 程序,任何入口都是 main 方法。
String home = SystemConfig . getHomePath()
拿到 mycat 的安装目录,Mycatserver server = Mycatserver.getInstance ( )
初始化一个mycatserver,然后调用一个 startup 方法。
点击 startup,在该方法中调用了如下:
aio=(system.getUsingAIO() ==1)
该配置是在 server.xml 中配置,拿到 aio 后往下存在一个if判断:是否是 AIO 的 IO 模型
if(aio) 中有一系列操作,其中:
manager = new AIOAcceptor( name:NAME+“Manager",system.getBindIp(),
system.getManagerPort(),mf,this.asyncChannelGroups[0] ) ;
//startup server
server =new AIOAcceptor ( name:NANE + "Server",system.getBindIp(),
system. getserverPort (), sf,this.asyncchannelGroups[0]);
new 了一个 AIOAcceptor,manager 是供后台的一个管理界面,AIOAcceptor 就是进去的服务,构造了这两个 AIOAcceptor 后往下看,会有 manager.start(); 和 server.start(); 就是调用上述代码的一个 start 方法
当我们点击 manager.start() 方法时,进入后点击 void start() 接口选择AIOAcceptor(io.mycat.net) Mycat-server。
之后代码中存在一个 this.pendingAccept,点击后会看到
private void pendingAccept () {
if (serverChannel.isOpen ( ) ){
serverChannel.accept ( ID_GENERATOR. getId(),handler: this);
}else {
throw new 工llegalstateException (
""MycaT Server Channel has been closed");
代码中有一个 accept,再点击,我们需要找到它的实现,可以看到它又调用了implAccept 抽象方法,继续找实现,调用了 invokeIndirectly 方法,
点击后选择
invokeIndirectly(AsynchronousChannel,CompletionHandler
(rt.jar),
进入后发现调用了 invokeUnchecked 方法,
继续点击就会找到
AIOAcceptor.completed(AsynchronousSocketChannel,Long)(io.mycat.net)
,点进后会发现调用了 accept 方法,再点击后就跟踪到了AIOAcceptor.java 中的 accept 方法。
在该方法中就会接受请求,而在此方法中又调用了 factory 方法,传递了 channel对象,创建一个连接对象 FrontendConnetion,如下:
FrontendConnection c = factory.make (channel) ;
c.setAccepted (true) ;
c.setId (id);
再设置一系列参数,再往下有 NIOAProcessor,又调用了一个方法 register:
NIOProcessorprocessor=MycatServer.getInstance ( ) .nextProcessor();
c.setProcessor (processor) ;
c.register() ;
那么 register 在此处需要注册什么呢?进行点击发现任务是需要发送数据包,发送认证的数据包,MySQL 握手协议包就在此时发送。此处内容涉及到以后内容Mycat 实现 MySQL 协议。
register 处的代码就是通过 java 在模拟 mysql 协议。
再回到最初,我们进入的是 if,接着来看 else,如果不是 AIO,就会走 else,在下面创建的就是 NIOAcceptor。
以上就是提到的在 mycat 中的 IO 架构,都是可以自己进行选择。选择 IO 架构只需要在 server.xml 中配置一项 property,设置为 1 代表 AIO,0 为 NIO。Mycat中支持的 IO 模型就是 AIO 或 NIO。