• Netty


    Linux下实现的IO模型:

      Linux的内核将所有外部设备都看做一个文件来操作,对一个文件的读写操作会调用内核提供的系统命令,返回一个file descriptor(fd,文件描述符)。对一个socket

    的读写也会有相应的描述符,称为socket描述符。描述符就是一个数字,它执行内核中的一个结构体(文件路径,数据区等一些属性)。

    因为程序运行在操作系统上,编程语言实现的IO操作API最终依赖于操作系统的IO实现。先理清阻塞、非阻塞、同步、异步这几个概念:

      阻塞:调用方发起调用请求,在没有返回结果之前,调用方线程被挂起,出于一直等待状态

      非阻塞:与阻塞相对,调用方发起调用请求,当前线程不会等待挂起,而会立即返回。后续可以通过轮询等手段来获取调用结果状态。

      同步:所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回

      异步:与同步相对,当一个异步过程调用发出后,调用者不会立刻得到结果,通过回调等措施来处理这个调用。

     Linux实现了5中IO模型

      1、阻塞IO模型

        默认情况下,所有的文件操作都是阻塞的,在进程空间中调用recvform(recvform函数,用于从Socket套接口上接收数据),其系统调用直到数据包到达且被

        复制到应用进程的缓冲区中或者发生错误才返回,在此期间会一直等待,进程在从调用recvform开始到它返回的整段时间内都是被阻塞的。

      2、非阻塞IO模型

        recvform从应用层到内核的时候,如果该缓冲区没有数据的话,就直接返回一个EWOULDBLOCK错误,一般都是对非阻塞IO模型进行轮询来检查这个状态,看

        内核是不是有数据到来。

      3、IO复用模型

        Linux提供select/poll,进程通过将一个或多个fd传递给select或者poll系统调用,阻塞在select操作上,这样select/poll可以帮我们侦测多个fd是否处于就绪状态。

        select/poll是顺序扫描fd是否就绪,而且支持的fd数量有限,因此它的使用受到了一些制约。Linux还提供了一个epoll系统调用,epoll使用基于事件驱动的方式

        代替顺序扫描,因此性能更高。当有fd就绪时,立即回调函数rollback。

        (Java核心类库Selector就是基于epoll的多路复用技术实现)

      4、信号驱动IO模型

        首先开启套接口信号驱动IO功能,并通过系统调用sigaction执行一个信号处理函数(此系统调用立即返回,进程继续工作,它是非阻塞的)。当数据准备就绪时,

        就为该进程生成一个SIGIO信号,通过信号回调通知应用程序调用recvform来读取数据,并通知主循环函数处理数据。(好了告诉我,我来处理,我先去忙)

      5、异步IO

        告知内核启动某个操作,并让内核在整个操作完成后(包括将数据从内核复制到用户自己的缓冲区)通知我们。与信号驱动模型的主要区别是:信号驱动IO是由内

        核通知我们何时可以开始一个IO操作;异步IO模型由内核通知我们何时已经完成。(等处理好了再告诉我)

      IO多路复用技术:在IO编程过程中,当需要同时处理多个客户端接入请求时,可以利用多线程或者IO多路复用技术进行处理。IO多路复用技术通过把多个IO的阻塞到

        同一个select的阻塞上,从而可以使系统在单线程的情况下同时处理多个客户端请求。而且与传统的多线程模型比,最大的优势是开销小,系统不需要创建新的额

        外进程或这线程,也不需要维护这些进程和线程的运行,降低了系统的维护工作量,节省了系统资源。

        IO多路复用的主要应用场景如下:

        1、服务器需要同时处理多个出于监听状态或者多个连接状态的套接字

        2、服务器需要同时处理多种网络协议的套接字

        epoll作为select的替代者,作了很大改进,如:

        1、支持一个进程打开的socket描述符(FD)不受限制(仅受限于操作系统的最大文件句柄数,如在1G内存的机器上是10万个左右),

          而select室友限制的,默认是1024

        2、IO效率不会随着FD数目的增加而线性下降。select/poll每次调用由于都会线性扫描全部的集合,导致效率呈线性下降。而epoll是根据每个fd上的callback

          函数实现的,只有活跃的socket才会去主动调用callback函数,其他idle状态的socket则不会

        3、使用mmap加速内核与用户的消息传递,epoll通过内核和用户空间mmap同一块内存来实现,避免了不必要的内存复制

        4、epoll的API更加简单

    Java的IO演进

      在Java1.4推出NIO之前,基于Java的所有Socket通信都采用了同步阻塞模式(BIO),一请求一应答,当并发访问量增大时响应时间延迟也增大,在性能和可靠性方面

     存在巨大的瓶颈。因此推出了NIO(Non-Blocked IO)

      但是它仍有很多不完善的地方,特别是对文件系统的处理能力仍显不足,主要问题如下:

      1)、没有统一的文件属性

      2)、API能力比较弱,例如目录的级联创建和递归遍历,往往需要自己实现

      3)、底层存储系统的一些高级API无法使用

      4)、所有的文件操作都是同步阻塞调用,不支持异步文件读写操作

    传统的BIO编程

      网络编程的基本模型是CS模型,也就是两个进程之间相互通信,其中服务端提供位置信息(绑定的IP地址和监听端口),客户端通过连接操作向服务端监听的地址发起

    连接请求,通过三次握手建立连接,如果连接建立成功,双方就可以通过网络套接字(Socket)进行通信。

      在基于传统同步阻塞模型开发中,ServerSocket负责绑定IP地址,启动监听端口,Socket负责发起连接操作。连接成功之后,双方通过输入和输出流进行同步阻塞式通信。

      ServerSocket的accept方法在连接传入前会一直阻塞。不过BIO主要的问题在于由一个独立的Acceptor线程负责监听客户端的连接,每当有一个新的客户端请求接入时,服

      务端必须创建一个新的线程处理新接入的客户端链路,一个线程只能处理一个客户端连接。处理完成之后,通过输出流返回应答给客户端,线程销毁。在高性能服务器应用

      领域,往往需要成千上万个客户端的并发连接,这种模型显然无法满足高性能、高并发接入的场景。

    NIO编程

      与Socket类和ServerSocket类相对应,NIO提供了SocketChannel和ServerScoketChannel两种不同的套接字实现,且都支持阻塞和非阻塞两种模式。

      NIO类库:

      1):Buffer,缓冲区

        Buffer是一个对象,它包含一些要写入或者要读出的数据。在NIO库中,所有的数据都是用缓冲区来处理的。在读取数据时,它是直接读缓冲区的数据;在写入数据时,

        写入到缓冲区中。任何时候访问NIO中的数据,都是通过缓冲区进行操作。

        缓冲区实质上是一个数组,通常是字节数组ByteBuffer。

      2):Channel,通道

        网络数据通过Channel读取和写入。通道与流的不同之处在于通道是双向的,而流是单向的,只在一个方向上移动(一个流必须是InputStream或OutputStream抽象类

        的子类),而通道是全双工的可以同时用于读写,且读写可同时进行。

      3):多路复用器Selector

        多路复用器提供选择已经就绪的任务的能力。Selecor会不断地轮询注册在其上的Channel,如果某个Channel上面发生读或者写事件,这个channel就处于就绪状态,会

        被selector轮询出来,然后通过SelectionKey可以获取就绪Channel的集合,进行后续的IO操作。底层使用操作系统提供的epoll函数。

    AIO编程

      NIO2.0引入了新的异步通道的概念,并提供了异步文件通道和异步套接字通道的实现。异步通道提供以下两种方式获取操作结果:

        1):通过java.util.concurrent.Future类来表示异步操作的结果

        2):在执行异步操作的时候传入一个java.nio.channels

      CompletionHandler接口的实现类作为操作完成的回调。

      NIO2.0的异步套接字通道是真正的异步非阻塞IO,对应于UNIX网络编程中的事件驱动IO(AIO)。它不需要通过多路复用器对注册的通道进行轮询操作即可实现异步读

    写,从而简化了NIO的编程模型。

    为什么选择Netty

      开发出高质量的NIO程序并不是一件简单的事情,除去NIO固有的复杂性和BUG不谈,作为一个NIO服务端,需要能够同时处理网络的闪断、客户端的重复接入、客户端

    的安全认证、消息的解编码、半包读写等情况。

      从可维护角度看,NIO采用了异步非阻塞编程模型,而且是一个IO线程处理多条链路,它的调试和跟踪非常麻烦,特别是生产环境的问题,我们无法进行有效的调试和跟

    踪,往往只能靠一些日志来辅助分析,定位难度大。

      不选择Java原生NIO编程的原因:

      1):NIO的类库和API频繁,使用麻烦,需要熟练掌握Selector、ServerSocketChannel、SocketChannel、ByteBuffer等

      2):需要熟悉Java多线程编程,因为NIO涉及到Reactor模式

      3):不齐可靠性能力的话,工作量和难度都非常大。例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常码流等问题。

      4):JDK NIO的bug,例如臭名昭著的epoll bug,会导致Selecotor空轮询,最终导致CPU 100%。

    而Netty的健壮性、功能、性能、可定制性和可扩展性都首屈一指,已经得到了成百上千的商业项目验证,如业界主流的RPC框架也使用Netty来构建高性能的异步通信能力。

    优点如下:

      1):API使用简单,开发门槛低

      2):功能强大,预置了多种编解码功能,支持多种主流协议

      3):定制能力强,可以通过ChannelHandler对通信框架进行灵活地扩展

      4):性能高,与其他NIO框架对比,Netty性能最优

      5):成熟、稳定,Netty修复了已经发现的所有的JDK NIO 的bug

      6):社区活跃,版本迭代周期短,发现的bug可以及时被修复,同时,更多的新功能会加入

      7):经历了大规模的商业应用考验,质量得到保证。

  • 相关阅读:
    Linux之vmware安装
    中秋之美
    青春无悔
    MSP430常见问题之指令系统类
    MSP430常见问题之LCD 显示驱动类
    MSP430常见问题之FLASH存储类
    MSP430常见问题之看门狗及定时器类
    MSP430常见问题之电源类
    MSP430常见问题之通信类
    MSP430常见问题之AD转换类
  • 原文地址:https://www.cnblogs.com/yangyongjie/p/11087278.html
Copyright © 2020-2023  润新知