深入理解Netty以及为什么项目中要使用?(一) IO模型

文章探讨了传统的BIO模型中HTTP请求的处理流程,包括同步阻塞IO的限制和非阻塞IO(如NIO)的引入,以及多路复用机制(如select和epoll)在提高性能和连接数方面的改进。最后提到Netty利用这些机制实现异步IO,简化开发复杂度。
摘要由CSDN通过智能技术生成

传统的BIO模型是怎样数据传输的?

当客户端发起一次Http请求时,服务端的处理流程时怎么样的?

简单来说可以分为以下几个步骤:

  1. 基于TCP协议建立网络通信。

  2. 开始向服务端端传输数据。

  3. 服务端接受到数据进行解析,开始处理本次请求逻辑。

  4. 服务端处理完成后返回结果给客户端。

在这个过程中,会涉及到网络IO通信,在传统的BIO模式下,客户端向服务端发起一个数据读取请求,客户端在收到服务端返回数据之前,一直处于阻塞状态,直到服务端返回数据后完成本次会话。这个过程就叫同步阻塞IO,在BIO模型中如果想实现异步操作,就只能使用多线程模型,也就是一个请求对应一个线程,这样就能够避免服务端的链接被一个客户端占用导致连接数无法提高。

同步阻塞IO主要体现在两个阻塞点

  • 服务端接收客户端连接时的阻塞。

  • 客户端和服务端的IO通信时,数据未就绪的情况下的阻塞。

 在这种传统BIO模式下,会造成一个非常严重的问题,如下图所示,如果同一时刻有N个客户端发起请求,按照BIO模型的特点,服务端在同一时刻只能处理一个请求。将导致客户端请求需要排队处理,带来的影响是,用户在等待一次请求处理返回的时间非常长。意味着服务端没有并发处理能力,这显然不合适。

非阻塞IO

从前面的分析发现,服务端在处理一次请求时,会处于阻塞状态无法处理后续请求,那是否能够让被阻塞的地方优化成不阻塞呢?于是就有了非阻塞IO(NIO)

非阻塞IO,就是客户端向服务端发起请求时,如果服务端的数据未就绪的情况下, 客户端请求不会被阻塞,而是直接返回。但是有可能服务端的数据还未准备好的时候,客户端收到的返回是一个空的, 那客户端怎么拿到最终的数据呢?

如图所示,客户端只能通过轮询的方式来获得请求结果。NIO相比BIO来说,少了阻塞的过程在性能和连接数上都会有明显提高。

 NIO仍然有一个弊端,就是轮询过程中会有很多空轮询,而这个轮询会存在大量的系统调用(发起内核指令从网卡缓冲区中加载数据,用户空间到内核空间的切换),随着连接数量的增加,会导致性能问题。

多路复用机制

I/O多路复用的本质是通过一种机制(系统内核缓冲I/O数据),让单个进程可以监视多个文件描述符,一旦某个描述符就绪(一般是读就绪或写就绪),能够通知程序进行相应的读写操作

什么是fd:在linux中,内核把所有的外部设备都当成是一个文件来操作,对一个文件的读写会调用内核提供的系统命令,返回一个fd(文件描述符)。而对于一个socket的读写也会有相应的文件描述符,称为socketfd。

常见的IO多路复用方式有【select、poll、epoll】,都是Linux API提供的IO复用方式,那么接下来重点讲一下select、和epoll这两个模型

  • select:进程可以通过把一个或者多个fd传递给select系统调用,进程会阻塞在select操作上,这样select可以帮我们检测多个fd是否处于就绪状态,这个模式有两个缺点

    • 由于他能够同时监听多个文件描述符,假如说有1000个,这个时候如果其中一个fd 处于就绪状态了,那么当前进程需要线性轮询所有的fd,也就是监听的fd越多,性能开销越大。

    • 同时,select在单个进程中能打开的fd是有限制的,默认是1024,对于那些需要支持单机上万的TCP连接来说确实有点少

  • epoll:linux还提供了epoll的系统调用,epoll是基于事件驱动方式来代替顺序扫描,因此性能相对来说更高,主要原理是,当被监听的fd中,有fd就绪时,会告知当前进程具体哪一个fd就绪,那么当前进程只需要去从指定的fd上读取数据即可,另外,epoll所能支持的fd上线是操作系统的最大文件句柄,这个数字要远远大于1024

【由于epoll能够通过事件告知应用进程哪个fd是可读的,所以我们也称这种IO为异步非阻塞IO,当然它是伪异步的,因为它还需要去把数据从内核同步复制到用户空间中,真正的异步非阻塞,应该是数据已经完全准备好了,我只需要从用户空间读就行】

I/O多路复用的好处是可以通过把多个I/O的阻塞复用到同一个select的阻塞上,从而使得系统在单线程的情况下可以同时处理多个客户端请求。它的最大优势是系统开销小,并且不需要创建新的进程或者线程,降低了系统的资源开销

客户端请求到服务端后,此时客户端在传输数据过程中,为了避免Server端在read客户端数据过程中阻塞,服务端会把该请求注册到Selector复路器上,服务端此时不需要等待,只需要启动一个线程,通过selector.select()阻塞轮询复路器上就绪的channel即可,也就是说,如果某个客户端连接数据传输完成,那么select()方法会返回就绪的channel,然后执行相关的处理即可。

异步IO

异步IO和多路复用机制,最大的区别在于:当数据就绪后,客户端不需要发送内核指令从内核空间读取数据,而是系统会异步把这个数据直接拷贝到用户空间,应用程序只需要直接使用该数据即可。

 在Java中,我们可以使用NIO的api来完成多路复用机制,实现伪异步IO。但是这样实现发现代码不仅仅繁琐,而且使用起来很麻烦。

所以Netty出现了,Netty的I/O模型是基于非阻塞IO实现的,底层依赖的是JDK NIO框架的多路复用器Selector来实现。

一个多路复用器Selector可以同时轮询多个Channel,采用epoll模式后,只需要一个线程负责Selector的轮询,就可以接入成千上万个客户端连接。

  • 18
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ADRU

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值