深入解析IO模型:从阻塞到异步的演进之路
为什么IO模型如此重要?
在现代软件开发中,IO操作往往是性能瓶颈的根源。可以通过一个生活案例来理解这个问题:传统阻塞IO就像排队买奶茶:每个人必须等前面的人买完才能轮到自己,效率极低;而异步IO就像网上点餐:下单后可以做其他事情,餐好了会通知你取餐,效率极高。
BIO
核心原理
字节流
查看代码
InputStream (抽象基类)
├── FileInputStream // 文件字节输入流
├── ByteArrayInputStream // 字节数组输入流
├── FilterInputStream // 过滤器输入流
│ ├── BufferedInputStream // 缓冲输入流
│ ├── DataInputStream // 数据输入流
│ └── PushbackInputStream // 回推输入流
├── ObjectInputStream // 对象输入流
├── PipedInputStream // 管道输入流
└── SequenceInputStream // 序列输入流
OutputStream (抽象基类)
├── FileOutputStream // 文件字节输出流
├── ByteArrayOutputStream // 字节数组输出流
├── FilterOutputStream // 过滤器输出流
│ ├── BufferedOutputStream // 缓冲输出流
│ ├── DataOutputStream // 数据输出流
│ └── PrintStream // 打印流
├── ObjectOutputStream // 对象输出流
└── PipedOutputStream // 管道输出流
FileInputStream & FileOutputStream
public class FileStreamDemo {
// 基本文件读取
public void readFile(String filename) throws IOException {
try (FileInputStream fis = new FileInputStream(filename)) {
int data;
while ((data = fis.read()) != -1) {
System.out.print((char) data);
}
}
}
// 批量读取提高效率
public void readFileWithBuffer(String filename) throws IOException {
try (FileInputStream fis = new FileInputStream(filename)) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
System.out.print(new String(buffer, 0, bytesRead));
}
}
}
// 文件写入
public void writeFile(String filename, String content) throws IOException {
try (FileOutputStream fos = new FileOutputStream(filename)) {
fos.write(content.getBytes());
}
}
// 追加写入
public void appendToFile(String filename, String content) throws IOException {
try (FileOutputStream fos = new FileOutputStream(filename, true)) {
fos.write(content.getBytes());
}
}
}
ByteArrayInputStream & ByteArrayOutputStream
public class ByteArrayStreamDemo {
public void demonstrateByteArrayStream() throws IOException {
// 将数据写入内存
ByteArrayOutputStream baos = new ByteArrayOutputStream();
String data = "Hello World";
baos.write(data.getBytes());
// 获取内存中的数据
byte[] bytes = baos.toByteArray();
// 从内存中读取数据
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
int b;
while ((b = bais.read()) != -1) {
System.out.print((char) b);
}
}
// 实际应用:数据转换
public byte[] convertImageToBytes(String imagePath) throws IOException {
try (FileInputStream fis = new FileInputStream(imagePath);
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
baos.write(buffer, 0, bytesRead);
}
return baos.toByteArray();
}
}
}
BufferedInputStream & BufferedOutputStream
public class BufferedStreamDemo {
// 缓冲流提升性能
public void copyFileWithBuffer(String source, String target) throws IOException {
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(source));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(target))) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = bis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
}
}
}
// 性能对比测试
public void performanceTest(String filename) throws IOException {
long startTime, endTime;
// 无缓冲
startTime = System.currentTimeMillis();
try (FileInputStream fis = new FileInputStream(filename)) {
while (fis.read() != -1) {
// 逐字节读取
}
}
endTime = System.currentTimeMillis();
System.out.println("无缓冲时间: " + (endTime - startTime) + "ms");
// 有缓冲
startTime = System.currentTimeMillis();
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filename))) {
while (bis.read() != -1) {
// 逐字节读取
}
}
endTime = System.currentTimeMillis();
System.out.println("有缓冲时间: " + (endTime - startTime) + "ms");
}
}
DataInputStream & DataOutputStream
public class DataStreamDemo {
// 写入基本数据类型
public void writeDataTypes(String filename) throws IOException {
try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(filename))) {
dos.writeInt(100);
dos.writeDouble(3.14);
dos.writeBoolean(true);
dos.writeUTF("Hello World");
dos.writeLong(System.currentTimeMillis());
}
}
// 读取基本数据类型
public void readDataTypes(String filename) throws IOException {
try (DataInputStream dis = new DataInputStream(new FileInputStream(filename))) {
int intValue = dis.readInt();
double doubleValue = dis.readDouble();
boolean booleanValue = dis.readBoolean();
String stringValue = dis.readUTF();
long longValue = dis.readLong();
System.out.println("Int: " + intValue);
System.out.println("Double: " + doubleValue);
System.out.println("Boolean: " + booleanValue);
System.out.println("String: " + stringValue);
System.out.println("Long: " + longValue);
}
}
}
字符流
查看代码
Reader (抽象基类)
├── InputStreamReader // 字节流到字符流的桥梁
│ └── FileReader // 文件字符输入流
├── BufferedReader // 缓冲字符输入流
├── StringReader // 字符串输入流
├── CharArrayReader // 字符数组输入流
├── PipedReader // 管道字符输入流
└── FilterReader // 过滤字符输入流
└── PushbackReader // 回推字符输入流
Writer (抽象基类)
├── OutputStreamWriter // 字符流到字节流的桥梁
│ └── FileWriter // 文件字符输出流
├── BufferedWriter // 缓冲字符输出流
├── StringWriter // 字符串输出流
├── CharArrayWriter // 字符数组输出流
├── PipedWriter // 管道字符输出流
├── PrintWriter // 打印字符输出流
└── FilterWriter // 过滤字符输出流
InputStreamReader & OutputStreamWriter
public class StreamReaderWriterDemo {
// 指定编码读取文件
public void readFileWithEncoding(String filename, String encoding) throws IOException {
try (InputStreamReader isr = new InputStreamReader(
new FileInputStream(filename), encoding)) {
char[] buffer = new char[1024];
int charsRead;
while ((charsRead = isr.read(buffer)) != -1) {
System.out.print(new String(buffer, 0, charsRead));
}
}
}
// 指定编码写入文件
public void writeFileWithEncoding(String filename, String content, String encoding) throws IOException {
try (OutputStreamWriter osw = new OutputStreamWriter(
new FileOutputStream(filename), encoding)) {
osw.write(content);
}
}
// 编码转换
public void convertFileEncoding(String inputFile, String outputFile,
String inputEncoding, String outputEncoding) throws IOException {
try (InputStreamReader isr = new InputStreamReader(
new FileInputStream(inputFile), inputEncoding);
OutputStreamWriter osw = new OutputStreamWriter(
new FileOutputStream(outputFile), outputEncoding)) {
char[] buffer = new char[1024];
int charsRead;
while ((charsRead = isr.read(buffer)) != -1) {
osw.write(buffer, 0, charsRead);
}
}
}
}
BufferedReader & BufferedWriter
public class BufferedReaderWriterDemo {
// 按行读取文件
public void readFileByLines(String filename) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(filename))) {
String line;
int lineNumber = 1;
while ((line = br.readLine()) != null) {
System.out.println(lineNumber + ": " + line);
lineNumber++;
}
}
}
// 按行写入文件
public void writeFileByLines(String filename, String[] lines) throws IOException {
try (BufferedWriter bw = new BufferedWriter(new FileWriter(filename))) {
for (String line : lines) {
bw.write(line);
bw.newLine(); // 写入系统相关的换行符
}
}
}
// 文本文件处理示例
public void processTextFile(String inputFile, String outputFile) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(inputFile));
BufferedWriter bw = new BufferedWriter(new FileWriter(outputFile))) {
String line;
int lineNumber = 1;
while ((line = br.readLine()) != null) {
// 处理每一行,例如添加行号
bw.write(lineNumber + ": " + line);
bw.newLine();
lineNumber++;
}
}
}
}
StringReader & StringWriter
public class StringReaderWriterDemo {
public void demonstrateStringStreams() throws IOException {
// StringWriter 用于构建字符串
StringWriter sw = new StringWriter();
sw.write("Hello ");
sw.write("World");
sw.write("!");
String result = sw.toString();
System.out.println("StringWriter result: " + result);
// StringReader 用于读取字符串
StringReader sr = new StringReader(result);
char[] buffer = new char[5];
int charsRead;
while ((charsRead = sr.read(buffer)) != -1) {
System.out.print(new String(buffer, 0, charsRead));
}
}
// 实际应用:模板处理
public String processTemplate(String template, Map<String, String> variables) throws IOException {
StringWriter result = new StringWriter();
try (StringReader sr = new StringReader(template)) {
int ch;
while ((ch = sr.read()) != -1) {
if (ch == '$') {
// 简单的变量替换逻辑
StringBuilder varName = new StringBuilder();
while ((ch = sr.read()) != -1 && Character.isLetterOrDigit(ch)) {
varName.append((char) ch);
}
String value = variables.get(varName.toString());
result.write(value != null ? value : "${" + varName + "}");
if (ch != -1) {
result.write(ch);
}
} else {
result.write(ch);
}
}
}
return result.toString();
}
}
字节流与字符流的选择
查看代码
public class StreamSelectionGuide {
// 处理二进制文件 - 使用字节流
public void copyBinaryFile(String source, String target) throws IOException {
try (FileInputStream fis = new FileInputStream(source);
FileOutputStream fos = new FileOutputStream(target)) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
}
}
// 处理文本文件 - 使用字符流
public void copyTextFile(String source, String target) throws IOException {
try (FileReader fr = new FileReader(source);
FileWriter fw = new FileWriter(target)) {
char[] buffer = new char[1024];
int charsRead;
while ((charsRead = fr.read(buffer)) != -1) {
fw.write(buffer, 0, charsRead);
}
}
}
// 需要指定编码 - 使用 InputStreamReader/OutputStreamWriter
public void copyTextFileWithEncoding(String source, String target, String encoding) throws IOException {
try (InputStreamReader isr = new InputStreamReader(new FileInputStream(source), encoding);
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(target), encoding)) {
char[] buffer = new char[1024];
int charsRead;
while ((charsRead = isr.read(buffer)) != -1) {
osw.write(buffer, 0, charsRead);
}
}
}
}
应用场景
文件类型检测
public class FileTypeDetector {
public String detectFileType(String filename) throws IOException {
try (FileInputStream fis = new FileInputStream(filename)) {
byte[] header = new byte[4];
fis.read(header);
// 检查文件头
if (header[0] == (byte) 0xFF && header[1] == (byte) 0xD8) {
return "JPEG";
} else if (header[0] == (byte) 0x89 && header[1] == (byte) 0x50 &&
header[2] == (byte) 0x4E && header[3] == (byte) 0x47) {
return "PNG";
} else if (header[0] == (byte) 0x47 && header[1] == (byte) 0x49 &&
header[2] == (byte) 0x46) {
return "GIF";
} else {
return "Unknown";
}
}
}
}
大文件处理
public class LargeFileProcessor {
// 分块处理大文件
public void processLargeFile(String filename, int chunkSize) throws IOException {
try (FileInputStream fis = new FileInputStream(filename)) {
byte[] buffer = new byte[chunkSize];
int bytesRead;
int chunkNumber = 1;
while ((bytesRead = fis.read(buffer)) != -1) {
System.out.println("Processing chunk " + chunkNumber + ", size: " + bytesRead);
// 处理当前块
processChunk(buffer, bytesRead);
chunkNumber++;
}
}
}
private void processChunk(byte[] chunk, int size) {
// 处理数据块的逻辑
System.out.println("Processing " + size + " bytes");
}
}
编码检测与转换
public class EncodingConverter {
// 检测文件编码(简单版本)
public String detectEncoding(String filename) throws IOException {
try (FileInputStream fis = new FileInputStream(filename)) {
byte[] bom = new byte[3];
fis.read(bom);
if (bom[0] == (byte) 0xEF && bom[1] == (byte) 0xBB && bom[2] == (byte) 0xBF) {
return "UTF-8";
} else if (bom[0] == (byte) 0xFF && bom[1] == (byte) 0xFE) {
return "UTF-16LE";
} else if (bom[0] == (byte) 0xFE && bom[1] == (byte) 0xFF) {
return "UTF-16BE";
} else {
return "Unknown";
}
}
}
// 转换文件编码
public void convertEncoding(String inputFile, String outputFile,
String fromEncoding, String toEncoding) throws IOException {
try (InputStreamReader isr = new InputStreamReader(
new FileInputStream(inputFile), fromEncoding);
OutputStreamWriter osw = new OutputStreamWriter(
new FileOutputStream(outputFile), toEncoding)) {
char[] buffer = new char[1024];
int charsRead;
while ((charsRead = isr.read(buffer)) != -1) {
osw.write(buffer, 0, charsRead);
}
}
}
}
性能优化
缓冲区优化
package com.io;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class BufferOptimization {
// 测试不同缓冲区大小的性能
public void testBufferSizes(String filename) throws IOException {
int[] bufferSizes = {1024, 4096, 8192, 16384, 32768};
for (int i = 0; i < 5; i++) {
int curBufferSize = bufferSizes[i];
new Thread(() -> {
long startTime = System.nanoTime();
try {
copyFileWithBufferSize(filename, "temp_" + curBufferSize, curBufferSize);
long endTime = System.nanoTime();
System.out.println(Thread.currentThread().getName() + "\tBuffer size: " + curBufferSize + ", Time: " + (endTime - startTime) / 1000000 + "ms");
} catch (IOException e) {
throw new RuntimeException(e);
}
}, "Thread-" + (i + 1)).start();
}
}
private void copyFileWithBufferSize(String source, String target, int bufferSize) throws IOException {
try (FileInputStream fis = new FileInputStream(source); FileOutputStream fos = new FileOutputStream(target)) {
byte[] buffer = new byte[bufferSize];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
}
}
public static void main(String[] args) throws IOException {
BufferOptimization ob = new BufferOptimization();
ob.testBufferSizes("D:\\random_words.txt");
}
}
内存使用优化
public class MemoryOptimization {
// 处理大文件时避免内存溢出
public void processLargeFileEfficiently(String filename) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(filename))) {
String line;
while ((line = br.readLine()) != null) {
// 逐行处理,避免将整个文件加载到内存
processLine(line);
// 处理完立即释放引用
line = null;
}
}
}
private void processLine(String line) {
// 处理单行数据
System.out.println("Processing: " + line);
}
}
存在的问题
在传统BIO模型中,每个连接都需要独立线程处理。当我们的系统面临1万个并发连接时,就需要1万个线程。以每个线程1MB的栈空间计算,仅线程栈就需要10GB内存。更严重的是,大量线程间的上下文切换会消耗大量CPU资源。
查看代码
// 传统BIO服务器 - 问题代码
public class BIOServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8080);
ExecutorService executor = Executors.newFixedThreadPool(1000); // 线程池很快耗尽
while (true) {
Socket socket = serverSocket.accept(); // 阻塞等待
executor.submit(() -> {
try {
handleClient(socket); // 每个连接一个线程
} catch (IOException e) {
e.printStackTrace();
}
});
}
}
}
NIO
核心原理
查看代码
/**
NIO的核心:用一个线程通过Selector监听多个Channel的事件(IO多路复用模型),当某个Channel有数据可读/可写时才去处理,避免了传统IO中线程阻塞等待的资源浪费。所有数据传输都通过Buffer作为中介,Channel负责传输,Buffer负责存储,Selector负责调度。
工作流程:首先将需要监听的Channel注册到Selector上并指定关注的事件类型(如连接、读、写),然后Selector通过select()方法轮询检查所有注册的Channel,找出有事件发生的Channel。一旦发现有事件的Channel,就遍历对应的SelectionKey集合,根据不同的事件类型分别处理,所有的数据传输都通过Buffer作为中介在Channel之间进行读写操作。
核心优势:用更少的线程处理更多的连接,打破了传统BIO中一个连接对应一个线程的限制。通过非阻塞和事件驱动机制,单个线程可以管理数千个并发连接,线程不会被I/O操作阻塞,只有当数据真正准备好时才进行处理,这大大提高了系统的并发能力和资源利用率。同时Buffer的重用机制减少了内存分配开销,整体上实现了高性能、低资源消耗的I/O处理模型。
**/
零拷贝
NIO的零拷贝技术能够显著提升大文件传输性能。在传统IO中,文件传输需要经过4次拷贝:磁盘 → 内核缓冲区 → 用户空间 → Socket缓冲区 → 网卡
而使用FileChannel.transferTo()方法,可以直接从内核缓冲区传输到Socket缓冲区,避免用户空间的拷贝。
ZeroCopyFileServer
public class ZeroCopyFileServer {
private static final int BUFFER_SIZE = 8192;
public void transferFile(String filePath, SocketChannel socketChannel) throws IOException {
try (RandomAccessFile file = new RandomAccessFile(filePath, "r");
FileChannel fileChannel = file.getChannel()) {
long position = 0;
long fileSize = fileChannel.size();
// 零拷贝传输,性能提升10倍以上
while (position < fileSize) {
long transferred = fileChannel.transferTo(position, fileSize - position, socketChannel);
position += transferred;
}
}
}
}
性能对比测试结果:
- 传统IO:传输1GB文件耗时45秒
- 零拷贝:传输1GB文件耗时4秒
- 性能提升:11.25倍
编程模型
NIO核心编程模型
// 1.创建选择器
Selector selector = Selector.open();
// 2.注册通道
server.register(selector, SelectionKey.OP_ACCEPT); // 监听连接
client.register(selector, SelectionKey.OP_READ); // 监听读取
// 3.事件循环
while (true) {
selector.select(); // 等待事件
// 处理事件
}
模型应用
NIO模型应用
package com.io.nio;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.net.InetSocketAddress;
import java.io.IOException;
import java.util.Iterator;
import java.util.Set;
/**
* NIO核心编程模型 - 这是理解Netty和RPC框架的基础
* <p>
* 核心思想:
* 1. 一个线程 + 一个Selector + 多个Channel
* 2. 事件驱动:只处理就绪的Channel
* 3. 非阻塞:不等待,立即返回
*/
public class NIOCorePattern {
// =============== 核心模型:Reactor模式 ===============
public static class NIOReactor {
private Selector selector;
private ServerSocketChannel serverChannel;
private boolean running = false;
public void start(int port) throws IOException {
// 1. 创建核心组件
selector = Selector.open();
serverChannel = ServerSocketChannel.open();
// 2. 配置非阻塞模式
serverChannel.configureBlocking(false);
serverChannel.bind(new InetSocketAddress(port));
// 3. 注册到Selector,监听连接事件
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
running = true;
System.out.println("NIO Reactor启动,端口: " + port);
// 4. 事件循环 - NIO核心
eventLoop();
}
/**
* 事件循环 - NIO Core
* 这个模式被Netty、RPC框架广泛使用
*/
private void eventLoop() throws IOException {
while (running) {
// 阻塞等待事件,但可以处理多个连接
int readyChannels = selector.select(); // 核心调用
if (readyChannels == 0) continue;
// 获取就绪的事件
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectedKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove(); // 重要:处理完必须移除
// 事件分发 - 根据不同事件类型处理
try {
handleEvent(key);
} catch (Exception e) {
handleException(key, e);
}
}
}
}
/**
* 事件处理器 - 这是业务逻辑的入口
*/
private void handleEvent(SelectionKey key) throws IOException {
if (key.isAcceptable()) {
// 新连接事件
handleAccept(key);
} else if (key.isReadable()) {
// 数据可读事件
handleRead(key);
} else if (key.isWritable()) {
// 数据可写事件
handleWrite(key);
}
}
/**
* 处理新连接 - 这是服务器的起点
*/
private void handleAccept(SelectionKey key) throws IOException {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel clientChannel = server.accept();
if (clientChannel != null) {
System.out.println("新连接: " + clientChannel.getRemoteAddress());
// 配置新连接
clientChannel.configureBlocking(false);
// 注册到Selector,监听读事件
SelectionKey clientKey = clientChannel.register(selector, SelectionKey.OP_READ);
// 为每个连接创建上下文 - 这是状态管理的关键
clientKey.attach(new ChannelContext());
}
}
/**
* 处理读事件 - 这是数据接收的核心
*/
private void handleRead(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
ChannelContext context = (ChannelContext) key.attachment();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = channel.read(buffer);
if (bytesRead > 0) {
buffer.flip();
// 数据处理 - 这里是业务逻辑
String message = processMessage(buffer);
System.out.println("收到消息: " + message);
// 准备响应数据
String response = "Echo: " + message;
context.setWriteBuffer(ByteBuffer.wrap(response.getBytes()));
// 注册写事件
key.interestOps(SelectionKey.OP_WRITE);
} else if (bytesRead == -1) {
// 连接关闭
System.out.println("连接关闭: " + channel.getRemoteAddress());
closeChannel(key);
}
}
/**
* 处理写事件 - 这是数据发送的核心
*/
private void handleWrite(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
ChannelContext context = (ChannelContext) key.attachment();
ByteBuffer writeBuffer = context.getWriteBuffer();
if (writeBuffer != null && writeBuffer.hasRemaining()) {
channel.write(writeBuffer);
if (!writeBuffer.hasRemaining()) {
// 写完了,重新监听读事件
key.interestOps(SelectionKey.OP_READ);
context.setWriteBuffer(null);
}
}
}
/**
* 异常处理 - 健壮性的保证
*/
private void handleException(SelectionKey key, Exception e) {
System.err.println("处理连接异常: " + e.getMessage());
closeChannel(key);
}
/**
* 关闭连接 - 资源清理
*/
private void closeChannel(SelectionKey key) {
try {
key.channel().close();
key.cancel();
} catch (IOException e) {
System.err.println("关闭连接失败: " + e.getMessage());
}
}
/**
* 消息处理 - 业务逻辑抽象
*/
private String processMessage(ByteBuffer buffer) {
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
return new String(bytes).trim();
}
public void stop() throws IOException {
running = false;
if (selector != null) selector.close();
if (serverChannel != null) serverChannel.close();
}
}
// =============== 连接上下文 - 状态管理 ===============
/**
* 每个连接的上下文信息
* 这是有状态协议处理的基础(如HTTP、RPC)
*/
static class ChannelContext {
private ByteBuffer writeBuffer;
private long connectTime;
private String clientId;
public ChannelContext() {
this.connectTime = System.currentTimeMillis();
this.clientId = "client_" + connectTime;
}
public ByteBuffer getWriteBuffer() {
return writeBuffer;
}
public void setWriteBuffer(ByteBuffer writeBuffer) {
this.writeBuffer = writeBuffer;
}
public long getConnectTime() {
return connectTime;
}
public String getClientId() {
return clientId;
}
}
// =============== 客户端模型 - 完整的通信模型 ===============
public static class NIOClient {
private Selector selector;
private SocketChannel clientChannel;
public void connect(String host, int port) throws IOException {
selector = Selector.open();
clientChannel = SocketChannel.open();
clientChannel.configureBlocking(false);
// 发起连接
clientChannel.connect(new InetSocketAddress(host, port));
clientChannel.register(selector, SelectionKey.OP_CONNECT);
// 客户端事件循环
while (true) {
selector.select();
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
if (key.isConnectable()) {
handleConnect(key);
} else if (key.isReadable()) {
handleRead(key);
} else if (key.isWritable()) {
handleWrite(key);
}
}
}
}
private void handleConnect(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
if (channel.finishConnect()) {
System.out.println("连接成功");
// 发送数据
String message = "Hello NIO Server!";
ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
channel.write(buffer);
// 注册读事件
key.interestOps(SelectionKey.OP_READ);
}
}
private void handleRead(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = channel.read(buffer);
if (bytesRead > 0) {
buffer.flip();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
System.out.println("收到响应: " + new String(bytes));
}
}
private void handleWrite(SelectionKey key) throws IOException {
// 客户端写事件处理
}
}
// =============== 这就是Netty的核心思想! ===============
public static void main(String[] args) {
System.out.println("=== NIO核心编程模型 - Netty/RPC基础 ===");
System.out.println();
System.out.println("这个模型就是:");
System.out.println("1. Netty的EventLoop原理");
System.out.println("2. RPC框架的网络层基础");
System.out.println("3. 高性能服务器的标准模式");
System.out.println();
System.out.println("核心组件映射:");
System.out.println("- NIOReactor.eventLoop() -> Netty的EventLoop");
System.out.println("- handleEvent() -> Netty的ChannelHandler");
System.out.println("- ChannelContext -> Netty的Channel");
System.out.println("- 事件驱动模型 -> RPC的异步处理");
System.out.println();
try {
// 启动服务器演示
NIOReactor reactor = new NIOReactor();
System.out.println("启动NIO服务器...");
System.out.println("可以用telnet localhost 8080测试");
// 在新线程中启动,避免阻塞
new Thread(() -> {
try {
reactor.start(8080);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
// 等待一段时间后关闭
Thread.sleep(5000);
reactor.stop();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println();
System.out.println("模型旨在说明:");
System.out.println("- 1、为什么Netty性能这么高");
System.out.println("- 2、RPC框架如何实现高并发");
System.out.println("- 3、现代网络编程的核心思想");
}
}
应用场景
高并发网络服务器
// 1. 高并发网络服务器 - 基于NIO的HTTP服务器
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class HighConcurrencyNIOServer {
private ServerSocketChannel serverChannel;
private Selector selector;
private final int port;
private final ExecutorService workerPool;
// 连接管理器 - 企业级应用中通常需要管理连接状态
private final ConcurrentHashMap<SocketChannel, ClientSession> sessions = new ConcurrentHashMap<>();
public HighConcurrencyNIOServer(int port) {
this.port = port;
this.workerPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);
}
public void start() throws IOException {
// 1. 创建ServerSocketChannel
serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.bind(new InetSocketAddress(port));
// 2. 创建Selector
selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("NIO服务器启动,监听端口: " + port);
// 3. 事件循环
while (true) {
// 阻塞等待事件
int eventCount = selector.select();
if (eventCount == 0) continue;
Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
keyIterator.remove();
try {
handleKey(key);
} catch (Exception e) {
handleException(key, e);
}
}
}
}
private void handleKey(SelectionKey key) throws IOException {
if (key.isAcceptable()) {
// 处理连接请求
handleAccept(key);
} else if (key.isReadable()) {
// 处理读事件
handleRead(key);
} else if (key.isWritable()) {
// 处理写事件
handleWrite(key);
}
}
private void handleAccept(SelectionKey key) throws IOException {
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel clientChannel = serverChannel.accept();
if (clientChannel != null) {
clientChannel.configureBlocking(false);
clientChannel.register(selector, SelectionKey.OP_READ);
// 创建会话
ClientSession session = new ClientSession(clientChannel);
sessions.put(clientChannel, session);
System.out.println("新客户端连接: " + clientChannel.getRemoteAddress());
}
}
private void handleRead(SelectionKey key) {
SocketChannel clientChannel = (SocketChannel) key.channel();
ClientSession session = sessions.get(clientChannel);
// 将IO操作提交给线程池处理,避免阻塞事件循环
workerPool.submit(() -> {
try {
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = clientChannel.read(buffer);
if (bytesRead > 0) {
buffer.flip();
String message = new String(buffer.array(), 0, buffer.limit());
System.out.println("收到消息: " + message);
// 业务处理
String response = processBusinessLogic(message, session);
// 准备响应
session.addResponse(response);
key.interestOps(SelectionKey.OP_WRITE);
selector.wakeup(); // 唤醒selector
} else if (bytesRead == -1) {
// 客户端断开连接
closeClient(clientChannel, key);
}
} catch (IOException e) {
closeClient(clientChannel, key);
}
});
}
private void handleWrite(SelectionKey key) throws IOException {
SocketChannel clientChannel = (SocketChannel) key.channel();
ClientSession session = sessions.get(clientChannel);
if (session != null && session.hasResponse()) {
String response = session.getResponse();
ByteBuffer buffer = ByteBuffer.wrap(response.getBytes());
while (buffer.hasRemaining()) {
clientChannel.write(buffer);
}
// 写完后切换回读模式
key.interestOps(SelectionKey.OP_READ);
}
}
private String processBusinessLogic(String message, ClientSession session) {
// 模拟业务处理 - 企业级应用中这里会有复杂的业务逻辑
return "HTTP/1.1 200 OK\r\n" +
"Content-Type: text/plain\r\n" +
"Content-Length: " + message.length() + "\r\n" +
"\r\n" +
"Echo: " + message;
}
private void closeClient(SocketChannel clientChannel, SelectionKey key) {
try {
sessions.remove(clientChannel);
key.cancel();
clientChannel.close();
System.out.println("客户端断开连接");
} catch (IOException e) {
e.printStackTrace();
}
}
private void handleException(SelectionKey key, Exception e) {
System.err.println("处理连接异常: " + e.getMessage());
if (key.channel() instanceof SocketChannel) {
closeClient((SocketChannel) key.channel(), key);
}
}
// 客户端会话类
private static class ClientSession {
private final SocketChannel channel;
private String pendingResponse;
private long lastActiveTime;
public ClientSession(SocketChannel channel) {
this.channel = channel;
this.lastActiveTime = System.currentTimeMillis();
}
public void addResponse(String response) {
this.pendingResponse = response;
}
public String getResponse() {
String response = pendingResponse;
pendingResponse = null;
return response;
}
public boolean hasResponse() {
return pendingResponse != null;
}
}
}
高性能文件处理
/** 2. 高性能文件处理 - 大文件复制和处理
零拷贝技术(transferTo)提升传输效率
内存映射文件处理大文件
分块处理避免内存溢出
**/
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
public class HighPerformanceFileProcessor {
// 使用零拷贝技术进行文件传输
public void copyFileWithZeroCopy(String sourcePath, String destPath) throws IOException {
try (FileChannel sourceChannel = FileChannel.open(Paths.get(sourcePath), StandardOpenOption.READ);
FileChannel destChannel = FileChannel.open(Paths.get(destPath),
StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) {
long fileSize = sourceChannel.size();
long transferred = 0;
// 使用transferTo实现零拷贝
while (transferred < fileSize) {
long count = sourceChannel.transferTo(transferred, fileSize - transferred, destChannel);
transferred += count;
}
System.out.println("文件复制完成,大小: " + fileSize + " 字节");
}
}
// 使用内存映射处理大文件
public void processLargeFileWithMemoryMapping(String filePath) throws IOException {
try (FileChannel channel = FileChannel.open(Paths.get(filePath), StandardOpenOption.READ)) {
long fileSize = channel.size();
// 内存映射文件
MappedByteBuffer mappedBuffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, fileSize);
// 统计文件中的行数 - 企业级应用中常见的日志分析需求
int lineCount = 0;
while (mappedBuffer.hasRemaining()) {
if (mappedBuffer.get() == '\n') {
lineCount++;
}
}
System.out.println("文件行数: " + lineCount);
}
}
// 分块处理大文件 - 适用于日志处理系统
public void processFileInChunks(String filePath, int chunkSize) throws IOException {
try (FileChannel channel = FileChannel.open(Paths.get(filePath), StandardOpenOption.READ)) {
ByteBuffer buffer = ByteBuffer.allocate(chunkSize);
long position = 0;
int chunkNumber = 1;
while (true) {
buffer.clear();
int bytesRead = channel.read(buffer, position);
if (bytesRead == -1) break;
buffer.flip();
// 处理当前块
processChunk(buffer, chunkNumber);
position += bytesRead;
chunkNumber++;
}
}
}
private void processChunk(ByteBuffer buffer, int chunkNumber) {
// 模拟数据处理 - 企业级应用中这里会有实际的业务逻辑
String content = new String(buffer.array(), 0, buffer.limit());
System.out.println("处理块 " + chunkNumber + ", 大小: " + buffer.limit() + " 字节");
// 例如:日志解析、数据统计等
long errorCount = content.lines().filter(line -> line.contains("ERROR")).count();
if (errorCount > 0) {
System.out.println("发现 " + errorCount + " 个错误日志");
}
}
}
消息队列实现
/** 3. 消息队列服务端 - 基于NIO
NIO在消息队列中的核心价值是通过非阻塞I/O和事件驱动机制,实现单线程处理数万并发连接,大幅提升系统性能和资源利用率。
Kafka - 基于NIO实现零拷贝传输,单台服务器可处理数十万并发连接,吞吐量达到百万级消息/秒
RocketMQ - 采用Netty框架(NIO封装),通过异步处理和事件驱动架构,支持万级QPS的消息处理能力
ActiveMQ - NIO传输连接器相比传统TCP连接器,并发处理能力提升10倍以上,内存占用减少70%
**/
public class NIOMessageQueueServer {
private ServerSocketChannel serverChannel;
private Selector selector;
private final Map<SocketChannel, ByteBuffer> clientBuffers = new ConcurrentHashMap<>();
private final BlockingQueue<Message> messageQueue = new LinkedBlockingQueue<>();
public void start(int port) throws IOException {
// 创建服务端通道
serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.bind(new InetSocketAddress(port));
// 创建选择器
selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("NIO消息队列服务启动,监听端口: " + port);
// 事件循环
while (true) {
selector.select(); // 阻塞等待事件
Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
keyIterator.remove();
if (key.isAcceptable()) {
handleAccept(key);
} else if (key.isReadable()) {
handleRead(key);
} else if (key.isWritable()) {
handleWrite(key);
}
}
}
}
// 处理客户端连接
private void handleAccept(SelectionKey key) throws IOException {
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel clientChannel = serverChannel.accept();
if (clientChannel != null) {
clientChannel.configureBlocking(false);
clientChannel.register(selector, SelectionKey.OP_READ);
clientBuffers.put(clientChannel, ByteBuffer.allocate(1024));
System.out.println("客户端连接: " + clientChannel.getRemoteAddress());
}
}
// 处理消息读取
private void handleRead(SelectionKey key) throws IOException {
SocketChannel clientChannel = (SocketChannel) key.channel();
ByteBuffer buffer = clientBuffers.get(clientChannel);
try {
int bytesRead = clientChannel.read(buffer);
if (bytesRead > 0) {
buffer.flip();
// 解析消息
Message message = parseMessage(buffer);
if (message != null) {
messageQueue.offer(message); // 入队
System.out.println("收到消息: " + message.getContent());
// 广播给所有客户端
broadcastMessage(message);
}
buffer.clear();
} else if (bytesRead == -1) {
// 客户端断开连接
clientChannel.close();
clientBuffers.remove(clientChannel);
key.cancel();
}
} catch (IOException e) {
clientChannel.close();
clientBuffers.remove(clientChannel);
key.cancel();
}
}
// 处理消息写入
private void handleWrite(SelectionKey key) throws IOException {
SocketChannel clientChannel = (SocketChannel) key.channel();
ByteBuffer buffer = (ByteBuffer) key.attachment();
if (buffer != null && buffer.hasRemaining()) {
clientChannel.write(buffer);
if (!buffer.hasRemaining()) {
key.interestOps(SelectionKey.OP_READ);
}
}
}
// 广播消息到所有客户端
private void broadcastMessage(Message message) {
ByteBuffer messageBuffer = ByteBuffer.wrap(message.toBytes());
for (SelectionKey key : selector.keys()) {
if (key.channel() instanceof SocketChannel && key.isValid()) {
SocketChannel clientChannel = (SocketChannel) key.channel();
try {
ByteBuffer buffer = messageBuffer.duplicate();
clientChannel.write(buffer);
} catch (IOException e) {
// 处理写入异常
key.cancel();
clientBuffers.remove(clientChannel);
}
}
}
}
// 消息解析
private Message parseMessage(ByteBuffer buffer) {
// 简化的消息解析逻辑
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
String content = new String(data, StandardCharsets.UTF_8);
return new Message(System.currentTimeMillis(), content.trim());
}
}
// 2. 消息实体类
class Message {
private long timestamp;
private String content;
public Message(long timestamp, String content) {
this.timestamp = timestamp;
this.content = content;
}
public String getContent() { return content; }
public long getTimestamp() { return timestamp; }
public byte[] toBytes() {
return (timestamp + ":" + content + "\n").getBytes(StandardCharsets.UTF_8);
}
}
// 3. NIO消息队列客户端
public class NIOMessageQueueClient {
private SocketChannel socketChannel;
private Selector selector;
private ByteBuffer buffer;
public void connect(String host, int port) throws IOException {
socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress(host, port));
selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_CONNECT);
buffer = ByteBuffer.allocate(1024);
// 客户端事件循环
while (true) {
selector.select();
Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
keyIterator.remove();
if (key.isConnectable()) {
handleConnect(key);
} else if (key.isReadable()) {
handleRead(key);
} else if (key.isWritable()) {
handleWrite(key);
}
}
}
}
private void handleConnect(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
if (channel.finishConnect()) {
System.out.println("连接到服务器成功");
key.interestOps(SelectionKey.OP_READ);
// 启动消息发送线程
new Thread(this::sendMessages).start();
}
}
private void handleRead(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
buffer.clear();
int bytesRead = channel.read(buffer);
if (bytesRead > 0) {
buffer.flip();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
System.out.println("收到广播消息: " + new String(data, StandardCharsets.UTF_8));
}
}
private void handleWrite(SelectionKey key) throws IOException {
// 处理写入逻辑
}
// 发送消息
public void sendMessage(String message) throws IOException {
ByteBuffer writeBuffer = ByteBuffer.wrap(message.getBytes(StandardCharsets.UTF_8));
socketChannel.write(writeBuffer);
}
// 模拟消息发送
private void sendMessages() {
try {
Thread.sleep(1000);
for (int i = 0; i < 5; i++) {
sendMessage("消息 " + i + " 来自客户端");
Thread.sleep(2000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 4. 启动示例
public class MessageQueueExample {
public static void main(String[] args) throws IOException {
// 启动服务端
new Thread(() -> {
try {
new NIOMessageQueueServer().start(8080);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
// 等待服务端启动
try { Thread.sleep(1000); } catch (InterruptedException e) {}
// 启动多个客户端
for (int i = 0; i < 3; i++) {
final int clientId = i;
new Thread(() -> {
try {
NIOMessageQueueClient client = new NIOMessageQueueClient();
client.connect("localhost", 8080);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
}
NIO配置管理
public class NIOConfiguration {
// 连接池配置
public static final int MAX_CONNECTIONS = 10000;
public static final int WORKER_THREADS = Runtime.getRuntime().availableProcessors() * 2;
public static final int BUFFER_SIZE = 8192;
public static final int TIMEOUT_SECONDS = 30;
// 文件处理配置
public static final int FILE_CHUNK_SIZE = 1024 * 1024; // 1MB
public static final int MAX_FILE_SIZE = 100 * 1024 * 1024; // 100MB
// 队列配置
public static final int QUEUE_CAPACITY = 100000;
public static final int BATCH_SIZE = 1000;
}
异常处理和监控
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.ConcurrentHashMap;
public class NIOMonitor {
private final AtomicLong totalConnections = new AtomicLong(0);
private final AtomicLong activeConnections = new AtomicLong(0);
private final AtomicLong totalRequests = new AtomicLong(0);
private final AtomicLong failedRequests = new AtomicLong(0);
private final ConcurrentHashMap<String, AtomicLong> requestCounts = new ConcurrentHashMap<>();
public void recordConnection() {
totalConnections.incrementAndGet();
activeConnections.incrementAndGet();
}
public void recordDisconnection() {
activeConnections.decrementAndGet();
}
public void recordRequest(String endpoint) {
totalRequests.incrementAndGet();
requestCounts.computeIfAbsent(endpoint, k -> new AtomicLong(0)).incrementAndGet();
}
public void recordFailure() {
failedRequests.incrementAndGet();
}
public void printStats() {
System.out.println("=== NIO服务器统计信息 ===");
System.out.println("总连接数: " + totalConnections.get());
System.out.println("活跃连接数: " + activeConnections.get());
System.out.println("总请求数: " + totalRequests.get());
System.out.println("失败请求数: " + failedRequests.get());
System.out.println("成功率: " + String.format("%.2f%%",
(totalRequests.get() - failedRequests.get()) * 100.0 / totalRequests.get()));
System.out.println("各端点请求统计:");
requestCounts.forEach((endpoint, count) ->
System.out.println(" " + endpoint + ": " + count.get()));
}
}
资源管理
public class ResourceManager {
private final ExecutorService ioExecutor;
private final ExecutorService businessExecutor;
private final ScheduledExecutorService scheduledExecutor;
public ResourceManager() {
// IO线程池 - 处理网络IO
this.ioExecutor = Executors.newFixedThreadPool(
NIOConfiguration.WORKER_THREADS,
r -> {
Thread t = new Thread(r, "NIO-IO-Worker");
t.setDaemon(true);
return t;
}
);
// 业务线程池 - 处理业务逻辑
this.businessExecutor = Executors.newFixedThreadPool(
NIOConfiguration.WORKER_THREADS * 2,
r -> {
Thread t = new Thread(r, "NIO-Business-Worker");
t.setDaemon(true);
return t;
}
);
// 定时任务线程池 - 处理定时任务
this.scheduledExecutor = Executors.newScheduledThreadPool(2,
r -> {
Thread t = new Thread(r, "NIO-Scheduled-Worker");
t.setDaemon(true);
return t;
}
);
}
public ExecutorService getIOExecutor() { return ioExecutor; }
public ExecutorService getBusinessExecutor() { return businessExecutor; }
public ScheduledExecutorService getScheduledExecutor() { return scheduledExecutor; }
public void shutdown() {
ioExecutor.shutdown();
businessExecutor.shutdown();
scheduledExecutor.shutdown();
}
}
高性能ByteBuffer池
public class ByteBufferPool {
private final ConcurrentLinkedQueue<ByteBuffer> directBuffers;
private final ConcurrentLinkedQueue<ByteBuffer> heapBuffers;
private final int bufferSize;
private final int maxPoolSize;
public ByteBufferPool(int bufferSize, int maxPoolSize) {
this.bufferSize = bufferSize;
this.maxPoolSize = maxPoolSize;
this.directBuffers = new ConcurrentLinkedQueue<>();
this.heapBuffers = new ConcurrentLinkedQueue<>();
// 预分配一些buffer
for (int i = 0; i < maxPoolSize / 2; i++) {
directBuffers.offer(ByteBuffer.allocateDirect(bufferSize));
heapBuffers.offer(ByteBuffer.allocate(bufferSize));
}
}
public ByteBuffer getDirectBuffer() {
ByteBuffer buffer = directBuffers.poll();
if (buffer == null) {
buffer = ByteBuffer.allocateDirect(bufferSize);
}
buffer.clear();
return buffer;
}
public ByteBuffer getHeapBuffer() {
ByteBuffer buffer = heapBuffers.poll();
if (buffer == null) {
buffer = ByteBuffer.allocate(bufferSize);
}
buffer.clear();
return buffer;
}
public void returnBuffer(ByteBuffer buffer) {
if (buffer.isDirect() && directBuffers.size() < maxPoolSize) {
directBuffers.offer(buffer);
} else if (!buffer.isDirect() && heapBuffers.size() < maxPoolSize) {
heapBuffers.offer(buffer);
}
}
}
性能调优配置
/** JVM参数建议
1. 堆内存 -Xms2g -Xmx2g"
2. 直接内存 -XX:MaxDirectMemorySize=1g
3. G1垃圾收集器 -XX:+UseG1GC
4. 最大GC暂停时间 -XX:MaxGCPauseMillis=20
5. -XX:+UnlockExperimentalVMOptions
6. ZGC低延迟垃圾收集器 -XX:+UseZGC
**/
/** 系统参数调优
1. 监听队列大小 net.core.somaxconn = 65535
2. 网络设备队列 net.core.netdev_max_backlog = 5000
3. SYN队列大小 net.ipv4.tcp_max_syn_backlog = 65535
4. FIN超时时间 net.ipv4.tcp_fin_timeout = 30
5. TCP keepalive时间 net.ipv4.tcp_keepalive_time = 1200
6. 文件句柄数 fs.file-max = 1000000
7. 用户级文件句柄限制 ulimit -n 1000000
**/
存在的问题
CPU空转
/**
CPU空转问题:就像一个焦虑的人不停地查看手机,即使没有新消息也要每秒检查100次。
产生的影响:
1、CPU利用率可能达到100%,但实际工作量很少
2、轮询开销:每次系统调用约消耗1-2微秒
3、1000个连接每秒轮询1000次 = 1-2毫秒的纯开销
**/
AIO
核心原理
查看代码
/**
基于操作系统的异步IO机制,通过异步通道(AsynchronousChannel)发起IO请求后立即返回,由操作系统内核在后台完成实际的IO操作,操作完成后通过CompletionHandler回调或Future机制通知应用程序,实现真正的非阻塞IO,避免线程在IO等待期间的阻塞,提高系统并发处理能力和资源利用率。
**/
核心组件
查看代码
/**
AsynchronousFileChannel(异步文件IO)
AsynchronousServerSocketChannel/AsynchronousSocketChannel(异步网络IO)
CompletionHandler(异步回调处理器)
Future(异步结果获取)
底层依赖操作系统的epoll/kqueue/IOCP等异步IO机制
**/
编程模型
文件I/O
// 核心模式:异步读写 + 回调处理
public class AIOFileTemplate {
public void asyncOperation(String filePath) {
AsynchronousFileChannel.open(Paths.get(filePath), StandardOpenOption.READ)
.read(ByteBuffer.allocate(1024), 0, null,
new CompletionHandler<Integer, Object>() {
@Override
public void completed(Integer result, Object attachment) {
// I/O完成回调
}
@Override
public void failed(Throwable exc, Object attachment) {
// I/O失败回调
}
});
}
}
网络I/O
// 服务端:接受连接 -> 读写数据 -> 处理完成
public class AIONetworkTemplate {
private AsynchronousServerSocketChannel serverChannel;
public void startServer(int port) {
serverChannel = AsynchronousServerSocketChannel.open()
.bind(new InetSocketAddress(port));
acceptLoop();
}
private void acceptLoop() {
serverChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
@Override
public void completed(AsynchronousSocketChannel client, Object attachment) {
acceptLoop(); // 继续接受新连接
handleClient(client); // 处理当前连接
}
@Override
public void failed(Throwable exc, Object attachment) {
// 连接失败处理
}
});
}
private void handleClient(AsynchronousSocketChannel client) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
client.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
// 数据读取完成,处理业务逻辑
processData(attachment);
// 继续读取或关闭连接
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
// 读取失败处理
}
});
}
}
应用场景
大文件处理系统
// 场景特点:大文件分块处理,避免内存溢出
@Service
public class LargeFileProcessor {
private static final int CHUNK_SIZE = 1024 * 1024; // 1MB 分块
public void processLargeFile(String inputPath, String outputPath) {
try {
AsynchronousFileChannel inputChannel = AsynchronousFileChannel.open(
Paths.get(inputPath), StandardOpenOption.READ);
AsynchronousFileChannel outputChannel = AsynchronousFileChannel.open(
Paths.get(outputPath), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
long fileSize = inputChannel.size();
// 核心:分块异步处理,避免大文件内存问题
processFileChunks(inputChannel, outputChannel, 0, fileSize);
} catch (Exception e) {
throw new RuntimeException("文件处理失败", e);
}
}
private void processFileChunks(AsynchronousFileChannel inputChannel,
AsynchronousFileChannel outputChannel,
long position, long totalSize) {
if (position >= totalSize) {
// 文件处理完成
closeChannels(inputChannel, outputChannel);
return;
}
ByteBuffer buffer = ByteBuffer.allocate(CHUNK_SIZE);
inputChannel.read(buffer, position, buffer,
new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
if (result > 0) {
attachment.flip();
// 处理数据块(例如:加密、压缩、格式转换等)
ByteBuffer processedData = processDataChunk(attachment);
// 异步写入处理后的数据
outputChannel.write(processedData, position, processedData,
new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
// 继续处理下一个数据块
processFileChunks(inputChannel, outputChannel,
position + result, totalSize);
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
log.error("写入数据块失败", exc);
}
});
} else {
// 读取完成
closeChannels(inputChannel, outputChannel);
}
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
log.error("读取数据块失败", exc);
closeChannels(inputChannel, outputChannel);
}
});
}
private ByteBuffer processDataChunk(ByteBuffer input) {
// 实际的数据处理逻辑
byte[] data = new byte[input.remaining()];
input.get(data);
// 例如:数据压缩
byte[] processedData = compress(data);
return ByteBuffer.wrap(processedData);
}
}
实时数据流处理
// 场景特点:持续的数据流,实时处理
@Component
public class RealTimeDataProcessor {
private final BlockingQueue<DataPacket> dataQueue = new LinkedBlockingQueue<>();
private final AtomicBoolean running = new AtomicBoolean(true);
public void startDataCollection(int port) {
// 启动数据收集服务
startDataServer(port);
// 启动数据处理
startDataProcessing();
}
private void startDataServer(int port) {
try {
AsynchronousServerSocketChannel serverChannel = AsynchronousServerSocketChannel.open();
serverChannel.bind(new InetSocketAddress(port));
acceptDataConnections(serverChannel);
} catch (Exception e) {
throw new RuntimeException("数据服务器启动失败", e);
}
}
private void acceptDataConnections(AsynchronousServerSocketChannel serverChannel) {
serverChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
@Override
public void completed(AsynchronousSocketChannel clientChannel, Object attachment) {
acceptDataConnections(serverChannel); // 继续接受新连接
// 开始接收数据流
receiveDataStream(clientChannel);
}
@Override
public void failed(Throwable exc, Object attachment) {
log.error("接受数据连接失败", exc);
}
});
}
private void receiveDataStream(AsynchronousSocketChannel clientChannel) {
ByteBuffer buffer = ByteBuffer.allocate(4096);
clientChannel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
if (result > 0) {
attachment.flip();
// 解析数据包
List<DataPacket> packets = parseDataPackets(attachment);
// 将数据包放入队列进行异步处理
packets.forEach(packet -> {
try {
dataQueue.put(packet);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
// 继续接收数据
attachment.clear();
clientChannel.read(attachment, attachment, this);
} else {
// 连接断开
closeChannel(clientChannel);
}
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
closeChannel(clientChannel);
}
});
}
private void startDataProcessing() {
// 启动多个处理线程
for (int i = 0; i < Runtime.getRuntime().availableProcessors(); i++) {
CompletableFuture.runAsync(() -> {
while (running.get()) {
try {
DataPacket packet = dataQueue.take();
processDataPacket(packet);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
});
}
}
private void processDataPacket(DataPacket packet) {
// 实时数据处理逻辑
// 例如:实时分析、告警检测、数据聚合等
// 异步写入结果
saveProcessingResult(packet);
}
private void saveProcessingResult(DataPacket packet) {
String resultPath = "results/" + packet.getId() + ".json";
try {
AsynchronousFileChannel resultChannel = AsynchronousFileChannel.open(
Paths.get(resultPath), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
ByteBuffer resultBuffer = ByteBuffer.wrap(packet.toJson().getBytes());
resultChannel.write(resultBuffer, 0, null,
new CompletionHandler<Integer, Object>() {
@Override
public void completed(Integer result, Object attachment) {
closeChannel(resultChannel);
}
@Override
public void failed(Throwable exc, Object attachment) {
log.error("保存处理结果失败", exc);
}
});
} catch (Exception e) {
log.error("创建结果文件失败", e);
}
}
}
高并发网络服务器
// 场景特点:大量并发连接,I/O密集型
@Component
public class HighConcurrencyServer {
private final AtomicInteger connectionCount = new AtomicInteger(0);
private final Map<String, AsynchronousSocketChannel> activeConnections = new ConcurrentHashMap<>();
public void startServer(int port) {
try {
AsynchronousServerSocketChannel serverChannel = AsynchronousServerSocketChannel.open();
serverChannel.bind(new InetSocketAddress(port));
// 核心:无限循环接受连接,每个连接独立处理
acceptConnections(serverChannel);
} catch (Exception e) {
throw new RuntimeException("服务器启动失败", e);
}
}
private void acceptConnections(AsynchronousServerSocketChannel serverChannel) {
serverChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
@Override
public void completed(AsynchronousSocketChannel clientChannel, Object attachment) {
// 立即继续接受下一个连接
acceptConnections(serverChannel);
// 处理当前连接
String clientId = "client-" + connectionCount.incrementAndGet();
activeConnections.put(clientId, clientChannel);
handleClientConnection(clientChannel, clientId);
}
@Override
public void failed(Throwable exc, Object attachment) {
log.error("接受连接失败", exc);
}
});
}
private void handleClientConnection(AsynchronousSocketChannel clientChannel, String clientId) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
clientChannel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
if (result > 0) {
attachment.flip();
// 处理接收到的数据
String message = StandardCharsets.UTF_8.decode(attachment).toString();
String response = processMessage(clientId, message);
// 异步发送响应
sendResponse(clientChannel, response, clientId);
// 继续读取下一条消息
attachment.clear();
clientChannel.read(attachment, attachment, this);
} else {
// 客户端断开连接
activeConnections.remove(clientId);
closeChannel(clientChannel);
}
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
activeConnections.remove(clientId);
closeChannel(clientChannel);
}
});
}
}
性能优化
通道组配置
@Configuration
public class AIOConfiguration {
@Bean
public AsynchronousChannelGroup channelGroup() {
try {
// 自定义线程池,优化性能
ThreadPoolExecutor executor = new ThreadPoolExecutor(
Runtime.getRuntime().availableProcessors(),
Runtime.getRuntime().availableProcessors() * 2,
60L, TimeUnit.SECONDS, // 空闲超时
new LinkedBlockingQueue<>(1000), // 队列大小
new ThreadFactoryBuilder()
.setNameFormat("aio-worker-%d")
.setDaemon(true)
.build()
);
return AsynchronousChannelGroup.withThreadPool(executor);
} catch (Exception e) {
throw new RuntimeException("创建通道组失败", e);
}
}
}
缓冲区优化
public class BufferOptimization {
// 缓冲区大小根据业务场景调整
private static final int SMALL_BUFFER = 4 * 1024; // 4KB - 小数据包
private static final int MEDIUM_BUFFER = 64 * 1024; // 64KB - 一般文件
private static final int LARGE_BUFFER = 1024 * 1024; // 1MB - 大文件
public ByteBuffer allocateBuffer(BufferSize size) {
return switch (size) {
case SMALL -> ByteBuffer.allocateDirect(SMALL_BUFFER);
case MEDIUM -> ByteBuffer.allocateDirect(MEDIUM_BUFFER);
case LARGE -> ByteBuffer.allocateDirect(LARGE_BUFFER);
};
}
}

浙公网安备 33010602011771号