Netty学习

Netty深入浅出

第一章 Netty——异步和事件驱动

1、引入——NIO和IO哪家强

1、1 传统阻塞IO连接方式及存在的问题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-65z9zhuQ-1640245095470)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210730171911124.png)]

​ 阻塞IO,只能同时处理一个连接,要管理多个并发客户端,需要为每个新的客户端Socket 创建一个新的 Thread,

即一个客户端对应一个Socket

​ =======》这就出了大问题

  • 在任何时候都可能有大量的线程处于休眠状态,只是等待输入或者输出数据就绪,这可能算是一种资源浪费。

  • 需要为每个线程的调用栈都分配内存,其默认值大小区间为 64 KB 到 1 MB,具体取决于操作系统。

  • 即使 Java 虚拟机(JVM)在物理上可以支持非常大数量的线程,但是远在到达该极限之前,上下文切换所带来的开销就会带来麻烦

1、2 Netty中NIO的牛逼

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kab6cuA6-1640245095478)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210730171847139.png)]

转眼回到netty,两个字,牛逼

  • 使用较少的线程便可以处理许多连接,因此也减少了内存管理和上下文切换所带来开销
  • 当没有 I/O 操作需要处理的时候,线程也可以被用于其他任务

———————————————————————————————————————————————————————————————————————

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rSYMCitz-1640245095481)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210730175353622.png)]

1.2、异步和事件驱动

  • 可伸缩性:一种系统、网络或者进程在需要处理的工作不断增长时,可以通过某种可行的方式或者扩大它的处理能力来适应这种增长

    的能力。

    异步和可伸缩性的的联系

    • 非阻塞网络调用使得我们可以不必等待一个操作的完成。完全异步的 I/O 正是基于这个特性构建的,并且更进一步:异步方法会立即返回,并且在它完成时,会直接或者在稍后的某个时间点通知用户。

    • 选择器使得我们能够通过较少的线程便可监视许多连接上的事件。

1.3、Netty核心组件

  • channel

  • 回调

  • Future

  • 事件和ChannelHandler

这些构建块代表了不同类型的构造:资源、逻辑、通知。你的应用程序将使用它们来访问网络以及流经网络的数据。

1.3.1 Channel

它代表一个到实体(如一个硬件设备、一个文件、一个网络套接字或者一个能够执行一个或者多个不同的I/O操作的程序组件)的开放连接,如读操作和写操作

1.3.2 回调

Netty 在内部使用了回调来处理事件;当一个新的连接已经被建立时,ChannelHandler 的 channelActive()回调方法将会被调用,并将打印出一条信息。

public class ConnectHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx){
	throws Exception {
		System.out.println(
		"Client " + ctx.channel().remoteAddress() + " connected");
	}
}
1.3.3 Future

Future 提供了另一种在操作完成时通知应用程序的方式。

每个 Netty 的出站 I/O 操作都将返回一个 ChannelFuture,也就是说,IO操作不会被阻塞——Netty是异步事件驱动的。

异步建立连接

Channel channel = ...;
ChannelFuture future = channel.connect(
	new InetSocketAddress("192.168.0.1", 25));

connect()方法将会直接返回,不会阻塞,该调用将会在后台完成。

如何利用 ChannelFutureListener。首先,要连接到远程节点上。然后,要注册一个新的 ChannelFutureListener 到对 connect()方法的调用所返回的ChannelFuture 上。当该监听器被通知连接已经建立的时候,要检查对应的状态 。如果该操作是成功的,那么将数据写到该 Channel。否则,要从 ChannelFuture 中检索对应的 Throwable。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MXA5UhH5-1640245095483)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210802101429517.png)]

1.3.4 事件和 ChannelHandler

Netty 使用不同的事件来通知我们状态的改变或者是操作的状态

  • 这使得我们能够基于已经发生的事件来触发适当的动作。比如:

    • 记录日志
    • 数据转换
    • 流控制
    • 应用程序逻辑
  • 由入站数据或者相关的状态更改而触发的事件

    • 连接已被激活或者连接失活
    • 数据读取
    • 用户事件
    • 错误事件
  • 出站事件是未来将会触发的某个动作的操作结果

    • 打开或者关闭到远程节点的连接
    • 、将数据写到或者冲刷到套接字

一个事件是如何被ChannelHandler链处理的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C1xm0ICh-1640245095484)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210802103235779.png)]

每个 ChannelHandler 的实例都类似于一种为了响应特定事件而被执行的回调。

Netty 通过触发事件将 Selector 从应用程序中抽象出来

第二章 如何构建一个基于 Netty 的客户端和服务器

2.1 编写服务器

多个客户端同时连接到一台服务器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T7rBam9a-1640245095486)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210802114711574.png)]

2.2 编写Echo服务器

2.2.1 ChannelHandler和业务逻辑

因为你的 Echo 服务器会响应传入的消息,所以它需要实现 ChannelInboundHandler 接口,用来定义响应入站事件的方法。

我们感兴趣的方法是:

  • channelRead()—对于每个传入的消息都要调用;

  • channelReadComplete()—通知ChannelInboundHandler最后一次对channelRead()的调用是当前批量读取中的最后一条消息;

  • exceptionCaught()—在读取操作期间,有异常抛出时会调用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0e5vB0Cn-1640245095489)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210803092331218.png)]

可以在exception中重新连接端掉的连接 ,

如果不捕获异常,会发生什么呢每个 Channel 都拥有一个与之相关联的ChannelPipeline,其持有一个 ChannelHandler 的实例链。在默认的情况下,ChannelHandler 会把对它的方法的调用转发给链中的下一个 ChannelHandler。因此,如果 exceptionCaught()方法没有在该链中的某处实现,那么所接收的异常将会被传递到 ChannelPipeline 的尾端并被记录。为此,你的应用程序应该提供至少有一个实现了exceptionCaught()方法的 ChannelHandler。(6.4 节详细地讨论了异常处理)。

2.2.2 引导服务器
  • 绑定到服务器将在其上监听并接受传入连接请求的端口;

  • 配置 Channel,以将有关的入站消息通知给 EchoServerHandler 实例。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KtrHQleP-1640245095491)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210803133612356.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yADKeanE-1640245095492)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210803133632619.png)]

服务器主要代码组件:

  • EchoServerHandler 实现了业务逻辑;

  • main()方法引导了服务器;

    • main()方法引导了服务器;

    • 创建并分配一个 NioEventLoopGroup 实例以进行事件的处理,如接受新连接以及读/写数据

    • 指定服务器绑定的本地的 InetSocketAddress;

    • 使用一个 EchoServerHandler 的实例初始化每一个新的 Channel;

    • 调用 ServerBootstrap.bind()方法以绑定服务器。

2.3 编写Echo客户端

客户端的功能:

  • 连接到服务器
  • 发送一个或多个消息
  • 对于多个消息,等待并接受从服务器发回的相同的消息
  • 关闭连接
2.3.1 通过 ChannelHandler 实现客户端逻辑

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7XHQArbD-1640245095493)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210803140157428.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qkWF2nP0-1640245095494)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210803140207868.png)]

你重写了 channelActive()方法,其将在一个连接建立时被调用。

你重写了 channelRead0()方法。每当接收数据时,都会调用这个方法。

2.3.2 引导客户端

客户端是使用主机和端口参数来连接远程地址,也就是这里的 Echo 服务器的地址,而不是绑定到一个一直被监听的端口。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sReWalk8-1640245095495)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210803142919233.png)]

  • 为初始化客户端,创建了一个 Bootstrap 实例;
  • 为进行事件处理分配了一个 NioEventLoopGroup 实例,其中事件处理包括创建新的连接以及处理入站和出站数据;
  • 为服务器连接创建了一个 InetSocketAddress 实例;
  • 当连接被建立时,一个 EchoClientHandler 实例会被安装到(该 Channel 的)ChannelPipeline 中;
  • 在一切都设置完成后,调用 Bootstrap.connect()方法连接到远程节点;

第三章 Netty的组件和设计

3.1 Channel接口

基本的 I/O 操作(bind()、connect()、read()和 write())依赖于底层网络传输所提供的原语。

Channel 也是拥有许多预定义的、专门化实现的广泛类层次结构的根:

  • EmbeddedChannel;
  • LocalServerChannel;
  • NioDatagramChannel;
  • NioSctpChannel;
  • NioSocketChannel。

3.2 EventLoop接口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j1iTWqCZ-1640245095497)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210803144247542.png)]

  • 一个 EventLoopGroup 包含一个或者多个 EventLoop;

  • 一个 EventLoop 在它的生命周期内只和一个 Thread 绑定;

  • 所有由 EventLoop 处理的 I/O 事件都将在它专有的 Thread 上被处理;

  • 一个 Channel 在它的生命周期内只注册于一个 EventLoop;

  • 一个 EventLoop 可能会被分配给一个或多个 Channel。

3.3 ChannelFuture接口

Netty 提供了ChannelFuture 接口,其 addListener()方法注册了一个 ChannelFutureListener,以便在某个操作完成时(无论是否成功)得到通知。

3.4 Channelhandler接口

Netty 的主要组件是 ChannelHandler,它充当了所有处理入站和出站数据的应用程序逻辑的容器。

channelHandler用来处理channnel上的各种事件,分为两种,一种是入站事件,一种是出站事件,所有的handler连成一串,就是pipeline

​ 入站事件通常是ChannelInboundhandlerAdapter的子类,主要用来读取客户端数据,写回结果

​ 出战事件通常是channelOutboundHandlerAdapter的子类,主要用来对鞋结果进行加工

3.5 ChannelPipeline 接口

ChannelPipeline 提供了 ChannelHandler 链的容器,并定义了用于在该链上传播入站和出站事件流的 API。当 Channel 被创建时,它会被自动地分配到它专属的 ChannelPipeline。

ChannelHandler 安装到 ChannelPipeline 中的过程:

  • 一个ChannelInitializer的实现被注册到了ServerBootstrap中 ①;

  • 当 ChannelInitializer.initChannel()方法被调用时,ChannelInitializer将在 ChannelPipeline 中安装一组自定义的 ChannelHandler;

  • ChannelInitializer 将它自己从 ChannelPipeline 中移除。

为什么需要适配器类

有一些适配器类可以将编写自定义的 ChannelHandler 所需要的努力降到最低限度,因为它们提供了定义在对应接口中的所有方法的默认实现。

下面这些是编写自定义 ChannelHandler 时经常会用到的适配器类:

  • ChannelHandlerAdapter

  • ChannelInboundHandlerAdapter

  • ChannelOutboundHandlerAdapter

  • ChannelDuplexHandler

3.6 编码器和解码器

入站消息会被解 码;也就是说,从字节转换为另一种格式,通常是一个 Java 对象。如果是出站消息,将从它的当前格式被编码为字节

所有由 Netty 提供的编码器/解码器适配器类都实现了 ChannelOutboundHandler 或者 ChannelInboundHandler 接口

3.7 抽象类 SimpleChannelInboundHandler

最常见的情况是,你的应用程序会利用一个 ChannelHandler 来接收解码消息,并对该数据应用业务逻辑。要创建一个这样的 ChannelHandler,你只需要扩展基类 SimpleChannelInboundHandler,其中 T 是你要处理的消息的 Java 类型 。在这个 ChannelHandler 中, 你将需要重写基类的一个或者多个方法,并且获取一个到 ChannelHandlerContext 的引用,这个引用将作为输入参数传递给 ChannelHandler 的所有方法。在这种类型的 ChannelHandler 中,最重要的方法是 channelRead0(ChannelHandlerContext,T)。除了要求不要阻塞当前的 I/O 线程之外,其具体实现完全取决于你。

第四章 传输

4.1 通过Netty使用OIO和NIO

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XGBDoeaQ-1640245095498)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210803170931187.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DxYjUxLa-1640245095500)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210803171005186.png)]

4.2 传输 API

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MG2Zfogm-1640245095501)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210803171422108.png)]

每个 Channel 都将会被分配一个 ChannelPipeline 和 ChannelConfig。ChannelConfig 包含了该 Channel 的所有配置设置,并且支持热更新。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DDcNHNjn-1640245095502)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210803171738755.png)]

写数据并将其冲刷到远程节点的任务

Netty 的 Channel 实现是线程安全的,因此你可以存储一个到 Channel 的引用,并且每当你需要向远程节点写数据时,都可以使用它,即使当时许多线程都在使用它。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q3RktWv1-1640245095504)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210803172430554.png)]

多个线程使用同一个channel

4.3 内置的传输

第五章 ByteBuf

直接内存VS堆内存

​ ByteBuf支持直接内存和堆内存

  • 直接内存创建和销毁的代价比较昂贵,但是读写性能高(少一次内存复制),适合配合池化功能一起使用

  • 直接内存对GC压力小,但是这部分额你存不受JVM垃圾回收机的管理,但是也要注意及时主动释放。

// 创建池化基于堆的ByteBuf
ByteBuf buffer = ByteBufAllocator.DEFAULT.heapBuffer(10);
// 创建池化基于直接内存的ByteBuf
ByteBuf buffer = ByteBufAllocator.DEFAULT.directBuffer(10);

池化VS非池化

netty中对于创建比较慢的东西,可以使用池化的思想来管理。就像对于数据库的连接,使用连接池来优化,对于创建昂贵的,就预先创建好,等创建的时候直接就拿过来使用就行。·

池化的最大意义在于可以重用ByteBuf,优点有

  • 没有池化,则每次都得创建新的ByteBuf实例,这个操作对直接内存代价昂贵,就算是堆内存,也会增加GC压力

  • 有了池化,则可以重用池中的ByteBuf实例,并且采用了与jemalloc类似的内存分配算法提升分配效率

  • 高并发时,池化功能更节约内存,减少内存溢出的可能

池化功能是否开启,可以通过下面的系统环境变量来设置
-Dio.netty.allocator.type={unpooled|pooled}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5vgYt4io-1640245095505)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210914145443569.png)]

4.1之后,非andriod平台默认启用池化实现,andriod平台启用非池化实现

4.1之前,池化功能还不成熟,默认是非池化实现

5.1 ByteBuf 的 API

  • 它可以被用户自定义的缓冲区类型扩展;

  • 通过内置的复合缓冲区类型实现了透明的零拷贝;

  • 容量可以按需增长(类似于 JDK 的 StringBuilder);

  • 在读和写这两种模式之间切换不需要调用 ByteBuffer 的 flip()方法;

  • 读和写使用了不同的索引;

  • 支持方法的链式调用;

  • 支持引用计数;

  • 支持池化。

5.2 ByteBuf 类——Netty 的数据容器

5.2.1 ByteBuf的使用模式
  1. 堆缓冲区

    最常用的 ByteBuf 模式是将数据存储在 JVM 的堆空间中。这种模式被称为支撑数组(backing array),它能在没有使用池化的情况下提供快速的分配和释放。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DEHJdXFH-1640245095506)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210803201127339.png)]

  2. 直接缓冲区

    NIO 在 JDK 1.4 中引入的 ByteBuffer 类允许 JVM 实现通过本地调用来分配内存。这主要是为了避免在每次调用本地 I/O 操作之前(或者之后)将缓冲区的内容复制到一个中间缓冲区(或者从中间缓冲区把内容复制到缓冲区)。

    直接缓冲区的主要缺点是,相对于基于堆的缓冲区,它们的分配和释放都较为昂贵。如果你正在处理遗留代码,你也可能会遇到另外一个缺点:因为数据不是在堆上,所以你不得不进行一次复制,

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-olmaDyxn-1640245095508)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210803201813611.png)]

  3. 复合缓冲区

    由两部分——头部和主体——组成的将通过 HTTP 协议传输的消息。这两部分由应用程序的不同模块产生,将会在消息被发送的时候组装。

    不想为每个消息都重新分配这两个缓冲区时,可以使用CompositeByteBuf

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hz3ys3jV-1640245095509)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210803202239462.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kXEfCYZB-1640245095510)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210803202304057.png)]

​ 上面的代码,分配和复制操作,以及伴随着对数组管理的需要,使得这个版本的实现效率低下而且笨拙。

下面的代码访问CompositeByteBuf 中的数据类似于(访问)直接缓冲区的模式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n2rnKCZ6-1640245095511)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210803202527947.png)]

Netty使用了CompositeByteBuf来优化套接字的I/O操作,尽可能地消除了由JDK的缓冲区实现所导致的性能以及内存使用率的惩罚。这种优化发生在Netty的核心代码中,因此不会被暴露出来,但是你应该知道它所带来的影响。

5.3 字节级操作

ByteBuf 提供了许多超出基本读、写操作的方法用于修改它的数据。那就需要字节级操作

5.3.1 随机访问索引

如同在普通的 Java 字节数组中一样,ByteBuf 的索引是从零开始的:第一个字节的索引是0,最后一个字节的索引总是 capacity() - 1。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fqdU5cyJ-1640245095512)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210803220309798.png)]

通过索引访问数据不会改变readerIndex和writeIndex

5.3.2 顺序访问索引

虽然 ByteBuf 同时具有读索引和写索引,但是 JDK 的 ByteBuffer 却只有一个索引,这也就是为什么必须调用 flip()方法来在读模式和写模式之间进行切换的原因。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4IJEcoyT-1640245095513)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210803220616642.png)]

5.3.3 可丢弃字节

有需要再去看,在调用discardReadBytes()之后,对可写分段的内容并没有任何的保证。除非内存非常紧张,不然不建议使用。

5.3.4 可读字节

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6KRjZDzZ-1640245095515)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804092434772.png)]

5.3.5 可写字节

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xtnvZzqz-1640245095517)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804095120095.png)]

5.3.6 索引管理

可以通过调用 markReaderIndex()、markWriterIndex()、resetWriterIndex()和 resetReaderIndex()来标记和重置 ByteBuf 的 readerIndex 和 writerIndex。

可以通过调用 readerIndex(int)或者 writerIndex(int)来将索引移动到指定位置。

通过调用 clear()方法来将 readerIndex 和 writerIndex 都设置为 0。并不会清除掉里面的内容

5.3.7 查找操作
  • 最简单的是使用indexOf()方法。
  • 较复杂的查找,ByteBufProcessor

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AUdWrgzx-1640245095518)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804101944504.png)]

5.3.8 派生缓冲区
  • duplicate();

  • slice();

  • slice(int, int);

  • Unpooled.unmodifiableBuffer(…);

  • order(ByteOrder);

  • readSlice(int)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jJPFTO0C-1640245095519)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804102219739.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rvlfnnzt-1640245095521)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804102355499.png)]

5.3.9 读写操作
  • get()和 set()操作,从给定的索引开始,并且保持索引不变;

  • read()和 write()操作,从给定的索引开始,并且会根据已经访问过的字节数对索 引进行调整。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HKpgcTrr-1640245095523)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804102523542.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YD1dnFUu-1640245095525)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804102551091.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zJ2vxAlS-1640245095526)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804102609068.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aUPSYyPF-1640245095527)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804102619878.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zzXtgq24-1640245095529)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804102712608.png)]

5.3.10 更多操作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aCoJzHEJ-1640245095530)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804102809654.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Da768eHe-1640245095531)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804102817783.png)]

5.4 ByteBufHolder 接口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ab76zoq4-1640245095532)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804102909236.png)]

5.5 ByteBuf 分配

5.5.1 按需分配:ByteBufAllocator 接口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ecJP0W6Y-1640245095533)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804103007269.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PRvmg7oR-1640245095534)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804103034676.png)]

5.5.2 Unpooled 缓冲区
5.5.3 ByteBufUtil 类

hexdump()方法,它以十六进制的表示形式打印ByteBuf 的内容。

5.6 引用计数

​ 引用计数背后的想法并不是特别的复杂;它主要涉及跟踪到某个特定对象的活动引用的数量。一个 ReferenceCounted 实现的实例将通常以活动的引用计数为 1 作为开始。只要引用计数大于 0,就能保证对象不会被释放。当活动引用的数量减少到 0 时,该实例就会被释放。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-js56VUYI-1640245095535)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804103951723.png)]

5.7扩容

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z3LZwBpj-1640245095537)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210914150252251.png)]

5.8 retain&release

由于netty中有堆外内存的ByteBuf实现,堆外内存最好时手动释放,而不是等GC垃圾回收

  • UnpooledheapByteBuf使用的是JVM的内存,只需要等GC回收内存即可
  • UnpooledFrectByteBuf使用的是直接内存,需要特殊的方法来回收内存
  • PooledByteBuf和他的子类使用了池化机制,需要更复杂的规则来回收内存。
回收内存的源码实现,请关注下面的不同实现
protected absrtract void deallocate()

Netty这里采用了引用计数法来控制回收内存,每个ByteBuf都实现了ReferenceCounted接口

  • 每个ByteBuf对象的初始计数为

  • 调用release方法计数减1,如果计数为0,ByteBuf内存被回收

  • 调用retain方法计数加1,表示调用者没用完之前,其它 handler即使调用了release 也不会造成回收·

  • 当计数为0时,底层内存会被回收,这时即使ByteBuf对象还在,其各个方法均无法正常使用

那我们该如何释放内存呢?

ByteBuf buf = ...
try{
...
}finally{
	buf.release();
}

那我们是在使用完ByteBuf后就释放ByteBuf吗,并非如此

我们来看一下pipeline组成

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WCwtVPcj-1640245095540)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210914151542012.png)]

因为pipeline的存在,一般需要将ByteBuf传递给下一个ChannelHandler,如果在finally 中 release了,就失去了传递性(当然,如果在这个ChannelHandler内这个ByteBuf已完成了它的使命,那么便无须再传递)

因此,释放ByteBuf应该是在最后一个handler或者说是最后一次使用ByteBuf的时候进行释放

附:head和tail中会释放一次,但是不等于就不用手动去释放了,因为有些情况就穿不到head或者tail中

5.9 slice

【零拷贝】的体现之—,对原始ByteBuf进行切片成多个ByteBuf,切片后的ByteBuf并没有发生内存复制,还是使用原始ByteBuf的内存,切片后的ByteBuf维护独立的read,write 指针

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CKzJsCty-1640245095541)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210914153041198.png)]

例如,原始ByteBuf进行一些初始操作

ByteBuf origin = ByteBufAllocator . DEFAULT. buffer (10);
origin.writeBytes(new byte[]{1, 234});
origin.readByte();
System.out.print1n(ByteBufuti1.prettyHexDump(origin)) ;

5.10 duplicate

【零拷贝的体现之一】,就好比截取了原始的ByteBuf所有内容,并且没有maxcapacity的限制,也是与原始的ByteBuf使用同一块底层内存,只是读写指针是独立的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MrWPvMw2-1640245095543)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210914160509885.png)]

5.11 copy

会将底层内存进行深拷贝,因此无论读写,都与原始的ByteBuf无关

5.12 unpooled

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BeyhK4Fv-1640245095544)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210914162123481.png)]

读写误解

我最初在认识上有这样的误区,认为只有在netty, nio 这样的多路复用I0模型时,读写才不会相互阻塞,才可以实现高效的双向通信,但实际上,Java Socket是全双工的:在任意时刻,线路上存在A到B和B到A的双向实现高效的双向通信,但实际上,Java Socket是全双工的:在任意时刻,线路上存在A到B和B到A的双向信号传输。即使是阻塞I0,读和写是可以同时进行的,只要分别采用读线程和写线程即可,读不会阻塞写、写也不会阻塞读。

服务器和客户端并非要实现一问一答通信模式。

第 六章 ChannelHandler和ChannelPipeline

6.1 ChannelHandler

6.1.1 Channel 的生命周期

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h1tZ8yiT-1640245095545)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804104408512.png)]

6.1.2 ChannelHandler 的生命周期

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G9DGy8PW-1640245095547)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804104507977.png)]

Netty 定义了下面两个重要的 ChannelHandler 子接口:

  • ChannelInboundHandler——处理入站数据以及各种状态变化;

  • ChannelOutboundHandler——处理出站数据并且允许拦截所有的操作。

6.1.3 ChannelInboundHandler 接口

这些方法将会在数据被接收时或者与其对应的 Channel 状态发生改变时被调用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B6E0tEB4-1640245095548)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804111510235.png)]

@Sharable 注解用来说明ChannelHandler是否可以在多个channel直接共享使用。

释放消息资源。当所有可读的字节都已经从 Channel 中读取之后,将会调用该回调方法;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BY76g8ay-1640245095550)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804141425876.png)]

6.1.4 ChannelOutboundHandler 接口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sDAjaEyV-1640245095551)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804141919142.png)]

6.1.5 ChannelHandler 适配器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EAomeWLk-1640245095553)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804142148329.png)]

6.1.6 资源管理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OBDgYh3Q-1640245095554)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804143520505.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cUk2bCXS-1640245095555)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804143529594.png)]

6.2 ChannelPipeline 接口

为ChannelPipeline是一个拦截流经Channel的入站和出站事件的ChannelHandler 实例链

每一个新创建的 Channel 都将会被分配一个新的 ChannelPipeline。这项关联是永久性的;Channel 既不能附加另外一个 ChannelPipeline,也不能分离其当前的。在 Netty 组件的生命周期中,这是一项固定的操作,不需要开发人员的任何干预。

6.2.1 修改 ChannelPipeline

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DfwVlwA4-1640245095557)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804145243319.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r5azw4CF-1640245095558)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804145251072.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FuEhVUE5-1640245095559)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804145446068.png)]

6.2.2 触发事件
6.3 ChannelHandlerContext 接口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oqxtAyDa-1640245095560)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804151503304.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Va3CwKkc-1640245095561)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804151515283.png)]

6.3.1 使用 ChannelHandlerContext

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O1g0HhTl-1640245095562)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804151621281.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p5kqFoXa-1640245095564)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804151638304.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lswgUNfV-1640245095565)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804151650313.png)]

要想调用从某个特定的 ChannelHandler 开始的处理过程,必须获取到在(ChannelPipeline)该ChannelHandler 之前的 ChannelHandler 所关联的 ChannelHandlerContext。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oyXnVovV-1640245095567)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804155852275.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-osc2EU5y-1640245095568)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804160048193.png)]

6.4 异常处理

6.4.1 处理入站异常

要想处理这种类型的入站异常,你需要在你的 ChannelInboundHandler 实现中重写下面的方法。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uVqRJkcW-1640245095570)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804161912207.png)]

6.4.2 处理出站异常

处理出战操作,基于以下通知机制:

  • 每个出站操作都将返回一个 ChannelFuture。注册到 ChannelFuture 的 ChannelFutureListener 将在操作完成时被通知该操作是成功了还是出错了。

  • 几乎所有的 ChannelOutboundHandler 上的方法都会传入一个 ChannelPromise的实例。作为 ChannelFuture 的子类,ChannelPromise 也可以被分配用于异步通知的监听器。

添加 ChannelFutureListener 只需要调用 ChannelFuture 实例上的 addListener(ChannelFutureListener)方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PqW51DdM-1640245095573)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804163147643.png)]

第二种方式:将 ChannelFutureListener 添加到即将作为参数传递给 ChannelOutboundHandler 的方法的 ChannelPromise。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uQg6C5gY-1640245095574)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804163235620.png)]

第 七章 EventLoop和线程模型

7.1 线程模型概述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c98ecFFv-1640245095575)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804163933615.png)]

7.2 EventLoop 接口

一个 EventLoop 将由一个永远都不会改变的 Thread 驱动,同时任务(Runnable 或者 Callable)可以直接提交给 EventLoop 实现,以立即执行或者调度执行。根据配置和可用核心的不同,可能会创建多个 EventLoop 实例用以优化资源的使用,并且单个EventLoop 可能会被指派用于服务多个 Channel。

7.3 任务调度

一个常见的用例是,发送心跳消息到远程节点,以检查连接是否仍然还活着。如果没有响应,你便知道可以关闭该 Channel 了。

7.3.1 JDK 的任务调度 API

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AiXRxZR6-1640245095576)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804165900924.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n4qfTOYO-1640245095577)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804170116866.png)]

7.3.2 使用 EventLoop 调度任务

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ukso5mEp-1640245095579)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804170235007.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JzJNCVw3-1640245095580)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804170353624.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8yWw0O8w-1640245095583)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804170421229.png)]

7.4 实现细节

7.4.1 线程管理
  1. 异步传输

    异步传输实现只使用了少量的 EventLoop(以及和它们相关联的 Thread),而且在当前的线程模型中,它们可能会被多个 Channel 所共享。

    一旦一个 Channel 被分配给一个 EventLoop,它将在它的整个生命周期中都使用这个EventLoop(以及相关联的 Thread)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B5m3yQ3n-1640245095590)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804170554321.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9tPm3fMi-1640245095592)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804170639309.png)]

2.阻塞传输

这里每一个 Channel 都将被分配给一个 EventLoop(以及它的 Thread)。

每个 Channel 的 I/O 事件都将只会被一个 Thread(用于支撑该 Channel 的 EventLoop 的那个 Thread)处理。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zGJzEpUK-1640245095595)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804170923424.png)]

第八章 引导

8.1 Bootstrap 类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y4OwK9Dd-1640245095597)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210805133135332.png)]

8.2 引导客户端和无连接协议

8.2.1 引导客户端

第九章 单元测试——EmbeddedChannel

9.1 测试入站消息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZznoIuko-1640245095599)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210804172001782.png)]

第十章 序列化数据

10.1 通过 Protocol Buffers 序列化

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HaVgBNVA-1640245095602)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210805112059003.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o3u0iPn7-1640245095604)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210805112231514.png)]

Netty 实战

1、为什么netty要使用异步的方法,netty异步提升的是什么

下面看一个例子

image-20210913182950991

但是这并不是netty的所作所为
image-20210913183128103

image-20210913183254370

要点:单线程没法异步提高效率,必须配合多线程,多核cpu才能发挥异步的优势。

异步没有缩短响应时间,反而有所增加。

       合理进行任务拆分,也是利用异步的关键。

响应时间不减反增,那提升的是什么呢,提升的是单位时间内数据的吞吐量。

误区:netty不是因为多线程,所以效率就高

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tUQTOcTS-1640245095617)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210826110344302.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dzqJ5yVy-1640245095619)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210826112041508.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QMkUYFe3-1640245095621)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210826144326830.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BweO1e3V-1640245095623)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210826142126944.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XWh9mYzG-1640245095625)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210826143610168.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TjiDyGgQ-1640245095627)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210826170852557.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T5M37wQ9-1640245095628)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210826171153700.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PgRwIqJQ-1640245095631)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210830155609739.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hTDBzPFU-1640245095633)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210830161052740.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YBDrTtB5-1640245095634)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210831093948577.png)]

register把

附录 netty在nginx上的连接配置

当系统发布jar包的时候,前后端都部署在nginx中,nginx的配置如下

协议的设计与解析

由于现有的协议,有一些缺点,比如多个数据之间使用回车进行分割 ,字节数不够紧凑。 但是由于本身需求的业务比较复杂,因而可以自行设计一些协议,字节数更紧凑效率更高的协议。

自定义协议要素

  • 魔数,用来在第一时间判定是否是无效数据包-------比如:java的二进制字节码,起始八个字节就是coffebaby

  • 版本号,可以支持协议的升级

  • 序列化算法,消息正文到底采用哪种序列化和反序列化的方式,可以由此扩展,例如:json,protobuf、hessian、jdk

  • 指令类型、是登录、注册、单聊、群聊、…还是业务相关

  • 请求序号、为了双工通信、提升异步能力

  • 正文长度

  • 消息正文---------------一般采用一种特定模式,常见的有json,对象流,xml…以前会使用xml,也可以使用二进制模式(对象流)

优化与源码

1.优化

1.1 扩展序列化算法

序列化,反序列化主要用在消息正文的转换上

  • 序列化时,需要将java对象变为要传输的数据(可以是byte[],或json等,最终都需要变成byte[])
  • 反序列化时,需要将传入的正文数据还原成java对象,便于处理

java自带的 序列化和 反序列化 机制,核心代码

//反序列化
byte[] body = new byte[bodyLength];
byteBuf.readBytes(body);
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(body));
Message message = (Message)in.readObject();
message.setSequenceId(sequenceId);
//序列化
ByteArrayOutputStream out = new ByteArrayOutputStream();
new ObjectOutputStream(out).writeObject(message);
byte[] bytes = out.toByteArray();

1.2参数调优

1).CONNECT_TIMEOUT_MILLIS

  • 属于SocketChannel参数
  • 用在客户端建立连接时,如果在指定毫秒内无法连接,会抛出timeout异常
  • SO_TIMEOUT主要用在阻塞IO,阻塞IO中accept,read等都是无限等待的,如果不希望永远阻塞,使用它调整超时时间
new BootStrap()
	.option()

 

2).SO_BACKLOG

3).ulimit-n ------->服务器如果要应对高并发,支持大量的连接,那么服务器就要调整这个参数--------建议放在启动脚本里

  • 属于操作系统参数

4)TCP_NODELAY --------如果是false,就启动nagle算法,这样会导致一些数据延迟(nagle会把小的数据包攒到一起发送,那么就可能会等待,导致延迟 --------如果是true,则会没有延迟传送(建议使用true) )

  • 属于SocketChannel参数

5)SO_SNDBUF&SO_RCVBUF --------尽量不要去调整这个参数,netty会自动帮我们选择适合的-------自己调整可能反而画蛇添足。

  • SO_SNDBUF属于SocketChannel参数
  • SO_RCVBUF即可用于SocketChannel参数,也可以用于ServerSocketChannel参数(建议设置在ServerSocketChannel上)

6)ALLOCATOR

  • 属于SocketChannel参数
  • 用来分配ByteBuf,ctx.alloc()

7)RCVBUF_ALLOCATOR

  • 属于SocketChannel参数

11

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值