题目
设计一个基于Java NIO的高性能HTTP服务器,支持10K并发连接
信息
- 类型:问答
- 难度:⭐⭐⭐
考点
NIO多路复用,HTTP协议解析,线程模型优化,资源泄漏防护,背压处理
快速回答
实现要点:
- 使用Selector实现非阻塞I/O多路复用
- 自定义HTTP协议解析器处理字节流
- 主从Reactor线程模型分离I/O和业务处理
- ByteBuffer对象池防止内存碎片
- 响应式背压控制防止OOM
- 使用WeakReference跟踪未关闭连接
核心架构设计
采用主从Reactor模式:
1. Main Reactor:单线程处理Accept事件
2. Sub Reactor:线程池处理Read/Write事件
3. Worker Threads:业务线程池处理HTTP请求
// 主Selector处理Accept
Selector mainSelector = Selector.open();
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.bind(new InetSocketAddress(8080));
ssc.configureBlocking(false);
ssc.register(mainSelector, SelectionKey.OP_ACCEPT);
// 从Selector线程池
ExecutorService subReactors = Executors.newFixedThreadPool(
Runtime.getRuntime().availableProcessors(),
new ThreadFactory() { /* 守护线程 */ }
);HTTP协议解析关键
需处理TCP粘包/拆包:
1. 基于状态机解析请求行/头
2. 动态扩容ByteBuffer
3. 处理chunked传输编码
enum ParseState { START_LINE, HEADERS, BODY, COMPLETE }
class HttpParser {
private ParseState state = ParseState.START_LINE;
void parse(ByteBuffer buf) throws MalformedRequestException {
while (buf.hasRemaining()) {
switch (state) {
case START_LINE:
// 解析"GET / HTTP/1.1\r\n"
if (scanLine(buf)) state = ParseState.HEADERS;
break;
// 其他状态处理...
}
}
}
}性能优化实践
- 内存管理:使用ThreadLocal缓存ByteBuffer,避免频繁分配
- I/O模型:Linux启用epoll(-Djava.nio.channels.spi.SelectorProvider=sun.nio.ch.EPollSelectorProvider)
- 负载控制:当待处理请求>队列容量80%时返回503
资源泄漏防护
// 使用PhantomReference跟踪未关闭Channel
ReferenceQueue queue = new ReferenceQueue<>();
Set refs = Collections.newSetFromMap(new WeakHashMap<>());
class ChannelRef extends PhantomReference {
void cleanUp() { /* 强制关闭连接并记录堆栈 */ }
}
// 监控线程
new Thread(() -> {
while (true) {
ChannelRef ref = (ChannelRef) queue.remove();
ref.cleanUp();
refs.remove(ref);
}
}).start(); 常见陷阱
- 事件驱动误用:在Sub Reactor中执行阻塞操作导致吞吐量下降
- ByteBuffer重用:未正确调用flip()/clear()导致数据错乱
- OOM风险:未限制header大小(需设置maxHeaderSize=8KB)
扩展知识
- 零拷贝优化:FileChannel.transferTo()发送静态文件
- SSL/TLS集成:使用SSLEngine实现非阻塞HTTPS
- 协议升级:处理WebSocket的Upgrade请求(Connection: Upgrade)