乐于分享
好东西不私藏

Netty源码分析(8) — Bootstrap 与 ServerBootstrap 启动流程源码

Netty源码分析(8) — Bootstrap 与 ServerBootstrap 启动流程源码

Netty源码分析(8) — Bootstrap 与 ServerBootstrap 启动流程源码

一、背景与问题

前面几篇我们聊了 Channel、Pipeline、EventLoop 这些核心组件,但有个问题一直悬着:

当你写下 bootstrap.bind(8080)bootstrap.connect("localhost", 8080) 那行代码时,Netty 背后到底发生了什么事?

有人觉得不就是 new 了几个对象嘛,bind 一个端口而已。但 Netty 的启动涉及到:

  • 线程池初始化(多少个线程?什么时候启动?)
  • Channel 创建(反射还是工厂?)
  • Pipeline 组装(handler 什么时候加进去?)
  • 端口绑定(底层 socket 怎么创建的?)
  • 客户端连接(NIO connect 异步怎么变成同步的?)

Netty 提供了两个入口类:

用途 角色
Bootstrap 客户端/无连接协议 connect() 建立连接
ServerBootstrap 服务端 bind() 监听端口

今天我们就从源码层面,把这两者的启动流程彻底扒干净。

二、Bootstrap 客户端启动流程源码

2.1 最简用例

EventLoopGroup group = new NioEventLoopGroup();
Bootstrap b = new Bootstrap();
b.group(group)
 .channel(NioSocketChannel.class)
 .handler(new ChannelInitializer<SocketChannel>() {
     @Override
     protected void initChannel(SocketChannel ch) {
         ch.pipeline().addLast(new StringDecoder());
     }
 });
ChannelFuture f = b.connect("127.0.0.1"8080).sync();

看起来就这几行,但每一行背后的工作量都不小。我们从 connect() 入手。

2.2 connect() 入口

// Bootstrap.java — connect 方法
public ChannelFuture connect(String inetHost, int inetPort) {
    return connect(InetSocketAddress.createUnresolved(inetHost, inetPort));
}

public ChannelFuture connect(SocketAddress remoteAddress) {
    // 参数校验
    ObjectUtil.checkNotNull(remoteAddress, "remoteAddress");
    // 核心:验证配置、创建 Channel、注册、发起连接
    return doResolveAndConnect(remoteAddress, config.localAddress());
}

这里的 doResolveAndConnect 才是真正的干活函数:

// Bootstrap.java
private ChannelFuture doResolveAndConnect(SocketAddress remoteAddress, 
                                           SocketAddress localAddress)
 {
    // 第一步:初始化并注册 Channel(这个是最关键的)
    ChannelFuture regFuture = initAndRegister();
    final Channel channel = regFuture.channel();

    // 如果注册已经完成,直接走连接逻辑
    if (regFuture.isDone()) {
        if (!regFuture.isSuccess()) {
            return regFuture;
        }
        return doResolveAndConnect0(channel, remoteAddress, localAddress, 
                                    channel.newPromise());
    } 
    // 否则等注册完成再连
    else {
        final PendingRegistrationPromise promise = 
            new PendingRegistrationPromise(channel);
        regFuture.addListener((ChannelFutureListener) future -> {
            Throwable cause = future.cause();
            if (cause != null) {
                promise.setFailure(cause);
            } else {
                promise.registered();
                doResolveAndConnect0(channel, remoteAddress, localAddress, promise);
            }
        });
        return promise;
    }
}

这段代码第一次看可能有点绕。核心逻辑是:

  1. initAndRegister() — 这是最关键的步骤,完成 Channel 的创建、初始化、注册
  2. 判断注册状态 — 如果注册是同步完成的(比如在调用线程是 EventLoop 线程时),直接连接;否则异步等
  3. doResolveAndConnect0() — 执行 DNS 解析 + 真正的 TCP 连接

2.3 initAndRegister():核心三件套

这就是整个客户端启动的发动机:

// AbstractBootstrap.java
final ChannelFuture initAndRegister() {
    Channel channel = null;
    try {
        // === 1. 创建 Channel:通过反射调用 NioSocketChannel 的无参构造 ===
        channel = channelFactory.newChannel();
        
        // === 2. 初始化 Channel:设置 options、attrs,组装 Pipeline ===
        init(channel);
    } catch (Throwable t) {
        ...
    }

    // === 3. 注册到 EventLoop:把 Channel 注册到 Selector 上 ===
    ChannelFuture regFuture = config().group().register(channel);
    if (regFuture.cause() != null) {
        if (channel.isRegistered()) {
            channel.close();
        } else {
            channel.unsafe().closeForcibly();
        }
    }
    return regFuture;
}

三步走:创建 → 初始化 → 注册。下面逐个拆解。

2.4 Channel 创建的反射机制

channelFactory.newChannel() 实际上就是调用了 ReflectiveChannelFactory

// ReflectiveChannelFactory.java
public class ReflectiveChannelFactory<C extends Channel
    implements ChannelFactory<C> {
    
    private final Constructor<? extends C> constructor;

    public ReflectiveChannelFactory(Class<? extends C> clazz) {
        // 拿到无参构造器
        this.constructor = clazz.getConstructor();
    }

    @Override
    public C newChannel() {
        // 反射创建
        return constructor.newInstance();
    }
}

这里有个小细节:以 NioSocketChannel 为例,它的无参构造做了哪些事?

// NioSocketChannel.java
public NioSocketChannel() {
    // 调用父类,最终创建 Java NIO 的 SocketChannel
    this(DEFAULT_SELECTOR_PROVIDER);
}

public NioSocketChannel(SelectorProvider provider) {
    // 创建 JDK 原生 SocketChannel,并设置为非阻塞模式
    this(newSocket(provider));
}

private static SocketChannel newSocket(SelectorProvider provider) {
    try {
        return provider.openSocketChannel();
    } catch (IOException e) {
        throw new ChannelException("Failed to open a socket.", e);
    }
}

public NioSocketChannel(Channel parent, SocketChannel socket) {
    super(parent, socket);
    // 创建读取和写入的 buffer 配置
    config = new NioSocketChannelConfig(this, socket.socket());
}

一个 NIO Channel 的创建链条:NioSocketChannel()SelectorProvider.openSocketChannel() → 拿到 JDK 的 SocketChannel → 存入 Netty 的 AbstractNioByteChannelch 字段。

2.5 Bootstrap.init():组装 Pipeline

客户端和服务端的 init 逻辑不同。先看客户端的:

// Bootstrap.java
void init(Channel channel) {
    ChannelPipeline p = channel.pipeline();
    
    // 设置 Channel 的 options 和 attrs
    setChannelOptions(channel, options, logger);
    setAttributes(channel, attrs);

    // 获取用户配置的 handler
    p.addLast(config.handler());
}

逻辑非常简洁:

  1. 拿到新创建的 Channel 的 Pipeline
  2. 设置 socket 参数(SO_KEEPALIVE, TCP_NODELAY 等)
  3. 把用户指定的 handler 加到 Pipeline 末尾

这里就是 b.handler(new ChannelInitializer<...>() {...}) 中那个 handler 的添加位置。

2.6 Channel 注册到 EventLoop

注册是在 MultithreadEventLoopGroup.register() 里完成的:

// MultithreadEventLoopGroup.java
@Override
public ChannelFuture register(Channel channel) {
    // 从线程池中选择一个 EventLoop(轮询策略)
    return next().register(channel);
}

注册的主体是 AbstractChannel.AbstractUnsafe.register()

// AbstractChannel.java — AbstractUnsafe.register()
@Override
public final void register(EventLoop eventLoop, 
                           final ChannelPromise promise)
 {
    // 各种校验...
    AbstractChannel.this.eventLoop = eventLoop;

    // 关键点:如果当前已经是 EventLoop 线程,直接执行
    // 否则提交到 EventLoop 的任务队列异步执行
    if (eventLoop.inEventLoop()) {
        register0(promise);
    } else {
        try {
            eventLoop.execute(new Runnable() {
                @Override
                public void run() {
                    register0(promise);
                }
            });
        } catch (Throwable t) {
            ...
        }
    }
}

这个 eventLoop.inEventLoop() 的判断非常关键。在启动阶段,当前线程不是 EventLoop 线程,所以会走 else 分支,把注册任务提交到 EventLoop 的 taskQueue 中异步执行

然后 register0() 里最终会调用:

// AbstractNioChannel.java — doRegister()
@Override
protected void doRegister() throws Exception {
    boolean selected = false;
    for (;;) {
        try {
            // 将 JDK 的 SocketChannel 注册到 Selector 上
            // 注意这里 ops=0,意味着注册时不关注任何事件
            // 事件关注是在后续的 bind() 或 connect() 中设置的
            selectionKey = javaChannel().register(
                eventLoop().unwrappedSelector(), 0this);
            return;
        } catch (CancelledKeyException e) {
            ...
        }
    }
}

这里有个容易被忽略的点:注册时传的 interestOps = 0。这意味着刚注册完的 Channel 在 Selector 上什么都不关注,纯粹是为了把 Channel 和 Selector 绑定起来。后续在 connect() 或 bind() 中才会设置真正关心的事件。

2.7 发起 TCP 连接

回到 doResolveAndConnect0()

// Bootstrap.java
private ChannelFuture doResolveAndConnect0(Channel channel, 
    SocketAddress remoteAddress, SocketAddress localAddress, 
    final ChannelPromise promise)
 {
    try {
        // DNS 解析(可能是异步的)
        AddressResolver<SocketAddress> resolver = ...;
        if (!resolver.isSupported(remoteAddress) || 
            resolver.isResolved(remoteAddress)) {
            // 不需要解析,直接连接
            doConnect(remoteAddress, localAddress, promise);
            return promise;
        }
        
        // 异步解析,解析完成后再连接
        ...
    }
}

private static void doConnect(
    final SocketAddress remoteAddress, 
    final SocketAddress localAddress, 
    final ChannelPromise connectPromise)
 {
    
    // 最终调用 Channel 的 connect 方法
    channel.connect(remoteAddress, connectPromise);
}

最终执行到 NioSocketChannel.doConnect()

// NioSocketChannel.java
@Override
protected boolean doConnect(SocketAddress remoteAddress, 
                            SocketAddress localAddress)
 throws Exception {
    if (localAddress != null) {
        // 绑定本地地址
        javaChannel().bind(localAddress);
    }

    boolean success = false;
    try {
        // 发起 TCP 连接(NIO 非阻塞模式,立即返回)
        boolean connected = javaChannel().connect(remoteAddress);
        if (!connected) {
            // 连接没有立即完成,需要关注 OP_CONNECT 事件
            // 等 Selector 检测到连接完成
            selectionKey().interestOps(SelectionKey.OP_CONNECT);
        }
        return connected;
    } finally {
        if (!success) {
            doClose();
        }
    }
}

NIO 的关键特性SocketChannel.connect() 在非阻塞模式下会立即返回。如果返回 true 表示连接瞬间建立了,返回 false 表示连接正在建立。此时我们需要关注 OP_CONNECT 事件,等待 Selector 告诉我们"连接已就绪"。

三、ServerBootstrap 服务端启动流程源码

3.1 最简用例

EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
 .channel(NioServerSocketChannel.class)
 .childHandler(new ChannelInitializer<SocketChannel>() {
     @Override
     protected void initChannel(SocketChannel ch) {
         ch.pipeline().addLast(new StringDecoder());
     }
 })
 .option(ChannelOption.SO_BACKLOG, 128)
 .childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture f = b.bind(8080).sync();

3.2 bind() 入口

// ServerBootstrap.java
public ChannelFuture bind(int inetPort) {
    return bind(new InetSocketAddress(inetPort));
}

public ChannelFuture bind(SocketAddress localAddress) {
    // 参数校验
    validate();
    return doBind(ObjectUtil.checkNotNull(localAddress, "localAddress"));
}

doBind() 是核心:

// AbstractBootstrap.java
private ChannelFuture doBind(final SocketAddress localAddress) {
    // 同样是:创建 Channel + 初始化 + 注册
    final ChannelFuture regFuture = initAndRegister();
    final Channel channel = regFuture.channel();
    
    if (regFuture.isDone()) {
        if (!regFuture.isSuccess()) {
            return regFuture;
        }
        // 注册完成,进行端口绑定
        return doBind0(regFuture, channel, localAddress, channel.newPromise());
    } else {
        // 注册未完成,异步等待
        final PendingRegistrationPromise promise = 
            new PendingRegistrationPromise(channel);
        regFuture.addListener((ChannelFutureListener) future -> {
            Throwable cause = future.cause();
            if (cause != null) {
                promise.setFailure(cause);
            } else {
                promise.registered();
                doBind0(regFuture, channel, localAddress, promise);
            }
        });
        return promise;
    }
}

和服务端一样,initAndRegister() 在前面已经分析过。但差别在 init() 方法。

3.3 ServerBootstrap.init():双流水线体系

// ServerBootstrap.java
void init(Channel channel) {
    // 设置 options 和 attrs
    setChannelOptions(channel, options, logger);
    setAttributes(channel, attrs);

    ChannelPipeline p = channel.pipeline();

    // 关键:把用户配置的 childGroup 和 childHandler 存到 Channel 的 attr 里
    final EventLoopGroup currentChildGroup = childGroup;
    final ChannelHandler currentChildHandler = childHandler;
    final Entry<ChannelOption<?>, Object>[] currentChildOptions;
    final Entry<AttributeKey<?>, Object>[] currentChildAttrs;

    synchronized (childOptions) {
        currentChildOptions = childOptions.entrySet().toArray(...);
    }
    synchronized (childAttrs) {
        currentChildAttrs = childAttrs.entrySet().toArray(...);
    }

    // 往 ServerSocketChannel 的 Pipeline 中加入一个特殊的 ChannelInitializer
    p.addLast(new ChannelInitializer<Channel>() {
        @Override
        public void initChannel(final Channel ch) {
            final ChannelPipeline pipeline = ch.pipeline();
            
            // 加入 boss handler(如果有配置的话)
            ChannelHandler handler = config.handler();
            if (handler != null) {
                pipeline.addLast(handler);
            }

            // 重点!用异步任务添加 ServerBootstrapAcceptor
            ch.eventLoop().execute(new Runnable() {
                @Override
                public void run() {
                    pipeline.addLast(new ServerBootstrapAcceptor(
                        currentChildGroup, currentChildHandler,
                        currentChildOptions, currentChildAttrs));
                }
            });
        }
    });
}

这个 ServerBootstrapAcceptor 是服务端启动的灵魂。等会儿单独说。

3.4 NioServerSocketChannel 的创建

// NioServerSocketChannel.java
public NioServerSocketChannel() {
    this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}

private static ServerSocketChannel newSocket(SelectorProvider provider) {
    try {
        // 创建 JDK 原生 ServerSocketChannel
        return provider.openServerSocketChannel();
    } catch (IOException e) {
        throw new ChannelException("Failed to open a server socket.", e);
    }
}

public NioServerSocketChannel(ServerSocketChannel channel) {
    // 注意 parent 传的是 null,因为 ServerSocketChannel 没有父 Channel
    super(null, channel, SelectionKey.OP_ACCEPT);
    config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}

注意这里的 SelectionKey.OP_ACCEPT,和客户端的 ops=0 不同,服务端 Channel 一开始就关注 ACCEPT 事件。

3.5 doBind0():真正的端口绑定

// AbstractBootstrap.java
private static void doBind0(
    final ChannelFuture regFuture, final Channel channel,
    final SocketAddress localAddress, final ChannelPromise promise)
 {

    // 注意:这个任务是在 EventLoop 线程中执行的
    channel.eventLoop().execute(new Runnable() {
        @Override
        public void run() {
            if (regFuture.isSuccess()) {
                // 绑定端口
                channel.bind(localAddress, promise).addListener(
                    ChannelFutureListener.CLOSE_ON_FAILURE);
            } else {
                promise.setFailure(regFuture.cause());
            }
        }
    });
}

bind 最终调用 NioServerSocketChannel.doBind()

// NioServerSocketChannel.java
@Override
protected void doBind(SocketAddress localAddress) throws Exception {
    // 如果 JDK 版本支持,打开 SO_REUSEADDR 防止端口重用问题
    if (PlatformDependent.javaVersion() >= 7) {
        javaChannel().bind(localAddress, config.getBacklog());
    } else {
        javaChannel().socket().bind(localAddress, config.getBacklog());
    }
}

这一行 javaChannel().bind(localAddress, config.getBacklog()) 就是把 Java NIO 的 ServerSocketChannel 绑定到指定端口。到这里,服务端才真正开始监听端口

绑定成功后,Netty 会触发 channelActive 事件:

// AbstractNioChannel.java
@Override
protected void doBeginRead() throws Exception {
    final SelectionKey selectionKey = this.selectionKey;
    if (!selectionKey.isValid()) {
        return;
    }
    readPending = true;
    
    // 把 interestOps 设为 OP_ACCEPT
    // 这样 Selector 才能接收到新的连接请求
    final int interestOps = selectionKey.interestOps();
    if ((interestOps & readInterestOp) == 0) {
        selectionKey.interestOps(interestOps | readInterestOp);
    }
}

readInterestOp 就是构造时传入的 SelectionKey.OP_ACCEPT

四、ServerBootstrapAcceptor:连接接受的秘密武器

ServerBootstrapAcceptorServerBootstrap 的一个内部类,它的职责很简单——接受新连接并分配给 worker 线程池

// ServerBootstrap.java — ServerBootstrapAcceptor
private static class ServerBootstrapAcceptor extends ChannelInboundHandlerAdapter {

    private final EventLoopGroup childGroup;
    private final ChannelHandler childHandler;
    private final Entry<ChannelOption<?>, Object>[] childOptions;
    private final Entry<AttributeKey<?>, Object>[] childAttrs;
    private final Runnable enableAutoReadTask;

    @Override
    @SuppressWarnings("unchecked")
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        // 接受的新连接就是这里的 msg
        final Channel child = (Channel) msg;

        // 把 childHandler 加到新连接的 Pipeline 里
        child.pipeline().addLast(childHandler);

        // 设置 child options 和 attrs
        setChannelOptions(child, childOptions, logger);
        setAttributes(child, childAttrs);

        try {
            // 将新连接注册到 worker EventLoopGroup
            childGroup.register(child).addListener(...);
        } catch (Throwable t) {
            forceClose(child, t);
        }
    }
}

当 boss EventLoop 的 Selector 检测到 OP_ACCEPT 事件时,会调用 NioMessageUnsafe.read(),最终在新连接到达时触发 pipeline 的 channelRead 事件。ServerBootstrapAcceptor 在这个事件里:

  1. 拿到新创建的 NioSocketChannel — 这就是 msg
  2. 把用户的 childHandler 加到它的 Pipelinechannel.pipeline().addLast(childHandler)
  3. 注册到 worker EventLoopchildGroup.register(child)

至此,一条新的客户端连接就分配到了 worker 线程来处理。

五、完整启动流程图

┌─────────────────────────────────────────────────────────┐
│                     启动流程总览                          │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  客户端 Bootstrap.connect()                              │
│  ┌─────────────────────────────────────────┐            │
│  │ 1. initAndRegister()                    │            │
│  │    ├─ channelFactory.newChannel()       │            │
│  │    │   └─ 反射创建 NioSocketChannel      │            │
│  │    ├─ init(channel)                     │            │
│  │    │   └─ pipeline.addLast(handler)     │            │
│  │    └─ group().register(channel)         │            │
│  │        └─ Selector 注册 (兴趣集=0)      │            │
│  │                                         │            │
│  │ 2. doConnect()                          │            │
│  │    └─ javaChannel().connect(addr)       │            │
│  │        └─ 设置 OP_CONNECT 事件          │            │
│  └─────────────────────────────────────────┘            │
│                                                         │
│  服务端 ServerBootstrap.bind()                           │
│  ┌─────────────────────────────────────────┐            │
│  │ 1. initAndRegister()                    │            │
│  │    ├─ NioServerSocketChannel(OP_ACCEPT) │            │
│  │    ├─ init()                            │            │
│  │    │   └─ 添加 ChannelInitializer       │            │
│  │    │       └─ 异步添加 ServerBootstrapAcceptor │     │
│  │    └─ register()                        │            │
│  │                                         │            │
│  │ 2. doBind0()                            │            │
│  │    └─ javaChannel().bind(port,backlog)  │            │
│  │        └─ 触发 channelActive            │            │
│  │            └─ 设置 OP_ACCEPT 事件       │            │
│  │                                         │            │
│  │ 3. 新连接到达 → ServerBootstrapAcceptor │            │
│  │    ├─ childHandler 添加到新连接 Pipeline│            │
│  │    └─ childGroup.register(child)        │            │
│  └─────────────────────────────────────────┘            │
└─────────────────────────────────────────────────────────┘

六、使用示例:完整可运行的服务端+客户端

6.1 服务端

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.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

public class EchoServer {
    public static void main(String[] args) throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class)
             .option(ChannelOption.SO_BACKLOG, 128)
             .childOption(ChannelOption.SO_KEEPALIVE, true)
             .childHandler(new ChannelInitializer<SocketChannel>() {
                 @Override
                 protected void initChannel(SocketChannel ch) {
                     ChannelPipeline p = ch.pipeline();
                     p.addLast(new StringDecoder());
                     p.addLast(new StringEncoder());
                     p.addLast(new SimpleChannelInboundHandler<String>() {
                         @Override
                         protected void channelRead0(ChannelHandlerContext ctx, 
                                                      String msg)
 {
                             System.out.println("收到: " + msg);
                             ctx.writeAndFlush("服务端回显: " + msg);
                         }
                     });
                 }
             });

            ChannelFuture f = b.bind(8080).sync();
            System.out.println("服务端启动,监听 8080 端口");
            f.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

6.2 客户端

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

public class EchoClient {
    public static void main(String[] args) throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group)
             .channel(NioSocketChannel.class)
             .option(ChannelOption.TCP_NODELAY, true)
             .handler(new ChannelInitializer<SocketChannel>() {
                 @Override
                 protected void initChannel(SocketChannel ch) {
                     ChannelPipeline p = ch.pipeline();
                     p.addLast(new StringDecoder());
                     p.addLast(new StringEncoder());
                     p.addLast(new SimpleChannelInboundHandler<String>() {
                         @Override
                         protected void channelRead0(ChannelHandlerContext ctx, 
                                                      String msg)
 {
                             System.out.println("收到服务端: " + msg);
                         }

                         @Override
                         public void channelActive(ChannelHandlerContext ctx) {
                             ctx.writeAndFlush("你好,Netty!");
                         }
                     });
                 }
             });

            ChannelFuture f = b.connect("127.0.0.1"8080).sync();
            System.out.println("客户端启动,连接 127.0.0.1:8080");
            f.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }
}

七、注意事项与常见坑

7.1 不要在 bind() 前写 else 逻辑

// ❌ 错误:在 bind 完成前就操作 Channel
ChannelFuture f = b.bind(8080);
f.channel().writeAndFlush("Hello"); // 可能失败!还没 bind 完成

// ✅ 正确:在 Listener 中异步操作
b.bind(8080).addListener(future -> {
    if (future.isSuccess()) {
        future.channel().writeAndFlush("Hello");
    }
});

7.2 ServerBootstrap 的 handler 和 childHandler 的区别

很多人搞混这两个:

  • handler() — 添加到 ServerSocketChannel 的 Pipeline,处理 boss 的事件
  • childHandler() — 添加到**每个新连接(SocketChannel)**的 Pipeline,处理 worker 的事件

大多数场景只需要 childHandlerhandler 很少用,除非你想在 boss 层面做些拦截。

7.3 bossGroup 的线程数

// 不推荐:默认线程数 = CPU 核数 * 2
EventLoopGroup bossGroup = new NioEventLoopGroup(); 

// 推荐:bossGroup 只需要 1 个线程
EventLoopGroup bossGroup = new NioEventLoopGroup(1);

boss Group 只负责 accept 新连接,一个线程完全够。默认用 NettyRuntime.availableProcessors() * 2 是在浪费。

7.4 Bootstrap 能否重用

Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class).handler(...);

// ✅ 可以多次 connect
ChannelFuture f1 = b.connect("host1"8080).sync();
ChannelFuture f2 = b.connect("host2"8080).sync(); // 同一 Bootstrap 实例

Bootstrap 是可重用的,但注意如果需要不同的 handler,需要重新设置。

7.5 doBind0 为什么用 EventLoop.execute() 异步执行?

这不是为了异步而异步。bind 必须在 Channel 注册完成后才能执行,因为注册事件是提交到 EventLoop 的任务队列的,所以后续所有操作也必须通过 EventLoop 执行,保证线程安全

八、总结

阶段 Bootstrap ServerBootstrap
Channel 类型 NioSocketChannel NioServerSocketChannel
Channel 创建 反射调用无参构造 反射调用无参构造
注册到 Selector interestOps = 0 OP_ACCEPT
初始化 添加用户 handler 添加 ChannelInitializer + ServerBootstrapAcceptor
启动操作 connect bind
事件关注 OP_CONNECT → OP_READ OP_ACCEPT
连接处理 自己处理 委托给 ServerBootstrapAcceptor → worker

本质上,Netty 的启动流程就是做了三件事:

  1. 创建 Channel — 反射创建 NIO Channel,建立与 JDK NIO 的桥梁
  2. 初始化 Pipeline — 把用户配置的 handler 组装进事件处理链
  3. 注册到 Selector — 将 Channel 注册到 EventLoop 的 Selector,交给 Reactor 线程驱动

搞懂了这个流程,Netty 在你眼里就不再是一个黑盒了。下一篇我们来深入 NioEventLoop.run(),看看那个永不停歇的 for 循环到底在干什么

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-06-12 02:12:28 HTTP/1.1 GET : https://www.yeyulingfeng.com/a/736340.html
  2. 运行时间 : 0.103221s [ 吞吐率:9.69req/s ] 内存消耗:4,778.63kb 文件加载:145
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=ced4180b725d51b5d652fbae47fc15c1
  1. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_static.php ( 6.05 KB )
  7. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/ralouphie/getallheaders/src/getallheaders.php ( 1.60 KB )
  10. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  11. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  12. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  13. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  14. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  15. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  16. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  17. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  18. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  19. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions_include.php ( 0.16 KB )
  21. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions.php ( 5.54 KB )
  22. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  23. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  24. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  25. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/provider.php ( 0.19 KB )
  26. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  27. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  28. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  29. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/common.php ( 0.03 KB )
  30. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  32. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/alipay.php ( 3.59 KB )
  33. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  34. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/app.php ( 0.95 KB )
  35. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cache.php ( 0.78 KB )
  36. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/console.php ( 0.23 KB )
  37. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cookie.php ( 0.56 KB )
  38. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/database.php ( 2.48 KB )
  39. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/filesystem.php ( 0.61 KB )
  40. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/lang.php ( 0.91 KB )
  41. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/log.php ( 1.35 KB )
  42. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/middleware.php ( 0.19 KB )
  43. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/route.php ( 1.89 KB )
  44. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/session.php ( 0.57 KB )
  45. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/trace.php ( 0.34 KB )
  46. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/view.php ( 0.82 KB )
  47. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/event.php ( 0.25 KB )
  48. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  49. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/service.php ( 0.13 KB )
  50. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/AppService.php ( 0.26 KB )
  51. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  52. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  53. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  54. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  55. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  56. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/services.php ( 0.14 KB )
  57. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  58. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  59. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  60. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  61. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  62. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  63. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  64. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  65. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  66. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  67. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  68. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  69. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  70. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  71. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  72. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  73. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  74. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  75. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  76. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  77. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  78. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  79. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  80. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  81. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  82. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  83. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  84. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  85. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  86. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  87. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/Request.php ( 0.09 KB )
  88. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  89. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/middleware.php ( 0.25 KB )
  90. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  91. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  92. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  93. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  94. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  95. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  96. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  97. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  98. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  99. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  100. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  101. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  102. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  103. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/route/app.php ( 3.94 KB )
  104. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  105. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  106. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Index.php ( 9.87 KB )
  108. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/BaseController.php ( 2.05 KB )
  109. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  110. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  111. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  112. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  113. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  114. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  115. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  116. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  117. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  118. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  119. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  120. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  121. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  122. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  123. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  124. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  125. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  126. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  127. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  128. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  129. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  130. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  131. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  132. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  133. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  134. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  135. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Es.php ( 3.30 KB )
  136. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  137. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  138. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  139. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  140. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  141. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  142. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  143. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  144. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/runtime/temp/c935550e3e8a3a4c27dd94e439343fdf.php ( 31.50 KB )
  145. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000692s ] mysql:host=127.0.0.1;port=3306;dbname=wenku;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001205s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000336s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000298s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000795s ]
  6. SELECT * FROM `set` [ RunTime:0.000245s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000894s ]
  8. SELECT * FROM `article` WHERE `id` = 736340 LIMIT 1 [ RunTime:0.000737s ]
  9. UPDATE `article` SET `lasttime` = 1781201548 WHERE `id` = 736340 [ RunTime:0.001191s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 64 LIMIT 1 [ RunTime:0.000297s ]
  11. SELECT * FROM `article` WHERE `id` < 736340 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000608s ]
  12. SELECT * FROM `article` WHERE `id` > 736340 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000525s ]
  13. SELECT * FROM `article` WHERE `id` < 736340 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.002027s ]
  14. SELECT * FROM `article` WHERE `id` < 736340 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.001106s ]
  15. SELECT * FROM `article` WHERE `id` < 736340 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.001091s ]
0.104875s