SOFA BOLT源码解析之设计要点-网络IO

版权声明: https://blog.csdn.net/beyondself_77/article/details/81025740

1  设计要点解析

        SOFA Bolt作为基础通信层,其功能在设计和实现的过程中,主要从以下几个方面考虑:

        1.   网络IO;

        2.   线程模型;

        3.   通信模型;

        4.   连接管理;

        5.   批量解包和批量提交;

        6.   事件触发和监听机制;

        7.   全双工通信;

        以下分别从服务端和客户端两个角度,分别看一下SOFA Bolt类图:

        1.  SOFA Bolt服务端类图:

        2.  SOFA Bolt客户端类图:

 

        下面从基础通信模型设计过程中的关键点,分别说明上述类图的各个部分。

1.1  网络IO

        在进行基础通信功能设计时,需要了解一下网络IO,以便我们能更好地选择IO模型,并根据所选的IO模块,优化操作系统,提高IO效率。

        此处以使用范围比较广的Linux内核为例,简单介绍一下IO模型,以便大家更好地进行基础通信功能的实现。

        此部分内容来源于网上的一篇关于网络IO的介绍:

        文章名称为:java nio及操作系统底层原理;

        链接地址为:http://blog.csdn.net/u014507083/article/details/73784898

        这篇文章以通俗易懂、简练的语言,简单的介绍了网络IO的概念,以及Java对于各种IO模型的支持,本人认为作为网络IO知识的普及文章,写的非常好,所以稍作修改,然后直接采用了其部分内容,在此对于作者表示感谢。

1.1.1  LinuxIO

        Linux内核将所有外部设备都可以看做一个文件来操作。

        我们对外部设备的操作都可以看做对文件进行操作。

        通过调用内核提供的系统调用函数(如:read),实现对一个文件的读写,内核返回给我们一个fd (file descriptor,文件描述符)。对一个socket的读写也会有相应的描述符,称为socketfd(socket描述符)。描述符就是一个数字(可以理解为一个索引),指向内核中一个结构体(文件路径,数据区,等一些属性)。应用程序对文件的读写就通过对描述符的读写完成。

        一个基本的IO,它会涉及到两个系统对象,一个是调用这个IO的进程,另一个就是系统内核(kernel)。当一个read操作发生时,它会经历两个阶段:发起IO请求(第1-3步)和实际的IO操作(第4步)。

        Read操作的简单过程如下:

        1.   通过read系统调用向内核发起读请求;

        2.   内核向硬件发送读指令,并等待读就绪;

        3.   内核把将要读取的数据复制到描述符所指向的内核缓存区中;

        4.   将数据从内核缓存区拷贝到用户进程空间中。

        在linux系统下面,根据IO操作的是否被阻塞以及同步异步问题进行分类,可以得到下面五种IO模型:   

同步

IO Multiplexing(select/poll/epoll)

阻塞

非阻塞

异步

Linux

Windows

.NET

AIO

IOCP

BeginInvoke/EndInvoke

         1.  阻塞I/O模型

        最常见的I/O模型是阻塞I/O模型。缺省情形下,所有文件操作都是阻塞的。

        以socket为例来简述阻塞IO模型:

        在进程空间中调用recvfrom,其系统调用直到数据报到达,且被拷贝到应用进程的缓冲区中或者发生错误才返回,期间一直在等待。

        可以看出,应用进程从系统调用recvfrom开始,到它返回的整段时间内是被阻塞的。

        2.  非阻塞I/O模型

        应用进程通过把一个socket设置成非阻塞来通知内核:当所请求的I/O操作不能满足要求时候,不要让本进程进入睡眠状态,而是返回一个错误,即当数据没有到达时并不等待,而是返回一个错误。

        3.  I/O复用模型

        Linux提供了select/poll复用模型。应用进程通过将一个或多个fd传递给select或poll系统调用,阻塞在select。这样,select/poll负责侦测注册的多个fd是否就绪。但是,select/poll是顺序扫描fd是否就绪,而且支持的fd数量有限。

        linux还提供了epoll系统调用,epoll是基于事件驱动方式,而不是顺序扫描,当有fd就绪时,立即回调函数rollback。

        4.  信号驱动异步I/O模型

        首先,开启socket信号驱动I/O功能, 并通过系统调用sigaction安装一个信号处理函数(此系统调用立即返回,进程继续工作,它是非阻塞的)。当数据报准备好被读时,就为该进程生成一个SIGIO信号。随即可以在信号处理程序中调用recvfrom来读数据报,并通知主循环数据已准备好被处理中。也可以通知主循环,让它来读数据报。

        5.  异步I/O模型

        告知内核启动某个操作,并让内核在整个操作完成后(包括将数据从内核拷贝到用户自己的缓冲区)通知我们。这种模型与信号驱动模型的主要区别是:信号驱动I/O:由内核通知我们何时可以启动一个I/O操作;异步I/O模型:由内核通知我们I/O操作何时完成。

        上述通过对5种IO模型的简单介绍,我们应该比较清楚的了解到,一个基本IO操作分成两个步骤:发起IO请求和实际的IO操作。

        阻塞IO和非阻塞IO的区别在于第一步,发起IO请求是否会被阻塞。如果阻塞直到完成那么就是传统的阻塞IO,如果不阻塞,那么就是非阻塞IO。

        同步IO和异步IO的区别就在于第二步,实际IO操作是否会被阻塞。如果实际的IO读写阻塞请求进程,那么就是同步IO,因此阻塞IO、非阻塞IO、IO复用、信号驱动IO都是同步IO。如果不阻塞,而是操作系统帮你做完IO操作,然后再将结果返回给你,那么就是异步IO。

        总之,前四种都是同步IO,在内核数据拷贝到用户空间时都是阻塞的。最后一种是异步IO,通过API把IO操作交由操作系统处理,当前进程不关心具体IO的实现,通过回调函数,或者信号量通知当前进程直接对IO返回结果进行处理。

1.1.2  Java对BIO、NIO、AIO的支持

        1.  Java BIO:

        BIO属于同步、阻塞IO。

        服务器实现模式为一个连接一个线程,即客户端有连接请求时,服务器就需要启动一个线程进行处理。如果这个连接不做任何事情会造成不必要的线程开销,可以通过线程池机制改善。

        2.  Java NIO:

        NIO属于同步、非阻塞IO。

        服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理(底层是epoll)。

        3.  Java AIO(NIO.2):

        AIO属于异步、非阻塞IO。

        服务器实现模式为一个有效请求一个线程,即客户端的I/O请求都是由OS先完成,然后再通知服务器应用去启动线程进行处理。

1.1.3  AIO、BIO、NIO适用场景

        AIO异步非阻塞IO,AIO方式适用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。

        NIO同步非阻塞IO,适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。

        BIO同步阻塞IO,适用于连接数目比较少且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。

1.1.4 总结

        基于上述网络IO的介绍,大家应该可以很容易想到SOFA Bolt采用的IO模型:NIO。

        SOFABolt作为蚂蚁中间件产品的基础通信层,主要应用于连接数量较大,并且连接比较短的WEB环境,所以采用同步非阻塞的NIO比较适合。


本系列文章目录:

SOFA BOLT源码解析之概述

SOFA BOLT源码解析之设计要点-网络IO

SOFA BOLT源码解析之设计要点-线程模型

SOFA BOLT源码解析之设计要点-通信模型

SOFA BOLT源码解析之设计要点-连接管理

SOFA BOLT源码解析之设计要点-通信协议


猜你喜欢

转载自blog.csdn.net/beyondself_77/article/details/81025740