Netty 学习随手笔记

Netty Server在启动时做了什么事情?

new NioServerSocketChannel() 做了什么事?

1. 通过java.nio.channels.spi.SelectorProvider.provider().openServerSocketChannel()生成了一个nio的ServerSocketChannel

2. 创建一个DefaultChannelPipeline放入NioServerSocketChannel对象中(pipeline对象默认存在head & tail节点)

3. 将创建出来的ServerSocketChannel对象保存到NioServerSocketChannel对象中

4. 将NioServerSocketChannel感兴趣的事件(SelectionKey.OP_ACCEPT)保存到NioServerSocketChannel对象中

5. 设置ServerSocketChannel对象为非阻塞模式

netty server的ServerBootstrap在调用bind(int port)时,做了什么事?

1. 将port封装成一个InetSocketAddress对象

2. initAndRegister()

1) channelFactory.newChannel():使用反射调用NioServerSocketChannel的空参构造器构造一个NioServerSocketChannel对象
2) init(channel):初始化NioServerSocketChannel
-> setChannelOptions(channel, options, logger):将配置的options设置到NioServerSocketChannel对象中
-> 将配置的attrs设置到NioServerSocketChannel对象中
-> channel.pipeline().addLast():给NioServerSocketChannel的pipeline中,添加一个处理器,这个处理器是Channel的初始化器(ChannelInitializer)
---> 此时NioServerSocketChannel的pipeline变成了这样【head <==> ChannelInitializer <==> tail】(当channel注册时,会调用initChannel(ctx)方法,会将NioServerSocketChannel的pipeline变成【head <==> ServerBootstrapAcceptor <==> tail】)
3) config().group().register(channel):从bossGroup里拿一个线程来处理channel的注册,将其注册到线程自己的selector上
-> next().register(channel) 在bossGroup中选择一个NioEventLoop,注册channel
-> eventLoop.execute(() -> register0(promise)) 
--> addTask(() -> register0(promise))
--> 如果是第一次调用eventLoop.execute,则执行以下逻辑
--> startThread() -->  doStartThread()
---> SingleThreadEventExecutor.this.run()这个其实就是NioEventLoop.run()
---> NioEventLoop.run() 是一个死循环,每次循环调用 select(wakenUp.getAndSet)false) -> processSelectedKeys() -> runAllTasks()
----> select(wakenUp.getAndSet(false)):死循环监听IO事件,调用selector.select(timeoutMillis),当超时或者有事件发生就会跳出循环执行下面的逻辑
----> processSelectedKeys():处理事件
----> runAllTasks():执行taskQueue里的任务

3. doBind0()

这个方法主要是提交了一个task到nioEventLoop的taskQueue中,提交的task如下

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());
        }
    }
});

就是调用NioServerSocketChannel的绑定接口,最终会调用javaChannel的bind接口,为javaChannel绑定端口。

netty server 启动注册了4个Task,这4个Task都干了什么事情?

1. register0(promise);

注册任务代码:

// io.netty.channel.AbstractChannel.AbstractUnsafe#register
eventLoop.execute(new Runnable() {
    @Override
    public void run() {
        register0(promise);
    }
});

这段代码是在 bind(int port) 方法中 initAndRegister() 时调用的。

register0(promise)方法对NioServerSocketChannel启动时来说,重要的逻辑有:

private void register0(ChannelPromise promise) {
    try {
        if (!promise.setUncancellable() || !ensureOpen(promise)) {
            return;
        }
        boolean firstRegistration = neverRegistered;
        // 将NioServerSocketChannel注册到selector上,
        // 但是这时候注册的是0没有真正的注册accept事件,
        // 在pipeline.fireChannelActive()才注册真正的accept事件
        doRegister();
        neverRegistered = false;
        registered = true;
    
        pipeline.invokeHandlerAddedIfNeeded();
    
        safeSetSuccess(promise);
        pipeline.fireChannelRegistered();
        if (isActive()) {
            if (firstRegistration) {
                pipeline.fireChannelActive();
            } else if (config().isAutoRead()) {
                beginRead();
            }
        }
    } catch (Throwable t) {
        closeForcibly();
        closeFuture.setClosed();
        safeSetFailure(promise, t);
    }
}

(1)doRegister():将NioServerSocketChannel注册到selector上,但是这时候注册的是0没有真正的注册accept事件,在pipeline.fireChannelActive()才注册真正的accept事件:

io.netty.channel.AbstractChannel#doRegister 
--> javaChannel().register(eventLoop().unwrappedSelector(), 0, this);

(2)pipeline.invokeHandlerAddedIfNeeded():判断是否是第一次注册,如果是第一次注册,执行注册前添加的ChannelHandlers,完成注册

pipeline.invokeHandlerAddedIfNeeded();
--> callHandlerAddedForAllHandlers():拿到完成注册前添加的所有ChannelHandlers:pendingHandlerCallbackHead
--> task.execute():调用PendingHandlerAddedTask的execute()方法
----> callHandlerAdded0(ctx)
------> ctx.callHandlerAdded()
--------> handler().handlerAdded(this):此时会调用ChannelInitializer的handlerAdded()方法,这个方法是ServerBootstrap在init(channel)是添加的
----------> initChannel(ctx):最终调用ChannelInitializer的initChannel(Channel ch)方法,此时ch还是NioServerSocketChannel
----------> removeState(ctx):删除NioServerSocketChannel的pipeline中的ChannelInitializer(一个Handler)

注意:这里的initChannel(ctx)方法会注册一个任务,后续有说,所以执行完这段逻辑之后的pipeline是这样的。

原始pipeline:HeadContext <--> ChannelInitializer <--> TailContext
执行完initChannel后的pipeline:HeadContext <--> TailContext
后续才为这个pipeline添加ServerBootstrapAcceptor(Handler)

ch.eventLoop().execute(new Runnable() {
    @Override
    public void run() {
        pipeline.addLast(new ServerBootstrapAcceptor(
                ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
    }
});

(3)pipeline.fireChannelRegistered():触发channel的注册事件,链式调用pipeline中的所有handler的channelRegister方法

注意:这里由于是NioServerSocketChannel的注册,所以并不会调用这段逻辑,因为这个时候NioServerSocketChannel还没有绑定端口

if (isActive()) {
    if (firstRegistration) {
        pipeline.fireChannelActive();
    } else if (config().isAutoRead()) {
        beginRead();
    }
}

// io.netty.channel.socket.nio.NioServerSocketChannel#isActive
// 这个是NioServerSocketChannel的isActive方法,因为这里Netty设计的是先注册再绑定端口号,所以javaChannel().socket().isBound() = false
public boolean isActive() {
    return isOpen() && javaChannel().socket().isBound();
}

// io.netty.channel.socket.nio.NioSocketChannel#isActive
// 这个是NioSocketChannel的isActive方法,由于在NioSocketChannel在accept之后就已经算连接了,所以这里isActive方法返回的是true,即如果是NioSocketChannel就会走上面的逻辑
public boolean isActive() {
    SocketChannel ch = javaChannel();
    return ch.isOpen() && ch.isConnected();
}

2. pipeline.addLast(new ServerBootstrapAcceptor(ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));

注册任务代码:

p.addLast(new ChannelInitializer<Channel>() {
    @Override
    public void initChannel(final Channel ch) throws Exception {
        final ChannelPipeline pipeline = ch.pipeline();
        ChannelHandler handler = config.handler();
        if (handler != null) {
            pipeline.addLast(handler);
        }

        ch.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                pipeline.addLast(new ServerBootstrapAcceptor(
                        ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
            }
        });
    }
});

这段代码是在ServerBoostrap的 bind(int port) 中在 init(channel) 时调用的一段代码,往channel添加了一个ChannelInitializer,在register的时候,会通过pipeline.invokeHandlerAddedIfNeeded()调用到该ChannelInitializer的 initChannel(final Channel ch) 方法,然后就会向eventLoop提交一个task

ch.eventLoop().execute(new Runnable() {
    @Override
    public void run() {
        pipeline.addLast(new ServerBootstrapAcceptor(
                ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
    }
});

这个task主要就是给NioServerSocketChannel的pipeline添加一个ServerBootstrapAcceptor,此时NioServerSocketChannel的pipeline如下:

原始:HeadContext <--> TailContext
添加后:HeadContext <--> ServerBootstrapAcceptor <--> TailContext

这里没有ChannelInitializer的原因是
ChannelInitializer只会调用一次,调用一次就会从pipeline中删除

3.channel.bind(localAddress,promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);

注册任务代码:

private static void doBind0(
        final ChannelFuture regFuture, final Channel channel,
        final SocketAddress localAddress, final ChannelPromise promise) {
    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());
            }
        }
    });
}

 这个task是ServerBoostrap在执行完initAndRegister()方法之后调用doBind0()方法,给nioEventLoop的taskQueue添加了这个task,这个task主要的逻辑有两个:

(1)从pipeline的tail向头调用bind()方法,最后在head去调用java nio的ServerSocketChannel去绑定端口号

channel.bind(localAddress, promise)
--> pipeline.bind(localAddress, promise)
----> tail.bind(localAddress, promise)
------> head.bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)
------> unsafe.bind(localAddress, promise)
--------> AbstarctChannel.doBind(localAddress)
----------> javaChannel().bind(localAddress, config.getBacklog());

最后给eventLoop提交一个任务
invokeLater(new Runnable() { 
    @Override
    public void run() {
        pipeline.fireChannelActive();
    }
});

(2)向eventLoop提交pipeline.fireChannelActive();任务

eventLoop().execute(new Runnable() {
    @Override
    public void run() {
        pipeline.fireChannelActive();
    }
});

注意:channel.bind(localAddress, promise)这个方法会调用pipeline的bind方法,它会从tail开始找有bind()方法的handler去执行,如果这时候你给pipeline添加了一个有bind()方法的handler,可能会导致执行不到javaChannel().bind(localAddress, config.getBacklog()); 最后不能绑定端口,client不能连接server

4. pipeline.fireChannelActive();

注册任务代码:

// ServerBoostrap 在bind时调用doBind0()方法会执行到下面这个方法
// io.netty.channel.AbstractChannel.AbstractUnsafe#bind
eventLoop().execute(new Runnable() {
    @Override
    public void run() {
        pipeline.fireChannelActive();
    }
});

这个pipeline.fireChannelActive()方法会从head到tail遍历所有pipeline中的handler去调用channelActive() 方法,此时pipeline中的handler有:

HeadContext <--> ServerBootstrapAcceptor <--> TailContext

分别看一下这个3个的channelActive方法

(1)HeadContext

@Override
public void channelActive(ChannelHandlerContext ctx) {
    ctx.fireChannelActive();

    readIfIsAutoRead();
}

private void readIfIsAutoRead() {
    if (channel.config().isAutoRead()) {
        channel.read();
    }
}

// io.netty.channel.AbstractChannel#read
// channel.read();
public Channel read() {
    pipeline.read();
    return this;
}

// io.netty.channel.DefaultChannelPipeline#read
// pipeline.read()
public final ChannelPipeline read() {
    tail.read();
    return this;
}

// io.netty.channel.AbstractChannelHandlerContext#read
// tail.read()
public ChannelHandlerContext read() {
    // 找到有read()方法的AbstractChannelHandlerContext
    final AbstractChannelHandlerContext next = findContextOutbound(MASK_READ);
    EventExecutor executor = next.executor();
    if (executor.inEventLoop()) {
        // 调用read()
        next.invokeRead();
    } else {
        Tasks tasks = next.invokeTasks;
        if (tasks == null) {
            next.invokeTasks = tasks = new Tasks(next);
        }
        executor.execute(tasks.invokeReadTask);
    }

    return this;
}

private void invokeRead() {
    if (invokeHandler()) {
        try {
            ((ChannelOutboundHandler) handler()).read(this);
        } catch (Throwable t) {
            notifyHandlerException(t);
        }
    } else {
        read();
    }
}

// 最后会调用到HeadContext的read()方法
public void read(ChannelHandlerContext ctx) {
    unsafe.beginRead();
}

// unsafe.beginRead() 会调用到这个方法
// io.netty.channel.AbstractChannel.AbstractUnsafe#beginRead
public final void beginRead() {
    assertEventLoop();

    // 如果还没有激活,即没有绑定端口,则返回
    if (!isActive()) {
        return;
    }

    try {
        // 开始读,即真正意义上的开始接受事件
        // 这里对于NioServerSocketChannel来说,会注册Accept事件到selector中
        doBeginRead();
    } catch (final Exception e) {
        invokeLater(new Runnable() {
            @Override
            public void run() {
                pipeline.fireExceptionCaught(e);
            }
        });
        close(voidPromise());
    }
}

// 在这里,才是真正意义上的开启NioServerSocketChannel的accept事件
// 如果是NioSocketChannel,这里是开启read事件
protected void doBeginRead() throws Exception {
    // Channel.read() or ChannelHandlerContext.read() was called
    final SelectionKey selectionKey = this.selectionKey;
    if (!selectionKey.isValid()) {
        return;
    }

    readPending = true;

    final int interestOps = selectionKey.interestOps();
    if ((interestOps & readInterestOp) == 0) {
        selectionKey.interestOps(interestOps | readInterestOp);
    }
}

这个fireChannelActive方法一定会调用HeadContext的channelActive方法,因为HeadContext就是第一个哈哈

public final ChannelPipeline fireChannelActive() {
    AbstractChannelHandlerContext.invokeChannelActive(head);
    return this;
}

注意:对于Netty Server端来说,必须走这段逻辑,否则Client完全会连不上Server,所以尽量不要给Server的bossEventLoop的pipeline中添加额外的handler,如果一定要加,那你必须继续触发bind()和read()操作

(2)ServerBootstrapAcceptor

这个handler没有channelActive方法跳过,不会调用到这个handler

(3)TailContext

// 调用到TailContext里的方法时,说明我们没有添加自己的channelActive方法
// 这是一个兜底操作
@Override
public void channelActive(ChannelHandlerContext ctx) {
    onUnhandledInboundChannelActive();
}

protected void onUnhandledInboundChannelActive() {
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值