Netty源码分析(三)之NioServerSocketChannel

文章目录

  • netty版本
  • NioServerSocketChannel的创建过程
    • 无参构造方法的执行流程
  • NioServerSocketChannel的初始化
  • AbstractChannel.doRegister
  • AbstractChannel.bind

netty版本

  1. 使用的netty版本是io.netty:netty-all:4.1.33.Final

NioServerSocketChannel的创建过程

  1. NioServerSocketChannel创建过程

无参构造方法的执行流程

  1. 构造方法

       public NioServerSocketChannel() {
            this(newSocket(DEFAULT_SELECTOR_PROVIDER));
        }
    
  2. newSocket()方法:这里为什么不用ServerSocketChannel.open()来创建,而是使用缓存的SelectorProvider?继续看下面分析

        private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
        private static ServerSocketChannel newSocket(SelectorProvider provider) {
                try {
                    return provider.openServerSocketChannel();
                } catch (IOException e) {
                    throw new ChannelException(
                            "Failed to open a server socket.", e);
                }
        }
        //构造方法调用父类构造
        public NioServerSocketChannel(ServerSocketChannel channel) {
            super(null, channel, SelectionKey.OP_ACCEPT);
            config = new NioServerSocketChannelConfig(this, javaChannel().socket());
        }
        
    

    构造方法调用父类的构造,设置感兴趣的事件SelectionKey.OP_ACCEPT,设置JDK底层的Channel为非阻塞模式

        protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
            super(parent, ch, readInterestOp);
        }
    
        protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
            super(parent);
            this.ch = ch;
            this.readInterestOp = readInterestOp;
            try {
                ch.configureBlocking(false);
            } catch (IOException e) {
                try {
                    ch.close();
                } catch (IOException e2) {
                    if (logger.isWarnEnabled()) {
                        logger.warn(
                                "Failed to close a partially initialized socket.", e2);
                    }
                }
    
                throw new ChannelException("Failed to enter non-blocking mode.", e);
            }
        }
    
    
  3. ServerSocketChannelopen()方法:每个新Channel创建都需要SelectorProvider.provider()(包含synchronized块)。 当应用程序创建大量连接时,这可能会导致不必要的阻塞。查看issue

        public static ServerSocketChannel open() throws IOException {
            return SelectorProvider.provider().openServerSocketChannel();
        }           
    
    
    
  4. 从构造方法中可以看到每个NioServerSocketChannel实例都关联一个NioServerSocketChannelConfig,NioServerSocketChannelConfig是TCP相关参数的配置,以及Netty中NioServerSocketChannel相关的配置

        private final class NioServerSocketChannelConfig extends DefaultServerSocketChannelConfig {
            private NioServerSocketChannelConfig(NioServerSocketChannel channel, ServerSocket javaSocket) {
                super(channel, javaSocket);
            }
    
            @Override
            protected void autoReadCleared() {
                clearReadPending();
            }
    
            @Override
            public <T> boolean setOption(ChannelOption<T> option, T value) {
                if (PlatformDependent.javaVersion() >= 7 && option instanceof NioChannelOption) {
                    return NioChannelOption.setOption(jdkChannel(), (NioChannelOption<T>) option, value);
                }
                return super.setOption(option, value);
            }
    
            @Override
            public <T> T getOption(ChannelOption<T> option) {
                if (PlatformDependent.javaVersion() >= 7 && option instanceof NioChannelOption) {
                    return NioChannelOption.getOption(jdkChannel(), (NioChannelOption<T>) option);
                }
                return super.getOption(option);
            }
    
            @SuppressWarnings("unchecked")
            @Override
            public Map<ChannelOption<?>, Object> getOptions() {
                if (PlatformDependent.javaVersion() >= 7) {
                    return getOptions(super.getOptions(), NioChannelOption.getOptions(jdkChannel()));
                }
                return super.getOptions();
            }
    
            private ServerSocketChannel jdkChannel() {
                return ((NioServerSocketChannel) channel).javaChannel();
            }
        }
    
    

NioServerSocketChannel的初始化

  1. ServerBootstrapinit(Channel channel)方法

    • 设置自定义的ChannelOptionAttributeKey
    • 设置自定义的ChildChannelOptionChildAttributeKey
    • 设置ChannelPipeline
    • 添加ServerBootstrapAcceptor
        @Override
        void init(Channel channel) throws Exception {
            final Map<ChannelOption<?>, Object> options = options0();
            synchronized (options) {
                setChannelOptions(channel, options, logger);
            }
    
            final Map<AttributeKey<?>, Object> attrs = attrs0();
            synchronized (attrs) {
                for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
                    @SuppressWarnings("unchecked")
                    AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
                    channel.attr(key).set(e.getValue());
                }
            }
    
            ChannelPipeline p = channel.pipeline();
    
            final EventLoopGroup currentChildGroup = childGroup;
            final ChannelHandler currentChildHandler = childHandler;
            final Entry<ChannelOption<?>, Object>[] currentChildOptions;
            final Entry<AttributeKey<?>, Object>[] currentChildAttrs;
            synchronized (childOptions) {
                currentChildOptions = childOptions.entrySet().toArray(newOptionArray(0));
            }
            synchronized (childAttrs) {
                currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(0));
            }
    
            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));
                        }
                    });
                }
            });
        }
    
    
    

AbstractChannel.doRegister

  1. 注册Channel

        @Override
        protected void doRegister() throws Exception {
            boolean selected = false;
            for (;;) {
                try {
                    //jdk中SelectableChannel类的	register(Selector sel, int ops, Object attachment),使用一个给定的Selector注册Channel。事件是0表示不关心任何事件(这里仅仅是将Channel绑定到Selector上)。 this表示attachment,即Netty的AbstractNioChannel,当Selector轮询到当前javaChannel()上时,可以通过attachment获取Netty的AbstractNioChannel。这是一个很巧妙的设计
                    selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
                    return;
                } catch (CancelledKeyException e) {
                    //当试图使用不再SelectionKey时,抛出此异常。
                    if (!selected) {
                        //`SelectionKey`代表Channel与Selector的注册关系,可以调用`cancel()`取消这种关系,但是这种注销关系并不是立即生效,因为尚未调用Select.select(),所以一旦发现此异常,需要调用selectNow()让这种取消关系生效。
                        eventLoop().selectNow();
                        selected = true;
                    } else {
                        // We forced a select operation on the selector before but the SelectionKey is still cached
                        // for whatever reason. JDK bug ?
                        throw e;
                    }
                }
            }
        }
    
    
    

AbstractChannel.bind

  1. 端口绑定

        public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
                assertEventLoop();
    
                if (!promise.setUncancellable() || !ensureOpen(promise)) {
                    return;
                }
    
                // See: https://github.com/netty/netty/issues/576
                if (Boolean.TRUE.equals(config().getOption(ChannelOption.SO_BROADCAST)) &&
                    localAddress instanceof InetSocketAddress &&
                    !((InetSocketAddress) localAddress).getAddress().isAnyLocalAddress() &&
                    !PlatformDependent.isWindows() && !PlatformDependent.maybeSuperUser()) {
                    // Warn a user about the fact that a non-root user can't receive a
                    // broadcast packet on *nix if the socket is bound on non-wildcard address.
                    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是否激活 @1
                boolean wasActive = isActive();
                try {
                // 调用JDK底层的bind @2
                    doBind(localAddress);
                } catch (Throwable t) {
                    safeSetFailure(promise, t);
                    closeIfClosed();
                    return;
                }
                //端口绑定完成会返回true @3
                if (!wasActive && isActive()) {
                    invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            //@4
                            pipeline.fireChannelActive();
                        }
                    });
                }
    
                safeSetSuccess(promise);
            }
    
    
    

@1: 对于NioServerSocketChannel来说,在服务端启动的时候一定是未激活的(因为端口绑定未完成)
@2:NioServerSocketChannel的doBind()方法

       protected void doBind(SocketAddress localAddress) throws Exception {
              if (PlatformDependent.javaVersion() >= 7) {
                  javaChannel().bind(localAddress, config.getBacklog());
              } else {
                  javaChannel().socket().bind(localAddress, config.getBacklog());
              }
          }
  

@3:此时端口绑定已经完成。校验是否是端口绑定完成之前是false,绑定完成之后是true
@4:这里的pipelineDefaultChannelPipeline实例对象,此pipeline的第一个节点是HeadContext(DefaultChannelPipeline的构造方法如下)。实际上调用的就是HeadContextchannelActive()方法

	```java
	        protected DefaultChannelPipeline(Channel channel) {
	            this.channel = ObjectUtil.checkNotNull(channel, "channel");
	            succeededFuture = new SucceededChannelFuture(channel, null);
	            voidPromise =  new VoidChannelPromise(channel, true);
	    
	            tail = new TailContext(this);
	            head = new HeadContext(this);
	    
	            head.next = tail;
	            tail.prev = head;
	        }
	```
  1. 分析HeadContext的channelActive

        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            ctx.fireChannelActive();
            //@1
            readIfIsAutoRead();
        }
        //触发一个read事件 @2
         private void readIfIsAutoRead() {
            if (channel.config().isAutoRead()) {
                channel.read();
            }
        }
        //调用pipeline的read方法 @3
        @Override
        public Channel read() {
            pipeline.read();
            return this;
        }
        
        //DefaultChannelPipeline的read() @4
        @Override
        public final ChannelPipeline read() {
            tail.read();
            return this;
        }
        //AbstractChannelHandlerContext的read() @5
        @Override
        public ChannelHandlerContext read() {
            final AbstractChannelHandlerContext next = findContextOutbound();
            EventExecutor executor = next.executor();
            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;
        }
        //AbstractChannelHandlerContext的invokeRead() @6
        private void invokeRead() {
            if (invokeHandler()) {
                try {
                    //调用HeadContext的read(ChannelHandlerContext ctx)
                    ((ChannelOutboundHandler) handler()).read(this);
                } catch (Throwable t) {
                    notifyHandlerException(t);
                }
            } else {
                read();
            }
        }
        //DefaultChannelPipeline中HeadContext的read() @7
        @Override
        public void read(ChannelHandlerContext ctx) {
            unsafe.beginRead();
        }
        
        //AbstractChannel.AbstractUnsafe类的beginRead @8
        @Override
        public final void beginRead() {
            assertEventLoop();
    
            if (!isActive()) {
                return;
            }
    
            try {
                doBeginRead(); //@9
            } catch (final Exception e) {
                invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.fireExceptionCaught(e);
                    }
                });
                close(voidPromise());
            }
        }
    

调用流程

        @1->@2->@3->@4->@5->@6->@7->@8

@9: AbstractNioChanneldoBeginRead()方法,this.selectionKey是服务端注册时返回的selectionKey

    
        @Override
        protected void doBeginRead() throws Exception {
            // Channel.read() or ChannelHandlerContext.read() was called
            final SelectionKey selectionKey = this.selectionKey;
            if (!selectionKey.isValid()) {
                return;
            }
    
            readPending = true;
            //获取感兴趣的事件,对于服务端,注册Channel之后,感兴趣的事件是0(即仅仅绑定而已),此处readInterestOp是OP_ACCEPT事件
            final int interestOps = selectionKey.interestOps();
            
            if ((interestOps & readInterestOp) == 0) {
                selectionKey.interestOps(interestOps | readInterestOp);
            }
        }

你可能感兴趣的:(#,netty源码分析)