netty底层源码探究:启动流程;EventLoop中的selector、线程、任务队列;监听处理accept、read事件流程;

探究源码

启动流程

nio启动流程

因为netty的底层使用的是nio,所以先回忆一下nio的启动流程对于接下来要探究的netty启动流程也是有好处的。

  1. 创建一个选择器,监听多个channel发生的各类事件

    Selector selector = Selector.open();
    
  2. 创建一个ServerSocketChannel,并且设置非阻塞

    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    serverSocketChannel.configureBlocking(false);
    
  3. 将serverSocketChannel注册进选择器中

    SelectionKey selectionKey = serverSocketChannel.register(selector, 0, null);
    

    这是jdk原生的ServerSocketChannel,将来如果selector发生了事件,会将这个事件交给Nio相应的类去处理,这里就使用到了attachment附件,通过附件将serverSocketChannel与NioServerSocketChannel进行绑定。

    NioServerSocketChannel nioServerSocketChannel = new NioServerSocketChannel();
    SelectionKey selectionKey = serverSocketChannel.register(selector, 0, attachment);
    
  4. 绑定端口

    serverSocketChannel.bid(new InetSocketAddress(8080));
    
  5. 在selectionKey上注册一个它关心的事件类型

    selectionKey.interestOps(SelectionKey.OP_ACCEPT);
    

概述

上面nio的五个步骤是如何在netty中实现的?

下方代码是使用netty创建一个服务器的基本步骤

new ServerBootstrap()
        .group(new NioEventLoopGroup())
        .channel(NioServerSocketChannel.class)
        .childHandler(new ChannelInitializer<NioSocketChannel>() {
   
            @Override
            protected void initChannel(NioSocketChannel socketChannel) throws Exception {
   
                socketChannel.pipeline().addLast(new StringDecoder());
            }
        })
        .bind(8080);

我们知道EventLoop包含了一个Selector和一个单线程执行器 ,也就是说.group(new NioEventLoopGroup()) 这行语句可以看为是完成Nio的第一步创建一个选择器的。

Nio剩下的四个步骤其实都是在.bind(8080); 这行语句完成的,然后我们点进bind()方法,接着会进入到第一个比较重要的方法doBind

private ChannelFuture doBind(final SocketAddress localAddress) {
   
    // initAndRegister()方法 所做的事情就是初始化和注册,相当于上面Nio的第二步和第三步
    // 它会将ServerSocketChannel创建好后注册进Selector中。该方法返回一个Future对象,就说明该方法是异步的,
    final ChannelFuture regFuture = this.initAndRegister();
    final Channel channel = regFuture.channel();
    if (regFuture.cause() != null) {
   
        return regFuture;
       // 这里就会利用future对象调用isDone()进行判断,如果上面initAndRegister()方法干的活比较快,就会执行if语句,
       // 但是一般情况下initAndRegister()方法中的nio线程将ServerSocketChannel和Selector进行绑定会比较慢 会执行else语句
    } else if (regFuture.isDone()) {
   
        ChannelPromise promise = channel.newPromise();
        // doBind0()方法是相当于Nio的第四步 绑定端口,监听事件
        doBind0(regFuture, channel, localAddress, promise);
        return promise;
    } else {
   
        final AbstractBootstrap.PendingRegistrationPromise promise = new 			                                                                              AbstractBootstrap.PendingRegistrationPromise(channel);
        // 从这里可以看出future对象采用了异步的方式执行下面的语句,下方的doBind0()方法也就不是主线程调用了
        regFuture.addListener(new ChannelFutureListener() {
   
                public void operationComplete(ChannelFuture future) throws Exception {
   
                    Throwable cause = future.cause();
                    if (cause != null) {
   
                        promise.setFailure(cause);
                    } else {
   
                        promise.registered();
                        // 进入到else语句后会在这里执行doBind0()方法
                        AbstractBootstrap.doBind0(regFuture, channel, localAddress, promise);
                    }

                }
            });
        return promise;
    }
}

在正式开始之前需要了解ServerBootstrap.bind(8080);是主线程调用的,然后进入到doBind()方法 在进入到initAndRegister()方法中,直到创建ServerSocketChannel都是主线程做的事,包括register的前一部分都是主线程,但是在Register中会启动Nio线程,后续的操作就不是在主线程中执行了,ServerSocketChannel注册进Selector中都是Nio线程做的事,如下图所示:

在这里插入图片描述

概述需要了解的就几件事

  • init是创建ServerSocketChannel
  • Register是将ServerSocketChannel注册进Selector中的,是nio线程执行的
  • initAndRegister()会返回一个future对象,然后使用该对象进行if判断,一般情况下都会进入到else语句
  • else语句中会利用future的异步方式,通过nio线程来执行doBind0()方法

init

接下来详细了解initAndRegister()方法中的init部分。

final ChannelFuture initAndRegister() {
   
    Channel channel = null;

    try {
   
        // 这行就是创建一个channel,创建的就是NioServerSocketChannel。
        // 再点进newChannel()方法就会发现里面是利用了反射调用无参构造方法获取的对象  constructor.newInstance()
        // 这里不仅仅会创建NioServerSocketChannel。还会创建jdk的ServerSocketChannel
        channel = this.channelFactory.newChannel();
        // 创建NioServerSocketChannel对象后就调用了init()方法,具体方法如下方代码所示
        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);
    }

    // 上面的init部分  从这里开始就是register部分了
    ChannelFuture regFuture = this.config().group().register(channel);
    if (regFuture.cause() != null) {
   
        if (channel.isRegistered()) {
   
            channel.close();
        } else {
   
            channel.unsafe().closeForcibly();
        }
    }

    return regFuture;
}

创建NioServerSocketChannel对象后就调用了init()方法

void init(Channel channel) throws Exception {
   
    。。。
    ChannelPipeline p = channel.pipeline();
   。。。
       // 这里就会发现创建NioServerSocketChannel后会往该channel的pipeline中添加一个Handler
       // 这个ChannelHandler和其他hander不同的地方在于该handler的initChannel()方法只会执行一次
       // 这里执行往pipeline中添加handler哦 还没有到执行的地步哦
    p.addLast(new ChannelHandler[]{
   new ChannelInitializer<Channel>() {
   
        public void initChannel(final Channel ch) throws Exception {
   
            final ChannelPipeline pipeline = ch.pipeline();
            ChannelHandler handler = ServerBootstrap.this.config.handler();
            if (handler != null) {
   
                pipeline.addLast(new ChannelHandler[]{
   handler});
            }

            ch.eventLoop().execute(new Runnable() {
   
                public void run() {
   
                    pipeline.addLast(new ChannelHandler[]{
   new ServerBootstrap.ServerBootstrapAcceptor(ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs)});
                }
            });
        }
    }});
}

所以initAndRegister()方法中的init部分的作用就是

  • 创建了一个NioServerSocketChannel,
  • 并往该channel的pipeline中添加了一个Handler。

Register

接下来就轮到了Register部分

final ChannelFuture initAndRegister() {
   
    Channel channel = null;
    // init
    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);
    }

// 上面的init部分  从这里开始就是register部分了
ChannelFuture regFuture = this.config().group().register(channel);
if (regFuture.cause() != null) {
   
    if (channel.isRegistered()) {
   
        channel.close();
    } else {
   
        channel.unsafe().closeForcibly();
    }
}

return regFuture;

首先是register部分的第一行代码ChannelFuture regFuture = this.config().group().register(channel);该方法返回的是有个ChannelFuture对象,那么我们笃定该方法是异步的。然后我们点进该方法,多点几次就会进入到核心

经过的类如下图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8bC8ZvbT-1628604267435)(E:\Java笔记\picture\image-20210806125339453.png)]

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 {
   
        AbstractChannel.this.eventLoop = eventLoop;
        // 到目前为止都是主线程在执行,下面这个if就是判断当前线程是否是nio线程,所以自然而然就进入到else语句中
        if (eventLoop.inEventLoop()) {
   
            this.register0(promise);
        } else {
   
            // 这里做的事情就是将正在做事的register0(promise)方法封装到了任务对象中,然后让eventLoop线程去执行
            try {
   
                // 这里第一次调用execute()方法会创建EventLoop线程,然后取执行run()方法,而不是早就创建好线程直接用。
                eventLoop.execute(new Runnable() {
   
                    public void run() {
   
                        AbstractUnsafe.this.
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值