面试题趣谈:客户端Bootstrap如何初始化的?

下面我们将从Channel简介、通过Bootstrap来创建NioSocketChannel、NioSocketChannel的初始化过程、ChannelPipeline的作用、EventLoop的初始化过程、Netty中Channel的注册过程、Handler的添加过程、客户端发起连接请求的过程这几个关键环节来剖析客户端Bootstrap如何初始化的。

Channel简介

Channel:Netty世界的"社交名媛"

在Netty的世界里,Channel就像是社交场合中的名媛,她不仅知道所有关于舞会(Socket连接)的状态,是热闹进行中还是已经散场,而且还能够优雅地进行读写操作,与宾客(数据)进行互动。每当一个新的舞会开始,就会有一位新的社交名媛Channel出现。

Netty的"舞会协议"

Netty不仅精通传统的TCP舞会,还熟悉许多其他异域风情的连接协议。每种协议都有其独特的NIO(非阻塞I/O)和OIO(Old-I/O,即传统的阻塞I/O)版本的舞蹈。不同的舞蹈(协议)和不同的舞蹈风格(阻塞类型)都有不同的Channel与之共舞。

通过Bootstrap来创建NioSocketChannel

Bootstrap:Netty的"社交活动策划师"

Bootstrap是Netty中的社交活动策划师,它负责策划和初始化舞会(客户端或服务端的Netty)。通过它,我们可以轻松地完成Netty的初始化工作。

客户端舞会初始化的"三步曲"

  1. EventLoopGroup(舞伴团队):无论是服务端还是客户端的舞会,都需要一群舞伴(EventLoopGroup)。在这个例子中,我们选择了NioEventLoopGroup,这是一群擅长NIO舞蹈的舞伴。

  2. ChannelType(舞会类型):我们需要指定舞会的类型。因为是客户端的舞会,我们选择了NioSocketChannel,这是一种适合客户端舞会的Channel。

  3. Handler(舞会主持人):设置舞会的主持人,他负责处理舞会上的各种数据互动。

NioSocketChannel的初始化过程

NioSocketChannel的"诞生记"

接下来,让我们看看客户端启动Bootstrap后都做了哪些工作?通过查看NioSocketChannel的类层次结构,我们可以得出以下结论:

  1. ChannelFactory(舞会场地建造者):在Bootstrap中,ChannelFactory的实现类是ReflectiveChannelFactory,它负责建造舞会的场地。

  2. Channel实例化(舞会场地的诞生):实例化过程就像是调用ChannelFactory的newChannel()方法,而实例化的Channel具体类型与初始化Bootstrap时传入的channel()方法的参数有关。因此,对于客户端的Bootstrap而言,创建的Channel实例就是NioSocketChannel。

NioSocketChannel:网络世界的"新生儿"

NioSocketChannel就像是一位新生儿,它的诞生过程充满了仪式感和精细的准备。

  1. 诞生之初(newSocket)

    • 首先,通过调用NioSocketChannel.newSocket(DEFAULT_SELECTOR_PROVIDER),一个全新的Java NioSocketChannel被迎接到这个世界上。
  2. 家庭背景登记(AbstractChannel初始化)

    • 每个Channel宝宝都会有一个唯一的id,就像社会安全号码一样独一无二。
    • parent属性默认为null,就像新生儿还没有确定家庭归属一样。
    • unsafe是一个保护者,通过newUnsafe()方法实例化,确保Channel宝宝的安全。
    • pipeline是Channel宝宝的启蒙老师,通过new DefaultChannelPipeline(this)为宝宝打下坚实的基础。
  3. 成长环境设置(AbstractNIOChannel属性赋值)

    • ch是Channel宝宝的摇篮,一个原生的Java NIO SocketChannel。
    • readInterestOp是宝宝的兴趣点,被设置为SelectionKey.OP_READ,预示着宝宝将来可能是个爱读书的好孩子。
    • 宝宝的摇篮被设置为非阻塞,ch.configureBlocking(false),让宝宝学会独立,不依赖别人。
  4. 个人形象打造(NioSocketChannel属性赋值)

    • config是宝宝的个人形象顾问,new NioSocketChannelConfig(this, socket()),帮助宝宝塑造自己的风格。

Unsafe:Channel的"保镖"

Unsafe就像是Channel的保镖,它在Channel诞生的过程中扮演着至关重要的角色。Unsafe是Netty上层与Java底层之间的桥梁,它封装了对Java底层Socket的所有操作。

ChannelPipeline的作用

ChannelPipeline:Channel的"成长路径"

ChannelPipeline就像是Channel的成长路径,每个Channel都有自己的成长路径,这在Channel诞生时自动创建。

  1. DefaultChannelPipeline

    • 它是Channel的成长路径图,每个NioSocketChannel对象都会有一个与之关联的DefaultChannelPipeline。
    • Head和Tail是成长路径的起点和终点,Head是一个OutBoundHandler,而Tail是一个InBoundHandler,它们定义了Channel的输入和输出方向。
  2. HeadContext和TailContext

    • HeadContext和TailContext就像是Channel的左右手,分别负责处理出站和入站数据。它们是双向链表的头和尾,以AbstractChannelHandlerContext为节点元素,是Netty实现Pipeline机制的关键。

EventLoop的初始化过程

接下来我们继续看看Netty中EventLoop的初始化过程。

EventLoop的诞生:Netty世界的"造星工厂"

EventLoop在Netty中就像是造星工厂,负责培养和维护一批批的网络通信明星(EventLoop实例)。

  1. NioEventLoopGroup的登场

    • 在我们的网络大戏《ChatClient》中,NioEventLoopGroup作为大戏的幕后推手,一开始就闪亮登场。
  2. 星探系统(系统属性读取)

    • Netty的星探首先会查看系统属性中是否有“io.netty.eventLoopThreads”的线索,如果没有,就按照默认的剧本(CPU核心数×2)来挑选新星。
  3. 造星策略(MultithreadEventLoopGroup构造器)

    • 接下来,Netty会根据新星的数量(nThreads)来决定使用哪种造星策略,如果是2的平方,就使用PowerOfTwoEventExecutorChooser,否则就用GenericEventExecutorChooser。
  4. 新星选拔(Chooser的next()方法)

    • 选拔过程中,Chooser会用一种巧妙的方式(数组索引循环位移)来挑选新星,确保每个新星都有平等的机会被选中。
  5. 新星的诞生(newChild()方法)

    • 最终,通过调用newChild()这个方法,Netty会实例化一个NioEventLoop对象,一个新的网络通信明星就此诞生。

NioEventLoop的"成长记"

  1. 选拔过程

    • 首先,Netty会创建一个大小为nThreads的SingleThreadEventExecutor数组,就像是为新星们准备的舞台。
  2. 造星策略的选择

    • 根据新星的数量,Netty会选择最合适的造星策略,无论是PowerOfTwo还是Generic,目标都是选出最闪亮的那颗星。
  3. 新星的舞台(children数组初始化)

    • 通过调用newChild()方法,Netty为每个新星分配一个舞台(EventExecutor实例)。
  4. NioEventLoop的特训

    • 在NioEventLoopGroup类中,newChild()方法会实例化一个NioEventLoop对象,然后对这个新星进行特训。
  5. 新星装备(NioEventLoop属性赋值)

    • 在特训过程中,新星会获得两件重要的装备:provider(SelectorProvider对象)和selector(Selector对象),这两件装备将帮助新星在网络世界中大放异彩。

通过这些幽默的比喻,我们可以更轻松地理解Netty中EventLoopGroup的初始化过程以及EventLoop的重要性和作用。

Netty中Channel的注册过程

接下来我们继续看看Netty中Channel的注册过程和Handler的添加过程。

Channel注册到Selector:Netty版的“非诚勿扰”

  1. 初识Stage(Bootstrap的initAndRegister())

    • 想象一下,Channel是一位害羞的单身汉,他在Netty版的“非诚勿扰”节目《Bootstrap》中首次亮相。这个亮相不仅仅是介绍自己,还要在节目上找到自己的心动女生(NioEventLoop的Selector)。
  2. 心动女生的选择(MultithreadEventLoopGroup的register())

    • 在这个过程中,Channel通过“月老”MultithreadEventLoopGroup的帮助,来选择一个合适的心动女生。通过next()方法,月老会为Channel挑选一个合适的SingleThreadEventLoop。
  3. 深入了解(SingleThreadEventLoop的register())

    • 一旦选择了心动女生,Channel就要通过channel.unsafe().register(this, promise)的方式,深入了解这个女生,获取她的联系方式(Unsafe的register()方法)。
  4. 正式表白(AbstractUnsafe的register())

    • 在AbstractUnsafe的register()方法中,Channel鼓起勇气,通过register0()方法向心动女生表白。
  5. 确定关系(AbstractUnsafe的register0())

    • 在AbstractUnsafe的register0()方法中,Channel通过doRegister()方法,正式确定和心动女生的关系。
  6. 公布恋情(AbstractNioChannel的doRegister())

    • 最后,Channel通过javaChannel().register(eventLoop().selector, 0, this)的方式,向全世界宣布他们的恋情,即把Java NIO的SocketChannel注册到Selector中,并把Channel作为Attachment与SocketChannel关联。

Handler的添加过程

Handler的添加过程:Netty版的“美食街”

  1. 美食街开张(自定义Handler机制)

    • 在Netty中,Pipeline就像一条美食街,你可以在这里自由添加各种“美食摊位”(Handler),每个摊位都有自己的特色美食(业务逻辑)。
  2. 选择摊位(添加Handler到ChannelPipeline)

    • 比如,如果你想要处理HTTP数据,你可以在美食街的入口处添加一个“HTTP特色小吃摊”(针对HTTP编解码的Handler),然后再添加你自己的“私房菜摊位”(业务逻辑Handler)。
  3. 美食流(数据流通过Handler)

    • 网络上的数据流就像顾客一样,从美食街的一端进入,流经各个摊位,品尝不同的美食(进行编解码),最终到达你的私房菜摊位。

下面我们通过一张图片来描述 Netty 中 ChannelPipeline 结构的流程图:

包含
包含
包含
下一个
下一个
初始化
下一个
下一个
ChannelPipeline
Head
Context/Handler
Tail
ChannelInitializer
Context/Handler
...
ChatClientHandler
添加或替换Handlers
Channel

这个流程图从上到下描述了以下步骤:

  1. ChannelPipeline 是整个处理器链的起点。
  2. Head 是链的第一个 ChannelHandler,通常是 ChannelInitializer
  3. Context/Handler 节点代表中间的 ChannelHandler,可以有多个,它们之间通过 “下一个” 关系相连。
  4. Tail 是链的最后一个 ChannelHandler
  5. ChannelInitializer 用于初始化 Channel,负责添加或替换 Handlers
  6. ChatClientHandler 是一个自定义的 Handler,用于处理特定的网络逻辑。

请注意,ChannelPipeline 中的 ChannelHandler 是按照添加的顺序排列的,HeadTail 的方向表示了 ChannelHandler 的执行顺序。

客户端发起连接请求

下面我们直接通过一张图来描述 NioEventLoop 与 NioSocketChannel 之间关系以及 connect 操作调用过程的流程:

管理
内部类
发起连接
Java NIO Socket
处理低级别操作
NioEventLoop
NioSocketChannel
SocketChannel
AbstractNioUnsafe
connect
doResolveAndConnect
doConnect
完成连接
底层Socket操作

这个流程图从左到右描述了以下步骤:

  1. NioEventLoop 是 Netty 中的事件循环,负责处理 I/O 事件,并且管理 NioSocketChannel
  2. NioSocketChannel 是 Netty 中的一个 Channel 实现,用于 TCP 连接,并基于 Java NIO 的 SocketChannel
  3. AbstractNioUnsafeNioSocketChannel 的内部类,负责处理低级别的 Socket 操作。
  4. connect 是由 NioEventLoop 发起网络连接的方法。
  5. doResolveAndConnect 是在 connect 方法过程中被调用的内部方法,用于执行地址解析和连接。
  6. doConnect 是在 doResolveAndConnect 之后被调用的方法,用于实际建立连接。
  7. SocketChannel 是 Java NIO 中的 Socket,是 NioSocketChannel 的基础,负责底层 Socket 操作。

请注意,这个流程图展示了 NioEventLoopNioSocketChannel 之间的交互,以及 connect 方法的调用链。

如果本文对您有所帮助的话,请收藏文章、关注作者,感激不尽。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Gemini技术窝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值