BIO、NIO 和 AIO 基础介绍
在 Java 网络编程中,BIO、NIO、AIO 是三种核心的 I/O 模型,分别对应不同的阻塞/非阻塞、同步/异步特性,适用于不同的业务场景。
一、核心概念
1.1 基本定义
- BIO:同步阻塞 I/O(Blocking I/O)
- NIO:同步非阻塞 I/O(Non-blocking I/O),也叫 New I/O,核心是「多路复用」
- AIO:异步非阻塞 I/O(Asynchronous I/O),也叫 NIO 2.0
1.2 重要概念区分
同步/异步 关注的是「是否需要等待 I/O 操作完成」
- 同步:线程主动等待 I/O 结果,直到操作完成
- 异步:线程无需等待,I/O 完成后由系统回调通知
阻塞/非阻塞 关注的是「等待时线程是否被挂起」
- 阻塞:线程在 I/O 过程中被挂起,无法做其他事
- 非阻塞:线程在 I/O 过程中不挂起,可继续执行其他逻辑
1.3 三种模型对比速览
| 模型 | 同步/异步 | 阻塞/非阻塞 | 线程模型 | 适用场景 |
|---|---|---|---|---|
| BIO | 同步 | 阻塞 | 一连接一线程 | 连接数少、低并发 |
| NIO | 同步 | 非阻塞 | 单线程处理多连接 | 高并发、短连接 |
| AIO | 异步 | 非阻塞 | 回调机制、无需轮询 | 高并发、长耗时 I/O |
二、BIO(同步阻塞 I/O)
2.1 核心特点
- 工作原理:线程发起 I/O 请求后,必须阻塞等待 I/O 操作完成(如读取数据、写入数据),期间线程无法做任何事;
- 线程模型:每处理一个连接需要一个独立线程,连接数多的时候会导致线程数暴涨,性能瓶颈明显;
- 编程模型:简单直观,适合连接数少、单次操作耗时短的场景(如简单的客户端-服务器通信)。
2.2 BIO 工作流程图
flowchart LR
Client1[客户端1] --> Server[服务器主线程]
Client2[客户端2] --> Server
Client3[客户端3] --> Server
Server --> Thread1[线程1]
Server --> Thread2[线程2]
Server --> Thread3[线程3]
Thread1 --> Block1[阻塞等待数据]
Thread2 --> Block2[阻塞等待数据]
Thread3 --> Block3[阻塞等待数据]
Block1 --> Process1[处理数据]
Block2 --> Process2[处理数据]
Block3 --> Process3[处理数据]
style Block1 fill:#f96
style Block2 fill:#f96
style Block3 fill:#f96
2.3 代码示例
(1)BIO 服务器
java
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* BIO 同步阻塞服务器示例(优化后:使用线程池)
* 每一个客户端连接对应一个线程,线程阻塞等待数据读取
*/
public class BioServer {
private static final int PORT = 8080;
private static final int MAX_THREADS = 100;
public static void main(String[] args) throws IOException {
// 使用线程池限制线程数量,避免无限创建线程
ExecutorService threadPool = Executors.newFixedThreadPool(MAX_THREADS);
try (ServerSocket serverSocket = new ServerSocket(PORT)) {
System.out.println("BIO 服务器已启动,监听端口 " + PORT + "...");
System.out.println("最大并发线程数:" + MAX_THREADS);
while (true) {
// 阻塞点1:accept() 阻塞直到有客户端连接
Socket socket = serverSocket.accept();
System.out.println("新客户端连接:" + socket.getInetAddress() +
":" + socket.getPort());
// 将连接提交给线程池处理
threadPool.submit(new ClientHandler(socket));
}
}
}
/**
* 客户端处理器(每个连接一个任务)
*/
static class ClientHandler implements Runnable {
private final Socket socket;
public ClientHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try (InputStream inputStream = socket.getInputStream()) {
byte[] buffer = new byte[1024];
while (true) {
// 阻塞点2:read() 阻塞直到有数据可读
int len = inputStream.read(buffer);
if (len == -1) { // 客户端关闭连接
System.out.println("客户端断开连接:" +
socket.getInetAddress() + ":" + socket.getPort());
break;
}
String msg = new String(buffer, 0, len);
System.out.println("[" + Thread.currentThread().getName() +
"] 收到消息:" + msg);
// TODO: 业务处理
// 模拟处理耗时
Thread.sleep(100);
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
(2)BIO 客户端
java
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
/**
* BIO 客户端示例
*/
public class BioClient {
private static final String SERVER_HOST = "localhost";
private static final int SERVER_PORT = 8080;
public static void main(String[] args) {
try (Socket socket = new Socket(SERVER_HOST, SERVER_PORT);
OutputStream outputStream = socket.getOutputStream();
Scanner scanner = new Scanner(System.in)) {
System.out.println("已连接到服务器 " + SERVER_HOST + ":" + SERVER_PORT);
System.out.println("请输入要发送的消息(输入 'exit' 退出):");
while (true) {
System.out.print("> ");
String msg = scanner.nextLine();
if ("exit".equalsIgnoreCase(msg)) {
break;
}
// 发送数据(阻塞,直到数据写入完成)
outputStream.write(msg.getBytes());
outputStream.flush();
System.out.println("消息已发送");
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("客户端已关闭");
}
}
2.4 BIO 优缺点
| 优点 | 缺点 |
|---|---|
| 编程模型简单,易于理解和实现 | 线程资源消耗大,连接数多时性能急剧下降 |
| 代码调试方便,执行流程清晰 | 线程阻塞造成资源浪费,CPU 利用率低 |
| 适合低并发、小数据量场景 | 系统稳定性差,容易因线程过多导致宕机 |
三、NIO(同步非阻塞 I/O)
3.1 核心特点
- 工作原理:线程发起 I/O 请求后,无需阻塞等待,可先去处理其他任务,定期轮询查看 I/O 是否完成;
- 核心组件 :
Channel(通道):双向读写,替代 BIO 的 StreamBuffer(缓冲区):数据读写的载体Selector(多路复用器):核心组件,单线程监听多个 Channel 的事件
- 多路复用:一个 Selector 可以监听多个 Channel 的事件(连接、读、写),单个线程就能处理大量连接,解决 BIO 线程膨胀问题;
- 适用场景:连接数多、单次操作耗时短的场景(如高并发的 Web 服务器)。
3.2 NIO 多路复用核心流程图
flowchart TD
A["创建 ServerSocketChannel"] --> B["配置为非阻塞模式"]
B --> C["绑定监听端口"]
C --> D["创建 Selector(多路复用器)"]
D --> E["将 ServerSocketChannel 注册到 Selector,关注 ACCEPT 事件"]
E --> F["Selector 轮询就绪事件 select()"]
F --> G{"是否有就绪事件?"}
G -- 无 --> F
G -- 有 --> H["获取就绪的 SelectionKey 集合"]
H --> I["遍历 SelectionKey 集合"]
I --> J{判断事件类型}
J -- ACCEPT新连接--> K["通过 ServerSocketChannel 接收 SocketChannel"]
K --> L["配置 SocketChannel 为非阻塞模式"]
L --> M["将 SocketChannel 注册到 Selector,关注 READ 事件"]
M --> F
J -- READ可读--> N["通过 SocketChannel 读取数据到 Buffer"]
N --> O["处理业务逻辑"]
O --> P{"是否需要返回数据?"}
P -- 是 --> Q["修改关注事件为 WRITE"]
Q --> F
P -- 否 --> F
J -- WRITE可写--> R["通过 SocketChannel 写入 Buffer 数据"]
R --> S{"数据写入完成?"}
S -- 是 --> T["修改关注事件为 READ"]
T --> F
S -- 否 --> R
3.3 NIO 代码示例
java
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* NIO 多路复用服务器示例(完整版)
* 单个线程通过 Selector 处理所有客户端连接和读写事件
*/
public class NioServer {
private static final int PORT = 8080;
private static final int BUFFER_SIZE = 1024;
private static final long SELECTOR_TIMEOUT = 1000; // 1秒超时
// 统计在线客户端数
private static final ConcurrentHashMap<SocketChannel, String> clients =
new ConcurrentHashMap<>();
public static void main(String[] args) {
try {
new NioServer().start();
} catch (IOException e) {
e.printStackTrace();
}
}
public void start() throws IOException {
// 1. 创建 ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 2. 配置为非阻塞模式
serverSocketChannel.configureBlocking(false);
// 3. 绑定端口
serverSocketChannel.bind(new InetSocketAddress(PORT));
System.out.println("NIO 服务器已启动,监听端口 " + PORT + "...");
// 4. 创建 Selector
Selector selector = Selector.open();
// 5. 注册 ServerSocketChannel 到 Selector,关注 ACCEPT 事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
// 6. 主循环:处理就绪事件
while (true) {
// 阻塞等待就绪事件(带超时)
int readyChannels = selector.select(SELECTOR_TIMEOUT);
if (readyChannels == 0) {
continue; // 无就绪事件,继续轮询
}
// 获取所有就绪的 SelectionKey
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
// 遍历处理每个就绪事件
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove(); // 必须移除,避免重复处理
try {
// 处理事件
if (!key.isValid()) {
continue;
}
if (key.isAcceptable()) {
handleAccept(key, selector);
} else if (key.isReadable()) {
handleRead(key);
} else if (key.isWritable()) {
handleWrite(key);
}
} catch (Exception e) {
e.printStackTrace();
// 异常时关闭连接
key.cancel();
closeChannel(key.channel());
}
}
// 可选:打印在线客户端数
System.out.println("当前在线客户端数:" + clients.size());
}
}
/**
* 处理新连接
*/
private void handleAccept(SelectionKey key, Selector selector) throws IOException {
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
// 接收新连接(非阻塞)
SocketChannel socketChannel = serverChannel.accept();
if (socketChannel == null) return;
// 配置为非阻塞模式
socketChannel.configureBlocking(false);
// 注册 READ 事件
socketChannel.register(selector, SelectionKey.OP_READ,
ByteBuffer.allocate(BUFFER_SIZE));
// 记录客户端
String clientInfo = socketChannel.getRemoteAddress().toString();
clients.put(socketChannel, clientInfo);
System.out.println("新客户端连接:" + clientInfo);
System.out.println("当前在线客户端数:" + clients.size());
}
/**
* 处理读事件
*/
private void handleRead(SelectionKey key) throws IOException {
SocketChannel socketChannel = (SocketChannel) key.channel();
ByteBuffer buffer = (ByteBuffer) key.attachment();
// 读取数据(非阻塞)
int bytesRead = socketChannel.read(buffer);
if (bytesRead == -1) {
// 客户端关闭连接
String clientInfo = clients.remove(socketChannel);
System.out.println("客户端断开连接:" + clientInfo);
key.cancel();
closeChannel(socketChannel);
return;
}
if (bytesRead > 0) {
// 切换到读模式
buffer.flip();
// 读取数据
byte[] data = new byte[buffer.limit()];
buffer.get(data);
String message = new String(data).trim();
// 获取客户端信息
String clientInfo = clients.get(socketChannel);
System.out.println("收到来自 " + clientInfo + " 的消息:" + message);
// 清空缓冲区,准备下次读取
buffer.clear();
// 准备响应数据
String response = "服务器已收到: " + message;
ByteBuffer responseBuffer = ByteBuffer.wrap(response.getBytes());
// 如果需要返回数据,修改关注事件为 WRITE
key.interestOps(SelectionKey.OP_WRITE);
key.attach(responseBuffer);
}
}
/**
* 处理写事件
*/
private void handleWrite(SelectionKey key) throws IOException {
SocketChannel socketChannel = (SocketChannel) key.channel();
ByteBuffer buffer = (ByteBuffer) key.attachment();
// 写入数据(非阻塞)
socketChannel.write(buffer);
if (!buffer.hasRemaining()) {
// 数据写入完成,重新关注 READ 事件
key.interestOps(SelectionKey.OP_READ);
key.attach(ByteBuffer.allocate(BUFFER_SIZE));
}
}
/**
* 关闭通道
*/
private void closeChannel(Channel channel) {
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3.4 NIO 优缺点
| 优点 | 缺点 |
|---|---|
| 单线程可处理大量连接,资源消耗小 | 编程复杂度高,需要处理各种边界情况 |
| 高并发下性能优秀,CPU 利用率高 | 调试会比较困难,容易出现 bug |
| 数据读写灵活,可精确控制缓冲区 | 需要处理半包/粘包问题 |
四、AIO(异步非阻塞 I/O)
4.1 核心特点
- 工作原理:线程发起 I/O 请求后,直接返回,无需轮询,I/O 操作由操作系统完成后,通过回调函数通知线程处理结果;
- 核心组件 :
AsynchronousServerSocketChannel:异步服务器通道AsynchronousSocketChannel:异步客户端通道CompletionHandler:回调处理器
- 优势:无需手动多路复用,由系统底层完成,编程模型更简洁;
- 适用场景:连接数多、单次操作耗时长的场景(如文件下载、大数据传输)。
4.2 AIO 工作流程图
sequenceDiagram
participant App as 应用程序
participant Kernel as 操作系统内核
participant Callback as 回调处理器
App->>Kernel: 1. 发起异步读操作
Note over App: 立即返回,不阻塞
App->>App: 2. 继续执行其他任务
Kernel->>Kernel: 3. 内核准备数据
Kernel->>Kernel: 4. 内核将数据拷贝到缓冲区
Kernel->>Callback: 5. I/O 完成,触发回调
Callback->>App: 6. 处理数据
Note over App,Kernel: 整个过程应用程序无需阻塞等待
4.3 AIO 代码示例
java
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* AIO 异步非阻塞服务器示例(完整版)
* 基于回调处理连接和读写事件,无需轮询
*/
public class AioServer {
private static final int PORT = 8080;
private static final int BUFFER_SIZE = 1024;
private static final ConcurrentHashMap<AsynchronousSocketChannel, String> clients =
new ConcurrentHashMap<>();
// 用于处理回调的业务线程池
private ExecutorService businessThreadPool = Executors.newFixedThreadPool(10);
public static void main(String[] args) {
try {
new AioServer().start();
} catch (IOException e) {
e.printStackTrace();
}
}
public void start() throws IOException {
// 1. 创建异步服务器通道
AsynchronousServerSocketChannel serverChannel =
AsynchronousServerSocketChannel.open();
// 2. 绑定端口
serverChannel.bind(new InetSocketAddress(PORT));
System.out.println("AIO 服务器已启动,监听端口 " + PORT + "...");
// 3. 异步接收连接
serverChannel.accept(null, new AcceptCompletionHandler(serverChannel));
// 4. 防止主线程退出
try {
Thread.currentThread().join();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
businessThreadPool.shutdown();
}
}
/**
* 连接接收回调处理器
*/
class AcceptCompletionHandler implements
CompletionHandler<AsynchronousSocketChannel, Void> {
private final AsynchronousServerSocketChannel serverChannel;
public AcceptCompletionHandler(AsynchronousServerSocketChannel serverChannel) {
this.serverChannel = serverChannel;
}
@Override
public void completed(AsynchronousSocketChannel socketChannel, Void attachment) {
// 立即再次接收新连接(关键:必须再次调用accept,形成循环)
serverChannel.accept(null, this);
try {
// 处理新连接
String clientInfo = socketChannel.getRemoteAddress().toString();
clients.put(socketChannel, clientInfo);
System.out.println("新客户端连接:" + clientInfo);
System.out.println("当前在线客户端数:" + clients.size());
// 创建缓冲区
ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
// 异步读取数据
socketChannel.read(buffer, buffer, new ReadCompletionHandler(socketChannel));
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, Void attachment) {
System.err.println("接收连接失败:" + exc.getMessage());
// 继续接收(失败后重试)
serverChannel.accept(null, this);
}
}
/**
* 读取数据回调处理器
*/
class ReadCompletionHandler implements
CompletionHandler<Integer, ByteBuffer> {
private final AsynchronousSocketChannel socketChannel;
public ReadCompletionHandler(AsynchronousSocketChannel socketChannel) {
this.socketChannel = socketChannel;
}
@Override
public void completed(Integer bytesRead, ByteBuffer buffer) {
if (bytesRead == -1) {
// 客户端关闭连接
String clientInfo = clients.remove(socketChannel);
System.out.println("客户端断开连接:" + clientInfo);
try {
socketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
return;
}
if (bytesRead > 0) {
// 切换到读模式
buffer.flip();
// 读取数据
byte[] data = new byte[buffer.limit()];
buffer.get(data);
String message = new String(data).trim();
// 获取客户端信息
String clientInfo = clients.get(socketChannel);
// 业务处理(提交到业务线程池,避免阻塞回调线程)
businessThreadPool.submit(() -> {
try {
System.out.println("收到来自 " + clientInfo + " 的消息:" + message);
// 模拟业务处理
String processedMsg = processMessage(message);
// 准备响应
String response = "服务器响应: " + processedMsg;
ByteBuffer responseBuffer = ByteBuffer.wrap(response.getBytes());
// 异步写入响应
socketChannel.write(responseBuffer, null,
new WriteCompletionHandler(socketChannel, buffer));
} catch (Exception e) {
e.printStackTrace();
}
});
}
}
@Override
public void failed(Throwable exc, ByteBuffer buffer) {
System.err.println("读取数据失败:" + exc.getMessage());
try {
socketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 模拟消息处理
*/
private String processMessage(String message) {
// TODO: 实际的业务处理
return "processed: " + message;
}
}
/**
* 写入数据回调处理器
*/
class WriteCompletionHandler implements
CompletionHandler<Integer, ByteBuffer> {
private final AsynchronousSocketChannel socketChannel;
private final ByteBuffer buffer;
public WriteCompletionHandler(AsynchronousSocketChannel socketChannel,
ByteBuffer buffer) {
this.socketChannel = socketChannel;
this.buffer = buffer;
}
@Override
public void completed(Integer result, ByteBuffer attachment) {
// 数据写入完成后,清空缓冲区并继续读取下一条消息
buffer.clear();
socketChannel.read(buffer, buffer, new ReadCompletionHandler(socketChannel));
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
System.err.println("写入数据失败:" + exc.getMessage());
try {
socketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
4.4 AIO 优缺点
| 优点 | 缺点 |
|---|---|
| 真正的异步 I/O,无需轮询 | 编程复杂度高,回调嵌套容易造成混乱 |
| 适合长连接、大文件传输 | JDK 对 AIO 的支持不如 NIO 成熟 |
| 系统资源利用率高 | 调试困难,错误处理复杂 |
五、三种 I/O 模型深度对比
5.1 数据流对比图
flowchart LR
subgraph BIO["BIO 流程"]
direction TB
B1["发起读请求"] --> B2["阻塞等待内核准备数据"]
B2 --> B3["阻塞等待内核拷贝数据"]
B3 --> B4["处理数据"]
end
subgraph NIO["NIO 流程"]
direction TB
N1["发起读请求"] --> N2{"数据是否就绪?"}
N2 -- 否 --> N3[立即返回
继续其他任务] N3 --> N4[轮询检查] N4 --> N2 N2 -- 是 --> N5[阻塞等待数据拷贝] N5 --> N6[处理数据] end subgraph AIO[AIO 流程] direction TB A1[发起异步读请求] --> A2[立即返回] A2 --> A3[继续其他任务] A1 --> K[内核准备数据并拷贝] K --> A4[回调通知] A4 --> A5[处理数据] end BIO ~~~ NIO ~~~ AIO
继续其他任务] N3 --> N4[轮询检查] N4 --> N2 N2 -- 是 --> N5[阻塞等待数据拷贝] N5 --> N6[处理数据] end subgraph AIO[AIO 流程] direction TB A1[发起异步读请求] --> A2[立即返回] A2 --> A3[继续其他任务] A1 --> K[内核准备数据并拷贝] K --> A4[回调通知] A4 --> A5[处理数据] end BIO ~~~ NIO ~~~ AIO
5.2 详细对比表
| 对比维度 | BIO | NIO | AIO |
|---|---|---|---|
| I/O 模型 | 同步阻塞 | 同步非阻塞 | 异步非阻塞 |
| 线程模型 | 一个连接一个线程 | 一个线程处理多个连接 | 回调机制,不占用线程 |
| 数据拷贝 | 应用线程负责 | 应用线程负责 | 内核完成,回调通知 |
| 编程复杂度 | 简单 | 中等 | 复杂 |
| 调试难度 | 容易 | 中等 | 困难 |
| CPU 利用率 | 低(线程阻塞) | 高 | 高 |
| 内存占用 | 高(线程多) | 低 | 低 |
| 并发能力 | 低 | 高 | 高 |
| 吞吐量 | 低 | 高 | 高 |
| 响应时间 | 中等 | 低 | 低 |
| 典型应用 | 传统 Socket,JDBC | Tomcat,Netty,Jetty | 文件服务器,专业中间件 |
| Java 版本 | JDK 1.0+ | JDK 1.4+ | JDK 1.7+ |
5.3 性能对比场景
| 场景 | BIO | NIO | AIO | 推荐方案 |
|---|---|---|---|---|
| 连接数 < 1000 | 较好 | 好 | 好 | BIO(简单) |
| 连接数 1000-10000 | 差 | 好 | 好 | NIO |
| 连接数 > 10000 | 不可用 | 较好 | 好 | AIO |
| 短连接、高频请求 | 差 | 优秀 | 好 | NIO |
| 长连接、低频交互 | 差 | 好 | 优秀 | AIO |
| 大文件传输 | 差 | 中等 | 优秀 | AIO |
| 实时性要求高 | 中等 | 优秀 | 优秀 | NIO/AIO |
六、实际应用建议
6.1 技术选型指南
-
简单应用,连接数少(<100):
- 选择 BIO,开发简单,维护容易
- 示例:小型管理系统、内部工具
-
高并发 Web 服务(1000-10000 连接):
- 选择 NIO,性能好,生态丰富
- 示例:Web 服务器、API 网关、即时通讯
-
大文件传输、长连接:
- 选择 AIO,真正的异步 I/O
- 示例:文件服务器、视频流媒体
-
中间件开发:
- 选择 NIO(如 Netty),稳定性好,社区活跃
- 示例:RPC 框架、消息队列
- 大多数场景:选择 NIO(通过 Netty 等框架)
- 简单场景:BIO 足够
- 特殊场景(大文件、长连接):考虑 AIO
6.2 常见框架对比
| 框架 | 底层 I/O 模型 | 适用场景 |
|---|---|---|
| Tomcat | NIO(默认)/BIO | Web 应用服务器 |
| Netty | NIO | 高性能网络应用 |
| Jetty | NIO | 嵌入式 Web 服务器 |
| Undertow | NIO | 高并发 Web 服务器 |
| Grizzly | NIO | 通用网络框架 |
6.3 最佳实践
- 避免在 NIO 的 Handler 中做耗时操作,应提交到业务线程池
- 合理设置缓冲区大小,避免内存浪费
- 处理半包/粘包问题,使用消息定长或分隔符,发送消息的时候先发送数据长度,再发送消息,根据消息长度再获取消息
- 做好异常处理,避免资源泄露
- 监控线程池和连接数,及时发现问题