Files
interview/10-中间件/Netty核心原理.md
yasinshaw 7aa971f511 feat: add Netty and Java NIO interview questions
Added 3 comprehensive interview documents covering Netty and Java NIO:

**1. Netty Core Principles (Netty核心原理.md)**
- Core components: Channel, EventLoop, ChannelPipeline, ByteBuf
- Reactor threading model (single/multi-threaded, master-slave)
- ByteBuf vs Java NIO ByteBuffer
- Zero-copy implementation (4 approaches)
- ChannelPipeline and ChannelHandler
- TCP sticky/unpacking problem solutions
- Heartbeat mechanism

**2. Java NIO Core Principles (Java NIO核心原理.md)**
- NIO vs BIO comparison
- Three core components: Channel, Buffer, Selector
- Selector multiplexing mechanism
- Channel vs Stream differences
- Buffer core attributes and usage
- Non-blocking I/O implementation
- Zero-copy with transferTo and MappedByteBuffer

**3. Netty Practice Scenarios (Netty实战场景.md)**
- High-performance RPC framework design
- WebSocket server implementation
- Million-connection IM system architecture
- Memory leak detection and resolution
- Graceful shutdown implementation
- Heartbeat and reconnection mechanisms

Each document includes:
- Detailed problem descriptions
- Complete code examples (Java)
- Architecture diagrams
- Best practices
- Performance optimization tips
- P7-level bonus points

Total: 3 documents, covering:
- Theoretical foundations
- Practical implementations
- Production scenarios
- Performance tuning
- Common pitfalls

Suitable for backend P7 interview preparation.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-03-06 10:20:11 +08:00

540 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Netty 核心原理
## 问题
1. Netty 的核心组件有哪些?
2. 什么是 EventLoop线程模型是怎样的
3. 什么是 ByteBuf与 Java NIO 的 ByteBuffer 有什么区别?
4. Netty 的零拷贝是如何实现的?
5. 什么是 ChannelPipelineChannelHandler 是如何工作的?
6. Netty 如何解决 TCP 粘包/拆包问题?
7. Netty 的心跳机制是如何实现的?
---
## 标准答案
### 1. Netty 核心组件
#### **核心组件架构**
```
┌─────────────────────────────────────────────┐
│ Netty 架构 │
├─────────────────────────────────────────────┤
│ Channel │
│ ├─ EventLoop (线程模型) │
│ ├─ ChannelPipeline (处理器链) │
│ │ └─ ChannelHandler (业务处理器) │
│ └─ ByteBuf (内存管理) │
├─────────────────────────────────────────────┤
│ Bootstrap (启动类) │
│ ├─ ServerBootstrap (服务端) │
│ └─ Bootstrap (客户端) │
└─────────────────────────────────────────────┘
```
#### **关键组件说明**
**1. Channel**
```java
// Channel 是 Netty 的网络操作抽象类
Channel channel = ...;
// 核心方法
channel.writeAndFlush(msg); // 写数据并刷新
channel.close(); // 关闭连接
channel.eventLoop(); // 获取 EventLoop
```
**2. EventLoop**
```java
// EventLoop 负责处理 I/O 操作
EventLoopGroup bossGroup = new NioEventLoopGroup(1); // Accept
EventLoopGroup workerGroup = new NioEventLoopGroup(); // I/O
// bossGroup 只负责监听并接受连接
// workerGroup 负责 I/O 读写
```
**3. ChannelPipeline**
```java
// Pipeline 是处理器链
ChannelPipeline pipeline = channel.pipeline();
// 添加处理器
pipeline.addLast("decoder", new Decoder());
pipeline.addLast("encoder", new Encoder());
pipeline.addLast("handler", new BusinessHandler());
```
**4. ByteBuf**
```java
// ByteBuf 是 Netty 的字节容器
ByteBuf buffer = Unpooled.buffer(1024);
// 写入数据
buffer.writeInt(123);
buffer.writeBytes("Hello".getBytes());
// 读取数据
int value = buffer.readInt();
```
---
### 2. EventLoop 与线程模型
#### **Reactor 线程模型**
Netty 采用 **Reactor 模式**,支持三种实现:
```
1. Reactor 单线程模型
┌──────────────┐
│ Reactor │
│ (单线程) │
│ │
│ - Accept │
│ - Read │
│ - Decode │
│ - Process │
│ - Encode │
│ - Send │
└──────────────┘
2. Reactor 多线程模型
┌──────────┐ ┌──────────────────┐
│ Reactor │────>│ Worker Threads │
│ (主线程) │ │ (线程池) │
│ │ │ │
│ - Accept │ │ - Read/Write │
│ │ │ - Decode/Encode │
│ │ │ - Process │
└──────────┘ └──────────────────┘
3. 主从 Reactor 多线程模型Netty 默认)
┌──────────┐ ┌──────────────────┐
│ Main │ │ Sub Reactors │
│ Reactor │────>│ (线程池) │
│ │ │ │
│ - Accept │ │ - Read/Write │
│ │ │ - Decode/Encode │
│ │ │ - Process │
└──────────┘ └──────────────────┘
```
#### **EventLoop 工作原理**
```java
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
// 每个 EventLoop:
// 1. 维护一个 Selector
// 2. 维护一个任务队列
// 3. 维护一个线程
// 工作流程:
// - 线程启动后,无限循环执行:
// 1. select() - 查询就绪的 Channel
// 2. processSelectedKeys() - 处理 I/O 事件
// 3. runAllTasks() - 执行任务队列中的任务
```
#### **任务调度**
```java
Channel channel = ...;
// 在 EventLoop 线程中执行任务
channel.eventLoop().execute(() -> {
System.out.println("Task in EventLoop thread");
});
// 定时任务
channel.eventLoop().schedule(() -> {
System.out.println("Delayed task");
}, 1, TimeUnit.SECONDS);
```
---
### 3. ByteBuf vs ByteBuffer
#### **对比表**
| 特性 | Java NIO ByteBuffer | Netty ByteBuf |
|------|---------------------|---------------|
| **长度** | 固定长度,扩容复杂 | 动态扩容 |
| **读写模式** | flip() 切换 | 独立的读写索引 |
| **引用计数** | 不支持 | 支持(池化) |
| **零拷贝** | 不支持 | 支持Composite |
| **性能** | 较低 | 高(对象池优化) |
#### **ByteBuf 三个重要指针**
```
ByteBuf 结构:
┌───────┬──────────┬──────────┬─────────┐
│ 0 │ readerIndex│ writerIndex│ capacity│
│ │ │ │ │
│ discard│ readable│ writable │ │
│ bytes │ bytes │ bytes │ │
└───────┴──────────┴──────────┴─────────┘
操作:
- mark() / reset(): 标记和重置
- readerIndex(): 读指针
- writerIndex(): 写指针
- capacity(): 容量
- maxCapacity(): 最大容量
```
#### **使用示例**
```java
// 创建 ByteBuf
ByteBuf buffer = Unpooled.buffer(1024);
// 写入数据
buffer.writeBytes("Hello".getBytes());
buffer.writeInt(123);
// 读取数据(不移动读指针)
byte[] data = new byte[5];
buffer.getBytes(buffer.readerIndex(), data);
// 读取数据(移动读指针)
buffer.readBytes(5);
// 引用计数(池化)
buffer.retain(); // 引用计数 +1
buffer.release(); // 引用计数 -1
```
---
### 4. 零拷贝实现
#### **零拷贝的三种实现**
**1. CompositeByteBuf组合缓冲区**
```java
// 将多个 ByteBuf 组合成一个逻辑上的 ByteBuf
ByteBuf header = Unpooled.buffer(10);
ByteBuf body = Unpooled.buffer(100);
// 零拷贝组合
CompositeByteBuf message = Unpooled.wrappedBuffer(header, body);
// 物理上不复制数据,只是逻辑组合
```
**2. wrappedBuffer包装**
```java
// 包装字节数组,不复制
byte[] bytes = "Hello Netty".getBytes();
ByteBuf buffer = Unpooled.wrappedBuffer(bytes);
// 底层数据共享,无拷贝
```
**3. slice切片**
```java
ByteBuf buffer = Unpooled.buffer(100);
// 切片,共享底层内存
ByteBuf sliced = buffer.slice(0, 50);
// 修改会互相影响
sliced.setByte(0, (byte) 1);
```
**4. FileChannel.transferTo文件传输**
```java
// 文件传输零拷贝
FileChannel sourceChannel = ...;
FileChannel destChannel = ...;
// 直接在内核空间传输,不经过用户空间
sourceChannel.transferTo(position, count, destChannel);
```
---
### 5. ChannelPipeline 与 ChannelHandler
#### **Pipeline 工作流程**
```
入站事件 (Inbound):
┌─────────────────────────────────────┐
│ ChannelPipeline │
│ │
│ ┌─────────┐ ┌─────────┐ ┌──────┐│
│ │Decoder1 │→ │Decoder2 │→ │Handler││
│ └─────────┘ └─────────┘ └──────┘│
│ ▲ ▲ ▲ │
│ └────────────┴─────────────┘ │
│ (数据流) │
└─────────────────────────────────────┘
出站事件 (Outbound):
┌─────────────────────────────────────┐
│ ChannelPipeline │
│ │
│ ┌──────┐ ┌─────────┐ ┌─────────┐│
│ │Handler│→ │Encoder1 │→ │Encoder2 ││
│ └──────┘ └─────────┘ └─────────┘│
│ ▲ ▲ ▲ │
│ └────────────┴─────────────┘ │
│ (数据流) │
└─────────────────────────────────────┘
```
#### **ChannelHandler 接口**
```java
// 入站处理器
@ChannelHandler.Sharable
public class InboundHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// 处理读事件
System.out.println("Received: " + msg);
ctx.fireChannelRead(msg); // 传递给下一个 Handler
}
@Override
public void channelActive(ChannelHandlerContext ctx) {
// 连接建立
System.out.println("Client connected");
}
@Override
public void channelInactive(ChannelHandlerContext ctx) {
// 连接断开
System.out.println("Client disconnected");
}
}
// 出站处理器
public class OutboundHandler extends ChannelOutboundHandlerAdapter {
@Override
public void write(ChannelHandlerContext ctx, Object msg,
ChannelPromise promise) {
// 处理写事件
System.out.println("Sending: " + msg);
ctx.write(msg, promise);
}
}
```
#### **Handler 生命周期**
```java
public interface ChannelHandler {
// 添加到 Pipeline
void handlerAdded(ChannelHandlerContext ctx);
// 从 Pipeline 移除
void handlerRemoved(ChannelHandlerContext ctx);
// 发生异常
void exceptionCaught(ChannelHandlerContext ctx, Throwable cause);
}
```
---
### 6. TCP 粘包/拆包解决方案
#### **问题原因**
```
TCP 粘包:
发送方: [包1] [包2] [包3]
接收方: [包1+包2+包3] // 粘在一起
TCP 拆包:
发送方: [大包]
接收方: [片段1] [片段2] // 被拆分
```
#### **Netty 提供的解码器**
**1. FixedLengthFrameDecoder固定长度**
```java
// 每个消息固定 10 字节
pipeline.addLast("framer", new FixedLengthFrameDecoder(10));
```
**2. LineBasedFrameDecoder分隔符**
```java
// 按换行符分割
pipeline.addLast("framer", new LineBasedFrameDecoder(8192));
```
**3. DelimiterBasedFrameDecoder自定义分隔符**
```java
// 自定义分隔符
ByteBuf delimiter = Unpooled.copiedBuffer("\t".getBytes());
pipeline.addLast("framer",
new DelimiterBasedFrameDecoder(8192, delimiter));
```
**4. LengthFieldBasedFrameDecoder长度字段**
```java
// 消息格式: [长度(4字节)] [数据]
pipeline.addLast("framer",
new LengthFieldBasedFrameDecoder(
8192, // maxFrameLength
0, // lengthFieldOffset
4, // lengthFieldLength
0, // lengthAdjustment
0 // initialBytesToStrip
));
```
**5. 自定义解码器**
```java
public class CustomDecoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx,
ByteBuf in, List<Object> out) {
// 检查是否有足够数据
if (in.readableBytes() < 4) {
return;
}
// 标记读指针
in.markReaderIndex();
// 读取长度
int length = in.readInt();
// 检查数据是否完整
if (in.readableBytes() < length) {
in.resetReaderIndex();
return;
}
// 读取数据
byte[] data = new byte[length];
in.readBytes(data);
out.add(data);
}
}
```
---
### 7. 心跳机制
#### **IdleStateHandler 实现**
```java
// 读写空闲检测
pipeline.addLast("idleStateHandler",
new IdleStateHandler(
30, // 读空闲时间(秒)
0, // 写空闲时间
0 // 读写空闲时间
));
// 心跳处理器
pipeline.addLast("heartbeatHandler", new HeartbeatHandler());
```
#### **心跳处理器**
```java
public class HeartbeatHandler extends ChannelInboundHandlerAdapter {
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
if (event.state() == IdleState.READER_IDLE) {
// 读空闲,关闭连接
System.out.println("Read idle, close connection");
ctx.close();
}
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// 处理心跳包
if ("PING".equals(msg)) {
ctx.writeAndFlush("PONG");
} else {
ctx.fireChannelRead(msg);
}
}
}
```
---
## P7 加分项
### 深度理解
- **Reactor 模式**:理解单线程、多线程、主从多线程三种模型
- **零拷贝原理**:理解用户空间和内核空间的数据拷贝
- **内存管理**ByteBuf 的池化、引用计数、内存泄漏
### 实战经验
- **高并发场景**EventLoopGroup 线程数调优
- **内存调优**ByteBuf 分配器选择(堆内存 vs 直接内存)
- **性能优化**:避免 Handler 链过长、减少对象创建
### 架构设计
- **协议设计**:自定义协议、编解码器设计
- **异常处理**:优雅关闭、重连机制
- **监控告警**:流量监控、连接数监控、延迟监控
### 常见问题
1. **内存泄漏**ByteBuf 未释放
2. **线程安全**:共享状态的同步
3. **连接池**Channel 复用与管理
4. **背压处理**:流量控制与慢消费
---
## 总结
Netty 的核心优势:
1. **高性能**零拷贝、内存池、Reactor 模式
2. **高可靠性**:心跳机制、重连机制、优雅关闭
3. **易扩展**Pipeline 机制、丰富的 Handler
4. **成熟稳定**:广泛应用在 Dubbo、gRPC、WebSocket
**最佳实践**
- 合理设置 EventLoopGroup 线程数
- 使用池化的 ByteBuf
- 及时释放 ByteBuf避免内存泄漏
- 使用 LengthFieldBasedFrameDecoder 处理粘包
- 添加心跳和重连机制
- 做好监控和日志