博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Netty源码分析第1章(Netty启动流程)---->第4节: 注册多路复用
阅读量:4336 次
发布时间:2019-06-07

本文共 6570 字,大约阅读时间需要 21 分钟。

 

Netty源码分析第一章:Netty启动流程

 

第四节:注册多路复用

 

回顾下以上的小节, 我们知道了channel的的创建和初始化过程, 那么channel是如何注册到selector中的呢?我们继续分析

回到上一小节的代码:

final ChannelFuture initAndRegister() {    Channel channel = null;    try {        //创建channel        channel = channelFactory.newChannel();        //初始化channel        init(channel);    } catch (Throwable t) {        //忽略非关键代码    }    //注册channel    ChannelFuture regFuture = config().group().register(channel);    //忽略非关键代码    return regFuture;}

我们讲完创建channel和初始化channel的关键步骤, 我们继续跟注册channel的步骤:

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

其中, 重点关注下register(channel)这个方法, 这个方法最终会调用到AbstractChannel中内部类AbstractUnsaferegister()方法, 具体如何调用到这个方法, 可以简单带大家捋一下

首先看下config()方法, 由于是ServerBootstrap调用的, 所以我们跟进去:

public final ServerBootstrapConfig config() {    return config;}

返回的configServerBootrap的成员变量config:

private final ServerBootstrapConfig config = new ServerBootstrapConfig(this);

 

 

跟到ServerBootstrapConfig的构造方法:

ServerBootstrapConfig(ServerBootstrap bootstrap) {    super(bootstrap);}

继续跟到其父类AbstractBootstrapConfig的构造方法:

protected AbstractBootstrapConfig(B bootstrap) {    this.bootstrap = ObjectUtil.checkNotNull(bootstrap, "bootstrap");}

我们发现我们创建的ServerBootstrap作为参数初始化了其成员变量bootstrap

 

回到initAndRegister()方法:

config()返回的是ServerBootstrapConfig对象

再继续跟到group()方法:

public final EventLoopGroup group() {    return bootstrap.group();}

这里调用Bootstrapgroup()方法:

public final EventLoopGroup group() {    return group;}

这里返回了AbstractBootstrap的成员变量group, 我们回顾下第一小节, 还记得AbstractBootstrapgroup(EventLoopGroup group)方法吗?

public B group(EventLoopGroup group) {    this.group = group;    return (B) this;}

group(EventLoopGroup group)方法初始化了我们boss线程, group()返回了boss线程, 也就是说 config().group().register(channel) 中的register()方法是boss线程对象调用的, 由于我们当初初始化的是NioEventLoopGroup, 因此走的是NioEventLoopGroup的父类的MultithreadEventLoopGroupregister()方法

跟到MultithreadEventLoopGroup的register()方法:

public ChannelFuture register(Channel channel) {    return next().register(channel);}

这里的代码看起来有点晕, 没关系, 以后会讲到, 现在可以大概做个了解, NioEventLoopGroup是个线程组, next()方法就是从线程组中选出一个线程, 也就是NioEventLoop线程, 所以这里的next()方法返回的是NioEventLoop对象, 其中register(channel)最终会调用NioEventLoop的父类SingleThreadEventLoopregister(channel)方法

跟到SingleThreadEventLoop的register(channel)方法:

public ChannelFuture register(Channel channel) {    return register(new DefaultChannelPromise(channel, this));}

其中DefaultChannelPromise类我们之后也会讲到

 

我们先跟到register(new DefaultChannelPromise(channel, this)):

public ChannelFuture register(final ChannelPromise promise) {    ObjectUtil.checkNotNull(promise, "promise");    promise.channel().unsafe().register(this, promise);    return promise;}

channel()会返回我们初始化的NioServerSocketChannel, unsafe()会返回我们创建channel的时候初始化的unsafe对象

跟进去看AbstractChannelunsafe()的实现:

public Unsafe unsafe() {    return unsafe;}

这里返回的unsafe, 就是我们初始化channel创建的unsafe

回顾下第二小节channel初始化的步骤:

protected AbstractChannel(Channel parent) {    this.parent = parent;    id = newId();    unsafe = newUnsafe();    pipeline = newChannelPipeline();}

我们看unsafe的初始化:unsafe=newUnsafe()

 

跟到newUnsafe(), 我们之前讲过NioServerSokectChannel的父类是AbstractNioMessageChannel, 所以会调用到到AbstractNioMessageChannel类中的newUnsafe()

跟到AbstractNioMessageChannel类中的newUnsafe():

protected AbstractNioUnsafe newUnsafe() {    return new NioMessageUnsafe();}

我们看到这里创建了NioMessageUnsafe()对象, 所以在 promise.channel().unsafe().register(this, promise) 代码中, unsafe()是返回的NioMessageUnsafe()对象, 最后调用其父类AbstractUnsafe(也就是AbstractChannel的内部类)register()方法,

 

简单介绍下unsafe接口, unsafe顾名思义就是不安全的, 因为很多对channelio方法都定义在unsafe, 所以netty将其作为内部类进行封装, 防止被外部直接调用, unsafe接口是Channel接口的内部接口, unsafe的子类也分别封装在Channel的子类中, 比如我们现在剖析的register()方法, 就是封装在AbstractChannel类的内部类AbstractUnsafe中的方法, 有关UnsafeChannel的继承关系如下:

1-4-1

以上内容如果不明白没有关系, 有关NioEventLoop相关会在后面的章节讲到, 目前我们只是了解是如何走到AbstractUnsafe类的register()即可

 

我们继续看看register()方法:

public final void register(EventLoop eventLoop, final ChannelPromise promise) {    //代码省略    //所有的复制操作, 都交给eventLoop处理(1)    AbstractChannel.this.eventLoop = eventLoop;    if (eventLoop.inEventLoop()) {        register0(promise);    } else {        try {            eventLoop.execute(new Runnable() {                @Override                public void run() {                    //做实际主注册(2)                    register0(promise);                }            });        } catch (Throwable t) {            //代码省略        }    }}

我们跟着注释的步骤继续走, 第一步, 绑定eventLoop线程:

AbstractChannel.this.eventLoop = eventLoop;

eventLoopAbstractChannel的成员变量, 有关eventLoop, 我们会在绪章节讲到, 这里我们只需要知道, 每个channel绑定唯一的eventLoop线程, eventLoop线程和channel的绑定关系就是在这里展现的

 

再看第二步, 做实际注册:

我们先看if判断, if(eventLoop.inEventLoop())

 

这里是判断是不是eventLoop线程, 显然我们现在是main()方法所在的线程, 所以走的else, eventLoop.execute()是开启一个eventLoop线程, register0(promise)就是再开启线程之后, 通过eventLoop线程执行的, 这里大家暂时作为了解

 

我们重点关注register0(promise), 跟进去:

private void register0(ChannelPromise promise) {    try {        //做实际的注册(1)        doRegister();        neverRegistered = false;        registered = true;        //触发事件(2)        pipeline.invokeHandlerAddedIfNeeded();        safeSetSuccess(promise);        //触发注册成功事件(3)        pipeline.fireChannelRegistered();        if (isActive()) {            if (firstRegistration) {                //传播active事件(4)                pipeline.fireChannelActive();            } else if (config().isAutoRead()) {                beginRead();            }        }    } catch (Throwable t) {        //省略代码    }}

我们重点关注doRegister()这个方法

 

doRegister()最终会调用AbstractNioChanneldoRegister()方法:

protected void doRegister() throws Exception {    boolean selected = false;    for (;;) {        try {            //jdk底层的注册方法            //第一个参数为selector, 第二个参数表示不关心任何事件            selectionKey = javaChannel().register(eventLoop().selector, 0, this);            return;        } catch (CancelledKeyException e) {            //省略代码        }    }}

我们终于看到和java底层相关的方法了

跟到javaChannel()的方法中:

protected SelectableChannel javaChannel() {    return ch;}

这个ch, 就是本章第二小节创建NioServerSocketChannel中初始化的jdk底层ServerSocketChannel

这里register(eventLoop().selector, 0, this)方法中eventLoop().selector, 是获得每一个eventLoop绑定的唯一的selector, 0代表这次只是注册, 并不监听任何事件, this是代表将自身(NioEventLoopChannel)作为属性绑定在返回的selectionKey当中, 这个selectionKey就是与每个channel绑定的jdk底层的SelectionKey对象, 熟悉nio的小伙伴应该不会陌生, 这里不再赘述

 

回到register0(ChannelPromise promise)方法, 我们看后续步骤:

步骤(2)是触发handler的需要添加事件, 事件传递的内容我们将在后续课程详细介绍, 这里不必深究

步骤(3)是触发注册成功事件(3), 同上

步骤(4)是传播active事件(4), 这里简单强调一下, 这里的方法pipeline.fireChannelActive()第一个注册是执行不到的, 因为isActive()会返回false, 因为链路没完成

本小节梳理了有注册多路复用的相关逻辑, 同学们可以跟着代码自己走一遍以加深印象

 

 

posted on
2018-12-31 18:11 阅读(
...) 评论(
...)

转载于:https://www.cnblogs.com/xiangnan6122/p/10202849.html

你可能感兴趣的文章
阶段3 3.SpringMVC·_07.SSM整合案例_08.ssm整合之Spring整合MyBatis框架
查看>>
小D课堂 - 零基础入门SpringBoot2.X到实战_第2节 SpringBoot接口Http协议开发实战_9、SpringBoot基础HTTP其他提交方法请求实战...
查看>>
小D课堂 - 零基础入门SpringBoot2.X到实战_第2节 SpringBoot接口Http协议开发实战_12、SpringBoot2.x文件上传实战...
查看>>
小D课堂 - 零基础入门SpringBoot2.X到实战_第4节 Springboot2.0单元测试进阶实战和自定义异常处理_19、SpringBoot个性化启动banner设置debug日志...
查看>>
小D课堂 - 零基础入门SpringBoot2.X到实战_第4节 Springboot2.0单元测试进阶实战和自定义异常处理_20、SpringBoot2.x配置全局异常实战...
查看>>
小D课堂 - 零基础入门SpringBoot2.X到实战_第5节 SpringBoot部署war项目到tomcat9和启动原理讲解_23、SpringBoot2.x启动原理概述...
查看>>
小D课堂 - 零基础入门SpringBoot2.X到实战_第4节 Springboot2.0单元测试进阶实战和自定义异常处理_21、SpringBoot2.x配置全局异常返回自定义页面...
查看>>
小D课堂 - 零基础入门SpringBoot2.X到实战_第8节 数据库操作之整合Mybaties和事务讲解_32..SpringBoot2.x持久化数据方式介绍...
查看>>
小D课堂 - 零基础入门SpringBoot2.X到实战_第8节 数据库操作之整合Mybaties和事务讲解_34、SpringBoot整合Mybatis实操和打印SQL语句...
查看>>
小D课堂 - 零基础入门SpringBoot2.X到实战_第8节 数据库操作之整合Mybaties和事务讲解_35、事务介绍和常见的隔离级别,传播行为...
查看>>
小D课堂 - 零基础入门SpringBoot2.X到实战_第9节 SpringBoot2.x整合Redis实战_40、Redis工具类封装讲解和实战...
查看>>
小D课堂 - 零基础入门SpringBoot2.X到实战_第9节 SpringBoot2.x整合Redis实战_37、分布式缓存Redis介绍...
查看>>
小D课堂 - 零基础入门SpringBoot2.X到实战_第10节 SpringBoot整合定时任务和异步任务处理_42、SpringBoot常用定时任务配置实战...
查看>>
小D课堂 - 零基础入门SpringBoot2.X到实战_第9节 SpringBoot2.x整合Redis实战_39、SpringBoot2.x整合redis实战讲解...
查看>>
小D课堂 - 零基础入门SpringBoot2.X到实战_第14节 高级篇幅之SpringBoot多环境配置_59、SpringBoot多环境配置介绍和项目实战...
查看>>
小D课堂 - 零基础入门SpringBoot2.X到实战_第10节 SpringBoot整合定时任务和异步任务处理_41、SpringBoot定时任务schedule讲解...
查看>>
小D课堂 - 零基础入门SpringBoot2.X到实战_第10节 SpringBoot整合定时任务和异步任务处理_43、SpringBoot2.x异步任务实战(核心知识)...
查看>>
小D课堂 - 新版本微服务springcloud+Docker教程_1_01课程简介
查看>>
小D课堂 - 零基础入门SpringBoot2.X到实战_第11节 Logback日志框架介绍和SpringBoot整合实战_45、SpringBoot2.x日志讲解和Logback配置实战...
查看>>
小D课堂 - 新版本微服务springcloud+Docker教程_1_02技术选型
查看>>