Netty笔记

1 基本介绍

Bootstrap

Netty应用程序通过设置 bootstrap(引导)类开始,该类提供了一个用于应用程序网络层配置的容器。Bootstrap有两种类型,一种是用于客户端的Bootstrap,一种是用于服务端的ServerBootstrap。不管应用程序使用哪种协议,无论是客户端还是服务器都需要使用引导。Bootstrap用来连接远程主机,有1个EventLoopGroup。ServerBootstrap用来绑定本地端口,有2个EventLoopGroup。一个ServerBootstrap可以认为有2个Channel集合,第一个集合包含一个单例 ServerChannel,代表持有一个绑定了本地端口的socket;第二集合包含所有创建的Channel,处理服务器所接收到的客户端进来的连接。

Channel

Netty中的Channel可以理解为与socket具有相同功能的组件,它定义了与socket丰富的操作集:bind, clone, config, connect, isActive, isOpen, isWritable, read, write等。Netty提供了大量专门的Channel,包括AbstractChanel, AbstactNioByteChannel, AbstractNioChannel, EmbeddedChannel, LocalServerChannel, NioSocketChannel等。

ChannelHandler

ChannelHandler用于数据处理,可由特定事件触发。它可适用于几乎所有的动作,包括将一个对象转换为字节。
ChannelHandler是应用程序的核心,ChannelPipeline是ChannelHandler链的容器。
Netty中有两个方向的数据流,入站(ChannelInboundHandler)和出站(ChannelOutboundHandler),它们之间有一个明显的区别:若数据是从用户应用程序到远程主机则是“出站(outbound)”,相反若数据时从远程主机到用户应用程序则是“入站(inbound)”。数据在ChannelPipeline中的ChannelHandler链中流动。
一个事件可以通过使用ChanneHandlerContext被转发到下一个处理器中的当前链传递到每个方法。当ChannelHandler被添加到的ChannelPipeline它得到一个 ChannelHandlerContext,它代表一个ChannelHandler和ChannelPipeline之间的“绑定”。

最常见的处理器是接收到解码后的消息并应用一些业务逻辑到这些数据。要创建这样一个ChannelHandler,你只需要扩展基类SimpleChannelInboundHandler,其中T是想要进行处理的类型。

ChannelPipeline

ChannelPipeline为ChannelHandler链提供了一个容器,并提供了沿着链入站和出站的事件流动的管理API。每个Channel都有自己的ChannelPipeline,当Channel创建时自动创建。ChannelHandler通过ChannelInitializer接口安装到ChannelPipeline中。ChannelInitializer子类通过ServerBootstrap进行注册,当它的方法initChannel被调用时,这个对象将安装自定义的ChannelHandler集合到pipeline中,当这个操作完成时,ChannelInitializer子类则从ChannelPipeline自动删除自身。

EventLoop

EventLoop用于处理Channel的I/O操作,一个单一的EventLoop通常会处理多个Channel事件。一个EventLoopGroup可包含多个EventLoop,并且提供了迭代遍历方法。

ChannelFuture

Netty所有的I/O操作都是异步的。因为一个操作可能无法立即返回,我们需要一种方法在以后确定它的结果。出于这个目的,Netty提供了ChannelFuture接口,ChannelFuture接口的addListener方法注册了一个ChannelFutureListener,当操作完成时,可以被通知(不管成功与否)。可以将ChannelFuture对象想象为一个未来操作结果的占位符。尽管不能确定何时执行,但肯定会执行。

2 ByteBuf

ByteBuf是一个很好的经过优化的数据容器,我们可以将字节数据有效的添加到ByteBuf中或从ByteBuf中获取数据。ByteBuf有2部分:一个用于读,一个用于写。我们可以按顺序的读取数据,也可以通过调整读取数据的索引或者直接将读取位置索引作为参数传递给get方法来重复读取数据。写入数据到ByteBuf后,writerIndex(写入索引)增加。开始读字节后,readerIndex(读取索引)增加。你可以读取字节,直到写入索引和读取索引处在相同的位置,ByteBuf变为不可读。当访问数据超过数组的最后位,则会抛出 IndexOutOfBoundsException。调用ByteBuf的"read"或"write"开头的任何方法都会提升 相应的索引。另一方面,"set"、"get"操作字节将不会移动索引位置;他们只会操作相关的通过参数传入方法的相对索引。可以给ByteBuf指定一个最大容量值,这个值限制着ByteBuf的容量。任何尝试将写入索引超过这个值的行为都将导致抛出异常。ByteBuf的默认最大容量限制是Integer.MAX_VALUE。

Heap Buffer

最常用的模式是ByteBuf将数据存储在JVM的堆空间,这是通过将数据存储在数组的实现。堆缓冲区可以快速分配,当不使用时也可以快速释放。它还提供了直接访问数组的方法,通过ByteBuf.array()来获取 byte[]数据。

Direct Buffer

通过免去中间交换的内存拷贝,提升IO处理速度;直接缓冲区的内容可以驻留在垃圾回收扫描的堆区以外。
DirectBuffer在-XX:MaxDirectMemorySize=xxM大小限制下,使用Heap之外的内存,GC对此”无能为力”,也就意味着规避了在高负载下频繁的GC过程对应用线程的中断影响。

Composite Buffer

最后一种模式是复合缓冲区,我们可以创建多个不同的ByteBuf,然后提供一个这些ByteBuf组合的视图。复合缓冲区就像一个列表,我们可以动态的添加和删除其中的ByteBuf。

ByteBuf分配

ByteBufAllocator

为了减少分配和释放内存的开销,Netty 通过支持池类 ByteBufAllocator,可用于分配的任何 ByteBuf 我们已经描述过的类型的实例。

Channel channel = ...;
ByteBufAllocator allocator = channel.alloc(); //1
....
ChannelHandlerContext ctx = ...;
ByteBufAllocator allocator2 = ctx.alloc(); //2
...

Unpooled (非池化)缓存

当未引用ByteBufAllocator时,上面的方法无法访问到ByteBuf。对于这种情况,Netty提供一个称为Unpooled的工具类,它提供了静态辅助方法来创建非池化的ByteBuf实例。

ByteBufUtil

ByteBufUtil静态辅助方法来操作ByteBuf,因为这个API是通用的,与使用池无关,这些方法已经在外面的分配类实现。

也许最有价值的是hexDump()方法,这个方法返回指定ByteBuf中可读字节的十六进制字符串,可以用于调试程序时打印ByteBuf的内容。一个典型的用途是记录一个ByteBuf的内容进行调试。

原文地址:https://www.cnblogs.com/coderland/p/5902987.html