文章目录
下面我们将从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的初始化工作。
客户端舞会初始化的"三步曲"
-
EventLoopGroup(舞伴团队):无论是服务端还是客户端的舞会,都需要一群舞伴(EventLoopGroup)。在这个例子中,我们选择了NioEventLoopGroup,这是一群擅长NIO舞蹈的舞伴。
-
ChannelType(舞会类型):我们需要指定舞会的类型。因为是客户端的舞会,我们选择了NioSocketChannel,这是一种适合客户端舞会的Channel。
-
Handler(舞会主持人):设置舞会的主持人,他负责处理舞会上的各种数据互动。
NioSocketChannel的初始化过程
NioSocketChannel的"诞生记"
接下来,让我们看看客户端启动Bootstrap后都做了哪些工作?通过查看NioSocketChannel的类层次结构,我们可以得出以下结论:
-
ChannelFactory(舞会场地建造者):在Bootstrap中,ChannelFactory的实现类是ReflectiveChannelFactory,它负责建造舞会的场地。
-
Channel实例化(舞会场地的诞生):实例化过程就像是调用ChannelFactory的newChannel()方法,而实例化的Channel具体类型与初始化Bootstrap时传入的channel()方法的参数有关。因此,对于客户端的Bootstrap而言,创建的Channel实例就是NioSocketChannel。
NioSocketChannel:网络世界的"新生儿"
NioSocketChannel就像是一位新生儿,它的诞生过程充满了仪式感和精细的准备。
-
诞生之初(newSocket):
- 首先,通过调用
NioSocketChannel.newSocket(DEFAULT_SELECTOR_PROVIDER)
,一个全新的Java NioSocketChannel被迎接到这个世界上。
- 首先,通过调用
-
家庭背景登记(AbstractChannel初始化):
- 每个Channel宝宝都会有一个唯一的id,就像社会安全号码一样独一无二。
parent
属性默认为null,就像新生儿还没有确定家庭归属一样。unsafe
是一个保护者,通过newUnsafe()
方法实例化,确保Channel宝宝的安全。pipeline
是Channel宝宝的启蒙老师,通过new DefaultChannelPipeline(this)
为宝宝打下坚实的基础。
-
成长环境设置(AbstractNIOChannel属性赋值):
ch
是Channel宝宝的摇篮,一个原生的Java NIO SocketChannel。readInterestOp
是宝宝的兴趣点,被设置为SelectionKey.OP_READ
,预示着宝宝将来可能是个爱读书的好孩子。- 宝宝的摇篮被设置为非阻塞,
ch.configureBlocking(false)
,让宝宝学会独立,不依赖别人。
-
个人形象打造(NioSocketChannel属性赋值):
config
是宝宝的个人形象顾问,new NioSocketChannelConfig(this, socket())
,帮助宝宝塑造自己的风格。
Unsafe:Channel的"保镖"
Unsafe就像是Channel的保镖,它在Channel诞生的过程中扮演着至关重要的角色。Unsafe是Netty上层与Java底层之间的桥梁,它封装了对Java底层Socket的所有操作。
ChannelPipeline的作用
ChannelPipeline:Channel的"成长路径"
ChannelPipeline就像是Channel的成长路径,每个Channel都有自己的成长路径,这在Channel诞生时自动创建。
-
DefaultChannelPipeline:
- 它是Channel的成长路径图,每个NioSocketChannel对象都会有一个与之关联的DefaultChannelPipeline。
- Head和Tail是成长路径的起点和终点,Head是一个OutBoundHandler,而Tail是一个InBoundHandler,它们定义了Channel的输入和输出方向。
-
HeadContext和TailContext:
- HeadContext和TailContext就像是Channel的左右手,分别负责处理出站和入站数据。它们是双向链表的头和尾,以
AbstractChannelHandlerContext
为节点元素,是Netty实现Pipeline机制的关键。
- HeadContext和TailContext就像是Channel的左右手,分别负责处理出站和入站数据。它们是双向链表的头和尾,以
EventLoop的初始化过程
接下来我们继续看看Netty中EventLoop的初始化过程。
EventLoop的诞生:Netty世界的"造星工厂"
EventLoop在Netty中就像是造星工厂,负责培养和维护一批批的网络通信明星(EventLoop实例)。
-
NioEventLoopGroup的登场:
- 在我们的网络大戏《ChatClient》中,NioEventLoopGroup作为大戏的幕后推手,一开始就闪亮登场。
-
星探系统(系统属性读取):
- Netty的星探首先会查看系统属性中是否有“io.netty.eventLoopThreads”的线索,如果没有,就按照默认的剧本(CPU核心数×2)来挑选新星。
-
造星策略(MultithreadEventLoopGroup构造器):
- 接下来,Netty会根据新星的数量(nThreads)来决定使用哪种造星策略,如果是2的平方,就使用PowerOfTwoEventExecutorChooser,否则就用GenericEventExecutorChooser。
-
新星选拔(Chooser的next()方法):
- 选拔过程中,Chooser会用一种巧妙的方式(数组索引循环位移)来挑选新星,确保每个新星都有平等的机会被选中。
-
新星的诞生(newChild()方法):
- 最终,通过调用newChild()这个方法,Netty会实例化一个NioEventLoop对象,一个新的网络通信明星就此诞生。
NioEventLoop的"成长记"
-
选拔过程:
- 首先,Netty会创建一个大小为nThreads的SingleThreadEventExecutor数组,就像是为新星们准备的舞台。
-
造星策略的选择:
- 根据新星的数量,Netty会选择最合适的造星策略,无论是PowerOfTwo还是Generic,目标都是选出最闪亮的那颗星。
-
新星的舞台(children数组初始化):
- 通过调用newChild()方法,Netty为每个新星分配一个舞台(EventExecutor实例)。
-
NioEventLoop的特训:
- 在NioEventLoopGroup类中,newChild()方法会实例化一个NioEventLoop对象,然后对这个新星进行特训。
-
新星装备(NioEventLoop属性赋值):
- 在特训过程中,新星会获得两件重要的装备:provider(SelectorProvider对象)和selector(Selector对象),这两件装备将帮助新星在网络世界中大放异彩。
通过这些幽默的比喻,我们可以更轻松地理解Netty中EventLoopGroup的初始化过程以及EventLoop的重要性和作用。
Netty中Channel的注册过程
接下来我们继续看看Netty中Channel的注册过程和Handler的添加过程。
Channel注册到Selector:Netty版的“非诚勿扰”
-
初识Stage(Bootstrap的initAndRegister()):
- 想象一下,Channel是一位害羞的单身汉,他在Netty版的“非诚勿扰”节目《Bootstrap》中首次亮相。这个亮相不仅仅是介绍自己,还要在节目上找到自己的心动女生(NioEventLoop的Selector)。
-
心动女生的选择(MultithreadEventLoopGroup的register()):
- 在这个过程中,Channel通过“月老”MultithreadEventLoopGroup的帮助,来选择一个合适的心动女生。通过next()方法,月老会为Channel挑选一个合适的SingleThreadEventLoop。
-
深入了解(SingleThreadEventLoop的register()):
- 一旦选择了心动女生,Channel就要通过channel.unsafe().register(this, promise)的方式,深入了解这个女生,获取她的联系方式(Unsafe的register()方法)。
-
正式表白(AbstractUnsafe的register()):
- 在AbstractUnsafe的register()方法中,Channel鼓起勇气,通过register0()方法向心动女生表白。
-
确定关系(AbstractUnsafe的register0()):
- 在AbstractUnsafe的register0()方法中,Channel通过doRegister()方法,正式确定和心动女生的关系。
-
公布恋情(AbstractNioChannel的doRegister()):
- 最后,Channel通过javaChannel().register(eventLoop().selector, 0, this)的方式,向全世界宣布他们的恋情,即把Java NIO的SocketChannel注册到Selector中,并把Channel作为Attachment与SocketChannel关联。
Handler的添加过程
Handler的添加过程:Netty版的“美食街”
-
美食街开张(自定义Handler机制):
- 在Netty中,Pipeline就像一条美食街,你可以在这里自由添加各种“美食摊位”(Handler),每个摊位都有自己的特色美食(业务逻辑)。
-
选择摊位(添加Handler到ChannelPipeline):
- 比如,如果你想要处理HTTP数据,你可以在美食街的入口处添加一个“HTTP特色小吃摊”(针对HTTP编解码的Handler),然后再添加你自己的“私房菜摊位”(业务逻辑Handler)。
-
美食流(数据流通过Handler):
- 网络上的数据流就像顾客一样,从美食街的一端进入,流经各个摊位,品尝不同的美食(进行编解码),最终到达你的私房菜摊位。
下面我们通过一张图片来描述 Netty 中 ChannelPipeline
结构的流程图:
这个流程图从上到下描述了以下步骤:
ChannelPipeline
是整个处理器链的起点。Head
是链的第一个ChannelHandler
,通常是ChannelInitializer
。Context/Handler
节点代表中间的ChannelHandler
,可以有多个,它们之间通过 “下一个” 关系相连。Tail
是链的最后一个ChannelHandler
。ChannelInitializer
用于初始化Channel
,负责添加或替换Handlers
。ChatClientHandler
是一个自定义的Handler
,用于处理特定的网络逻辑。
请注意,ChannelPipeline
中的 ChannelHandler
是按照添加的顺序排列的,Head
到 Tail
的方向表示了 ChannelHandler
的执行顺序。
客户端发起连接请求
下面我们直接通过一张图来描述 NioEventLoop 与 NioSocketChannel 之间关系以及 connect 操作调用过程的流程:
这个流程图从左到右描述了以下步骤:
NioEventLoop
是 Netty 中的事件循环,负责处理 I/O 事件,并且管理NioSocketChannel
。NioSocketChannel
是 Netty 中的一个 Channel 实现,用于 TCP 连接,并基于 Java NIO 的SocketChannel
。AbstractNioUnsafe
是NioSocketChannel
的内部类,负责处理低级别的 Socket 操作。connect
是由NioEventLoop
发起网络连接的方法。doResolveAndConnect
是在connect
方法过程中被调用的内部方法,用于执行地址解析和连接。doConnect
是在doResolveAndConnect
之后被调用的方法,用于实际建立连接。SocketChannel
是 Java NIO 中的 Socket,是NioSocketChannel
的基础,负责底层 Socket 操作。
请注意,这个流程图展示了 NioEventLoop
和 NioSocketChannel
之间的交互,以及 connect
方法的调用链。
如果本文对您有所帮助的话,请收藏文章、关注作者,感激不尽。