网络编程与Netty(四) Netty源码-(Ⅷ)

在上一小节的讲解中,我们对doBind方法中的initAndRegister()方法进行了底层源码的分析,而在这小节中主要是对doBind方法中另一个比较重要的方法调用做讲解—doBind0()方法,这个方法做了实际的绑定工作:

private static void doBind0(
            final ChannelFuture regFuture, final Channel channel,
            final SocketAddress localAddress, final ChannelPromise promise) {
   //这里又用到了eventLoop.execute方法,这个方法前面讲解了,就不再赘述了
   //eventLoop().execute
        channel.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                if (regFuture.isSuccess()) {
                  //实际调用了AbstractChannel的bind方法
                    channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
                } else {
                    promise.setFailure(regFuture.cause());
                }
            }
        });
    }
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
    //bind是出站事件,所以应该有出站handler处理,从tail向前找到第一个出站handler
    //而此时pipeline中只有三个handler:HeadContext(入/出)->ServerBootstrapAcceptor(入)->TailContext(入)
        return pipeline.bind(localAddress, promise);
    }
public final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
    //来到了tail.bind,在这个方法会向前找到最先找到的出站handler
        return tail.bind(localAddress, promise);
    }
public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
        if (localAddress == null) {
            throw new NullPointerException("localAddress");
        }
        if (isNotValidPromise(promise, false)) {
            // cancelled
            return promise;
        }
	//findContextOutbound这个方法就是查找到最近的出站handler(context)
        final AbstractChannelHandlerContext next = findContextOutbound();
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeBind(localAddress, promise);
        } else {
            safeExecute(executor, new Runnable() {
                @Override
                public void run() {
                    next.invokeBind(localAddress, promise);
                }
            }, promise, null);
        }
        return promise;
    }
private AbstractChannelHandlerContext findContextOutbound() {
    //思路就是以当前handler为起点向前遍历,判断是否为outbound,不是则继续向前,直到找到一个outbound为止,这个很好理解。
        AbstractChannelHandlerContext ctx = this;
        do {
            ctx = ctx.prev;
        } while (!ctx.outbound);
        return ctx;
    }

​ 因为主channel的pipeline中只有HeadContext能够处理出站事件,最终肯定是来到HeadContext:

public void bind(
                ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)
                throws Exception {
    	//这里是调用的AbstractUnsafe的bind方法,这个之前讲过一次,在Unsafe系列源码中,这里就再详细说说
            unsafe.bind(localAddress, promise);
        }
public final void bind(SocketAddress localAddress, ChannelPromise promise) {
            this.assertEventLoop();
            if (promise.setUncancellable() && this.ensureOpen(promise)) {
                if (Boolean.TRUE.equals(AbstractChannel.this.config().getOption(ChannelOption.SO_BROADCAST)) && localAddress instanceof InetSocketAddress && !((InetSocketAddress)localAddress).getAddress().isAnyLocalAddress() && !PlatformDependent.isWindows() && !PlatformDependent.maybeSuperUser()) {
                    AbstractChannel.logger.warn("A non-root user can't receive a broadcast packet if the socket is not bound to a wildcard address; binding to a non-wildcard address (" + localAddress + ") anyway as requested.");
                }
           //先获取绑定前的Channel状态是否为active激活状态
                boolean wasActive = AbstractChannel.this.isActive();
                try {
           //doBind方法是一个抽象方法,在NioSocketChannel和NioServerSocketChannel中有不同的实现
                    AbstractChannel.this.doBind(localAddress);
                } catch (Throwable var5) {
                    this.safeSetFailure(promise, var5);
                    this.closeIfClosed();
                    return;
                }
           //这里表示如果绑定前为未激活,绑定后为激活,说明Channel是在此次绑定过程中激活的
           //那么就将fireChannelActive交给EventLoop中的线程执行
                if (!wasActive && AbstractChannel.this.isActive()) {
                    this.invokeLater(new Runnable() {
                        public void run() {
                          //这里会传递ChannelActive事件,这是一个入站事件,所以从Head开始
                            AbstractChannel.this.pipeline.fireChannelActive();
                        }
                    });
                }
                this.safeSetSuccess(promise);
            }
        }

​ 先来看看两种doBind方法的实现,然后在看看invokeLater方法。

NioServerSocketChannelprotected void doBind(SocketAddress localAddress) throws Exception {
        if (PlatformDependent.javaVersion() >= 7) {
       //可以设置backlog参数
            this.javaChannel().bind(localAddress, this.config.getBacklog());
        } else {
            this.javaChannel().socket().bind(localAddress, this.config.getBacklog());
        }
    }
NioSocketChannelprivate void doBind0(SocketAddress localAddress) throws Exception {
        if (PlatformDependent.javaVersion() >= 7) {
            SocketUtils.bind(this.javaChannel(), localAddress);
        } else {
            SocketUtils.bind(this.javaChannel().socket(), localAddress);
        }
    }

​ 接下来看invokeLater,主要是将fireChannelActive打包成Runnable交给EventLoop执行

private void invokeLater(Runnable task) {
            try {
             //这个eventLoop().execute(task)看不过最少三五遍了吧,前面有详细讲解过,不再赘述
                AbstractChannel.this.eventLoop().execute(task);
            } catch (RejectedExecutionException var3) {
                AbstractChannel.logger.warn("Can't invoke task later as EventLoop rejected it", var3);
            }
        }

​ AbstractUnsafe的bind方法中执行完了doBind方法后会传递channelActive事件,这个事件是入站事件,HeadContext(入/出)->ServerBootstrapAcceptor(入)->TailContext(入)这三个handler都是处理入站事件的,不过ServerBootstrapAcceptor 和 TailContext 的 channelActive 方法都没有做任何实质性的事情,只有HeadContext 有实质性代码

​ 先来看看是如何选择到headContext:

public final ChannelPipeline fireChannelActive() {
 	//这里可以看到,将head作为参数传入,因为入站方法默认从头开始找起,进入invokeChannelActive方法
        AbstractChannelHandlerContext.invokeChannelActive(head);
        return this;
    }
static void invokeChannelActive(final AbstractChannelHandlerContext next) {
    //next是headContext
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeChannelActive();
        } else {
          //executor.execute这个多次出现,不再赘述
            executor.execute(new Runnable() {
                @Override
                public void run() {
                  //next实际上是参数HeadContext,因为Headcontext就是第一个入站处理器,所以直接调用Headcontext.invokeChannelActive
                    next.invokeChannelActive();
                }
            });
        }
    }
private void invokeChannelActive() {
        if (invokeHandler()) {
            try {
               //这个handler就是HeadContext,接下来就看channelActive事件是如何处理的
                ((ChannelInboundHandler) handler()).channelActive(this);
            } catch (Throwable t) {
                notifyHandlerException(t);
            }
        } else {
            fireChannelActive();
        }
    }

​ 接下来就看到这个事件在pipeline中各个handler中的传递和处理:ServerBootstrapAcceptor 和 TailContext 的 channelActive 方法都没有做任何实质性的事情,只有HeadContext 有实质性代码

public void channelActive(ChannelHandlerContext ctx) throws Exception {
    	//这个是往后传递,也就是ServerBootstrapAcceptor
            ctx.fireChannelActive();
		//向后传递后,执行readIfIsAutoRead方法
            readIfIsAutoRead();
        }
//ServerBootstrapAcceptor 父类ChannelInboundHandlerAdapter中的channelActive,只是简单的先后传递
public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.fireChannelActive();
    }

//TailContext的channelActive,不做处理逻辑
public void channelActive(ChannelHandlerContext ctx) throws Exception {
            onUnhandledInboundChannelActive();
        }
private void readIfIsAutoRead() {
            if (channel.config().isAutoRead()) {
             //AbstractChannel 的 read 方法
                channel.read();
            }
        }
public Channel read() {
    //调用了 DefaultChannelPipeline 的 read 方法
        pipeline.read();
        return this;
    }
public final ChannelPipeline read() {
    //AbstractChannelHandlerContext.read方法
        tail.read();
        return this;
    }
public ChannelHandlerContext read() {
    //read是一个Outbound事件,因此findContextOutbound()会以tail为起点向前一个个查找出站处理器,前面有看过这个方法就不赘述了
    //因为HeadContext(入/出)->ServerBootstrapAcceptor(入)->TailContext(入)中只有HeadContext是出站处理器,所以这个next只能是HeadContext
        final AbstractChannelHandlerContext next = findContextOutbound();
        EventExecutor executor = next.executor();
    //这里判断当前线程与eventLoop中的线程是否为同一个,如果是则直接执行next.invokeRead,如果不是则交给eventLoop执行
        if (executor.inEventLoop()) {
            next.invokeRead();
        } else {
            Runnable task = next.invokeReadTask;
            if (task == null) {
                next.invokeReadTask = task = new Runnable() {
                    @Override
                    public void run() {
                        next.invokeRead();
                    }
                };
            }
            executor.execute(task);
        }
        return this;
    }
private void invokeRead() {
        if (invokeHandler()) {
            try {
              //这个handler实际是HeadContext,所以来到HeadContext中的read方法
                ((ChannelOutboundHandler) handler()).read(this);
            } catch (Throwable t) {
                notifyHandlerException(t);
            }
        } else {
            read();
        }
    }
public void read(ChannelHandlerContext ctx) {
    //AbstractUnsafe的beginRead方法
            unsafe.beginRead();
        }
public final void beginRead() {
	//判断当前的线程是否为eventLoop中的线程
            assertEventLoop();
            if (!isActive()) {
                return;
            }
            try {
             //最重要的就是doBeginRead方法,这个方法在讲解Channel系列源码的AbstractNioChannel中详细讲到过,
                doBeginRead();
            } catch (final Exception e) {
                invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.fireExceptionCaught(e);
                    }
                });
                close(voidPromise());
            }
        }
protected void doBeginRead() throws Exception {
        SelectionKey selectionKey = this.selectionKey;
    //判断是否有效
        if (selectionKey.isValid()) {
            this.readPending = true;
            int interestOps = selectionKey.interestOps();
     //条件为true则表示当前channel还没有设置对readInterestOp所表示的事件感兴趣
            if ((interestOps & this.readInterestOp) == 0) {
              //这里就是真正的对readInterestOp表示的事件进行监听,可以看出这些地方都是用位运算,效率高
              //这里用到的readInterestOp是哪里设置到记得不?是在构造函数中设置进来的
              //如果是NioServerSocketChannel则对accept事件感兴趣,如果是NioSocketChannel则对read读事件感兴趣
                selectionKey.interestOps(interestOps | this.readInterestOp);
            }
        }
    }

​ 到了这里,doBind方法基本就完成了,也就代表着整个服务器启动流程已经结束了。整个流程下来,是不是很多的之前讲到的东西基本都串起来了,从Channel到pipeline,从pipeline到HandlerContext,从HandlerContext到Handler,从Handler到Unsafe,全部组件都用自己的任务,相互协作完成了整个启动流程。接下来一下节就是讲讲Netty在处理事件上的整体流程。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值