网络编程中的五种I/O模型

内存空间分为2部分

  • 内核空间:提供给操作系统使用
  • 用户空间:提供给用户程序使用
     

IO操作分为2步

  • 向操作系统发起IO请求,等待数据准备好(文件 -> 内核 -> 进程)
  • 实际IO操作

内核空间、用户空间是中文翻译的,原文是把数据从内核复制到进程中。

 

网络编程中的五种I/O模型

1、阻塞式IO
当前线程发起系统调用(向操作系统发起IO请求) -> 当前线程阻塞,等待操作系统把要操作的数据从内核空间复制到用户空间 -> 数据复制完成,操作系统返回成功提示 -> 当前线程操作用户空间中的数据。

 

2、非阻塞式IO
当前线程发起系统调用(向操作系统发起IO请求) -> 当前线程往下继续执行 ,并轮询复制是否完成 -> 数据复制完成, 当前线程操作用户空间中的数据。

 

3、IO多路复用
I/O指的是网络I/O, 多路指的是多个TCP连接(即socket或者channel),复用指的是复用线程。原本需要用一个线程处理一个TCP连接,多路复用是用少量线程处理多个TCP连接,减少系统开销。

linux用一个文件保存一个连接状态,建立一个连接就会生成打开一个文件,产生一个文件描述符(fd),1G内存大概支持10万个fd。

IO多路复用的3种技术

  • select:遍历fd数组找到对应的fd,然后进行IO操作,如果fd(连接数)很多,性能会很差。且linux默认单个进程最多只能打开1024个fd,数量有限制,虽然可以修改上限,但效率依旧很低。
  • poll:poll和select差不多,区别是poll使用链表存储fd,fd没有数量限制。
  • epoll:select、poll都是通过轮询判断数据是否准备好,epoll则是通过回调机制(callback)进行IO操作,数据准备好后,会自动进行相关IO操作,不需要遍历fd列表,效率高。fd没有数量限制,单机也能建立百万连接,最大连接数受机器硬件条件限制。但编码比select、poll复杂。

 

4、信号驱动IO
用得较少

 

5、异步IO
同步和异步指的是内核、进程之间的消息通知机制,如何知道数据准备好了?同步是主动轮询,异步是使用回调机制,由操作系统通知进程。前四种基本都是同步IO。

阻塞、非阻塞指的是线程状态,阻塞是IO操作会阻塞当前线程,非阻塞则IO操作不会阻塞当前线程。

常见的是:同步阻塞、同步非阻塞、异步非阻塞。

 

NIO简介

传统的IO是Blocking IO(BIO,阻塞式IO),Tomcat7之前默认使用的也是BIO。从JDK1.4开始,Java提供了一系列新IO,位于nio包中,称为NIO,NIO一说指的是New IO,一说指的是Non-blocking IO。
 

NIO可以进行通道映射,将内核中的数据映射到进程中,通过内存镜像直接读写内核数据,不必进行数据复制,效率更高。
 

NIO中的主要类

  • Channel  可以将内核数据映射到进程中
  • Buffer  缓冲,用于批量读写数据
  • Charset  字符集,用于字符序列的编码、解码
  • Selector  提供非阻塞式的IO
     

JDK1.7推出了NIO 2,又称为AIO,支持文件和网络套接字的异步IO。

 

Netty简介

Netty是基于NIO的网络异步通信框架,成熟稳定,ElasticSearch、Dubbo的网络通信都是使用Netty实现的。
 

Netty的特点

  • 异步非阻塞
  • 事件驱动
  • 高性能、高可靠性
     

为什么Netty使用NIO而不是AIO?

在Linux系统中,AIO、NIO的底层实现都是使用epoll,性能上AIO没有明显优势,且NIO更加成熟稳定。

 

编码、解码

计算机中文件、数据底层都是基于二进制的,编码(Encode)是把人能看懂的明文转换为二进制序列,解码(Decode)是把二进制序列转换为人能看懂的明文。

字符集:包含字符、二进制序列之间的对应关系,一个字符对应一个二进制序列。

乱码:编码、解码使用的字符集不一致。