重学Netty(四)——进一步了解Netty的组件及其作用

在上一篇文章简单的编写了EchoServer和EchoCLient,接下来就对这些常用组件进行进一步的了解

ByteBuf

从开始学习Java网络编程开始,不知道大家有没有发现API所规定的的数据传输最小单元就是字节,比如NIO中的IntBuffer,LongBuffer等等都是基于ByteBuffer而来的,因此Netty中对NIO中的ByteBuffer类进行了进一步的封装和优化。

ByteBuf的构造如下图所示,它维护了两个指针,一个是读指针,一个是写指针,如果是数据读取操作读指针会自动后移,如果是写操作,写指针会自动后移。因此,这样就不用手动的进行flip()操作了,减少了操作Buffer的复杂性。
在这里插入图片描述
它的一些操作方法一般都ByteBuf接口中有所规定的。

ByteBuf的几种模式:

  1. 堆缓冲区: 将数据存在JVM堆里的一个字节数组,在没有使用内存池的情况下可以提供快速的分配和释放,也被成为支撑数组(backing array)
  2. 直接缓冲区:直接缓冲区模式可以避免不必要的中间内存拷贝,它的内存分配并不在JVM堆里,相对于堆来说它的分配和释放代价比较大,而且如果要对它里面的数据进行操作的话还需要将里面的内容拷贝到一个数组里操作。它比较适合大数据量的传输且不带有数据处理的一些操作。
  3. 复合缓冲区:它可以看做是多个ByteBuf聚合后的视图,可以根据需要进行ByteBuf实例的添加和删除(这个在JDK中的复合缓冲区是没有这个特性的),Netty中通过CompositeByteBuf(ByteBuf的子类)实现了这个模式,里面可以同时包含有直接缓冲区和非直接缓冲区。它的结构如下图所示,相当于是一个链表结构,它的内部也带有迭代器。但是它也有缺点,也就是它不支持直接访问数组,和直接缓冲区一样,需要先转化为数组,才能进行操作。
    在这里插入图片描述

Channel

在传统的BIO编程中,我们都会使用Socket进行端口绑定,连接等操作,但是在NIO中我们使用的是SocketChannel(可以简单的理解为Socket+Channel),它也可以进行绑定,连接,读写等操作,也可以完成Channel的关闭操作,因此不难发现Channel的一些增强类提供了一些API让我们不需要直接去使用Socket,减小了开发的复杂性。
下图为netty下的Channel接口里的方法,可以看到它直接调用Unsafe方法来完成Socket的功能
在这里插入图片描述
进入io.netty.channel包下可以看到它有明显的包结构划分,这也就是常说的Netty给提供的一些数据传输方式
在这里插入图片描述

  • Embedded:不需要一个真正的基于网络的传输,但是可以使用ChannelHandler,一般用来测试ChannelHandler。
  • Epoll:它是完全非阻塞IO,比普通NIO传输要快,只能在支持Linux环境下应用
  • ChannelGroup:是一个线程安全的集合,里面包含一些开放的Channel并可以对这些Channel实施批量操作,可以将一些满足某些条件的Channel放到一个Group,关闭Channel的时候自动会从set里移除,因此它可以用来进行群发(广播)
  • Local:可以在虚拟机内部通过管道进行本地传输通信
  • NIO:在java.nio.channels包基础下,使用选择器方式(Selector)
  • OIO:在java.net包的基础下,使用阻塞IO流的方式
  • ChannelPool:就是Channel池(通道连接池),实现连接复用,此实现对通道池中的通道使用后进先出顺序。
  • RXTX:可以实现Java与串口应用的通信
  • SCTP:流控制传输协议(SCTP,Stream Control Transmission Protocol)是一种在网络连接两端之间同时传输多个数据流的协议,所提供的服务类似于TCP和UDP
  • Socket:里面包含nio的一些Socket还有oio的一些Socket,比如NioServerSocketChannel,OioServerSocketChannel等等
  • UDT:UDP-based Data Transfer Protocol,简称UDT,UDT建于UDP之上,并引入新的拥塞控制和数据可靠性控制机制,因此是一个面向连接的。它同时支持可靠的数据流传输和部分可靠的数据报传输。
  • Unix:下面放的是一些Unix系统下支持传输方式

EventLoop和EventLoopGroup

用于处理连接生命周期内所发生的的事件,就不用手动注册和消除事件监听和处理逻辑的调用了。
下图为EventLoop的UML图(后面进行解释)
在这里插入图片描述

先说说Channel,EventLoop,EventLoopGroup之间的关系,如下图所示:
在这里插入图片描述
可以对上图之间的包含关系做一个整理:

  1. 一个EventLoopGroup包含一个或多个EventLoop
  2. 一个EventLoop在它生命周期中只和一个Thread绑定
  3. 所有有EventLoop处理的IO事件都将在它专有线程中被处理(因此就不会有线程同步问题)
  4. 可以被分配给一个或多个Channel(regist方法),但一个Channel在它的生命周期中只能注册一个到EventLoop

因此,在前文说到EventLoop很像是一个Selector。
接下来就来看看EventLoop的类图,我借助书上的一张图(和上面的图是一样的,只不过书上的图将不同的类别进行了分块,易于阅读)
在这里插入图片描述
可以从上图中很明显的看到,它的实现是借助了java.util.concurrent包下的线程池来完成的,线程池主要就是用来提供任务执行器,因此netty的EventLoopGroup是netty与jdk的协同设计。

下面这句话是摘抄于书上(将的很透彻):
在这个模型中,一个EventLoop 将由一个永远都不会改变的 Thread 驱动,可以将任务(Runnable 或者Callable)直接提交给EventLoop,以及执行或者调度执行。根据配置和可用核心的不 同,可能会创建多个EventLoop 实例用以优化资源的使用,并且单 个EventLoop 可能会被指派用于服务多个Channel 。

ChannelFuture

ChannelFuture可以看做是一个线程执行结果的占位符,因为它的执行不确定性因素十分大,谁也不能确定异步信息什么时候会得到结果。在前文也提到过,在ChannelFuture中扩展了J.U.C的Future,它可以使用addListener()注册一个ChannelFutureListener的一个监听器,以便于可以在某个操作完成之后得到结果(无论是成功还是失败)。
在同一个Channel的异步任务是可以保证它们的顺序调用执行

ChannelHandler和ChannelPipeline

上篇文章也简单介绍了ChannelHandler,它可以对出站入站的数据进行处理,相应的网络事件的出发也就伴随着相应的ChannelHandler的执行

  • ChannelInboundHandler:对入站的数据处理
  • ChannelOutboundHandler:对出站的数据处理

ChannelPipeline则是它的容器,在ChannelPipeline中包含有处理对应Channel的所有ChannelHandler,在这个ChannelPipeline中规定有数据的进站和出站处理的一些列ChannelHandler,如下图所示
在这里插入图片描述
采用的是责任链模式,这种模式简化了手动的逻辑判断,比如在Tomcat中也是使用到了责任链模式

BootStrap

BootStrap就是常常听到的引导,比如SpringBoot中的启动类上的注解带有BootStrap,因此可以知道BootStrap是一个程序的启动入口,也就是引导的意思。在Netty中引导可以分为两种:服务端引导和客户端引导

  • ServerBootStrap:作用于服务端,可以绑定到一个本地端口
  • BootStrap:作用于客户端,可以连接一个远程主机和端口

也可以从对照下图
在这里插入图片描述
BootStrap里只有一个EventLoopGroup,而ServerBootStrap中有两个EventLoopGroup,这是为什么呢?

  • 因为服务器需要对本地端口进行绑定,因此它会需要一个专用的ServerChannel来连接正在开放监听的端口(这是一个EventLoopGroup,里面只包含一个ServerChannel,因此相应的ServerChannel也就只使用到一个EventLoop)。
  • 它还要进行客户端的连接处理,因此第二组包含的是已创建的用来处理客户端连接的Channel。

而客户端不需要本地端口绑定,因此只有连接的Channel,这样更进一步的应征了上一篇文章说得到的ChannelGroup,它可以将不同类型的Channel分为一组。

服务端的两个EventLoopGroup工作流程如下
在这里插入图片描述

总结: 在编写服务端的时候需要使用ServerBootStrap来绑定端口和配置一些其他信息,编写客户端是需要使用BootStrap指明发送链接的目的地和其他配置,一个Channel只能绑定一个EventLoop,一个EventLoop可以给多个Channel绑定,通过ChannelFuture来进行异步事件的通知,当事件被触发后,在ChannelPipeline中会有相应的ChannelHandler可以对当前数据进行处理。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值