BIO、NIO、AIO、Netty从简单明确到使用

[复制链接]
发表于 2025-10-21 05:07:09 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
Java编程中BIO、NIO、AIO是三种差异的I/O(输入/输出)模子,它们代表了差异的I/O处置惩罚方式。
Netty就是基于Java的NIO(New Input/Output)类库编写的一个高性能、异步变乱驱动的网络应用步调框架,用于快速开发可维护的高性能协议服务器和客户端。
先来相识一下根本的三种根本的io模子:
BIO(Blocking I/O,壅闭I/O)

界说BIO是Java最传统的I/O模子,基于流的同步壅闭I/O操纵。每个毗连都会占用一个线程,当举行I/O操纵时,线程会被壅闭,直到操纵完成。
特点
同步壅闭:每个I/O操纵都会壅闭当火线程,直到操纵完成。
线程占用:每个毗连必要一个独立的线程,线程资源斲丧较大。
实现简单:代码实现相对简单,易于明确和编写。
作用:实用于毗连数较少且固定的架构,如传统的C/S架构。由于着实现简单、编程直观,因此在一些简单的网络编程场景中仍然被使用。
创建客户端和服务端演示数据吸收与传输。
  1. //服务端
  2. public static void main(String[] args) {
  3.         try (ServerSocket serverSocket = new ServerSocket(9999)) {
  4.             while (true) {
  5.                 Socket socket = serverSocket.accept(); // 持续监听新连接
  6.                 new Thread(() -> { // 为每个客户端创建独立线程
  7.                     try (BufferedReader reader = new BufferedReader(
  8.                             new InputStreamReader(socket.getInputStream()))) {
  9.                         String line;
  10.                         while ((line = reader.readLine()) != null) {
  11.                             System.out.println("客户端[" + socket.getPort() + "] 消息: " + line);
  12.                         }
  13.                     } catch (IOException e) {
  14.                         e.printStackTrace();
  15.                     }
  16.                 }).start();
  17.             }
  18.         } catch (IOException e) {
  19.             e.printStackTrace();
  20.         }
  21.     }
  22. //客户端
  23. public static void main(String[] args) {
  24.         System.out.println("客户端启动...");
  25.         try {
  26.             //创建套接字
  27.             Socket socket = new Socket("127.0.0.1", 9999);
  28.             //获取输出流发送消息
  29.             PrintStream ps = new PrintStream(socket.getOutputStream());
  30.             Scanner sc = new Scanner(System.in);
  31.             while (true){
  32.                 System.out.println("请输入信息:");
  33.                 ps.println(sc.nextLine());
  34.                 ps.flush();
  35.             }
  36.         } catch (IOException e) {
  37.             throw new RuntimeException(e);
  38.         }
  39.     }
复制代码
NIO(Non-blocking I/O,非壅闭I/O)

界说:NIO是Java 1.4引入的新I/O API,基于通道(Channel)和缓冲区(Buffer)的非壅闭I/O操纵。
特点
非壅闭:I/O操纵不会壅闭线程,线程可以在期待数据时实行其他使命。
多路复用:通过Selector可以管理多个Channel,进步了资源使用率。
性能:实用于高并发场景,镌汰线程开销和上下文切换。
焦点组件
缓冲区(Buffer):用于存储数据的固定巨细的内存地域,提供了多种范例的缓冲区,如ByteBuffer、CharBuffer等。
通道(Channel):用于数据读写的通道,支持非壅闭模式,与缓冲区共同使用。
选择器(Selector):允许单个线程同时处置惩罚多个通道的I/O变乱。
作用:实用于毗连数较多且毗连较短的架构,如高并发的服务器端应用。NIO提供了更高效、更机动的I/O操纵方式,明显进步了体系的并发性能和吞吐量。
  1. //服务端
  2.     public static void main(String[] args) throws Exception {
  3.         //创建连接通道,ServerSocketChannel 绑定端口,监听新的客户端连接请求。它本身不处理任何数据传输。
  4.         ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
  5.         // 设置通道为非阻塞模式
  6.         serverSocketChannel.configureBlocking(false);
  7.         //绑定端口
  8.         serverSocketChannel.bind(new InetSocketAddress(9999));
  9.         //创建 Selector 并注册 ACCEPT 事件
  10.         Selector selector = Selector.open();
  11.         serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
  12.         System.out.println("NIO 服务端启动...");
  13.         //阻塞等待事件发生
  14.         while (selector.select()>0){
  15.             //获取到所有事件
  16.             Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
  17.             //遍历所有事件
  18.             while (iterator.hasNext()){
  19.                 SelectionKey key = iterator.next();
  20.                 //检查是否有新的连接请求
  21.                 if(key.isAcceptable()){
  22.                     //为每一个创建新的通道
  23.                     SocketChannel clientChannel = serverSocketChannel.accept();
  24.                     clientChannel.configureBlocking(false);
  25.                     clientChannel.register(selector,SelectionKey.OP_READ);
  26.                 }else if(key.isReadable()){
  27.                     //处理读数据
  28.                     SocketChannel channel = (SocketChannel)key.channel();
  29.                     ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
  30.                     int bytesRead = 0;
  31.                     while ((bytesRead = channel.read(byteBuffer)) > 0){
  32.                         byteBuffer.flip();
  33.                         String message = new String(byteBuffer.array(),0,bytesRead);
  34.                         System.out.println("接收客户端信息:"+message);
  35.                         byteBuffer.clear();
  36.                         //回写数据给客户端
  37.                         ByteBuffer response = ByteBuffer.wrap(("ECHO: " + message).getBytes());
  38.                         channel.write(response);
  39.                     }
  40.                 }
  41.                 iterator.remove();
  42.             }
  43.         }
  44.     }
  45. //客户端
  46. public static void main(String[] args) throws Exception {
  47.         // 1. 创建 SocketChannel 并连接服务器
  48.         SocketChannel socketChannel = SocketChannel.open();
  49.         socketChannel.connect(new InetSocketAddress("127.0.0.1", 9999));
  50.         socketChannel.configureBlocking(false); // 非阻塞模式
  51.         System.out.println("NIO 客户端已连接");
  52.         //接收线程
  53.         new Thread(() -> {
  54.             ByteBuffer buffer = ByteBuffer.allocate(1024);
  55.             while (true) {
  56.                 try {
  57.                     int bytesRead = socketChannel.read(buffer);
  58.                     if (bytesRead > 0) {
  59.                         buffer.flip();
  60.                         byte[] data = new byte[buffer.remaining()];
  61.                         buffer.get(data);
  62.                         System.out.println("收到服务端响应: " + new String(data));
  63.                         buffer.clear();
  64.                     }
  65.                 } catch (IOException e) {
  66.                     System.out.println("连接已断开");
  67.                     break;
  68.                 }
  69.             }
  70.         }).start();
  71.         //发送消息
  72.         ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
  73.         Scanner sc = new Scanner(System.in);
  74.         while (true){
  75.             System.out.println("请输入信息:");
  76.             String msg = sc.nextLine();
  77.             byteBuffer.put(("AA:"+msg).getBytes(StandardCharsets.UTF_8));
  78.             byteBuffer.flip();
  79.             socketChannel.write(byteBuffer);
  80.             byteBuffer.clear();
  81.         }
  82.     }
复制代码
AIO(Asynchronous I/O,异步I/O)

界说:AIO是Java 7引入的异步I/O API,基于异步通道(AsynchronousChannel)和异步回调机制。
特点
异步非壅闭:I/O操纵是异步的,通过回调处置惩罚结果,不壅闭线程。
回调机制:通过回调函数处置惩罚I/O操纵结果,进步了步调的机动性。
高性能:实用于高并发、高吞吐量的场景。
工作方式
步调发起一个异步I/O哀求,并提供一个回调函数。
步调无需期待I/O操纵完成,而是立刻返回,继续实行其他使命。
当I/O操纵完成后,体系会调用之条件供的回调函数,转达结果或状态信息。
作用:实用于毗连数较多且毗连时间较长的架构,如高并发的服务器端应用。AIO提供了真正的异步I/O操纵方式,进一步进步了体系的并发性能和吞吐量。
Netty

先相识一下netty出现的缘故原由,是干嘛用的?

  • Netty出现的缘故原由:
Java NIO的复杂性:
Java NIO固然提供了非壅闭IO的本领,但其API设盘算为底层,使用起来比力复杂。开发者必要处置惩罚大量的细节,如选择器的管理、缓冲区的操纵等,这增长了开发的难度和堕落的风险。
性能瓶颈:
在高并发场景下,直接使用Java NIO举行网络编程大概会碰到性能瓶颈。比方,选择器的实现和多线程管理的复杂性大概导致性能降落。
缺乏高级功能
Java NIO仅提供了根本的非壅闭IO机制,缺乏一些高级功能,如协议编解码、连继承理等。这些功能在开发网络应用时非常紧张,但实现起来却相对复杂。

  • Netty的具体用途是什么?
简化网络编程:
Netty封装了Java NIO的复杂性,提供了一套简便易用的API。开发者可以使用这些API快速构建网络应用,而无需关注底层的细节。
进步性能:
Netty采取了异步非壅闭IO模子,并支持零拷贝等技能,可以在包管高性能的同时,镌汰CPU和内存资源的斲丧。这使得Netty在高并发场景下表现尤为精彩。
支持多种协议:
Netty内置了对多种协议的支持,如TCP、UDP、HTTP、WebSocket等。开发者可以轻松地使用这些协议构建网络应用,而无需自己实现协议编解码等复杂功能。
机动的扩展性:
Netty提供了丰富的扩展点,如ChannelHandler、Codec等。开发者可以通过实现这些接口来扩展Netty的功能,以满足特定的业务需求。
广泛的应用场景:
Netty颠末广泛的使用和验证,具有高稳固性和可靠性,实用于各种网络应用场景,如分布式体系、微服务架构中的通讯组件、及时通讯体系、游戏服务器等。
它提供了丰富的错误处置惩罚和规复机制,可以大概有用地处置惩罚网络通讯中的各种非常环境。
netty焦点上风:
异步变乱驱动、零拷贝、内存池、高度可定制。

  • 焦点组件
先大概相识一下 Reactor 模子(单线程、多线程、主从多线程)。
由于Reactor 模式是 Netty 高性能和高并发本领的焦点计划根本。Netty 的线程模子、变乱驱动机制和异步非壅闭 I/O 都深度依靠 Reactor 模式。
Reactor 模式是一种 变乱驱动的计划模式,用于处置惩罚高并发的 I/O 哀求。其焦点头脑是 用一个或多个线程监听变乱(如毗连、读写哀求),并将变乱分发给对应的处置惩罚器异步处置惩罚,制止线程壅闭和资源浪费。
Reactor模子的焦点组件
变乱源(Event Source):指任何可以产生I/O变乱的对象,比方网络毗连、文件、装备等。
变乱循环(Event Loop):Reactor模子的焦点,负责监控监控全部变乱源的状态,并在有变乱发生时将其分发给相应的处置惩罚步调(即回调函数)。
变乱处置惩罚器(Event Handler):处置惩罚特定变乱的代码模块,通常实现为回调函数或方法。针对特定的变乱源,每一个变乱源通常都有一个相应的处置惩罚器,用于处置惩罚该变乱源的I/O变乱。
多路复用器(Demultiplexer):用于监督多个变乱源并将发生的变乱关照给Reactor。常见的实现包罗Java的Selector、Linux的epoll等。
Reactor模子的实现方式
单Reactor单线程模子:全部操纵(毗连、读写)由一个线程完成。计划简单,但在高并发场景下轻易成为性能瓶颈。
单Reactor多线程模子:Reactor线程负责监听和分发变乱,而变乱的处置惩罚则交给线程池中的工作线程完成。这种方式可以大概充实使用多核CPU的处置惩罚本领,但在高并发场景下,Reactor线程大概成为性能瓶颈。
主从Reactor多线程模子(Netty 默认模子-高并发):也称为多Reactor多线程模子。主Reactor线程负责监听和分发毗连变乱,当有新的毗连到来时,将其分发给从Reactor线程处置惩罚。从Reactor线程负责监听和分发读写变乱,并交给线程池中的工作线程处置惩罚。这种方式可以大概进一步进步体系的并发处置惩罚本领和可扩展性。
写个netty服务端示例看一下:
  1. // 1. 创建主 Reactor(处理连接)管理一组 EventLoop,每个线程绑定一个 EventLoop(事件循环线程)
  2. EventLoopGroup bossGroup = new NioEventLoopGroup(1);  // 主线程组(通常 1 个线程)
  3. // 2. 创建子 Reactor(处理 I/O)
  4. EventLoopGroup workerGroup = new NioEventLoopGroup();  // 子线程组(默认 CPU 核数 × 2)
  5. //Reactor 模式配置入口        绑定主/子线程组、设置 Channel 类型
  6. ServerBootstrap server = new ServerBootstrap();
  7. server.group(bossGroup, workerGroup)  // 绑定主从线程组
  8.        .channel(NioServerSocketChannel.class)  // 设置 Channel 类型(NIO)代表一个 Socket 连接或监听端口
  9.        .childHandler(new ChannelInitializer<SocketChannel>() {
  10.            @Override
  11.            protected void initChannel(SocketChannel ch) {
  12.                ch.pipeline().addLast(new ServerHandler());  // 添加业务处理器,这里需要自己实现
  13.            }
  14.        });
  15. // 3. 绑定端口并启动服务
  16. ChannelFuture future = server.bind(8080).sync();
  17. //等待服务器通道关闭
  18. future.channel().closeFuture().sync();
  19. //关闭通道
  20. finally {
  21.             workerGroup.shutdownGracefully();
  22.             bossGroup.shutdownGracefully();
  23.         }
  24. //业务处理器
  25. public class ServerHandler extends ChannelInboundHandlerAdapter {
  26.     @Override
  27.     public void channelActive(ChannelHandlerContext ctx) throws Exception {
  28.         System.out.println("客户端连接成功...");
  29.     }
  30.    }
复制代码

  • 组件分析:
Channel:
在Netty中,Channel是一个焦点概念,Channel提供了数据的读取和写入操纵,以及与长途端点举行通讯的本领。
通过Channel可以获取到许多信息,比如:本地所在、长途所在、Channel的EventLoop、Channel的Pipeline等。
Channel 生命周期:
channelRegistered (初始化操纵)→ channelActive (毗连)→ channelRead(数据吸收) → channelInactive(断开)→ channelUnregistered(注销)
Selector:
Selector是Java NIO(New Input/Output)中的一个焦点组件,用于监控监控多个Channel(通道)的状态,比方毗连、读、写等变乱。在netty中Selector被封装在NioEventLoop中,当Channel注册到Selector并指定感爱好的变乱范例(如毗连、读、写等)后,会返回一个SelectionKey,用于表现Selector与Channel之间的关联关系。Selector会不绝地轮询其注册的Channel,假如有变乱发生,Selector会将相应的SelectionKey放入停当变乱聚集中。变乱循环线程会从停当变乱聚集中取出SelectionKey,并根据变乱范例调用相应的ChannelHandler举行处置惩罚。处置惩罚完成后,变乱循环线程会将Channel放回到Selector中,继续期待下一次变乱的发生。
EventLoopGroup:
管理一组 EventLoop,通常分为 bossGroup(处置惩罚毗连)和 workerGroup(处置惩罚 I/O)。
设置线程数:new NioEventLoopGroup(4) 表现 4 个线程。
ServerBootstrap:
用于启动服务器端的引导类,设置服务器参数和处置惩罚器链设置、启动服务器、关闭服务器。

  • 设置服务器参数:
线程模子:ServerBootstrap允许您设置两个紧张的线程组:bossGroup和workerGroup。
bossGroup用于处置惩罚客户端的毗连哀求,而workerGroup用于处置惩罚已毗连的客户端的I/O操纵。
通道范例:通过channel方法,您可以指定服务器使用的通道范例。对于NIO传输,通常使用NioServerSocketChannel。
通道选项:使用option方法可以设置服务器通道的选项,如SO_BACKLOG(TCP毗连哀求的最大队列长度)和SO_REUSEADDR(允许所在重用)。
子通道选项:childOption方法用于设置已毗连客户端的通道选项,如SO_KEEPALIVE(保持毗连活动状态)。
Option:可以应用于Channel或Bootstrap上。
ChannelOption:专门用于设置Channel的参数。

  • 处置惩罚器链设置:
父处置惩罚器:通过handler方法,您可以设置处置惩罚服务器通道I/O变乱的处置惩罚器。这通常用于处置惩罚毗连哀求。
子处置惩罚器:childHandler方法用于设置处置惩罚已毗连客户端I/O变乱的处置惩罚器。这是开发者编写业务逻辑处置惩罚代码的地方,通常通过添加一系列的ChannelHandler来实现。
ChannelHandler
ChannelHandler是ChannelPipeline中的根本处置惩罚单位,负责处置惩罚或拦截入站和出站变乱。每个ChannelHandler都有一个关联的ChannelHandlerContext,通过它可以方便地与其他组件举行交互。ChannelPipeline则是ChannelHandler的容器,负责ChannelHandler的管理和变乱拦截。
pipeline、ChannelPipeline
Pipeline是ChannelPipeline的简称。ChannelPipeline用于处置惩罚网络变乱和数据的活动。负责将一系列的处置惩罚逻辑(称为ChannelHandler)串联起来,形成一个处置惩罚链,对网络变乱举行拦截和处置惩罚。
变乱处置惩罚:ChannelPipeline负责处置惩罚入站(Inbound)和出站(Outbound)变乱,如数据读取、数据写入、毗连创建、毗连断开等。
拦截器链:ChannelPipeline内部维护了一个拦截器链(或称为处置惩罚器链),每个拦截器(即ChannelHandler)都可以对变乱举行处置惩罚或拦截。
动态性:ChannelPipeline允许在运行时动态地添加、删除或更换拦截器,从而机动地扩展和定制网络处置惩罚逻辑。

  • 启动服务器:
    设置完成后,通过调用bind方法并传入服务器的端标语,ServerBootstrap将启动服务器并绑定到指定的端口。
    bind方法返回一个ChannelFuture对象,您可以使用sync方法期待服务器启动完成。
    ChannelFuture是Netty框架中特有的接口,继续自Java的Future接口。在Netty中,全部的I/O操纵都是异步的,ChannelFuture用于在操纵完成时关照应用步调,以便应用步调可以实行某些操纵或检索操纵的结果。
  • 关闭:
当服务器必要关闭时,可以调用ChannelFuture对象的channel().closeFuture().sync()方法,期待服务器通道关闭。
末了,调用bossGroup和workerGroup的shutdownGracefully方法,以优雅地断开毗连并关闭线程组。
ChannelHandlerContext:
ChannelHandlerContext封装了Channel和ChannelPipeline,使得ChannelHandler可以方便地访问和操纵ChannelPipeline和Channel。
ChannelHandlerContext提供了许多方法,用于操纵和流传变乱,如写入数据、革新数据、触发读变乱等。
ChannelHandlerContext允许ChannelHandler将变乱转达给ChannelPipeline中的下一个处置惩罚器。比方,当一个入站变乱(如数据读取)发生时,ChannelHandlerContext可以调用fireChannelRead方法,将变乱转达给下一个入站处置惩罚器。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
继续阅读请点击广告
回复

使用道具 举报

×
登录参与点评抽奖,加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表