相比于 Java BIO 一请求一线程的模式,底层使用 epoll 的 Java NIO 的 IO 多路复用模式可以处理更多的请求。
一个简单的NIO demo 一般就是这样,只有一个主线程完成监听和处理工作,可以同时处理多个请求。
java
public class NioMain {
private static final int PORT = 9999;
private Selector selector;
private ServerSocketChannel serverSocketChannel;
public static void main(String[] args) throws IOException {
new NioMain().startServer();
}
private void startServer() {
try {
selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.socket().bind(new InetSocketAddress(PORT));
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("NIO 服务器启动成功,监听端口:" + PORT);
while (true) {
int selectChannels = selector.select();
if (selectChannels == 0) {
continue;
}
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectionKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey selectionKey = keyIterator.next();
keyIterator.remove();
if (selectionKey.isAcceptable()) {
handleAccept(selectionKey);
} else if (selectionKey.isReadable()) {
handleRead(selectionKey);
}
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (serverSocketChannel != null) serverSocketChannel.close();
if (selector != null) selector.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void handleRead(SelectionKey key) {
try {
SocketChannel clientChannel = (SocketChannel) key.channel();
// 获取客户端专属的读缓冲区
ByteBuffer readBuffer = (ByteBuffer) key.attachment();
readBuffer.clear(); // 重置缓冲区:position=0, limit=capacity
// 1. 读取客户端数据(非阻塞,返回读取的字节数)
int readBytes = clientChannel.read(readBuffer);
if (readBytes == -1) {
// 客户端关闭连接
System.out.println("客户端断开连接:" + clientChannel.getRemoteAddress());
clientChannel.close();
key.cancel();
return;
}
if (readBytes == 0) {
return; // 无数据可读,直接返回
}
// 2. 解码完整字符串(关键:flip() 切换为读模式)
readBuffer.flip(); // position=0, limit=已读取字节数
// 读取实际有效的字节(避免空字节干扰)
byte[] data = new byte[readBuffer.remaining()];
readBuffer.get(data);
String receivedMsg = new String(data, StandardCharsets.UTF_8).trim();
System.out.println("收到客户端消息:" + receivedMsg);
// 3. 处理退出指令
if ("exit".equalsIgnoreCase(receivedMsg)) {
System.out.println("客户端主动断开连接:" + clientChannel.getRemoteAddress());
clientChannel.close();
key.cancel();
return;
}
// 4. 回复客户端(核心修复:循环写入,确保完整发送)
String response = "服务器已接收:" + receivedMsg + "\r\n"; // 加换行,客户端可换行显示
ByteBuffer writeBuffer = StandardCharsets.UTF_8.encode(response);
// 非阻塞写可能只发送部分字节,需循环写入直到全部发送
while (writeBuffer.hasRemaining()) {
clientChannel.write(writeBuffer);
}
// 5. 重置读缓冲区,准备下次读取
readBuffer.clear();
} catch (IOException e) {
e.printStackTrace();
}
}
private void handleAccept(SelectionKey selectionKey) {
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
try {
SocketChannel clientChannel = serverSocketChannel.accept();
clientChannel.configureBlocking(false);
SocketAddress remoteAddress = clientChannel.getRemoteAddress();
System.out.println("客户端[" + remoteAddress + "]连接成功:" + remoteAddress);
clientChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
如果请求过多,一般会分成两个部分,一个部分即主线程负责处理客户端的连接请求,每个主线程持有一个 ServerSocketChannel,当它监听到有新连接时;另一个部分即工作线程池负责处理已连接客户端的读写请求,每一个工作线程持有一个 Selector 选择器,在客户端建立连接时把客户端 Channel 注册到这个选择器上,后续的读写请求都通过唤醒这个 工作Selector 来进行处理。这就是 Reactor 模式,
java
public class MasterSlaveReactorServer {
// 监听端口
private static final int PORT = 9999;
// CPU核心数(用于计算从Reactor线程数)
private static final int CPU_CORES = Runtime.getRuntime().availableProcessors();
// 主Reactor线程数(建议1~2个)
private static final int MASTER_THREAD_NUM = 2;
// 从Reactor线程数(CPU核心数*2)
private static final int SLAVE_THREAD_NUM = 8 ;
// 主Reactor线程池(处理Accept事件)
private final ExecutorService masterExecutor;
// 从Reactor线程池(处理Read/Write事件)
private final ExecutorService slaveExecutor;
// 从Reactor的Selector数组(每个从Reactor一个Selector)
private final Selector[] slaveSelectors;
// 轮询索引(负载均衡:分配连接到不同从Reactor)
private final AtomicInteger slaveIndex = new AtomicInteger(0);
public static void main(String[] args) throws IOException {
MasterSlaveReactorServer server = new MasterSlaveReactorServer();
server.start();
Runtime.getRuntime().addShutdownHook(new Thread(server::shutdown));
}
public MasterSlaveReactorServer() throws IOException {
this.masterExecutor = Executors.newFixedThreadPool(MASTER_THREAD_NUM);
// 初始化从Reactor线程池(固定线程数)
this.slaveExecutor = Executors.newFixedThreadPool(SLAVE_THREAD_NUM);
// 初始化从Reactor的Selector
this.slaveSelectors = new Selector[SLAVE_THREAD_NUM];
for (int i = 0; i < SLAVE_THREAD_NUM; i++) {
slaveSelectors[i] = Selector.open();
// 启动从Reactor线程
slaveExecutor.execute(new SlaveReactorRunnable(slaveSelectors[i]));
}
}
public void start() throws IOException {
System.out.println("多线程主Reactor服务器启动,监听端口:" + PORT);
System.out.println("主Reactor线程数:" + MASTER_THREAD_NUM + ",从Reactor线程数:" + SLAVE_THREAD_NUM);
// 启动多个主Reactor线程(每个线程绑定同一个端口)
for (int i = 0; i < MASTER_THREAD_NUM; i++) {
// 每个主Reactor线程创建独立的ServerSocketChannel(共享端口)
ServerSocketChannel serverChannel = createServerSocketChannel();
// 提交主Reactor任务
masterExecutor.execute(new MasterReactorRunnable(serverChannel, i));
}
}
/**
* 创建ServerSocketChannel(开启端口复用,支持多线程共享端口)
*/
private ServerSocketChannel createServerSocketChannel() throws IOException {
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
// 核心:开启端口复用,允许多个 ServerSocketChannel 绑定同一端口
serverChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
// 增大监听队列长度(Accept队列)
// serverChannel.setOption(StandardSocketOptions.SO_BACKLOG, 65535);
// 绑定端口(多个主Reactor线程的ServerSocketChannel都绑定此端口)
serverChannel.socket().bind(new InetSocketAddress(PORT));
return serverChannel;
}
public void shutdown() {
masterExecutor.shutdown();
slaveExecutor.shutdown();
for (Selector selector : slaveSelectors) {
try {
selector.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("服务器已优雅关闭");
}
class SlaveReactorRunnable implements Runnable {
private final Selector slaveSelector;
public SlaveReactorRunnable(Selector slaveSelector) {
this.slaveSelector = slaveSelector;
}
@Override
public void run() {
System.out.println("从Reactor线程启动:" + Thread.currentThread().getName());
int emptyPollCount = 0;
final int MAX_EMPTY_POLL = 100;
while (!Thread.currentThread().isInterrupted()) {
try {
int readKeys = slaveSelector.select(1000);
if (readKeys == 0) {
emptyPollCount++;
if (emptyPollCount > MAX_EMPTY_POLL) {
rebuildSelector();
emptyPollCount = 0;
}
continue;
}
emptyPollCount = 0;
Set<SelectionKey> keys = slaveSelector.selectedKeys();
Iterator<SelectionKey> iterator = keys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
if (key.isReadable()) {
handleRead(key);
} else if (key.isWritable()) {
handleWrite(key);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void handleWrite(SelectionKey key) {
SocketChannel clientChannel = (SocketChannel) key.channel();
ByteBuffer writeBuffer = (ByteBuffer) key.attachment();
try {
while (writeBuffer.hasRemaining()) {
clientChannel.write(writeBuffer);
}
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
key.attach(ByteBuffer.allocateDirect(4096));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void handleRead(SelectionKey key) {
SocketChannel clientChannel = (SocketChannel) key.channel();
ByteBuffer buffer = (ByteBuffer) key.attachment();
buffer.clear();
try {
int readBytes = clientChannel.read(buffer);
if (readBytes == -1) {
System.out.println("客户端断开连接:" + clientChannel.getRemoteAddress());
key.cancel();
clientChannel.close();
return;
}
if (readBytes == 0) {
return;
}
// 解码数据
buffer.flip();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
String msg = new String(data, StandardCharsets.UTF_8).trim();
System.out.println("[" + Thread.currentThread().getName() + "] 收到消息:" + clientChannel.getRemoteAddress() + " -> " + msg);
// 退出指令
if ("exit".equalsIgnoreCase(msg)) {
System.out.println("客户端主动退出:" + clientChannel.getRemoteAddress());
key.cancel();
clientChannel.close();
return;
}
// 构建响应
String response = "[" + Thread.currentThread().getName() + "] 服务器已接收:" + msg + "\r\n";
ByteBuffer writeBuffer = StandardCharsets.UTF_8.encode(response);
while (writeBuffer.hasRemaining()) {
int writeBytes = clientChannel.write(writeBuffer);
if (writeBytes == 0) {
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
key.attach(writeBuffer);
break;
}
}
if (!writeBuffer.hasRemaining()) {
key.attach(writeBuffer);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void rebuildSelector() {
Selector oldSelector = this.slaveSelector;
try {
Selector newSelector = Selector.open();
for (SelectionKey key : oldSelector.keys()) {
if (key.isValid()) {
key.channel().register(newSelector, key.interestOps(), key.attachment());
}
}
synchronized (this) {
oldSelector.close();
}
System.out.println("从Reactor重建Selector:" + Thread.currentThread().getName());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
class MasterReactorRunnable implements Runnable {
private final ServerSocketChannel serverSocketChannel;
private final int masterId;
public MasterReactorRunnable(ServerSocketChannel serverSocketChannel, int masterId) {
this.serverSocketChannel = serverSocketChannel;
this.masterId = masterId;
}
@Override
public void run() {
System.out.println("主Reactor线程" + masterId + "启动:" + Thread.currentThread().getName());
try {
Selector masterSelector = Selector.open();
serverSocketChannel.register(masterSelector, SelectionKey.OP_ACCEPT);
while (!Thread.currentThread().isInterrupted()) {
// 阻塞等待Accept事件(超时1s,避免永久阻塞)
int readyKeys = masterSelector.select(1000);
if (readyKeys == 0) {
continue;
}
Set<SelectionKey> keys = masterSelector.selectedKeys();
Iterator<SelectionKey> iterator = keys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
if (key.isAcceptable()) {
// 处理客户端连接(多主Reactor线程竞争Accept)
handleAccept(key);
}
}
}
} catch (IOException e) {
System.err.println("主Reactor线程" + masterId + "异常:" + e.getMessage());
} finally {
try {
serverSocketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void handleAccept(SelectionKey key) {
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
try {
SocketChannel clientChannel = serverChannel.accept();
if (clientChannel == null) return;
// 客户端通道配置
clientChannel.configureBlocking(false);
clientChannel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
clientChannel.setOption(StandardSocketOptions.TCP_NODELAY, true);
clientChannel.setOption(StandardSocketOptions.SO_RCVBUF, 4096);
clientChannel.setOption(StandardSocketOptions.SO_SNDBUF, 4096);
System.out.println("主Reactor线程" + masterId + " 接受连接:" + clientChannel.getRemoteAddress());
// 负载均衡:轮询选择从Reactor
int slaveIdx = slaveIndex.getAndIncrement() % SLAVE_THREAD_NUM;
Selector slaveSelector = slaveSelectors[slaveIdx];
// 唤醒从Reactor(避免select阻塞)
slaveSelector.wakeup();
// 注册到从Reactor,监听Read事件
clientChannel.register(slaveSelector, SelectionKey.OP_READ, ByteBuffer.allocateDirect(4096));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}