前言
书接上文,在计算机网络专栏中,笔者较为系统地揭示了网络通信的底层原理。正是基于这些基础认知,我们才能深入剖析 Netty 框架的核心机制。作为业界领先的开源 NIO 框架,Netty 以其高性能、易扩展的架构设计,在网络基础设施、中间件以及游戏服务器等场景中得到了广泛应用,具备不可替代的行业地位。
本系列文章适用于具备一定计算机网络与 Java 网络编程基础的研发人员,旨在帮助读者进一步提升对 Netty 的理解与实践能力。
服务端启动示例
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.util.AttributeKey;
public class NettyServer {
private final int port;
public NettyServer(int port) {
this.port = port;
}
public void start() throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
// 创建 AttributeKey
AttributeKey<String> serverNameKey = AttributeKey.valueOf("serverName");
AttributeKey<String> clientKey = AttributeKey.valueOf("clientKey");
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
// 服务端 Channel 的初始化逻辑(通常用于启动日志)
.handler(new ChannelInitializer<NioServerSocketChannel>() {
@Override
protected void initChannel(NioServerSocketChannel ch) {
ch.pipeline().addLast(new LoggingHandler(LogLevel.INFO));
}
})
// 客户端连接 SocketChannel 的初始化逻辑
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new SimpleServerHandler());
}
})
// 给服务端 ServerChannel 设置属性
.attr(serverNameKey, "nettyServer")
// 给每个客户端连接的 Channel 设置属性
.childAttr(clientKey, "clientValue")
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture future = bootstrap.bind(port).sync();
System.out.println("Netty server started on port: " + port);
future.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws InterruptedException {
new NettyServer(8080).start();
}
}
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class SimpleServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
String clientVal = ctx.channel().attr(AttributeKey.valueOf("clientKey")).get();
System.out.println("Client connected with attr: " + clientVal);
super.channelActive(ctx);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
System.out.println("收到消息: " + msg);
ctx.writeAndFlush(msg); // 回写消息(回显服务器)
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
这是一个简单的服务端启动示例代码,我们通过构造一个辅助启动类ServerBootstrap并设置一些参数,最终调用这个辅助类的bind()方法来启动服务端。bind()方法返回ChannelFuture对象,调用ChannelFuture对象的sync()方法阻塞当前线程,直到绑定操作完成。在netty中,所有的IO操作都是异步的。
future.channel().closeFuture().sync();这行会让主线程等待服务端关闭事件发生,确保 Netty 服务一直运行,这是一个阻塞等待关闭的机制。
ServerBootstrap类和它的方法。
ServerBootstrap这个类继承抽象类AbstractBootstrap,定义了我们示例代码中传入的所有变量。
group()方法
bootstrap.group(bossGroup, workerGroup)
这个方法的实现如下
ServerBootstrap
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
super.group(parentGroup);
if (childGroup == null) {
throw new NullPointerException("childGroup");
}
if (this.childGroup != null) {
throw new IllegalStateException("childGroup set already");
}
this.childGroup = childGroup;
return this;
}
AbstractBootstrap
public B group(EventLoopGroup group) {
if (group == null) {
throw new NullPointerException("group");
}
if (this.group != null) {
throw new IllegalStateException("group set already");
}
this.group = group;
return (B) this;
}
将工作线程组保存在子类的childGroup中,将boss线程组保存在父类的group中。这里要说明下线程组的类型 NioEventLoopGroup是childGroup和group的类型EventLoopGroup的实现类。
另外,这里使用抽象类的作用之一是:客户端没有boss线程,所以定义了ServerBootstrap类来保存服务端独有的一些参数。
channel()方法
.channel(NioServerSocketChannel.class)
// 1
public B channel(Class<? extends C> channelClass) {
if (channelClass == null) {
throw new NullPointerException("channelClass");
}
return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}
//3
@Deprecated
@SuppressWarnings("unchecked")
public B channelFactory(ChannelFactory<? extends C> channelFactory) {
if (channelFactory == null) {
throw new NullPointerException("channelFactory");
}
if (this.channelFactory != null) {
throw new IllegalStateException("channelFactory set already");
}
this.channelFactory = channelFactory;
return (B) this;
}
//2
@SuppressWarnings({ "unchecked", "deprecation" })
public B channelFactory(io.netty.channel.ChannelFactory<? extends C> channelFactory) {
return channelFactory((ChannelFactory<C>) channelFactory);
}
ReflectiveChannelFactory这个类定义如下
public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {
private final Class<? extends T> clazz;
public ReflectiveChannelFactory(Class<? extends T> clazz) {
if (clazz == null) {
throw new NullPointerException("clazz");
}
this.clazz = clazz;
}
@Override
public T newChannel() {
try {
return clazz.newInstance();
} catch (Throwable t) {
throw new ChannelException("Unable to create Channel from class " + clazz, t);
}
}
@Override
public String toString() {
return StringUtil.simpleClassName(clazz) + ".class";
}
}
到这里就可看到,channel()方法的作用是给channelFactory属性赋值,在后续的流程中,会通过真正的工厂类ReflectiveChannelFactory的newChannel()方法来实例化NioServerSocketChannel的一个实例。
handler()方法
handler(new ChannelInitializer<NioServerSocketChannel>() {
@Override
protected void initChannel(NioServerSocketChannel ch) {
ch.pipeline().addLast(new LoggingHandler(LogLevel.INFO));
}
})
这个方法的入参是抽象类ChannelInitializer的一个实现并重写了initChannel()方法,可以看到,我们在这个方法中获取NioServerSocketChannel这个channel的pipline对象并给这个对象添加我们的自定义handler(),initChannel()方法会再后续初始化和注册方法中真正执行。这个抽象类是ChannelInboundHandlerAdapter的一个子类,这说明这个handler可以处理Inbound事件。
public B handler(ChannelHandler handler) {
if (handler == null) {
throw new NullPointerException("handler");
}
this.handler = handler;
return (B) this;
}
同样的,这个new出来的handler会被赋值给AbstractBootstrap的handler属性。
childHandler()方法
// 客户端连接 SocketChannel 的初始化逻辑
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new SimpleServerHandler());
}
})
这个方法与handler()方法类似,区别在于这个方法是处理客户端连接的逻辑,会给客户端的channel添加对象的handler
剩下的几个attr()和option()方法笔者就不再介绍了,各位读者可以自行翻阅源码。
bind()方法
这个方法是ServerBootstrap这个类的核心方法,因为涉及到的类比较复杂,迭代较深,因此笔者将使用流程图的方式将bind()方法执行逻辑展示出来。
sequenceDiagram
autonumber
ServerBootstrap->>AbstractBootstrap: bind(port)
AbstractBootstrap-)AbstractBootstrap: doBind(localAddress)
AbstractBootstrap-)AbstractBootstrap: initAndRegister()
AbstractBootstrap->>ReflectiveChannelFactory: newChannel()
ReflectiveChannelFactory->>NioServerSocketChannel: NioServerSocketChannel()
NioServerSocketChannel-)NioServerSocketChannel: NioServerSocketChannel()
NioServerSocketChannel-)NioServerSocketChannel: newSocket(SelectorProvider provider)
NioServerSocketChannel->>SelectorProviderImpl: openServerSocketChannel()
SelectorProviderImpl->>ServerSocketChannelImpl: ServerSocketChannelImpl(SelectorProvider var1)
ServerSocketChannelImpl-->>SelectorProviderImpl: return java领域的serverSocketchannel
SelectorProviderImpl-->>NioServerSocketChannel: return java领域的serverSocketchannel
NioServerSocketChannel-)NioServerSocketChannel: NioServerSocketChannel(java领域的channel)
NioServerSocketChannel->>AbstractNioMessageChannel: super(null, channel,SelectionKey.OP_ACCEPT)
AbstractNioMessageChannel->>AbstractNioChannel: super(parent, ch, readInterestOp)
AbstractNioChannel->>AbstractChannel: super(null)
AbstractChannel-)AbstractChannel:id = newId(),netty领域的channel唯一id
AbstractChannel-)AbstractChannel:id = unsafe = newUnsafe(),netty领域的channel的IO操作对象
AbstractChannel->>AbstractNioMessageChannel:newUnsafe(),调子类方法
AbstractNioMessageChannel-)AbstractNioMessageChannel:newUnsafe(),内部类
AbstractNioMessageChannel-->>AbstractChannel:return 内部类对象
AbstractChannel-)AbstractChannel:pipeline = newChannelPipeline(),netty领域的channel的pipline
AbstractChannel->>DefaultChannelPipeline:new DefaultChannelPipeline(netty领域的channel)
DefaultChannelPipeline-)DefaultChannelPipeline:new TailContext(this)内部类
DefaultChannelPipeline->>TailContext:TailContext(DefaultChannelPipeline pipeline)构造函数
TailContext->>AbstractChannelHandlerContext:super(pipeline, null, TAIL_NAME, true, false)
AbstractChannelHandlerContext-)AbstractChannelHandlerContext:this.pipline = pipline,每个context都持有pipline外围对象
TailContext->>AbstractChannelHandlerContext:setAddComplete设置节点状态
AbstractChannelHandlerContext-)AbstractChannelHandlerContext:cas设置节点状态(对象字段Atomic更新器)
TailContext -->>DefaultChannelPipeline:return 双向链表的尾节点
DefaultChannelPipeline-)DefaultChannelPipeline:new HeadContext(this)内部类
DefaultChannelPipeline->>HeadContext:HeadContext(DefaultChannelPipeline pipeline)构造函数
HeadContext->>AbstractChannelHandlerContext:super(pipeline, null, HEAD_NAME, false, true)
AbstractChannelHandlerContext-)AbstractChannelHandlerContext:this.pipline = pipline,每个context都持有pipline外围对象
HeadContext->>AbstractChannel:获取之前创建的unsafe对象
AbstractChannel-->>HeadContext:return unsafe
HeadContext-)HeadContext:this.unsafe = unsafe
HeadContext->>AbstractChannelHandlerContext:setAddComplete设置节点状态
AbstractChannelHandlerContext-)AbstractChannelHandlerContext:cas设置节点状态(对象字段Atomic更新器)
HeadContext -->>DefaultChannelPipeline:return 双向链表的头节点
DefaultChannelPipeline -->>AbstractChannel:return pipline
AbstractNioChannel-)AbstractNioChannel:this.ch = ch,将java领域的serverSocketChannel赋值给类变量(设计模式?)
AbstractNioChannel-)AbstractNioChannel:this.readInterestOp = readInterestOp,accept事件
AbstractNioChannel-)AbstractNioChannel:ch.configureBlocking(false),设置为非阻塞
NioServerSocketChannel-)NioServerSocketChannel:config = new NioServerSocketChannelConfig(this, javaChannel().socket());
NioServerSocketChannel->>ServerSocketChannelImpl:socket(),javaChannel()方法来拿到之前创建的java领域的serverSocketChannel,就是ServerSocketChannelImpl对象
ServerSocketChannelImpl-)ServerSocketChannelImpl:给java领域的channel设置ServerSocket,对象互相引用
NioServerSocketChannel->>NioServerSocketChannelConfig:NioServerSocketChannelConfig()构造函数
NioServerSocketChannelConfig->>DefaultServerSocketChannelConfig:super(channel, javaSocket)
DefaultServerSocketChannelConfig->>DefaultChannelConfig:super(channel)
DefaultChannelConfig-)DefaultChannelConfig:构建一个自动调节分配的ByteBuf大小的分配器
DefaultChannelConfig-)DefaultChannelConfig:this.channel = channel
DefaultServerSocketChannelConfig-)DefaultServerSocketChannelConfig:this.javaSocket =javaSocket;
NioServerSocketChannelConfig-->>NioServerSocketChannel:return config对象
ReflectiveChannelFactory-->>AbstractBootstrap: return NioServerSocketChannel实例
AbstractBootstrap -)AbstractBootstrap:Channel channel = 返回的NioServerSocketChannel
AbstractBootstrap ->>ServerBootstrap:init(Channel channel)
ServerBootstrap -)ServerBootstrap:保存server端的attr和option分别到\n当前channel和channel的config
ServerBootstrap -)DefaultChannelPipeline:addLast(ChannelHandler... handlers)\n向当前channel的pipline中添加handler,\n这个handler会添加handler()方法传入的handler\n接着通过EventLoop异步添加ServerBootstrapAcceptor处理器\n,确保它在用户自定义处理器之后执行,\n以正确处理客户端连接.\n
DefaultChannelPipeline -)DefaultChannelPipeline:执行addLast()方法,准备添加handler
DefaultChannelPipeline -)DefaultChannelPipeline:newContext()方法,包装成ctx
DefaultChannelPipeline ->>AbstractChannelHandlerContext:super()方法,
DefaultChannelPipeline ->>DefaultChannelHandlerContext:this.handler = handler
DefaultChannelPipeline -)DefaultChannelPipeline:addLast0()方法,向pipline中添加handler
DefaultChannelPipeline ->>AbstractChannelHandlerContext:设置ctx的状态为pending,\n表示等待回调hanlerAdd方法
DefaultChannelPipeline -)DefaultChannelPipeline:callHandlerCallbackLater,\n向pendingHandlerCallbackHead添加回调任务\n pendingHandlerCallbackHead\n维护一个回调任务队列
AbstractBootstrap -)AbstractBootstrap:config().group().register(channel)\ngroup()方法传入的线程组,父类为MultithreadEventLoopGroup
AbstractBootstrap ->>MultithreadEventLoopGroup:register(Channel channel)
MultithreadEventLoopGroup ->>SingleThreadEventLoop:register(Channel channel)
SingleThreadEventLoop -)SingleThreadEventLoop:register(new DefaultChannelPromise(channel, this))
SingleThreadEventLoop -)SingleThreadEventLoop:promise.channel().unsafe().register(this, promise)
SingleThreadEventLoop ->>AbstractUnsafe:register(EventLoop eventLoop, final ChannelPromise promise)\nAbstractUnsafe是AbstractChannel内部类,\n此刻,设计完成了闭环
AbstractUnsafe -)AbstractUnsafe:AbstractChannel.this.eventLoop = eventLoop\n这行代码为当前channel绑定一个boss线程
AbstractChannel -)AbstractChannel:异步执行register0(ChannelPromise promise)
AbstractChannel -->>AbstractNioChannel:register0调用doRegister()
AbstractNioChannel -)AbstractNioChannel:javaChannel().register(eventLoop().selector, 0, this)\n这里将java领域的channel和selector绑定\n并将当前netty领域的channel最为附件添加到selectionKey
AbstractChannel ->>DefaultChannelPipeline:invokeHandlerAddedIfNeeded()
DefaultChannelPipeline -)DefaultChannelPipeline:callHandlerAddedForAllHandlers
DefaultChannelPipeline ->>PendingHandlerAddedTask:异步调用execute()
PendingHandlerAddedTask -->>DefaultChannelPipeline:execute调用callHandlerAdded0(ctx)
DefaultChannelPipeline -) DefaultChannelPipeline:回调当前handler的added方法 \n在这里,前面添加的ChannelInitializer的\n initChannel()方法被执行,\n handler()方法添加的服务端处理器添加到pipline中
DefaultChannelPipeline -) DefaultChannelPipeline:将当前handler的状态设置为ADD_COMPLETE
AbstractChannel ->> NioServerSocketChannel:检验java领域的channel的\n localAddress是否非空 \n初始化的时候是空值,后续非空时会执行\n fireChannelActive()或beginRead()
AbstractChannel ->> DefaultChannelPipeline:执行fireChannelRegistered方法
DefaultChannelPipeline ->> AbstractChannelHandlerContext:执行所有handler的重载\n channelRegistered()方法
AbstractBootstrap ->>AbstractChannel:doBind(localAddress)
AbstractChannel->>NioServerSocketChannel:javaChannel().bind(localAddress, config.getBacklog()) \n 执行真正的绑定操作,设置Tcp属性\n 将fd和协议栈开辟的空间关联
AbstractBootstrap ->>AbstractChannel:pipeline.fireChannelActive() \n与其他几个方法如added类似,不在赘述
整个bind()方法的流程过于长,如果将整个流程放到同一个时序图中,不方便观看。因此我将其拆分为数个部分。
1、实例化ServerSocketChannel-1
1、实例化ServerSocketChannel-2
2、初始化ServerSocketChannel-1
2、初始化ServerSocketChannel-2
到这里整个启动流程就结束了,我们对上述流程进行总结,可以看到netty的启动流程大致分为以下几个步骤。
- 创建JDK底层Channel,创建对应Config对象,设置该Channel为非阻塞模式。
- 创建Server对应的Channel,创建各大组件,包括ChannelConfig、ChannelId、ChannelPipeline、ChannelHandler、Unsafe等。
- 初始化Server对应的Channel,设置Option、Attr,以及设置子Channel的Option、Attr,给Server的Channel添加连接接入器,用于接收新连接,并触发addHandler、register等事件。
- 调用JDK底层注册Selector,将Netty领域的Channel当作attachment注册到Selector。
- 调用JDk底层做端口绑定,并触发active事件。当active事件被触发时,才真正做服务端口绑定。
总结
本节内容讲解了 Netty 服务端的启动流程,笔者带着大家一步步拆解了其中的各个环节。相信读到这里,大家对 Netty 的启动机制已经有了较为清晰的认识。
回顾开篇的启动示例代码,其中 EventLoopGroup bossGroup = new NioEventLoopGroup(1); 这段用于创建线程组 EventLoopGroup 的逻辑,我们尚未深入探讨。这个线程组是 Netty 高性能的重要支撑之一,我们将把它的原理与实现放到下一节进行详细解析。