I/O 的概念

I/O(Input/Output)指输入输出操作,是程序与外部设备(如磁盘、网络、键盘等)交互的过程。核心目标是实现数据的高效读写,分为磁盘 I/O、网络 I/O 等类型。


同步 vs 异步、阻塞 vs 非阻塞

同步/异步:关注任务完成的通知机制。同步需等待任务完成,异步通过回调或事件通知。
阻塞/非阻塞:关注等待时的线程状态。阻塞会挂起线程,非阻塞立即返回状态。

示例:

  • 同步阻塞:读取文件时线程阻塞直到数据就绪。
  • 异步非阻塞:通过回调通知数据就绪,线程可处理其他任务。

BIO(Blocking I/O)

BIO 是同步阻塞模型,每个连接需独立线程处理。线程在读写时阻塞直到操作完成。
示例:

ServerSocket serverSocket = new ServerSocket(8080);
while (true) {
    Socket socket = serverSocket.accept(); // 阻塞
    new Thread(() -> handleRequest(socket)).start();
}

NIO(Non-blocking I/O)

NIO 是同步非阻塞模型,通过 Selector 监听多个 Channel 的事件(如读写就绪),避免线程阻塞。
核心组件:

  • Channel:双向数据传输通道。
  • Buffer:数据容器。
  • Selector:多路事件监听器。

示例:

Selector selector = Selector.open();
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_READ);
while (true) {
    selector.select(); // 阻塞直到事件就绪
    Set<SelectionKey> keys = selector.selectedKeys();
    // 处理事件...
}

AIO(Asynchronous I/O)

AIO 是异步非阻塞模型,操作系统完成 I/O 后主动回调,无需线程轮询。
核心类:

  • AsynchronousSocketChannel
  • CompletionHandler

示例:

AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open();
server.bind(new InetSocketAddress(8080));
server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
    @Override
    public void completed(AsynchronousSocketChannel client, Void attachment) {
        // 处理连接...
    }
});

Netty 简介

Netty 是基于 NIO 的高性能网络框架,提供事件驱动、零拷贝等特性,简化 NIO 编程。
核心优势

  • 线程模型优化(如主从 Reactor)。
  • 解决粘包/半包问题(如 LengthFieldBasedFrameDecoder)。

BIO、NIO、AIO 的区别

特性 BIO NIO AIO
模型 同步阻塞 同步非阻塞 异步非阻塞
线程开销 高(1连接1线程) 低(多路复用) 极低(回调驱动)
适用场景 低并发 高并发短连接 高并发长连接

Java IO 流分类

  1. 按方向
    • 输入流(InputStream/Reader)
    • 输出流(OutputStream/Writer)
  2. 按单位
    • 字节流(操作二进制数据)
    • 字符流(处理文本,如 FileReader)

内核空间

内核空间是操作系统核心代码的运行区域,用户程序需通过系统调用(如 read())访问硬件资源。I/O 操作本质是数据在用户空间与内核空间之间的拷贝。


五种 I/O 模型

  1. 阻塞 I/O
  2. 非阻塞 I/O
  3. I/O 多路复用(如 select/poll/epoll)
  4. 信号驱动 I/O
  5. 异步 I/O(如 AIO)

Bit、Byte、Char 的区别

  • Bit:二进制最小单位(0/1)。
  • Byte:8 bits,存储基本数据类型(如 byte)。
  • Char:16 bits(Java 中),表示 Unicode 字符。

对象序列化与反序列化

序列化:将对象转换为字节流(如网络传输或持久化)。
反序列化:将字节流恢复为对象。
示例:

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("data.obj"));
oos.writeObject(myObject); // 序列化

serialVersionUID 的作用

用于版本控制,确保序列化与反序列化的类兼容。未显式定义时,JVM 自动生成,但类结构变化可能导致反序列化失败。

生成方式:

private static final long serialVersionUID = 1L; // 手动指定

BufferedReader

属于字符缓冲流,提升读取效率。
常用方法:

  • readLine():读取一行文本。
  • close():关闭流。

Java IO 流顶级超类

  • 字节流:InputStreamOutputStream
  • 字符流:ReaderWriter

图片/视频必须用字节流

媒体文件是二进制数据,字符流可能因编码转换损坏内容。字节流(如 FileInputStream)直接操作原始字节。


BIO 服务端为何多线程

单线程处理连接时会阻塞,无法并发响应。多线程允许同时处理多个客户端请求,但线程开销大(C10K 问题)。


多线程 BIO 瓶颈

  • 线程创建/销毁成本高。
  • 线程数过多导致上下文切换频繁,CPU 利用率下降。

线程池能否彻底解决 BIO

不能。线程池仅缓解线程创建开销,但高并发时仍受限于线程数量(如池满后请求排队)。


NIO 核心组件

  1. Channel:数据传输管道(如 SocketChannel)。
  2. Buffer:数据容器(如 ByteBuffer)。
  3. Selector:监听多个 Channel 的事件。

Buffer 重要属性

  • capacity:缓冲区容量。
  • position:当前读写位置。
  • limit:可操作数据边界。
  • flip():切换读写模式。

Selector 工作原理

通过系统调用(如 epoll)监听注册的 Channel,当 I/O 事件(如读就绪)发生时,返回可处理的 SelectionKey 集合。


NIO 编程难点

  • 事件处理逻辑复杂(需管理 Buffer、Channel 状态)。
  • 需处理半包/粘包问题(如自定义编解码器)。

AIO 核心类

  • AsynchronousFileChannel:异步文件操作。
  • AsynchronousSocketChannel:异步网络通信。

Netty 线程模型

主从 Reactor 模式:

  • BossGroup:处理连接请求。
  • WorkerGroup:处理 I/O 事件。
  • 每个 Channel 绑定到固定 EventLoop 避免竞争。

Netty 解决粘包/半包

  • 固定长度解码器FixedLengthFrameDecoder)。
  • 分隔符解码器DelimiterBasedFrameDecoder)。
  • 长度字段解码器LengthFieldBasedFrameDecoder)。

Netty 零拷贝

  • Direct Buffer:避免 JVM 堆与 Native 内存拷贝。
  • CompositeByteBuf:合并多个 Buffer 减少拷贝次数。
  • 文件传输:通过 FileRegion 直接发送文件。

Netty 心跳机制

使用 IdleStateHandler 检测空闲连接:

pipeline.addLast(new IdleStateHandler(30, 0, 0, TimeUnit.SECONDS));
pipeline.addLast(new HeartbeatHandler());

ChannelPipeline

责任链模式,包含一系列 ChannelHandler,按顺序处理入站/出站事件。示例:

pipeline.addLast(new DecoderHandler());
pipeline.addLast(new BusinessLogicHandler());

Linux 底层支持

  • BIO:基于系统调用 read()/write()
  • NIO:基于 epoll(高效多路复用)。
  • AIO:依赖 Linux 的 io_uring 或 glibc 的线程池模拟。

Netty 不直接用 AIO

  • Linux AIO 对非文件 I/O 支持不完善。
  • NIO(epoll)已满足高性能需求,且更稳定。

优化 Netty 性能

  • 调整 EventLoopGroup 线程数(通常为 CPU 核数 * 2)。
  • 使用对象池(如 Recycler)减少 GC 压力。
  • 开启 Native EPoll(EpollEventLoopGroup)。

Netty 内存泄漏排查

  • 启用 ResourceLeakDetector
    System.setProperty("io.netty.leakDetection.level", "PARANOID");
    
  • 检查未释放的 ByteBuf 或 Channel。

Netty 应用案例

  • Dubbo:RPC 框架。
  • RocketMQ:消息队列。
  • Elasticsearch:分布式搜索。
  • Spring WebFlux:响应式 Web 框架。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐