初学netty

1. 什么是netty
(感谢 Java陈序员的分享,参考文献:https://chencoding.top/rearend/java/Netty.html#_1-1-netty介绍)

  • Netty 是一个异步的、基于事件驱动的网络应用框架,用以快速开发高性能、高可靠性的网络 IO 程序;

2. I/O模型

  • BIO 同步并阻塞(传统阻塞型),服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销 ;
  • NIO 同步非阻塞,服务器实现模式为一个线程处理多个请求(连接),即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求就进行处理;
  • AIO 异步非阻塞,AIO 引入异步通道的概念,采用了Proactor 模式,简化了程序编写,有效的请求才启动线程,它的特点是先由操作系统完成后才通知服务端程序启动线程去处理,一般适用于连接数较多且连接时间较长的应用。

3. NIO结构了解

  • NIO 有三大核心部分:Channel(通道),Buffer(缓冲区), Selector(选择器),NIO是 面向缓冲区 ,或者面向块编程的。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动,这就增加了处理过程中的灵活性,使用它可以提供非阻塞式的高伸缩性网络

  • BIO 以流的方式处理数据,而 NIO 以块的方式处理数据,块 I/O 的效率比流 I/O 高很多,

    1. BIO 是阻塞的,NIO 则是非阻塞的,BIO基于字节流和字符流进行操作,而 NIO 基于 Channel(通道)和 Buffer(缓冲区)进行操作,数据总是从通道读 取到缓冲区中,或者从缓冲区写入到通道中。
    2. Selector(选择器)用于监听多个通道的事件(比如:连接请求,数据到达等),因此使用单个线程就可以监听多个客户端通道。
  • Java NIO的非阻塞模式,使一个线程从某通道发送请求或者读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取,而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此,一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。通俗理解:NIO是可以做到用一个线程来处理多个操作的。假设有10000个请求过来,根据实际情况,可以分配50或者100个线程来处理。不像之前的阻塞IO那样,非得分配10000个

NIO核心: Buffer类及其子类

  • 在 NIO 中,Buffer 是一个顶层父类,它是一个抽象类, 类的层级关系图:

  • Buffer类定义了所有的缓冲区都具有的四个属性来提供关于其所包含的数据元素的信息:

NIO核心: 通道(Channel)

  • NIO的通道类似于流,但有些区别如下: 通道可以同时进行读写,而流只能读或者只能写; 通道可以实现异步读写数据; 通道可以从缓冲读数据,也可以写数据到缓冲;
  • BIO 中的 stream 是单向的,例如 FileInputStream 对象只能进行读取数据的操作,而 NIO 中的通道(Channel)是双向的,可以读操作,也可以写操作
  • Channel在NIO中是一个接口 常用的 Channel 类有:FileChannel、DatagramChannel、ServerSocketChannel 和 SocketChannel。【ServerSocketChanne 类似 ServerSocket , SocketChannel 类似 Socket】;FileChannel 用于文件的数据读写,DatagramChannel 用于 UDP 的数据读写,ServerSocketChannel 和 SocketChannel 用于 TCP 的数据读写。

NIO核心:选择器(Selector)

  • selector 能够检测多个注册的通道上是否有事件发生(注意:多个Channel以事件的方式可以注册到同一个Selector),如果有事件发生,便获取事件然后针对每个事件进行相应的处理,这样就可以只用一个单线程去管理多个通道,也就是管理多个连接和请求;

  • 当线程从某客户端 Socket 通道进行读写数据时,若没有数据可用时,该线程可以进行其他任务,只有在连接/通道 真正有读写事件发生时,才会进行读写,就大大地减少了系统开销,并且不必为每个连接都创建一个线程,不用去维护多个线程;避免了多线程之间的上下文切换导致的开销,线程通常将非阻塞 IO 的空闲时间用于在其他通道上执行 IO 操作,所以单独的线程可以管理多个输入和输出通道,由于读写操作都是非阻塞的,这就可以充分提升 IO 线程的运行效率,避免由于频繁 I/O 阻塞导致的线程挂起

  • Netty 的 IO 线程 NioEventLoop 聚合了 Selector(选择器,也叫多路复用器),可以同时并发处理成百上千个客户端连接

NIO与零拷贝

  1. 零拷贝: 零拷贝技术指的是在计算机执行操作时,CPU不需要先将数据从一个内存区域复制到另一个内存区域,从而可以减少上下文以及CPU的拷贝时间。(详情见 https://blog.csdn.net/m0_62645012/article/details/139268955)

  2. 在 Java 程序中,常用的零拷贝有mmap(内存映射) 和 sendFile

  • mmap 通过内存映射,将文件映射到内核缓冲区,同时,用户空间可以共享内核空间的数据。这样,在进行网络传输时,就可以减少内核空间到用户控件的拷贝次数。

  • Linux 2.1 版本 提供了 sendFile 函数,其基本原理如下:数据根本不经过用户态,直接从内核缓冲区进入到 Socket Buffer,同时,由于和用户态完全无关,就减少了一次上下文切换;

  1. mmap 和 sendFile 的区别
  • mmap 适合小数据量读写,sendFile 适合大文件传输,mmap 需要 4 次上下文切换,3 次数据拷贝;sendFile 需要 3 次上下文切换,最少 2 次数据拷贝。sendFile 可以利用 DMA 方式,减少 CPU 拷贝,mmap 则不能(必须从内核拷贝到 Socket 缓冲区)

AIO

  • 在进行 I/O 编程中,常用到两种模式:Reactor和 Proactor。Java 的 NIO 就是 Reactor,当有事件触发时,服务器端得到通知,进行相应的处理。
    AIO 即 NIO2.0,叫做异步不阻塞的 IO。AIO 引入异步通道的概念,采用了 Proactor 模式,简化了程序编写,有效的请求才启动线程,它的特点是先由操作系统完成后才通知服务端程序启动线程去处理,一般适用于连接数较多且连接时间较长的应用。目前 AIO 还没有广泛应用,Netty 也是基于NIO, 而不是AIO

Netty线程模型

目前存在的线程模型有:传统阻塞 I/O 服务模型, Reactor 模式,Netty 线程模式

Reactor 对象通过select 监控客户端请求事件, 收到事件后,通过dispatch进行分发;
根据 Reactor 的数量和处理资源池线程的数量不同,有 3 种典型的实现

  • 单 Reactor 单线程

  • 单 Reactor 多线程

  • 主从 Reactor 多线程

Netty模型
BossGroup 线程维护Selector , 只关注Accecpt;
当接收到Accept事件,获取到对应的SocketChannel, 封装成 NIOScoketChannel并注册到Worker 线程(事件循环), 并进行维护;
当Worker线程监听到selector 中通道发生自己感兴趣的事件后,就进行处理(就由handler), 注意handler 已经加入到通道。

posted @ 2025-04-21 22:45  V02445  阅读(36)  评论(0)    收藏  举报