Java IO与网络性能优化
IO操作是系统性能瓶颈,优化IO能显著提升吞吐量。
IO模型对比
| 模型 | 特点 | 适用场景 |
|---|---|---|
| BIO | 阻塞,一连接一线程 | 连接数少 |
| NIO | 非阻塞,多路复用 | 连接数多 |
| AIO | 异步,回调通知 | 高并发场景 |
NIO核心组件
Buffer缓冲区
Java
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 写入数据
buffer.put(data);
// 切换读模式
buffer.flip();
// 读取数据
byte[] output = new byte[buffer.remaining()];
buffer.get(output);
// 清空/重用
buffer.clear();
// 直接缓冲区(零拷贝)
ByteBuffer directBuffer = ByteBuffer.allocateDirect(1024);
Channel通道
Java
// 文件Channel
FileChannel fileChannel = new FileInputStream("file.txt").getChannel();
// Socket Channel
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false); // 非阻塞
// ServerSocket Channel
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.bind(new InetSocketAddress(8080));
Selector选择器
Java
Selector selector = Selector.open();
// 注册Channel
channel.register(selector, SelectionKey.OP_READ);
// 选择就绪Channel
while (selector.select() > 0) {
Set<SelectionKey> keys = selector.selectedKeys();
for (SelectionKey key : keys) {
if (key.isAcceptable()) {
// 处理连接
}
if (key.isReadable()) {
// 处理读取
}
if (key.isWritable()) {
// 处理写入
}
}
keys.clear();
}
零拷贝技术
传统IO拷贝流程
Java
磁盘 → 内核缓冲区 → 用户缓冲区 → 内核缓冲区 → 网卡
(DMA拷贝) (CPU拷贝) (CPU拷贝) (DMA拷贝)
总共4次拷贝,4次上下文切换
零拷贝优化
Java
// FileChannel.transferTo(sendfile)
FileChannel source = new FileInputStream("source.txt").getChannel();
SocketChannel dest = SocketChannel.open(new InetSocketAddress("host", 80));
source.transferTo(0, source.size(), dest);
// 拷贝流程:
// 磁盘 → 内核缓冲区 → 网卡
// (DMA拷贝) (DMA拷贝)
// 2次拷贝,2次上下文切换
mmap内存映射
Java
// mmap:文件映射到内存
MappedByteBuffer mappedBuffer = new RandomAccessFile("file.txt", "r")
.getChannel()
.map(FileChannel.MapMode.READ_ONLY, 0, fileSize);
// 直接访问内存,无拷贝
mappedBuffer.get(bytes);
文件IO优化
缓冲读写
Java
// 不推荐:无缓冲
InputStream in = new FileInputStream("file.txt");
int b = in.read(); // 每次系统调用
// 推荐:缓冲流
BufferedInputStream buffered = new BufferedInputStream(in);
int b = buffered.read(); // 批量读取,减少系统调用
// 推荐:NIO FileChannel
FileChannel channel = new FileInputStream("file.txt").getChannel();
ByteBuffer buffer = ByteBuffer.allocate(8192);
channel.read(buffer);
批量读写
Java
// 批量读取大文件
ByteBuffer buffer = ByteBuffer.allocateDirect(64 * 1024); // 64KB直接缓冲
while (channel.read(buffer) != -1) {
buffer.flip();
processBuffer(buffer);
buffer.clear();
}
// 批量写入
ByteBuffer[] buffers = new ByteBuffer[10];
channel.write(buffers); // 批量写入
网络IO优化
NIO服务端实现
Java
public class NioServer {
public void start(int port) throws IOException {
Selector selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.bind(new InetSocketAddress(port));
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select();
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
if (key.isAcceptable()) {
SocketChannel client = serverChannel.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
}
if (key.isReadable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int len = client.read(buffer);
if (len > 0) {
buffer.flip();
client.write(buffer);
}
}
}
}
}
}
Netty优化配置
Java
// Netty高性能配置
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup(); // 默认CPU核数*2
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024) // 连接队列大小
.option(ChannelOption.SO_REUSEADDR, true) // 端口复用
.childOption(ChannelOption.TCP_NODELAY, true) // 禁用Nagle
.childOption(ChannelOption.SO_KEEPALIVE, true) // 保持连接
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new MyHandler());
}
});
网络参数优化
TCP参数
Bash
// Socket选项设置
Socket socket = new Socket();
// 发送缓冲区
socket.setSendBufferSize(64 * 1024);
// 接收缓冲区
socket.setReceiveBufferSize(64 * 1024);
// 禁用Nagle算法(减少小包延迟)
socket.setTcpNoDelay(true);
// 连接超时
socket.setSoTimeout(5000);
// 保持连接
socket.setKeepAlive(true);
系统参数(Linux)
Java
# 文件句柄数
ulimit -n 100000
# TCP参数
sysctl -w net.core.somaxconn=65535 # 连接队列
sysctl -w net.ipv4.tcp_max_syn_backlog=65535 # SYN队列
sysctl -w net.ipv4.tcp_tw_reuse=1 # TIME_WAIT复用
sysctl -w net.ipv4.tcp_fin_timeout=30 # FIN超时
异步IO(AIO)
text
// AIO异步文件读写
AsynchronousFileChannel channel = AsynchronousFileChannel.open(
Path.of("file.txt"), StandardOpenOption.READ);
ByteBuffer buffer = ByteBuffer.allocate(1024);
Future<Integer> future = channel.read(buffer, 0); // 异步读取
// 回调方式
channel.read(buffer, 0, null, new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
System.out.println("读取完成: " + result);
}
@Override
public void failed(Throwable exc, Void attachment) {
exc.printStackTrace();
}
});
IO优化总结
| 优化点 | 方法 |
|---|---|
| 减少拷贝 | transferTo、mmap、DirectBuffer |
| 减少系统调用 | BufferedInputStream、批量读写 |
| 高并发网络 | NIO Selector、Netty |
| 减少小包延迟 | TCP_NODELAY |
| 连接复用 | KeepAlive、连接池 |
注意事项
直接缓冲区分配开销大,适合长生命周期
NIO适合连接数多,BIO适合连接数少
零拷贝需要操作系统支持(Linux sendfile)
Netty默认已优化大部分参数
避免频繁创建销毁连接,使用连接池
要点总结
- NIO多路复用适合高并发,一个线程管理多个连接
- 零拷贝用transferTo/mmap减少数据拷贝和上下文切换
- DirectBuffer堆外内存,减少GC压力
- TCP_NODELAY禁用Nagle,减少小包延迟
- Netty封装NIO,生产推荐使用
📝 发现内容有误?点击此处直接编辑