zoukankan      html  css  js  c++  java
  • Netty服务端启动过程相关源码分析

    1、Netty 是怎么创建服务端Channel的呢?

    我们在使用ServerBootstrap.bind(端口)方法时,最终调用其父类AbstractBootstrap中的doBind方法,相关源码如下:

     private ChannelFuture doBind(final SocketAddress localAddress) {
            //初始化和注册
            final ChannelFuture regFuture = initAndRegister();
            .....
    

    我们继续跟进initAndRegister()这个方法,发现是使用channelFactory.newChannel() 完成channel的创建:

    final ChannelFuture initAndRegister() {
            Channel channel = null;
            try {
                channel = channelFactory.newChannel();
                init(channel);
            } catch (Throwable t) {......}
    

    ChannelFactory在实现类ReflectiveChannelFactory中的实现细节,内部使用了反射的方式创建Channel:

        public T newChannel() {
            try {
                return clazz.newInstance();
            } catch (Throwable t) {......}
        }
    

    这里的ChannelFactory是通过 bootstrap.channel(NioServerSocketChannel.class) 加入的:

        public B channel(Class<? extends C> channelClass) {
            if (channelClass == null) {
                throw new NullPointerException("channelClass");
            }
            return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
        }
    
    • 综上所述,Netty就是调用jdk底层方法创建NIO的channel,也就是通过反射完成NIO的channel创建。最后其包装成Netty自己的Channel.

    2、初始化服务端Channel是怎么样的执行流程?

    在创建了Channel之后就调用AbstractBootstrap的init(channel)抽象方法完成初始化:

        abstract void init(Channel channel) throws Exception;
    

    服务器端ServerBootstrap的init方法从源码来看主要完成工作为:

    • 配置相关的Options和Attribute。
    • 通过ChannelPipeline 添加相关逻辑处理器 ChannelHandler。

    最后这些属性会传入ServerBootstrapAcceptor连接器,通过ServerBootstrapAcceptor连接器完成相应的初始化。

                    // We add this handler via the EventLoop as the user may have used a ChannelInitializer as handler.
                    // In this case the initChannel(...) method will only be called after this method returns. Because
                    // of this we need to ensure we add our handler in a delayed fashion so all the users handler are
                    // placed in front of the ServerBootstrapAcceptor.
                    ch.eventLoop().execute(new Runnable() {
                        @Override
                        public void run() {
                            pipeline.addLast(new ServerBootstrapAcceptor(
                                    currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                        }
                    });
    

    3、怎么注册selector?

    在Java NIO 中注册通道Channel到多路复用器Selector,并说明关注点SelectionKey.OP_ACCEPT,监听ACCEPT事件通常我们会这样写:

    serverSocket.register(selector, SelectionKey.OP_ACCEPT);
    

    Netty在底层将Channel注册到事件轮询器selector上就是基于此方法:

    • 首先在初始化Channel后执行:
    ChannelFuture regFuture = config().group().register(channel);
    

    上面的代码实际是调用AbstractChannel的register方法,完成eventLoop的绑定。内部方法register0()中会调用AbstractNioChannel的doRegister() 方法:

        protected void doRegister() throws Exception {
            boolean selected = false;
            for (;;) {
                try {
                    selectionKey = javaChannel().register(eventLoop().selector, 0, this);
                    return;
                } catch (CancelledKeyException e) {......}
            }
        }
    

    这里其实就是调用NIO SelectableChannel的register方法。

    • 从源码可以知道,注册成功后这里会以此执行服务器handler中的回调方法:handlerAdded ,channelActive

    4、端口怎么绑定呢?

    一切OK之后就会调用AbstractChannel中的 bind 方法,这个方法又会调用NioServerSocketChannel 的 doBind 方法,从doBind方法可知是调用的原生NIO 的bind做绑定:

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

    绑定完成后会执行代码:

                if (!wasActive && isActive()) {
                    invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            pipeline.fireChannelActive();
                        }
                    });
                }
    
    

    这里会调用DefaultChannelPipeline中的内部类HeadContext的channelActive方法进行事件传播:

            @Override
            public void channelActive(ChannelHandlerContext ctx) throws Exception {
                ctx.fireChannelActive();
    
                readIfIsAutoRead();
            }
    

    那么服务端到底是在哪里 accept 连接的呢?
    通过上面的代码我们跟进 AbstractChannel 的beginRead() 方法,继而找到 AbstractNioChannel 的 doBeginRead() 方法:

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

    上面的代码就是NIO编程中常用的写法,这里监听ACCEPT事件就是NioServerSocketChannel构造函数调用父类传入的SelectionKey.OP_ACCEPT:

        public NioServerSocketChannel(ServerSocketChannel channel) {
            super(null, channel, SelectionKey.OP_ACCEPT);
            config = new NioServerSocketChannelConfig(this, javaChannel().socket());
        }
    
  • 相关阅读:
    docker harbor 修改密码 重置密码 sql
    mongodb监控并在服务挂掉后自动重启脚本
    centos7 ffmpeg安装 rtsp相关
    vscode vue 自动格式化代码
    开启go module
    Python3.x:打包为exe执行文件(window系统)
    Docker 日志都在哪里?怎么收集?
    HttpsURLConnection信任任何证书
    SP3734 PERIODNI
    联赛前的记录
  • 原文地址:https://www.cnblogs.com/monkjavaer/p/11312392.html
Copyright © 2011-2022 走看看