Netty NioServerSocket 启动流程1

转载请附上原文链接

1.首先贴io.netty.example.echo.EchoServer的EchoServer的部分,这个是Netty官方给的例子。

#1

public final class EchoServer {

    static final boolean SSL = System.getProperty("ssl") != null;
    static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));

    public static void main(String[] args) throws Exception {
        // Configure SSL.
        final SslContext sslCtx;
        if (SSL) {
            SelfSignedCertificate ssc = new SelfSignedCertificate();
            sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
        } else {
            sslCtx = null;
        }

        // Configure the server.
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        final EchoServerHandler serverHandler = new EchoServerHandler();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class)
             .option(ChannelOption.SO_BACKLOG, 100)
             .handler(new LoggingHandler(LogLevel.INFO))
             .childHandler(new ChannelInitializer() {
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception {
                     ChannelPipeline p = ch.pipeline();
                     if (sslCtx != null) {
                         p.addLast(sslCtx.newHandler(ch.alloc()));
                     }
                     //p.addLast(new LoggingHandler(LogLevel.INFO));
                     p.addLast(serverHandler);
                 }
             });

            // Start the server.
            ChannelFuture f = b.bind(PORT).sync();

            // Wait until the server socket is closed.
            f.channel().closeFuture().sync();
        } finally {
            // Shut down all event loops to terminate all threads.
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

2. ServerBootstrap先把各种配置类让你配好,配置过程也就是第一点的代码块中的22-26那一堆链式的调用,这里大概说一下这个链式调用里面干了什么,其实就是ServerBootStrap里面有一大堆属性,然后最里面做的事情其实是给ServerBootStrap这些属性赋值的操作,赋值的属性给之后进行的初始化-注册-绑定的操作做基础的。然后调用ServerBootstap的bind开始绑定服务器,然后这个bind只是调用了它实现的AbsractBootstrap中的bind类,ServerBootrap继承了AbstractBootstrap但是他又没有bind方法,所以调用的是AbstractBootstrap里面的bind方法。然后里面真正干事的是ChannelFuture doBind(final SocketAddress localAddress)方法,代码如下面的#1代码块所示。这里面首先调用了AbstractBootstrap的initAndRegister()方法。这里有很多陌生的类我这里大概说一下这些类是干什么的。(我当初学源码的时候非常迷茫,我看的源码的教程,有一些东西的作用是所有东西学完了,最后才讲,但是不符合我的思维习惯,所以我觉得自己写一个真正适合自己的笔记)

首先,initAndRegister()他会返回一个实现了ChannelFuture的类,而这里返回的类是DefaultChannelPromise,那这个类其实在学源码的过程中会碰见很多次,很多源码教程让你看到这个类的时候忽略掉他,不过我这里会首先讲一下这个东西的大概作用,把他当作api来用,然后不至于在后面看到他的各种调用的时候而感到迷茫。他是Netty封装的一个用于异步调用的类,Netty实现的NioServerSocketChannel是一个异步非阻塞的服务器,非阻塞是依赖于NIO的(不懂的可以去学一下NIO,这篇文章主要还是介绍netty)。

而Netty里面的DefaultChannelPromise的异步怎么理解呢?先讲一下对同步和异步的理解,同步就是A交给B一个任务,然后B去执行,A等B这个任务执行完了之后,A继续干别的事情。而异步的意思是A交给B一个任务,B去执行,但是A自己直接去干别的事情了,然后B干完了自己手上的事情之后,B通知A任务做完了,然后A去执行相应的参数。而这个DefaultChannelPromise就是干这个事情的,由于initAndRegister()方法在代码里面 本身是同步的,但是他里面一些任务是通过提交给一个线程池来实现的(Netty自己实现的串行无锁化的线程池,也就是说,任务都在一个队列里面,在某个地方,按照顺序把任务一个一个取出来顺序执行),而这个DefaultChannelPromise保持了对这个线程池的引用,而某些任务的某个地方通过某个api(后面会详细讲)执行相应的回调函数。而这个回调函数就是通过DefaultChannelPromise去添加的。

#1

private ChannelFuture doBind(final SocketAddress localAddress) {
    final ChannelFuture regFuture = initAndRegister();
    final Channel channel = regFuture.channel();
    if (regFuture.cause() != null) {
        return regFuture;
    }

    if (regFuture.isDone()) {
        // At this point we know that the registration was complete and successful.
        ChannelPromise promise = channel.newPromise();
        doBind0(regFuture, channel, localAddress, promise);
        return promise;
    } else {
        // Registration future is almost always fulfilled already, but just in case it's not.
        final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
        regFuture.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                Throwable cause = future.cause();
                if (cause != null) {
                    // Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an
                    // IllegalStateException once we try to access the EventLoop of the Channel.
                    promise.setFailure(cause);
                } else {
                    // Registration was successful, so set the correct executor to use.
                    // See https://github.com/netty/netty/issues/2586
                    promise.registered();

                    doBind0(regFuture, channel, localAddress, promise);
                }
            }
        });
        return promise;
    }
}

3 initAndRegister()方法里面主要流程分为3步

第一步也就是把AbstractBootstrap中的channel属性赋值,用了channelFactory.newChannel()方法,之前链式调用做设置的时候有一个属性是b.channel(NioServerSocketChannel.class),那这里面做了两件事,首先创建了一个ReflectiveChannelFactory类,调用的是一个以Class为参数的构造方法,里面进去的也就是NioServerSocketChannel.class(可以看到3.#2@1的方法),然后在创建完这个类之后,把创建完的Reflective类的对象作为参数传入channelFactory方法,然后看到3.#2@2 和 3.#2@3,也就印证了我之前说的,这组链式调用是用于给AbstractBootStrap里面的属性赋值的(服务器真正调用的是ServerBootStrap)。从名字来看,这是一个通过反射来创建类的方法的工厂,然后回到#1的第四行,有一个channelFactory.newChannel()方法,而这个channelFactory的值是什么呢,由于b.bind发生在那个链式调用的后面,链式调用到的b.channel(NioServerSocketChannel.class);就给AbstractBootstrap(由于此时的实际调用者是ServerBootstrap,所以当且仅当ServerBootstrap没有属性或者方法的时候,才会调用到AbstractBootstrap方法)中的channelFactory赋值了,那么#1第四行,的channelFacotry也就是之前设置的ReflectiveChannelFactory工厂,那就会调用到这个工厂里面的newChannel()方法,我把他贴在了3.#2@4,可以看到他以反射方式调用了clazz的构造函数来创建对象,这个clazz是什么呢,其实是之前在创建ReflectiveChannelFactory时作为构造函数传进去的NioServerSocketChannel.class,那么也就是说,这里调用的是NioServerSocketChannel的无参构造方法,那么这时候就该去到他的无参构造方法继续分析了。

而NioServerSocketChannel的无参构造方法调用了另外一个构造方法NioServerSocketChannel(SelectorProvider provider) ,

private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();

public NioServerSocketChannel() {

this(newSocket(DEFAULT_SELECTOR_PROVIDER));

}

而在这个NioServerSocketChannel中的newSocket方法调用了provider.openServerSocketChannel();

,这个就是原生NIO创建ServerSocketChannel的方法。也就是说NioServerSocketChannel是Netty自己对于原生NIO的一个封装。可以看到#3的NioServerSocketChannel也调用了他父类的构造方法然后他父类的构造方法继续调用了他父类的构造方法,总结一下,这组构造方法干了这么几件事,1.设置感兴趣的事件(这个应该是给原生NIO用的),2.把创建的NioServerSocketChannel对象封装在抽象类里面(稍微补充一下为什么这么做,因为Netty本来就不只有NIO,抽象类是把不同的服务器抽取出来的公共代码,定义了这些服务器的公共行为,这样做的好处就是,以后要扩展的时候,你只要按照他提供的接口的规范去扩展就好了,当然这个和Netty无关,这就回到Java本身应该怎么去写代码的问题上了)3.创建了Unsafe类和ChannelPipeline类,这两个类非常重要,我会在讲init的地方中断,然后开始讲这两个方法。NioServerSocketChannel中的config属性就是在这里被赋值的,config就是NioServerSocketChannelConfig类的变量,他包装了NioServerSocketChannel本身以及原生的NIO socketChannel,其实这只是给NioServerSocketChannel里面的一个属性赋值而已,但是为什么要有config这个属性而不是直接把这些东西放在,我的猜测是,这些事NioServerSocketChannel独立于抽象类的属性,然后别人用的时候可以拿到这里面的东西。

第二部就是init方法了,在创建完那个NioServerSocketChannel之后,就调用ServerBootstrap中的init(channel)来对NioServerSocketChannel对象进行初始化,代码放在4.#4@1,然后出现了几个陌生的东西,他们分别为pipeline的相关的方法(比如p.addLast 方法),eventLoop().execute()方法。要搞懂这个地方,就得想搞懂Netty里面的一些东西,也就是pipeline相关和eventLoop相关,从这里开始,如果不搞懂Netty相关的其他知识点的话,这启动流程之后的源码将寸步难行。

#1

final ChannelFuture initAndRegister() {
    Channel channel = null;
    try {
        channel = channelFactory.newChannel();
        init(channel);
    } catch (Throwable t) {
        if (channel != null) {
            channel.unsafe().closeForcibly();
            return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
        }
        return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
    }

    ChannelFuture regFuture = config().group().register(channel);
    if (regFuture.cause() != null) {
        if (channel.isRegistered()) {
            channel.close();
        } else {
            channel.unsafe().closeForcibly();
        }
    }
    return regFuture;
}

#2 

//来自AbstractBootstrap类 @1
public B channel(Class channelClass) {
    if (channelClass == null) {
        throw new NullPointerException("channelClass");
    }
    return channelFactory(new ReflectiveChannelFactory(channelClass));
}

//来自AbstractBootstrap类 @2,
public B channelFactory(io.netty.channel.ChannelFactory channelFactory) {
    return channelFactory((ChannelFactory) channelFactory);
}

//来自AbstractBootstrap类 @3
public B channelFactory(ChannelFactory channelFactory) {
    if (channelFactory == null) {
        throw new NullPointerException("channelFactory");
    }
    if (this.channelFactory != null) {
        throw new IllegalStateException("channelFactory set already");
    }
    this.channelFactory = channelFactory;
    return self();
}

//来自AbstractBootstrap类 @4
@Override
public T newChannel() {
    try {
        return clazz.getConstructor().newInstance();
    } catch (Throwable t) {
        throw new ChannelException("Unable to create Channel from class " + clazz, t);
    }
}

#3 

//来自NioServerSocketChannel @1
public NioServerSocketChannel() {
    this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}

//来自NioServerSocketChannel @2
public NioServerSocketChannel(ServerSocketChannel channel) {
    super(null, channel, SelectionKey.OP_ACCEPT);
    config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}

//来自@2的super的连续调用
//AbstractNioMessageChannel
protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
    super(parent, ch, readInterestOp);
}

//来自@2的super的连续调用
//AbstractNioChannel
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);
    }
}

//来自@2的super的连续调用
//AbstractChannel
protected AbstractChannel(Channel parent) {
    this.parent = parent;
    id = newId();
    unsafe = newUnsafe();
    pipeline = newChannelPipeline();
}

#4 

//来自ServerBootstrap @1
    @Override
    void init(Channel channel) throws Exception {
        final Map, Object> options = options0();
        synchronized (options) {
            setChannelOptions(channel, options, logger);
        }

        final Map, Object> attrs = attrs0();
        synchronized (attrs) {
            for (Entry, Object> e: attrs.entrySet()) {
                @SuppressWarnings("unchecked")
                AttributeKey key = (AttributeKey) e.getKey();
                channel.attr(key).set(e.getValue());
            }
        }

        ChannelPipeline p = channel.pipeline();

        final EventLoopGroup currentChildGroup = childGroup;
        final ChannelHandler currentChildHandler = childHandler;
        final Entry, Object>[] currentChildOptions;
        final Entry, Object>[] currentChildAttrs;
        synchronized (childOptions) {
            currentChildOptions = childOptions.entrySet().toArray(newOptionArray(0));
        }
        synchronized (childAttrs) {
            currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(0));
        }

        p.addLast(new ChannelInitializer() {
            @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));
                    }
                });
            }
        });
    } 
  

写到最后,虽然学了一遍Netty的主要流程 源码,但是我远远低估了源码解读类文章的工作量,所以Netty源码解读我打算当成一个系列来,不过我最近忙着准备面试,这个系列更新的应该不会很快。

你可能感兴趣的:(Netty,java,后端)