没搞清楚网络I/O模型?那怎么入门Netty

简介: 没搞清楚网络I/O模型?那怎么入门Netty

1.掌握五种I/O模型的关键钥匙


Unix系统下的五种基本I/O模型大家应该都有所耳闻,分为:


  • blocking I/O(同步阻塞IO,BIO)
  • nonblocking I/O(同步非阻塞IO,NIO)
  • I/O multiplexing (I/O多路复用)
  • signal driven I/O(信号驱动I/O)
  • asynchronous I/O(异步I/O,AIO)


每种I/O的特性如何,尤其是同步/非同步、阻塞/非阻塞的区别,其实很多人并不能准确地进行区分。


所以,我们先把最核心的“钥匙”告诉大家,带着这把“钥匙”再来看I/O模型的关键问题,就能手到擒来了。


当一次网络IO发生时,主要涉及到三个对象


  • 发起此次IO操作的Process或者Application
  • 系统内核kernel。用户进程无法直接操作I/O设备,必须通过系统内核kernel与I/O设备交互。
  • I/O设备,包括网络、磁盘等。本文主要针对网络。

49.jpg


真正的I/O过程,主要分为两个阶段


  • 等待数据准备阶段。
  • 数据拷贝阶段。数据准备完毕,从内核kernel拷贝到进程process中


以一个socket上的输入操作为例。


第一步通常涉及等待数据从网络中到达。当所等待分组到达时,它被复制到内核中的某个缓冲区。


第二步就是把数据从内核缓冲区复制到用户态缓冲区。


这里,我们先记住这 两个阶段,掌握所有I/O模型区别的“关键钥匙”就在它们身上。


2.五种I/O模型详解


2.1 同步阻塞I/O, BIO


我们一般使用最多的,最基础的I/O模型就是同步阻塞I/O。

典型应用:

阻塞socket、Java BIO

50.jpg


我们来解读一下BIO的过程:


  • 应用进程向内核发起 I/O 请求,发起调用的线程 一直阻塞,等待内核返回结果。
  • 数据准备完毕,从内核kernel拷贝到用户态内存(仍旧阻塞),然后kernel返回结果,用户进程process结束阻塞,重新运行。


“关键钥匙”分析:

BIO的特点就是在IO执行的 两个阶段 都被 阻塞 了。


所以,我们日常使用BIO模型的时候,提高性能的方式,就是采用 多线程


在一般的场景中,多线程模型下的BIO是成本较低、收益较高的方式。但是,如果在高并发的场景下,过多的创建线程,会严重占据系统资源,降低系统对外界响应效率。


那是不是可以考虑使用“线程池”或者“连接池”呢?


一定程度上可以。“池化”的目的在于减少创建和销毁线程的频率,让空闲的线程重新承担新的执行任务,维持一个合理的线程数量,可以很好的降低系统开销。


但是,“池化”技术只能一定程度上缓解了频繁调用IO接口带来的资源占用。如果“池”上限100,而我们需要1000的IO,那并不能解决性能问题,这是由于BIO模型本身的限制决定的。


所以,需要非阻塞I/O来尝试解决这个问题。


2.2 同步非阻塞I/O, NIO


BIO的阻塞问题,让我们考虑使用非阻塞的NIO模型。


典型应用:

socket的非阻塞模式

51.jpg


应用进程向内核发起 I/O 请求后,如果kernel中的数据还没有准备好,不再会“阻塞”等待结果,而是会立即返回。


从用户进程角度讲 ,它发起一个IO操作后,并不需要等待,而是马上就得到了一个结果。


用户进程判断结果是一个error时,它就知道数据还没有准备好,于是它开始发起轮训操作。


直到kernel中的数据准备好了,一旦用户再轮训过来,就马上将数据拷贝到了用户内存,然后返回。


所以,在非阻塞式IO中,用户进程其实是需要不断地主动询问kernel数据准备好了没有。


“关键钥匙”分析:

非阻塞NIO模型相比于BIO的显著差异在于,在“数据等待”阶段,不再“阻塞”,立即返回。

但是在“数据拷贝”阶段,仍然是“阻塞”的。


虽然非阻塞模型避免了“数据等待”阶段的阻塞,但是,采用轮询方式,会导致系统上下文切换开销很大,会大幅度推高CPU 占用率。


因此,单独使用非阻塞 I/O 模型的效率并不高。而且随着并发量的提升,非阻塞 I/O 会存在严重的性能浪费。


我们可以看到,轮训的目的只是检测“数据是否已经就绪”,而操作系统提供了更为高效的检测接口,


例如select()多路复用模式,可以一次检测多个连接是否活跃。


2.3 多路复用IO


多路复用实现了一个线程处理多个 I/O 句柄的操作,有些地方也称这种IO方式为事件驱动IO(event driven IO)。


  • 多路 指的是多个数据通道
  • 复用 指的是使用一个或多个固定线程来处理每一个 Socket。


典型应用:

select、poll、epoll三种方案

Java NIO

52.jpg


多个的进程的IO可以注册到一个复用器(selector)上,然后用一个进程调用select,select会监听所有注册进来的IO。


如果selector所有监听的IO在内核缓冲区都没有可读数据,select调用进程会被阻塞;同时,kernel会“监视”所有select负责的socket,如果任何一个socket中的数据准备好了,select就会返回;


然后select调用进程可以自己或通知另外的进程(注册进程)来再次发起读取IO,然后process将数据从kernel拷贝到用户进程,读取内核中准备好的数据。


可以看到,多个进程注册IO后,只有一个select调用进程被阻塞


多路复用解决了同步阻塞 I/O 和同步非阻塞 I/O 的问题,是一种非常高效的 I/O 模型。我们可以直观看到,这个模型的好处在于单个process就可以同时处理多个网络连接的IO。


“关键钥匙”分析:

多路复用I/O,select阶段,对于多路socket的“数据等待”阶段而言,是“非阻塞”。

对单个socket的“数据拷贝”阶段,也是“阻塞”。


这里需要特别注意!!!!


其实如果处理的IO数不多的情况下,使用多路复用IO的web server不一定比使用 池化+BIO 的web server性能更好,可能延迟还更大。

考虑极端情况下,只有一个IO,多路复用需要 2 次系统调用(select + recvfrom),而BIO只需要 1 次系统调用(recvfrom)。


所以,多路复用IO的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。


2.4 信号驱动I/O


在使用信号驱动 I/O 时,当数据准备就绪后,内核通过发送一个 SIGIO 信号通知应用进程,应用进程就可以开始读取数据了。

53.jpg


信号驱动I/O模型的最大特点,就是不需要process进程不断轮训内核是否已经准备就绪。


“关键钥匙”分析:

信号驱动I/O在"数据等待"阶段“非阻塞”。

当数据准备完成后,信号通知process,process开始“数据拷贝”阶段,这里仍然是“阻塞”的。


信号驱动 I/O 有几个缺陷:


1)在大量 IO 操作时可能会因为信号队列溢出导致没法通知。


2)信号驱动 I/O 尽管对于处理 UDP 套接字来说有用,信号通知意味着到达一个数据报,或者返回一个异步错误。


但是,对于 TCP 而言,信号驱动的 I/O 方式不太好用。因为导致信号通知的情况有非常多种,每一个来进行判别会消耗很大资源。


所以信号驱动I/O模式用得非常少。


而且尤其需要注意,在“数据拷贝”阶段,它仍然是“阻塞”的。


2.5 异步I/O,AIO


真正的异步I/O,就是AIO。


典型应用:

JAVA7 AIO、高性能服务器

54.jpg


根据前面四个模型的分析,相信大家已经能明显看懂这个模型的运行方式了。


用户进程发起I/O请求后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它收到一个请求之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它I/O操作完成了。


AIO最重要的一点是 从内核缓冲区拷贝数据到用户态缓冲区的过程也是由系统异步完成,应用进程只需要在指定的数组中引用数据即可。


AIO 与信号驱动 I/O 的主要区别:

信号驱动 I/O 由内核通知何时可以开始一个 I/O 操作,而异步 I/O 由内核通知 I/O 操作何时已经完成。


“关键钥匙”分析:

"数据等待"阶段,非阻塞

"数据拷贝”阶段,非阻塞


AIO是真正的异步模型,它不会对请求进程产生任何的阻塞。


3. 同步=阻塞?异步=非阻塞?


日常使用过程中,我们往往把 同步I/O 等同于 阻塞I/O,异步I/O 等同于 非阻塞I/O。

实际上,严格意义来说,这两组概念还是有很大的区别的。


3.1 阻塞I/O 与 非阻塞I/O


阻塞与非阻塞的区别比较明显,也很好理解。


结合I/O模型来说,阻塞I/O会一直block对应的进程直到操作完成,而非阻塞 IO在kernel 在"等待数据准备"阶段会立刻返回。


所以我们一般认为,阻塞I/O只有BIO,另外四个模型都是属于非阻塞I/O。


3.2 同步I/O 与 异步I/O


先来看看 同步I/O 和 异步I/O 的定义是什么,根据POSIX的定义:


  • 同步I/O : A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes;
  • 异步I/O : An asynchronous I/O operation does not cause the requesting process to be blocked;


两者的区别就在于同步I/O做 "IO operation”的时候会将process阻塞。


那么按照这个定义,我们看看前面每个模型的“关键钥匙”分析部分,可以明显看到,BIO,NIO,IO多路复用、信号驱动IO 四种模型都属于 同步IO。


因为它们在IO的第二阶段,真正执行“数据拷贝”的阶段,都是“阻塞”的。以NIO为例,在执行recvfrom这个系统调用的时候,如果kernel的数据没有准备好,这时候不会block进程。但是当kernel中数据准备好的时候,recvfrom会将数据从kernel拷贝到用户内存中,这个时候进程是被block了。


同理,信号驱动IO,当内核中IO数据就绪时以SIGIO信号通知请求进程,请求进程再把数据从内核读入到用户空间,这一步也是阻塞的。


所以,真正的异步I/O只有一个,就是AIO。当进程发起IO操作之后,就直接返回再也不管了,直到kernel发送一个信号,告诉进程说IO完成。在这整个过程中,进程完全没有被阻塞。如定义所说,不会因为IO操作阻塞。


4. Netty采用了哪种I/O模型呢?


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


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


更具体的实现方式和模型,我们下一期再展开说明。


对了,一定有同学想问,Netty为什么不采用AIO呢?


因为 AIO 的目的是希望 I/O 线程不阻塞主线程,属于异步 I/O,由内核通知 I/O 操作何时完成。AIO 适用于连接数多的且需要长时间连接的场景。


对于AIO来说,目前操作系统支持程度有限且实现起来复杂。


Netty也尝试过AIO,但是效果不是很理想,最终废弃了。


参考书目:

《UNIX Network Programming(Volume1,3rd)》

目录
相关文章
|
2天前
|
JSON Dart 前端开发
鸿蒙应用开发从入门到入行 - 篇7:http网络请求
在本篇文章里,您将掌握鸿蒙开发工具DevEco的基本使用、ArkUI里的基础组件,并通过制作一个简单界面掌握使用
28 8
|
7天前
|
JSON 算法 Java
Nettyの网络聊天室&扩展序列化算法
通过本文的介绍,我们详细讲解了如何使用Netty构建一个简单的网络聊天室,并扩展序列化算法以提高数据传输效率。Netty的高性能和灵活性使其成为实现各种网络应用的理想选择。希望本文能帮助您更好地理解和使用Netty进行网络编程。
25 12
|
24天前
|
机器学习/深度学习 资源调度 算法
图卷积网络入门:数学基础与架构设计
本文系统地阐述了图卷积网络的架构原理。通过简化数学表述并聚焦于矩阵运算的核心概念,详细解析了GCN的工作机制。
65 3
图卷积网络入门:数学基础与架构设计
|
3天前
|
网络协议 安全 网络安全
探索网络模型与协议:从OSI到HTTPs的原理解析
OSI七层网络模型和TCP/IP四层模型是理解和设计计算机网络的框架。OSI模型包括物理层、数据链路层、网络层、传输层、会话层、表示层和应用层,而TCP/IP模型则简化为链路层、网络层、传输层和 HTTPS协议基于HTTP并通过TLS/SSL加密数据,确保安全传输。其连接过程涉及TCP三次握手、SSL证书验证、对称密钥交换等步骤,以保障通信的安全性和完整性。数字信封技术使用非对称加密和数字证书确保数据的机密性和身份认证。 浏览器通过Https访问网站的过程包括输入网址、DNS解析、建立TCP连接、发送HTTPS请求、接收响应、验证证书和解析网页内容等步骤,确保用户与服务器之间的安全通信。
25 1
|
8天前
|
监控 安全 BI
什么是零信任模型?如何实施以保证网络安全?
随着数字化转型,网络边界不断变化,组织需采用新的安全方法。零信任基于“永不信任,永远验证”原则,强调无论内外部,任何用户、设备或网络都不可信任。该模型包括微分段、多因素身份验证、单点登录、最小特权原则、持续监控和审核用户活动、监控设备等核心准则,以实现强大的网络安全态势。
|
14天前
|
Web App开发 网络协议 安全
网络编程懒人入门(十六):手把手教你使用网络编程抓包神器Wireshark
Wireshark是一款开源和跨平台的抓包工具。它通过调用操作系统底层的API,直接捕获网卡上的数据包,因此捕获的数据包详细、功能强大。但Wireshark本身稍显复杂,本文将以用抓包实例,手把手带你一步步用好Wireshark,并真正理解抓到的数据包的各项含义。
64 2
|
21天前
|
机器学习/深度学习 人工智能 算法
深度学习入门:用Python构建你的第一个神经网络
在人工智能的海洋中,深度学习是那艘能够带你远航的船。本文将作为你的航标,引导你搭建第一个神经网络模型,让你领略深度学习的魅力。通过简单直观的语言和实例,我们将一起探索隐藏在数据背后的模式,体验从零开始创造智能系统的快感。准备好了吗?让我们启航吧!
52 3
|
28天前
|
数据采集 XML 存储
构建高效的Python网络爬虫:从入门到实践
本文旨在通过深入浅出的方式,引导读者从零开始构建一个高效的Python网络爬虫。我们将探索爬虫的基本原理、核心组件以及如何利用Python的强大库进行数据抓取和处理。文章不仅提供理论指导,还结合实战案例,让读者能够快速掌握爬虫技术,并应用于实际项目中。无论你是编程新手还是有一定基础的开发者,都能在这篇文章中找到有价值的内容。
|
1月前
|
机器学习/深度学习 人工智能 算法框架/工具
深度学习中的卷积神经网络(CNN)入门
【10月更文挑战第41天】在人工智能的璀璨星空下,卷积神经网络(CNN)如一颗耀眼的新星,照亮了图像处理和视觉识别的路径。本文将深入浅出地介绍CNN的基本概念、核心结构和工作原理,同时提供代码示例,带领初学者轻松步入这一神秘而又充满无限可能的领域。
|
1月前
|
存储 网络协议 安全
30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场
本文精选了 30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场。
87 2