相关文章链接
processSelectedKeys() vs runAllTasks()
NioServerSocketChannel-Unsafe初始化详解
NioServerSocketChannel Unsafe 初始化详解
问题
在 Netty 服务端启动过程中,NioServerSocketChannel 的 unsafe 字段到底是什么类型?为什么是这个类型?
答案
NioServerSocketChannel 的 unsafe 字段是 NioMessageUnsafe 类型的实例。
本文档将从构造函数开始,逐步追踪代码,解释这个结论是如何得出的。
前置知识:类继承关系
在开始追踪代码之前,先了解 NioServerSocketChannel 的继承关系:
AbstractChannel (最顶层父类)
↑ 继承
AbstractNioChannel
↑ 继承
AbstractNioMessageChannel (实现了 newUnsafe() 方法)
↑ 继承
NioServerSocketChannel (我们创建的类)
关键点:
AbstractChannel定义了unsafe字段AbstractChannel定义了抽象方法newUnsafe()AbstractNioMessageChannel实现了newUnsafe()方法
代码追踪:从构造函数到 unsafe 初始化
步骤 1:创建 NioServerSocketChannel 对象
// ServerBootstrap 启动时
Channel channel = new NioServerSocketChannel();
这行代码会触发 NioServerSocketChannel 的构造方法。
步骤 2:NioServerSocketChannel 无参构造方法
位置:io.netty.channel.socket.nio.NioServerSocketChannel
public class NioServerSocketChannel extends AbstractNioMessageChannel
implements ServerSocketChannel {
private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER =
SelectorProvider.provider();
// 【入口】无参构造方法
public NioServerSocketChannel() {
// 调用本类的另一个构造方法
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
// 创建 Java NIO 的 ServerSocketChannel
private static ServerSocketChannel newSocket(SelectorProvider provider) {
try {
return provider.openServerSocketChannel();
} catch (IOException e) {
throw new ChannelException("Failed to open a server socket.", e);
}
}
}
说明:
- 调用
newSocket()创建 Java NIO 的ServerSocketChannel - 然后调用本类的重载构造方法
步骤 3:NioServerSocketChannel 有参构造方法
位置:io.netty.channel.socket.nio.NioServerSocketChannel
public NioServerSocketChannel(ServerSocketChannel channel) {
// 调用父类 AbstractNioMessageChannel 的构造方法
// 参数:parent=null, channel=ServerSocketChannel, readInterestOp=OP_ACCEPT
super(null, channel, SelectionKey.OP_ACCEPT);
// 创建配置对象(与 unsafe 无关,略过)
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
说明:
- 调用父类
AbstractNioMessageChannel的构造方法 - 传入
OP_ACCEPT表示关注接受连接事件
步骤 4:AbstractNioMessageChannel 构造方法
位置:io.netty.channel.nio.AbstractNioMessageChannel
public abstract class AbstractNioMessageChannel extends AbstractNioChannel {
protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
// 继续调用父类 AbstractNioChannel 的构造方法
super(parent, ch, readInterestOp);
}
}
说明:
- 这个类没有做额外处理,直接调用父类构造方法
- 注意:这个类后面会实现
newUnsafe()方法(见步骤 7)
步骤 5:AbstractNioChannel 构造方法
位置:io.netty.channel.nio.AbstractNioChannel
public abstract class AbstractNioChannel extends AbstractChannel {
private final SelectableChannel ch;
protected final int readInterestOp;
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
// 继续调用父类 AbstractChannel 的构造方法
super(parent);
// 保存 Java NIO 的 Channel
this.ch = ch;
this.readInterestOp = readInterestOp;
try {
// 设置为非阻塞模式
ch.configureBlocking(false);
} catch (IOException e) {
// ... 异常处理
}
}
}
说明:
- 保存 Java NIO 的
SelectableChannel - 设置为非阻塞模式
- 继续调用父类
AbstractChannel的构造方法
步骤 6:AbstractChannel 构造方法(核心!)
位置:io.netty.channel.AbstractChannel
public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {
private final Channel parent;
private final ChannelId id;
private final Unsafe unsafe; // unsafe 字段在这里定义
private final DefaultChannelPipeline pipeline;
protected AbstractChannel(Channel parent) {
this.parent = parent;
// 创建唯一 ID
id = newId();
// 【核心】调用 newUnsafe() 方法创建 unsafe 实例
unsafe = newUnsafe();
// 创建 Pipeline
pipeline = newChannelPipeline();
}
// 抽象方法,由子类实现
protected abstract AbstractUnsafe newUnsafe();
protected ChannelId newId() {
return DefaultChannelId.newInstance();
}
protected DefaultChannelPipeline newChannelPipeline() {
return new DefaultChannelPipeline(this);
}
}
说明:
unsafe字段在这里被初始化- 调用
newUnsafe()方法创建实例 newUnsafe()是抽象方法,需要子类实现
关键问题:这里调用的是哪个类的 newUnsafe() 方法?
步骤 7:多态机制 - 实际调用的是哪个 newUnsafe()?
虽然 newUnsafe() 方法在 AbstractChannel 中被调用,但是:
当前对象的实际类型是 NioServerSocketChannel!
让我们看看继承链上哪个类实现了 newUnsafe() 方法:
AbstractChannel(定义抽象方法)
protected abstract AbstractUnsafe newUnsafe();
AbstractNioChannel(没有实现)
// 这个类没有实现 newUnsafe() 方法
AbstractNioMessageChannel(实现了!)
位置:io.netty.channel.nio.AbstractNioMessageChannel
public abstract class AbstractNioMessageChannel extends AbstractNioChannel {
@Override
protected AbstractNioUnsafe newUnsafe() {
// 返回 NioMessageUnsafe 实例
return new NioMessageUnsafe();
}
}
找到了! AbstractNioMessageChannel 实现了 newUnsafe() 方法。
NioServerSocketChannel(没有重写)
// 这个类没有重写 newUnsafe() 方法
// 所以使用父类 AbstractNioMessageChannel 的实现
步骤 8:Java 多态机制详解
为什么调用的是 AbstractNioMessageChannel.newUnsafe()?
这是 Java 的多态机制决定的。让我们用一个简单的例子说明:
// 类比例子
class Animal {
void init() {
sound = makeSound(); // 调用抽象方法
}
abstract String makeSound();
}
class Dog extends Animal {
@Override
String makeSound() {
return "汪汪"; // Dog 实现了 makeSound
}
}
// 创建对象
Animal animal = new Dog(); // 实际类型是 Dog
// 虽然 init() 方法在 Animal 类中
// 但 makeSound() 调用的是 Dog 的实现
对应到 Netty:
// 创建对象
new NioServerSocketChannel();
// 调用链路
NioServerSocketChannel 构造
↓
AbstractNioMessageChannel 构造
↓
AbstractNioChannel 构造
↓
AbstractChannel 构造
↓
unsafe = newUnsafe(); // 在 AbstractChannel 中调用
关键点:
- 虽然
newUnsafe()在AbstractChannel中被调用 - 但当前对象的实际类型是
NioServerSocketChannel - Java 虚拟机会从实际类型开始,向上查找
newUnsafe()的实现 - 查找顺序:
NioServerSocketChannel→ 没有实现AbstractNioMessageChannel→ 找到了!
- 所以调用的是
AbstractNioMessageChannel.newUnsafe()
步骤 9:NioMessageUnsafe 是什么
位置:io.netty.channel.nio.AbstractNioMessageChannel
public abstract class AbstractNioMessageChannel extends AbstractNioChannel {
@Override
protected AbstractNioUnsafe newUnsafe() {
return new NioMessageUnsafe();
}
// NioMessageUnsafe 是 AbstractNioMessageChannel 的内部类
private final class NioMessageUnsafe extends AbstractNioUnsafe {
private final List<Object> readBuf = new ArrayList<Object>();
@Override
public void read() {
assert eventLoop().inEventLoop();
final ChannelConfig config = config();
final ChannelPipeline pipeline = pipeline();
final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
allocHandle.reset(config);
boolean closed = false;
Throwable exception = null;
try {
try {
do {
// 【核心】调用 doReadMessages 接受新连接
int localRead = doReadMessages(readBuf);
if (localRead == 0) {
break;
}
if (localRead < 0) {
closed = true;
break;
}
allocHandle.incMessagesRead(localRead);
} while (allocHandle.continueReading());
} catch (Throwable t) {
exception = t;
}
int size = readBuf.size();
for (int i = 0; i < size; i++) {
readPending = false;
// 【核心】触发 channelRead 事件,传递新的 SocketChannel
pipeline.fireChannelRead(readBuf.get(i));
}
readBuf.clear();
allocHandle.readComplete();
pipeline.fireChannelReadComplete();
// ... 异常处理和关闭逻辑
} finally {
// ...
}
}
}
// 由子类实现,用于接受新连接
protected abstract int doReadMessages(List<Object> buf) throws Exception;
}
说明:
NioMessageUnsafe是AbstractNioMessageChannel的内部类read()方法的核心功能:- 调用
doReadMessages()接受新连接 - 将新连接(
NioSocketChannel)添加到readBuf - 触发
channelRead事件,传递新连接
- 调用
完整调用链路图
new NioServerSocketChannel()
↓
NioServerSocketChannel()
↓
NioServerSocketChannel(ServerSocketChannel)
↓
super(null, channel, OP_ACCEPT)
↓
AbstractNioMessageChannel(parent, ch, readInterestOp)
↓
super(parent, ch, readInterestOp)
↓
AbstractNioChannel(parent, ch, readInterestOp)
↓
super(parent)
↓
AbstractChannel(parent)
↓
unsafe = newUnsafe() ← 在这里调用
↓
【多态机制】查找实际类型的 newUnsafe() 实现
↓
NioServerSocketChannel.newUnsafe()? → 没有
↓
AbstractNioMessageChannel.newUnsafe()? → 找到了!
↓
return new NioMessageUnsafe()
↓
unsafe 字段被赋值为 NioMessageUnsafe 实例
为什么是 NioMessageUnsafe?
设计原因:
NioServerSocketChannel 的工作是接受新连接,不是读取字节数据。
- 每次
read()操作返回的是一个对象(新的NioSocketChannel) - 不是返回字节数据
- 所以使用
NioMessageUnsafe(处理"消息/对象")
对比:
| Channel 类型 | Unsafe 类型 | read() 方法作用 |
|---|---|---|
NioServerSocketChannel | NioMessageUnsafe | 接受新连接,返回 NioSocketChannel 对象 |
NioSocketChannel | NioByteUnsafe | 读取字节数据,返回 ByteBuf |
总结
-
unsafe字段在哪里定义?- 在
AbstractChannel中定义
- 在
-
unsafe字段在哪里初始化?- 在
AbstractChannel构造方法中,通过调用newUnsafe()初始化
- 在
-
为什么调用的是
AbstractNioMessageChannel.newUnsafe()?- 因为 Java 多态机制
- 虽然调用代码在
AbstractChannel中 - 但实际对象类型是
NioServerSocketChannel - JVM 从实际类型开始向上查找
newUnsafe()的实现 - 在
AbstractNioMessageChannel中找到了实现
-
unsafe的实际类型是什么?NioMessageUnsafe
-
为什么是这个类型?
- 因为
NioServerSocketChannel的工作是接受新连接(返回对象) - 不是读取字节数据
- 所以使用
NioMessageUnsafe(处理消息/对象)
- 因为
核心答案:服务端的 unsafe 是 NioMessageUnsafe 实例,由 Java 多态机制决定调用 AbstractNioMessageChannel.newUnsafe() 方法创建。
719

被折叠的 条评论
为什么被折叠?



