从BIO,NIO到多路复用IO——认识Netty框架
同步简单来说同步是一种可靠的有序运行机制当我们进行同步操作时后续的任务是等待当前调用返回才会进行下一步。在IO模型中如果程序本身去进行系统调用并且等待系统调用完成后执行IO读取操作这就是同步与IO读取后是否开启新线程进行业务操作无关。所以无论是BIONIO还是多路复用都属于同步IO模型。BIOBlock IO和PIO伪异步BIO即同步阻塞IO。专门有一个线程accept负责监听客户端的请求。只要有客户端和服务端建立了一个请求创建一个新的线程来处理。每个连接必须要开一个线程来处理并且没有处理完线程不退出。PIO就是在BIO的基础上引入了线程池每次从线程池中选择线程来处理请求减少了频繁创建和销毁线程的性能消耗。在连接数不多的情况下传统IO编写容易使用。但是随着连接数的增多传统的IO就不行了NIO Non-Block IO同步非阻塞在BIO中默认创建的socket都是阻塞的非阻塞IO要求socket被设置为NONBLOCK即accept方法非阻塞有数据就处理没数据就跳过。这种方式就不需要每个连接创建一个线程来处理通过轮询就可以处理多个连接。NIO的缺点NIO相对BIO而言避免了多线程问题但是由于程序中无法判断IO是否准备就绪所以仍然只能遍历所有连接的fd进行accept或recv系统调用复杂度是O(n),程序进行系统调用的accept和recv操作涉及到用户态和内核态的转换所以频繁的系统调用是的性能也受影响著名的C10K问题多路复用IOSelectPollEPoll多路复用器解决了IO状态判断的问题通过系统调用得到IO可用状态的fds集合多路复用的模型本质上仍然都是同步 IO。Select同时监听多个fd把所有连接描述符一次性发送给select系统调阻塞等待系统调用返回IO状态结果集程序遍历返回结果集合有针对性地对fds进行读写调用这种方式减少了那些因无法判断IO状态而进行的不必要的accept和recv操作的次数从而避免了用户态到内核态的频繁切换提高了性能。但是select系统调用存在fds个数限制默认最多是1024。Poll与select类似唯一区别是没有使用fdset而是pollset对fd的个数没有限制。select、poll的缺点把fd的监听列表放在用户空间由用户空间管理每次通过系统调用判断IO状态时都需要将fd的set集合从用户空间复制到和内核空间。每次执行系统调用 都需要遍历整个fd的set集合进行IO状态判断执行效率会随着fdset的增大而线性下降。EPoll基于select和poll的缺点epoll的出现解决了这些问题epoll可以理解为Event Poll。对于每一个新创建的连接而言程序通过调用epoll_create系统调用创建指定连接的fd标识然后通过epoll_ctl系统调用把fd标识add到内核空间这两个操作在连接的生命周期中只执行一次。EPoll模型中会给每个监听的fd增加一个回调函数当IIO就绪状态时将fd放入就绪列表中用户程序只需要通过epoll_wait系统调用从就绪列表中获取就绪的fd进行IO操作即可。不需要将fd从用户态复制到内核态使用MMAP共享内存映射实现。有fd内核事件时通过回调把该fd放到就绪队列中应用程序不需要每次传递所有的fd执行系统调用获取IO状态。没有fd限制1g内存可以处理10w个fdNettyNetty是由JBOSS提供的一个java开源框架。也就是说Netty 是一个基于EPoll模型实现的的客户、服务器端编程框架。Netty 抽象出两组EventLoopGroup类型的线程池 一个是BossGroup专门负责接收客 户端连接一个是WorkerGroup专门负责网络读写操作。在每个线程组中存在多个NioEventLoop表示一个不断循环执行处理 任务的线程每个 NioEventLoop 都有一个selector用于监听绑定在其上的 socket 网络通道。初始化线程组时如果不指定参数的话NioEventLoop的数量默认情况下是当前COU核数*2客户端会被依次分配给空闲的NioEventLoop。例如在4核CPU中启动server后依次启动10个客户端对应会分配给WorkGroup中的1-8的WorkGroup最后两个重新分配给1和2连接请求到BossGroup中NioEventLoop不断轮询accept接受新连接创建NioSocketChannel然后把NioSocketChannel注册到WorkerGroup中的某个NioEventLoop中的selector上。WorkerGroup中的NioEventLoop负责轮询执行IO操作 NioEventLoop 内部采用串行化设计 从消息的读取-解码-处理-编码-发送 始终由 IO 线 程 NioEventLoop 负责。大部分的javaweb应用都是基于Servlet容器tomcat实现的Http服务但是当并发或者性能需要改善时我们就可以考虑采用基于NIO网络模型的Netty来实现HTTP服务以此提高性能和吞吐量Netty内置了HTTP相关的编解码器让用户可以很方便的开发出高性能的HTTP协议的服务Spring Webflux默认是使用的Netty。例如基于Webflux的springcloud gateway默认就是自己与netty的不需要其他web容器