1.Netty之IO浅析

传统Socket 同步阻塞服务端

采用while(true)方式,每个连接分配单独的线程的模型,之所以使用多线程是因为该模型同步阻塞的,当一个连接在处理I/O的时候,后续的请求是阻塞的。如果是单线程的话必然就挂死在那里,后续的请求无法进入;于是优化采取每个请求新建一个线程去处理。

稍微好点的是直接用线程池去接收请求,来减小线程池生命周期的开销。

这个模型最本质的问题在于,严重依赖于线程。但线程是很"贵"的资源,主要表现在:

  1. 线程的创建和销毁成本很高,在Linux/Unix操作系统上,线程本质上就是一个进程。创建和销毁都是重量级的系统函数。
  2. 线程本身占用较大内存,像Java的线程栈,一般是接近1M的空间,如果系统中的线程数过万,恐怕整个JVM的内存都会被吃掉一半。
  3. 线程的切换成本是很高的。操作系统发生线程切换的时候,需要保留线程的上下文,然后执行系统调用。如果线程数过高,可能执行线程切换的时间甚至会大于线程执行的时间。
  4. 如果网络环境不是很稳定,就很容易造成大量请求的结果同时返回,激活大量阻塞线程从而使系统负载压力过大。

通过观察我们可以看到主要原因在于socket.accept()、socket.read()、socket.write()三个主要函数都是同步阻塞的,也就意味着该请求的接受和读/写都是阻塞的,那么会多次阻塞。

这种方式对应的是Unix网络编程里对应的阻塞I/O模型,即从发起请求数据开始,接收数据到完成读写都是阻塞的,整个资源都是处于占用状态。

---------------------------------------------------------------------------------------------------------------------

NIO 非阻塞IO服务器

NIO对应的Unix网络编程里的I/O多路复用模型,处理读写的操作对应的linux底层操作指令是epoll(Nginx底层调用的也是这个函数),先去检查系统是否有能力处理请求,如果有能力接受请求,则直接接受到一个队列中,然后有操作系统去从队列中取这一批数据去做处理;反之则直接返回0,客户端永远不会阻塞

介绍了I/O模型并且结合代码,我们可以发现NIO中的socket主要的读、写、注册和接收函数,在等待就绪阶段都是非阻塞的,阻塞操作只在CPU处理读写上了(这种更加的高效)。

NIO的线程模型采取了非常高效的Reactor模型:

标准/典型的Reactor:

  • 步骤1:等待事件到来(Reactor负责)。
  • 步骤2:将读就绪事件分发给用户定义的处理器(Reactor负责)。
  • 步骤3:读数据(用户处理器负责)。
  • 步骤4:处理数据(用户处理器负责)。

NIO的模型图为

可以看到Reactor起到了接收请求非阻塞的关键,这个组件在NIO模型中扮演该角色的是Selector。

       NIO一个新的特点就是不依赖输入输出流他依赖一个新的组件Channel,它用于源节点与目标节点的连接。在 Java NIO 中负责缓冲区中数据的传输。Channel 本身不存储数据,因此需要配合缓冲区进行传输,此外通道之间还有聚合和分散的操作。

 

区别通过Channel
支持异步不支持支持
是否可双向传输数据不能,只能单向支持
是否结合 Buffer 使用必须
性能较低较高

然后介绍下NIO还用了比IO更先进的数据缓冲区Buffer,它的好处非常明显:

  1. 使用数组的方式不够灵活且性能差。
  2. 缓冲区的很多操作(clear、flip、rewind)都是操作limit和position的值来实现重复读写。

也就是说这个Buffer是一个可以重复利用的数据结构,它相对于之前的数组非常的高效。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值