探索服务端启动流程

前言

书接上文,在计算机网络专栏中,笔者较为系统地揭示了网络通信的底层原理。正是基于这些基础认知,我们才能深入剖析 Netty 框架的核心机制。作为业界领先的开源 NIO 框架,Netty 以其高性能、易扩展的架构设计,在网络基础设施、中间件以及游戏服务器等场景中得到了广泛应用,具备不可替代的行业地位。

本系列文章适用于具备一定计算机网络与 Java 网络编程基础的研发人员,旨在帮助读者进一步提升对 Netty 的理解与实践能力。

服务端启动示例

java 复制代码
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();
    }
}
java 复制代码
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()方法

java 复制代码
bootstrap.group(bossGroup, workerGroup)

这个方法的实现如下

ServerBootstrap

java 复制代码
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

java 复制代码
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()方法

java 复制代码
.channel(NioServerSocketChannel.class)
java 复制代码
// 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这个类定义如下

java 复制代码
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属性赋值,在后续的流程中,会通过真正的工厂类ReflectiveChannelFactorynewChannel()方法来实例化NioServerSocketChannel的一个实例。

handler()方法

java 复制代码
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事件。

java 复制代码
public B handler(ChannelHandler handler) {
    if (handler == null) {
        throw new NullPointerException("handler");
    }
    this.handler = handler;
    return (B) this;
}

同样的,这个new出来的handler会被赋值给AbstractBootstrap的handler属性。

childHandler()方法

java 复制代码
// 客户端连接 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的启动流程大致分为以下几个步骤。

  1. 创建JDK底层Channel,创建对应Config对象,设置该Channel为非阻塞模式。
  2. 创建Server对应的Channel,创建各大组件,包括ChannelConfig、ChannelId、ChannelPipeline、ChannelHandler、Unsafe等。
  3. 初始化Server对应的Channel,设置Option、Attr,以及设置子Channel的Option、Attr,给Server的Channel添加连接接入器,用于接收新连接,并触发addHandler、register等事件。
  4. 调用JDK底层注册Selector,将Netty领域的Channel当作attachment注册到Selector。
  5. 调用JDk底层做端口绑定,并触发active事件。当active事件被触发时,才真正做服务端口绑定。

总结

本节内容讲解了 Netty 服务端的启动流程,笔者带着大家一步步拆解了其中的各个环节。相信读到这里,大家对 Netty 的启动机制已经有了较为清晰的认识。

回顾开篇的启动示例代码,其中 EventLoopGroup bossGroup = new NioEventLoopGroup(1); 这段用于创建线程组 EventLoopGroup 的逻辑,我们尚未深入探讨。这个线程组是 Netty 高性能的重要支撑之一,我们将把它的原理与实现放到下一节进行详细解析。

相关推荐
心月狐的流火号2 天前
线程池ThreadPoolExecutor源码分析(JDK 17)
java·源码阅读
深圳蔓延科技2 天前
NioEventLoopGroup 完全指南
netty
深圳蔓延科技3 天前
如何使用 Netty 实现 NIO 方式发送 HTTP 请求
netty
Derek_Smart9 天前
Netty 客户端与服务端选型分析:下位机连接场景
spring boot·后端·netty
用户38022585982410 天前
vue3源码解析:编译之编译器代码生成过程
前端·vue.js·源码阅读
Derek_Smart11 天前
工业级TCP客户端高可靠连接架构设计与Netty优化实践
java·性能优化·netty
c_zyer13 天前
FreeSWITCH与Java交互实战:从EslEvent解析到Spring Boot生态整合的全指南
spring boot·netty·freeswitch·eslevent
eason_fan14 天前
React 源码执行流程
前端·源码阅读
用户38022585982415 天前
vue3源码解析:编译之解析器实现原理
前端·vue.js·源码阅读