Netty源码分析-Netty服务端启动流程解析

前言

上一章节 中,我们讲解了java的普通的IO和NIO,总结了它们的优缺点,然后使用netty提供的api将我们之前的代码进行了改造,本章就让我们来进行netty的服务端启动流程的分析,首先还是先上一下代码实例(看本章之前最好对NIO和Netty的服务端编写有一些了解,关于这些可以去看上一章节来进行学习)

NIO服务端启动流程代码

既然netty是对java的NIO进行的封装,那么这里先列出来NIO的启动流程,这样下面方便对比

 

package chapter2;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

/**
 * @author zhtttylz
 */
public class ServerNIO {

    public static void main(String[] args) throws IOException {

        // 创建两个selector用来进行新连接的监控和旧连接发送消息的处理
        Selector serverSelector = Selector.open();
        Selector clientSelector = Selector.open();

        // 同样是创建一个线程进行连接的处理
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    // 获取一个通道
                    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
                    // 将端口8989绑定到这个通道上
                    serverSocketChannel.socket().bind(new InetSocketAddress(8989));
                    // 将这个通道注册给selector,标记这个通道的状态是1<<4 也就是16(准备就绪)
                    serverSocketChannel.register(serverSelector, SelectionKey.OP_ACCEPT);
                    // 设置为非阻塞状态
                    serverSocketChannel.configureBlocking(false);

                    // 将selector进行轮询
                    while (true) {

                        // 设置阻塞时间是1ms
                        if (serverSelector.select(1) > 0) {

                            // 获取通道中发生了变化的连接,可能是新连接,也有可能是旧的连接发送了数据
                            Set<SelectionKey> set = serverSelector.selectedKeys();
                            Iterator<SelectionKey> keyIterator = set.iterator();

                            while (keyIterator.hasNext()) {
                                SelectionKey key = keyIterator.next();

                                if (key.isAcceptable()) {
                                    try {
                                        // 如果新进来的连接是准备状态,也就是新连接进来的连接,那么就将其设置为可读取状态,将其注册到轮询器中
                                        SocketChannel newChannel = ((ServerSocketChannel) key.channel()).accept();
                                        newChannel.configureBlocking(false);
                                        // 注意:这里要将已经进来的管道注册到clientSelector中中
                                        newChannel.register(clientSelector, SelectionKey.OP_READ);
                                    } finally {
                                        // 注意:这里要将处理过的节点进行删除
                                        keyIterator.remove();
                                    }
                                }
                            }
                        }
                    }
                } catch (ClosedChannelException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    while (true) {

                        // 设置阻塞时间是1ms
                        if (clientSelector.select(1) > 0) {

                            // 获取通道中发生了变化的连接,可能是新连接,也有可能是旧的连接发送了数据
                            Set<SelectionKey> set = clientSelector.selectedKeys();
                            Iterator<SelectionKey> keyIterator = set.iterator();

                            while (keyIterator.hasNext()) {
                                SelectionKey key = keyIterator.next();

                                // 如果连接进来的通道发送了读操作
                                if(key.isReadable()){

                                    try {
                                        SocketChannel socketChannel = (SocketChannel)key.channel();
                                        // 建立一个缓冲区,进行数据的读取
                                        ByteBuffer buf = ByteBuffer.allocate(1024);
                                        socketChannel.read(buf);
                                        System.out.println(buf.toString());
                                    }finally {
                                        keyIterator.remove();
                                    }
                                }
                            }
                        }
                    }
                } catch (ClosedChannelException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

从上面可以看出来,java的远程NIO启动需要的组件有:

  • selector(多路复用器):用于进行将连接通道内变化的连接输出出来
  • ServerSocketChannel(服务端通道):用来等待客户端的连接
  • SocketChannel(客户端通道):用来与客户端进行通信

在这里我们来梳理一下NIO的服务端启动流程

关于原生的NIO就先知道这些,我们接着看Netty的服务端启动代码 

  1. 创建两个selector(多路复用器),用来监控连接通道变化,一个用于监控已经连接进来的客户端的动作
  2. 创建一个ServerSocketChannel,将其绑定到selector上面,让selector来监控这个channel中的变化
  3. 当ServerSocketChannel中有新的连接的时候,就将其拿出来,转换为一个SocketChannel,放入监控客户端动作的selector中 

Netty服务端的启动代码

package chapter2;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

/**
 * @author zhtttylz
 */

public class NettyServer {

    public static void main(String[] args) {

        // 创建两个线程组,分别用来对应传统NIO中的serverSelector和clientSelector
        NioEventLoopGroup boss = new NioEventLoopGroup();
        NioEventLoopGroup worker = new NioEventLoopGroup();

        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(boss, worker)
                    // 着这里绑定一个NIOSocketChannel,用于处理新连接进来的客户端的通道,类似于NIO中的ServerSocketChannel
                    .channel(NioServerSocketChannel.class)
                    // 在这里添加我们自定义的拦截器,这里为了方便观看,我直接使用内部类进行书写
                    .childHandler(new ChannelInitializer<NioSocketChannel>() {
                        protected void initChannel(NioSocketChannel ch) {

                            // 在这里绑定编解码器
                            ch.pipeline().addLast(new StringDecoder());
                            ch.pipeline().addLast(new StringEncoder());

                            // 在这里添加我们的服务端逻辑,为了方便观看,同样使用的内部类
                            ch.pipeline().addLast(new SimpleChannelInboundHandler<String>() {
                                @Override
                                protected void channelRead0(ChannelHandlerContext ctx, String msg) {
                                    System.out.println(msg);
                                }
                            });
                        }
                    });

            // 绑定服务端的端口
            ChannelFuture f = b.bind(8989).sync();

            // 等待服务端监听端口关闭
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {

            // 优雅的退出,释放线程资源
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }
}

 可以说如果你看了上面的NIO的代码,会发现Netty的服务端启动类中有很多名字相同的组件,比如说NioServerSocketChannel,还有创建了一个boss和一个worker,很类似于我们之前创建的serverSelector和clientSelector等等....,接下来就让我们从boss和worker的创建开始

小结

 在这里根据NIO和netty的启动流程对比提出几个疑问:

  1. netty创建的ServerBootstrap 的作用是什么
  2. netty如何设置并创建并启动reactor线程的
  3. netty是如何设置并绑定服务端 Channel的
  4. netty是如何创建并初始化 ChannelPipeline(本节暂时不进行讲解,会有单独的章节进行讲解)
  5. netty是如何添加并设置 ChannelHandler的  (本节暂时不进行讲解,会有单独的章节进行讲解)
  6. netty是如何绑定并启动监听端口 (本节暂时不进行讲解,会有单独的章节进行讲解)

带着这两个疑问我们开始着手netty的服务端代码

netty创建的ServerBootstrap 的作用是什么(每段代码下面的文字是这段代码的说明)

NioEventLoopGroup boss = new NioEventLoopGroup();
NioEventLoopGroup worker = new NioEventLoopGroup();

 bossGroup 为 boss 线程组,用于服务端接受客户端的连接, workerGroup 为 worker 线程组,用于进行 SocketChannel 的网络读写。当然也可以创建一个并共享,接下来我们进入他们内部查看他们的内部实现

public class NioEventLoopGroup extends MultithreadEventLoopGroup {

    /**
     * Create a new instance using the default number of threads, the default {@link ThreadFactory} and
     * the {@link SelectorProvider} which is returned by {@link SelectorProvider#provider()}.
     */
    public NioEventLoopGroup() {
        this(0);
    }

    /**
     * Create a new instance using the specified number of threads, {@link ThreadFactory} and the
     * {@link SelectorProvider} which is returned by {@link SelectorProvider#provider()}.
     */
    public NioEventLoopGroup(int nThreads) {
        this(nThreads, (Executor) null);
    }

NioEventLoopGroup:顾名思义,就是NioEventLoop组,包含了若干个NioEventLoop,关于NioEventLoop是什么,我会开一个单独的章节进行详细说明,在这里只需要知道NioEventLoop是netty中的reactor线程就可以了

protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
        super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
    }

最终调用了父类的构造方法,注意:如果nThreads,也就是 reactor线程数为0,说明没有进行默认设置,这时,netty就会给它一个默认值

protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                            EventExecutorChooserFactory chooserFactory, Object... args) {
        if (nThreads <= 0) {
            throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
        }

        if (executor == null) {
            executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
        }

        // 这里维护了一个EventExecutor类型的数组,其大小是我们从NioEventLoopGroup通过构造传进来的
        children = new EventExecutor[nThreads];

        for (int i = 0; i < nThreads; i ++) {
            boolean success = false;
            try {
                // 注意,在这里将NioEventLoop注入了当前的reactor管理组
                children[i] = newChild(executor, args);

 可以看到,在NioEventLoopGroup的父类MultithreadEventExecutorGroup中维护了一个reactor线程组,长度为传进去的nThrads的大小,注意:EventExecutor是NioEventLoop的父类,然后循环将NioEventLoop注入到数组中

小结

到这里为止,我们知道了NioEventLoopGroup的作用:NioEventLoopGroup的父类MultithreadEventExecutorGroup维护了一个数组,用于存放netty中的reactor线程,方便接下来每次来一个新的连接,方便进行工作线程的调度

netty如何设置并创建并启动reactor线程的(每段代码下面的文字是这段代码的说明)

@Override
    protected EventLoop newChild(Executor executor, Object... args) throws Exception {
        return new NioEventLoop(this, executor, (SelectorProvider) args[0],
            ((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
    }

 创建reactor线程: 通过点击上面的newChild()方法,进入到了NioEventLoopGroup中的newChild方法,在这里可以清楚的看到,new了一个NioEventLoop,并设置了很多的参数放入其构造函数中进行初始化,也就是在这里进行的reactor线程的创建

启动reactor线程: 

// 绑定服务端的端口
            ChannelFuture f = b.bind(8989).sync();

首先让我们进入启动类中的bind方法,这个方法是启动辅助类ServerBootstrap的一个方法

// 通过内部的bing方法进行调用进入的这个方法
    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;

上面的代码是通过bind的方法的调用而执行的,这里能够看到initAndRegister()方法返回了一个future,通过这个future获取了一个channel,之后在判断如果future确定将channel返回了,就执行doBind0方法,将端口和channel传入,这里很想我们写的原生的jdkapi中的给serverSocketChannel绑定端口,这里我再把原生jdk的代码贴出来

// 将端口8989绑定到这个通道上
                    serverSocketChannel.socket().bind(new InetSocketAddress(8989));

上面是原生的jdk绑定代码,接下来让我们到initAndRegister()中观看netty的处理

注: 这里是一个小分支,主要是分析绑定到Boss NioEventLoop上面的NioServerSocketChannel的创建,如果知道了可以跳过

final ChannelFuture initAndRegister() {
        Channel channel = null;

        try {
            channel = this.channelFactory.newChannel();
            this.init(channel);
        } catch (Throwable var3) {
            if (channel != null) {
                channel.unsafe().closeForcibly();
                return (new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE)).setFailure(var3);
            }

            return (new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE)).setFailure(var3);
        }

        ChannelFuture regFuture = this.config().group().register(channel);

我将以上的步骤拆分一下,一个是通过channel工厂创建一个channel,然后调用register将这个channel进行注册,接下来让我们来具体的分析一下,首先是newChannel()方法

public T newChannel() {
        try {
            return (Channel)this.clazz.getConstructor().newInstance();
        } catch (Throwable var2) {
            throw new ChannelException("Unable to create Channel from class " + this.clazz, var2);
        }
    }

发现经过调用最终到达了ReflectiveChannelFactory的newChannel方法,在这里可以看出来是通过反射来新建一个实例

 在这里编译器提示我们这个class对象是NioServerSocketChannel的类对象(.class对象),也就是上面的newInstance()方法创建的是一个NioServerSocket对象,那么这个NioServerSokcet又是从哪里传进来的呢,让我们回到启动类中

 b.group(boss, worker)
                    // 着这里绑定一个NIOSocketChannel,用于处理新连接进来的客户端的通道,类似于NIO中的ServerSocketChannel
                    .channel(NioServerSocketChannel.class)

在这里我们给启动辅助类传入了一个NioServerSocketChannel的类对象,让我们进去看一下

public B channel(Class<? extends C> channelClass) {
        if (channelClass == null) {
            throw new NullPointerException("channelClass");
        } else {
            return this.channelFactory((io.netty.channel.ChannelFactory)(new ReflectiveChannelFactory(channelClass)));
        }
    }

在这里我们将NioServerSocketChannel传入reflectiveChannelFactory的构造函数中,然后在下面进行reflectiveChannelFactory的实例化

public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {
    private final Class<? extends T> clazz;

    public ReflectiveChannelFactory(Class<? extends T> clazz) {
        if (clazz == null) {
            throw new NullPointerException("clazz");
        } else {
            this.clazz = clazz;
        }
    }

然后将构造函数中传入的NioServerSocketChannel赋值给reflectiveChannelFactory中的clazz引用,所以这里的clazz就是我们传入进来的NioServerSocketChannel对象,然后我们进入NioServerSocketChannel看看它的构造方法

    private static java.nio.channels.ServerSocketChannel newSocket(SelectorProvider provider) {
        try {
            return provider.openServerSocketChannel();
        } catch (IOException var2) {
            throw new ChannelException("Failed to open a server socket.", var2);
        }
    }

    public NioServerSocketChannel() {
        this(newSocket(DEFAULT_SELECTOR_PROVIDER));
    }

最后发现是调用原生jdk的方法进行的channel的创建

NioServerSocketChannel的创建小结

 到这里我们分析了NioServerSocketChannel的创建,比如说是由工厂类reflectiveChannelFactory进行创建,也知道了创建的方式是调用原生的jdk进行创建,到此为止,initAndRegister()方法分析完成,(貌似有点偏了,应该说的明明是reactor线程启动 汗~)

channel绑定到NioEventLoop(reactor)上

 分析完了netty是如何创建NioServerSocketChannel的,下面在看一下Netty是如何将这个Channel绑定到NioEventLoop上面的

final ChannelFuture initAndRegister() {
        Channel channel = null;

        try {
            channel = this.channelFactory.newChannel();
            this.init(channel);
        } catch (Throwable var3) {
            if (channel != null) {
                channel.unsafe().closeForcibly();
                return (new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE)).setFailure(var3);
            }

            return (new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE)).setFailure(var3);
        }
        
        // 着重看这里,将刚刚创建的channel传入了,返回一个future对象
        ChannelFuture regFuture = this.config().group().register(channel);
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly();
            }
        }

        return regFuture;
    }

发现在最后调用了一个register(channel)方法,并且将新创建的channel传入(就是上面的代码里面我写了注释的部分),让我们进入一下看看

// 这个方法返回了一个reactor管理组,也就是一个NioEventLoop组
public final EventLoopGroup group() {
        return this.bootstrap.group();
    }

调用group获取了一个NioEventLoop管理组,这个管理组就是我们传入的boss管理组,然后调用上面的register方法

// 这个是MultithreadEventExecutorGroup类中的方法
public EventExecutor next() {
        return this.chooser.next();
    }


// 这个是DefaultEventExecutorChooserFactory的内部类GenericEventExecutorChooser类中的方法
public EventExecutor next() {
            return this.executors[Math.abs(this.idx.getAndIncrement() % this.executors.length)];
        }

上面的两个方法来自两个不同的类(已经有注释说明),可以看出最后是获取了一个EventExecutor类型的对象,也就是NioEventLoop对象(NioEventLoop实现了EventExecutor),至于里面的获取方式大概分为几步

  1. 使用idx.getAndIncrement()将idx的值++,这个方法是原子操作,可以保证数据一致性
  2. 使用idx % Executors(NioEventLoop管理组)的长度,目的是按顺序从executor循环中取值,举个例子(当idx等于executors长度的n倍,idx % executors,length为0,idx++,然后下一次相 % 得到的就是1,依次列推)
  3. 最后返回NioEventLoop
// NioEventLoop的父类SingleThreadEventLoop
public ChannelFuture register(Channel channel) {
        return this.register((ChannelPromise)(new DefaultChannelPromise(channel, this)));
    }

通过next()方法获取到了NioEventLoop,然后调用其父类SingleThreadEventLoop里面的register方法,这个方法是new了一个DefaultChannelPromise对象,然后把NioEventLoop和channel传进去,并且强制转换成了ChannelPromise,然我们进入DefaultChannelPromise类看一下

// 这个方法是DefaultChannelPromise的构造方法,主要就是校验了一下channel,然后把executor(NioEventLoop)传给父类构造
public DefaultChannelPromise(Channel channel, EventExecutor executor) {
        super(executor);
        this.channel = (Channel)ObjectUtil.checkNotNull(channel, "channel");
    }

发现只是校验了一下channel然后进行记录,将NioEventLoop向上传递

// 这个方法是AbstractChannel内部类AbstractUnsafe内部的
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
            if (eventLoop == null) {
                throw new NullPointerException("eventLoop");
            } else if (AbstractChannel.this.isRegistered()) {
                promise.setFailure(new IllegalStateException("registered to an event loop already"));
            } else if (!AbstractChannel.this.isCompatible(eventLoop)) {
                promise.setFailure(new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
            } else {
                // 在这里将选定出来装载NioServerSocketChannel的NioEventLoop注入当前类中的属性里面
                AbstractChannel.this.eventLoop = eventLoop;
                if (eventLoop.inEventLoop()) {
                    this.register0(promise);
                } else {
                    try {
                        eventLoop.execute(new Runnable() {
                            public void run() {
                                AbstractUnsafe.this.register0(promise);
                            }
                        });
                    } catch (Throwable var4) {
                        AbstractChannel.logger.warn("Force-closing a channel whose registration task was not accepted by an event loop: {}", AbstractChannel.this, var4);
                        this.closeForcibly();
                        AbstractChannel.this.closeFuture.setClosed();
                        this.safeSetFailure(promise, var4);
                    }
                }

            }
        }

register()的重载方法指向了AbstractUnsafe内部的register方法,传入的两个参数是NioEventLoop和包装过的channel,注意代码中我加了注释的部分,将传入的NioEventLoop赋值给了全局变量eventLoop,然后调用了register0这个方法,传入了我们封装的DefaultChannelPromise对象(内部封装了channel和NioEventLoop),我们接着进入到register0()这个方法看一下

// 经过内部重载调用
private void register0(ChannelPromise promise) {
            try {
                if (!promise.setUncancellable() || !this.ensureOpen(promise)) {
                    return;
                }

                boolean firstRegistration = this.neverRegistered;

                // 重点是这里,调用了doRegister方法
                AbstractChannel.this.doRegister();
                this.neverRegistered = false;
                AbstractChannel.this.registered = true;
                AbstractChannel.this.pipeline.invokeHandlerAddedIfNeeded();
                this.safeSetSuccess(promise);
                AbstractChannel.this.pipeline.fireChannelRegistered();
                if (AbstractChannel.this.isActive()) {
                    if (firstRegistration) {
                        AbstractChannel.this.pipeline.fireChannelActive();
                    } else if (AbstractChannel.this.config().isAutoRead()) {
                        this.beginRead();
                    }
                }
            } catch (Throwable var3) {
                this.closeForcibly();
                AbstractChannel.this.closeFuture.setClosed();
                this.safeSetFailure(promise, var3);
            }

        }

注意看我标注了注释的地方,调用了doRegister()方法,接着向下跟踪

这个方法有很多的重写,因为我们使用的是NioServerSocketChannel,所以就可以锁定其父类就是我选中的,至于Epoll的有兴趣的可以查一下具体的使用

// AbstractNioChannel类中的方法
protected void doRegister() throws Exception {
        boolean selected = false;

        while(true) {
            try {
                // 这里可以看出是调用原生的jdk进行绑定
                this.selectionKey = this.javaChannel().register(this.eventLoop().unwrappedSelector(), 0, this);
                return;
            } catch (CancelledKeyException var3) {
                if (selected) {
                    throw var3;
                }

                this.eventLoop().selectNow();
                selected = true;
            }
        }
    }

 到这里就可以看出,是使用的jdk原生方法进行的注册,this.javaChannel()获取的也是jdk的serverSockerChannel,到这里channel的绑定已经完成

channel的绑定的小结

 其实netty的绑定并不是很难,经历了一些列过程,除了将一些引用进行了赋值之外,最后还是调用了原生的jdk方法,可以说跟netty索要体现出来的主题一样,对nio进行封装

 

NioEventLoop的启动(这里可能有些简略,会在将NioEventLoop的章节详细说明)

接下来让我们进入到主类中的bind()方法中

private ChannelFuture doBind(final SocketAddress localAddress) {
        // 初始化channel
        final ChannelFuture regFuture = this.initAndRegister();
        // 获取channel
        final Channel channel = regFuture.channel();
        if (regFuture.cause() != null) {
            return regFuture;
        } else if (regFuture.isDone()) {
            // 在这里将channel进行绑定,绑定的条件是regFutrue是否执行完成了
            ChannelPromise promise = channel.newPromise();
            doBind0(regFuture, channel, localAddress, promise);
            return promise;
        } else {
            final AbstractBootstrap.PendingRegistrationPromise promise = new AbstractBootstrap.PendingRegistrationPromise(channel);
            regFuture.addListener(new ChannelFutureListener() {
                public void operationComplete(ChannelFuture future) throws Exception {
                    Throwable cause = future.cause();
                    if (cause != null) {
                        promise.setFailure(cause);
                    } else {
                        promise.registered();
                        AbstractBootstrap.doBind0(regFuture, channel, localAddress, promise);
                    }

                }
            });
            return promise;
        }
    }

 上面的代码可以看出,这里是调用了doBind0()方法,而调用的依据就是future,idDone()方法是否执行完成了

private static void doBind0(final ChannelFuture regFuture, final Channel channel, final SocketAddress localAddress, final ChannelPromise promise) {
        channel.eventLoop().execute(new Runnable() {
            public void run() {
                if (regFuture.isSuccess()) {
                    channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
                } else {
                    promise.setFailure(regFuture.cause());
                }

            }
        });
    }

进入到doBind0()方法,可以看到这里是从channel中取出来了一个eventLoop,然后执行这个eventLoop的execute方法,上面已经分析过了,channel初始化的时候就绑定了NIoEventLoop,所以这里就是取出来了这个channel绑定的channel(这个NioEventLoop是从channel的父类AbstractChannel中拿出来的,具体的注入上面的channel绑定有详细介绍)

// 在NioEventLoop的父类SingleThreadEventExecutor中的方法
public void execute(Runnable task) {
        if (task == null) {
            throw new NullPointerException("task");
        } else {
            // 当前线程是否是channel绑定的reactor线程
            boolean inEventLoop = this.inEventLoop();
            // 将当前的task添加进任务队列
            this.addTask(task);
            if (!inEventLoop) {
                // 开启线程
                this.startThread();
                if (this.isShutdown() && this.removeTask(task)) {
                    reject();
                }
            }

            if (!this.addTaskWakesUp && this.wakesUpForTask(task)) {
                
                // 唤醒selector开始轮询
                this.wakeup(inEventLoop);
            }

        }
    }

可以看出,execute中,先判断当前线程是不是reactor线程,如果不是的话,说明reactor线程还没有启动,将reactor启动,在最后调用wakeup方法进行selector轮询(具体的后面会单独在reactor运行的章节进行讲解)

总结

至此netty的启动流程大概就已经讲完了,本篇从创建NioEventLoop,创建NioServerSocketChannel,将channel进行绑定,开启NioEventLoop轮询这几方面进行总结,通过分析netty各个组件的创建和启动过程,发现nettty最终也是使用的原生jdk的api,也正如netty的本质一样,是对NIO的封装

 

第二章的代码地址 : https://github.com/zhtttylz/Netty_code_demo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值