Netty:ChannelPipeline和ChannelHandler为什么会鬼混在一起?

前言

数据流是编程范式中的其中一种,何为数据流?

数据流具有无穷数据源数据流向的特点:

在网络编程中,客户端想要跟服务器进行数据传递,首先就要通过三次握手建立传输通道,绑定好端口,此后通过这个socket进行数据传输。

当通道关闭后,如果想要建立连接,还需再次由客户端发起建立连接的请求,所以后面演进了使用长连接的方式来优化连接频繁地建立和销毁。

在网络编程中,Channel就是干这个事情的。

类似于NIO的Channel,Netty提供了自己的Channel,使得更符合自身框架,叫做ChannelPiple。

Channel

JDK的NIO类库种Channel分2种:

客户端的:SockerChannel 服务端的:ServerSocketChannel

Unsafe是一个内部接口,聚合在Channel种协助进行网络读写相关的操作

Unsafe本身是作为Channel的一个内部辅助类来用的,其实并不应该被Netty框架的上层使用者使用,所以是被命名为Unsafe

Unsafe

是Channel内部的一个内部实现类,是一个接口类型

他不应该被用户代码直接调用,而是由实际负责IO的读写线程来调用。这也是他的名字Unsafe的含义。

Unsafe中基础方法

registry方法

作用:将Channel注册到EventLoop的多路复用器上

为了避免多线程并发操作Channel的问题,这里还需要做并发处理

判断当前线程是否是Channel对应的NioEventLoop线程

bind方法

给Channel绑定对应的Socket

对于服务端,用于绑定监听端口 对于客户端,用于指定客户端Channel的本地绑定Socker地址

write方法

把消息添加到唤醒发送数组:ChannelOutboundBuffer里面,并不是真正的写Channel,flush才是把消息写进Channel

ChannelPipeline功能说明

io.netty.channel.Channel是Netty网络操作抽象类,里面聚合了JDK的Channel对象

可以Channel理解为操作网络的一个对象,委派角色,他聚合了一组功能,包括但不限于:

JDK原生Channel

JDK 也有NIO原生的Channel,但是Netty是另起炉灶自己抽象出来一个新的Channel,那这里又要讲讲,为什么不用原生JDK的东西了

  1. JDK的SocketChannelServerSocketChannel主要职责是负责网络IO操作。由于他们是SPI类接口,所以通过继承SPI功能类来扩展其功能难度大
  2. SPI和API的区别!还不懂的赶紧去复习,面试常考内容!

  1. Netty的Channel能够跟Netty的整体架构融合在一起,跟架构亲和力更足
  2. 自定义Channel,功能实现更加灵活(感觉这个才是最重要的)

Netty的Channel设计理念

  1. 在Channel接口层,采用Facade(外观模式)进行统一封装,相当于一个聚合了一个JDK原生Channel的一个操作对象
  2. 为SocketChannel、ServerSocketChannel提供统一的视图,公共功能在父类中实现,子类实现更为具体的功能
  3. 具体实现采用聚合而非包含的方式,将相关的功能聚合在Channel种,由Channel统一负责分配和调度

网络读写操作

网络IO操作会触发ChannelPipeline中对应的事件方法

Neety是基于事件驱动,当Channel进行IO操作时,会产生对应的IO事件,然后驱动事件在ChannelPipeline中传播,最后由ChannelHandler对事件进行拦截和处理,不关心的事件可以直接忽略。

采用事件驱动的方式,有很多优点:

网络IO操作直接调用DefaultChannelPipeline的相关方法,由前者中对应的ChannelHandler来进行逻辑处理。

AbstractChannel网络IO操作源码实现

成员变量:

Channel的注册

doRegister,把Channel注册到EventLoop中

可以看到第二个参数ops,代表感兴趣的事件,doRegistry的时候直接传了个0,代表对任何事件都不感兴趣

Channel对哪些事件感兴趣呢,比如:

第三个参数是把自己this传过去了,这是为了能让触发事件的时候返回SelectionKey。

根据SelectionKey是可以在多路复用器拿到对应感兴趣的Channel对象,然后去进行处理。

ChannelPipeline

ChannelPipeline和ChannelHandler

ChannelPipleine和ChannelHandler机制类似于ServletFilter过滤器

本质上是责任链模式的实现。

Servlet Filter能够以声明式的方法插入到HTTP请求处理过程中。用于拦截请求和响应,实现对请求的前置处理后置处理

Netty的Channel过滤器实现原理跟Servlet Filter机制一致,它将Channel的数据管道抽象成:ChannelPipeline。

数据在ChannelPipeline中流动和传递,ChannelPipeline中持有IO事件拦截器ChannelHandler的链表

意味着:ChannelHandler可以对IO事件进行拦截和处理

ChannelHandler是可插拔式的,并且我们可以通过新增或者删除ChannelHandler来实现不同业务逻辑,

ChannelPipeline功能说明

ChannelPipeline是ChannelHandler的容器

负责ChannelHandler的管理和事件拦截与调度

主要是:负责事件的分发和预处理

一个消息被ChannelPipeline的ChannelHandler链拦截和处理的全过程:

  1. 底层的SocketChannel #read方法读取ByteBuf,触发ChannelRead事件(OP_READ),由IO线程NioEventLoop调用ChannelPipeline的#fireChannelRead方法,将消息(ByteBuf)传输到ChannelPipeline
  2. 消息依次被添加好的ChannelHandler链处理,首先是HeadHandler,然后是ChannelHandler1、ChannelHandler2….,任何Handler都可以中断消息的传递,也就是他们都可以把消息丢掉
  3. 注意:这里是先是被HeadHandler处理,后面是反过来,是有顺序的
  4. ChannelHandlerContext的#write方法发送消息,消息从TailHandler开始,途径ChannelHandlerN,ChannelHandlerN-1….
  5. 最终被添加到消息发送缓冲区中,等待刷新(flush)和发送(write)

注意:这里为什么是具有顺序性地处理,因为有些ChanelHandler就有顺序性,比如解码那些,需要先将ByteBuf数据解析成对象,然后再将对象二次解码成pojo对象。

Netty中的事件分为:inbound事件和outbound事件,图中左半边对应inboud事件,右半边对应outbound事件

ChannelInboundHandler处理inboud事件

ChannelOutboundHandler处理outbound事件

误区:区分inboud、outbound并不是简单的IO流,而是触发事件的源头

总结

本篇简单介绍了JDK自带的Channel以及Unsafe其内部的一个实现类。并且从Unsafe的职责来更好的理解它这个名字的含义。

Netty提供的ChannelPipeline内部聚合了一个ChannelHandler链表。ChannelPipeline负责网络IO事件中的调度,而ChannelHandler则是负责拦截,如果是对应感兴趣的事件则处理,否则抛给下一个ChannleHandler。

粗略地讲了一下ChannelHandler为什么要基于责任链的设计模式,做成链表的形式。下一篇文章会着重讲一下ChannelHandler在Netty里面干了些什么事情。


原文链接:https://juejin.cn/post/7213653429416362039

展开阅读全文

页面更新:2024-06-09

标签:线程   客户端   对象   消息   事件   操作   功能   方法   数据   网络

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top