源码解析-Netty源码之Bootstrap创建,初始化Channel,Pipeline,handler

 从Netty官方给出的example包着手点分析,echo 回声,也就是客户端传什么,服务端传回什么

 

先从客户端开始看

 

属性,ip地址,端口号,数据大小之类的 四个写死了的

 判断ssl是否为空,来决定是否需要初始化SslContext (可以理解为一些客户端的初始化配置,我们的例子中默认是null)

下面重点来了,前面暂时都可以忽略

核心点几步:

1. 创建一个线程池组group;  //初始化线程池系列 包括group loop的初始化,配置了chooser,以及引用EventExecutor代表loop

2. 创建一个客户端Bootstrap;

3. 加入线程池组,

4. 加入socketChannel,     //创建channelFactory 将来用来创建NioSocketChannel

5. 定义了ChannelInitializer,重写initChannel()方法,调用Channel的pipeline,加入了head,tail和自定义的handel;

6. 异步connect;       //这一步做的事比较多,new一个channel,初始化一个pipeline,将ChannelInitializer内部的handler全部加入到新的pipeline。从group中拿一个loop 将channel注入到loop中,启动loop线程,将channel注册到selector上

7. 关闭channel;

8. 关闭线程池组group;

 

这一篇重点分析第2,3,4,5步 

这些不是最重点的,但是也是必要的

本篇简单介绍一下初始化的过程,包括创建BootStrap 初始化handel,定义channelFactory等过程

---

 

new Bootstrap()

ServerBootstrap 类用于创建服务端实例,Bootstrap 用于创建客户端实例

可以理解为这是Netty留给外界调用的一个入口,大大简化了我们对Netty的使用

源码走起

 

 bootstrap继承自顶层Channel,至于Channel之后会仔细分析,暂时认知他就是个管道吧,每一次连接就会创建一个channel,然后会不断地通过channel传输数据然后传给pipeline处理

bootstrap的定位就是一个客户端,封装了connect,group,channel,handel等方法,将这些组件组装在一起供我们使用

 

bootstrap.group()

 

非常非常的简单,就是把之前初始化的group配置进来,self()就是将配置完的自己返回,用来继续配置(builder模式)

 

bootstrap.channel()

传进来未知类型的class类型,封装成ChannelFactory然后存起来,以后需要这个class的时候通过反射实例化出来。

落地其实就是传入个NioSocketChannel ,把他封装成ReflectiveChannelFactory然后存起来,需要NioSocketChannel的时候 就会反射的方式通过构造方法 实例化一个NioSocketChannel出来

 

这一步判断传进来的class无参构造方法是否存在

 将包装好的ReflectiveChannelFactory存起来,返回bootstrap

 

new ChannelInitializer<SocketChannel>(){ initChannel() }    这一步比较重点,定义ChannelPipeline,加入各种handler

ChannelInitializer这个东西就是将来辅助生产pipeline的

 

单看bootstrap.handler()这一步很简单,就是把传入一个ChannelHander,然后配置进来

仔细研究一下传入的这个ChannelInitializer 这是一个很重要的组件

ChannelInitializer 继承自ChannelHandler,这点毋庸置疑

首先解释一下handler与channel的关系:

handler会对传入的数据进行处理,各种各样的handler实现了对数据的各种各样的处理

这些 handler 会组成一个 pipeline,用于处理 IO 事件

每个 Channel 内部都有一个 pipeline,pipeline 由多个 handler 组成,handler 之间的顺序是很重要的,因为 IO 事件将按照顺序顺次经过 pipeline 上的 handler,这样每个 handler 可以专注于做一点点小事,由多个 handler 组合来完成一些复杂的逻辑。

 首先,我们看两个重要的概念:Inbound 和 Outbound。在 Netty 中,IO 事件被分为 Inbound 事件和 Outbound 事件。

比如 connect、write、flush 这些 IO 操作是往外部方向进行的,它们就属于 Outbound 事件。 其他的,诸如 accept、read 这种就属于 Inbound 事件。

对于 Inbound 操作,按照添加顺序执行每个 Inbound 类型的 handler;而对于 Outbound 操作,是反着来的,从后往前,顺次执行 Outbound 类型的 handler。

看一下继承关系,总接口ChannelHandler,适配器ChannelHandlerAdapter 最后落实到两个抽象类 ChannelInboundHandlerAdapter   channelOutboundHandlerAdapter  

刚才说的ChannelInitializer 就是InboundHndler的实现

至于这个适配器Adapter什么用呢?

马老师,发生什么事了 .. 原来 是有handler 同时实现了Inbound 和 Outbound,通过Adapter完成的,比如 LoggingHandler 这种既可以用来处理 Inbound 也可以用来处理 Outbound 事件的 handler。

 

继续回到代码

 

看一下ChannelInitiallzer内部的pipeline 如何初始化的

这里是直接获取了AbstractChannel的pipeline,

so 就客户端而言,当NioSocketChannel通过构造方法反射来实例化的时候,调用父类构造器new了一个pipeline  还new了一个unsafe 以后会说

(至于这里为什么用反射,因为灵活啊,比如我们可以不用NioSocketChannel自己写一个继承 AbstractNioByteChannel的类,当bootstra调用.channel()时候可以将我们自己的类传入,通过反射也会实现相应的功能。)

 

重点来了,pipeline的实现,默认DefaultChannelPipeline,  pipeline内部定义了一个channel就是刚才的NioSocketChannel,以后会用到

这里还定义了两个重要参数 head tail

熟悉数据结构链表的同学对这个不会不敏感,好像链表的头节点和尾节点

emmm 还是个双向链表 

 

尾节点实例化 传进来刚才的pipeline  这个TailContext还继承了ChannelIncoundHandler  说白了 他就是个handler

调用父类构造,设置了pipeline

啥意思呢,head tail 其实就是特殊的handler。它的内部还存了pipeline

捋一捋,ChannelInitializer内部构造了一个pipeline,pipeline内部构造了两个handler-head和tail,head和tail又将pipeline设置到自己的内部 (禁止套娃???)

ChannelInitializer,head,tail 都属于handler    也就是hanler内套pipeline内套handler内套pipeline  套娃警告!!

 

再看一下head的实现

head明显比tail要复杂,内部存了一个unsafe  这个unsafe直接用的NioSocketChannel的unsafe  这个unsafe类似于jdk的unsafe类,会调用操作系统层的一些资源

head同时继承了Inbound和OutBound!!!

 

此时此刻pipeline是这个样子地,有一个head 和tail

马上,很快啊,ChannelInitializer就会辅助自定义的handler加入进来

 

至于ChannelInitializer什么时候initChannel()辅助生成完整Pipeline的呢。下篇在讲,耗子尾汁.

 

第一篇 源码解析-Netty源码之EventLoopGroup线程池分析

第二篇    源码解析-Netty源码之Bootstrap创建,初始化Channel,Pipeline,handler

posted @ 2020-12-29 15:28  六小扛把子  阅读(693)  评论(0编辑  收藏  举报