Day18_Netty

文章目录

快速记忆

同步与异步

并发与并行:并发就是交替执行,并行就是真正的并行

同步与异步:从字面意思上就可以理解,异步里面有个异字,这个异字代表别人,所以同步与异步区分一个现场还是两个线程;

阻塞与非阻塞:强调等待还是不等待

5种阻塞模型:同步阻塞I/O、同步非阻塞I/O、同步多路复用、异步阻塞(没有此情况)、异步非阻塞I/O、信号驱动 I/O

netty的优点

《netty的关键字:Ractors+Selector+ByteBuf+线程池+编码解码器》:

  • 事件驱动(Reactors): Netty采用了Reactors设计模式,网络操作(如连接、读取数据、写入数据等)都被视为事件。Netty的事件驱动模型通过注册事件监听器,当特定事件发生时,会触发相应的回调方法。这种模型避免了线程阻塞,提高了并发性能。
  • NIO+Selector多路复用: Netty使用了非阻塞的IO模型,通过Selector实现多路复用,使得一个线程可以处理多个Channel的IO事件。
  • 池化和复用: 池化的最大意义在于可以重用 ByteBuf,以此减少内存分配和垃圾回收的开销,提高了系统的性能。
  • 自带编解码器解决 TCP 粘包/拆包问题:ByteToMessageDecoder、MessageToByteEncoder、StringDecoder和StringEncoder、ObjectDecoder和ObjectEncoder、LengthFieldBasedFrameDecoder
  • Netty支持多种常见的网络协议,包括TCP、UDP、HTTP、WebSocket等。这使得它适用于各种应用场景

Netty的核心组件是什么?

巧计:一个EventLoop绑定了多个Channel,一个Channel会关联一个ChannelPipeline,一个ChannelPipeline里面会有很多的ChannelHandler

Netty的核心组件包括以下几个主要部分:

  1. Channel(通道):
    • Channel代表了一个网络连接,可以是客户端到服务器的连接,也可以是服务器之间的连接。每个Channel都由一个EventLoop负责处理,而一个EventLoop可以管理多个Channel。
    • Channel是处理数据的通道。Channel负责所有的I/O操作(读取、写入、连接和绑定等)
  2. EventLoop(事件循环): 每个Channel都与一个EventLoop关联,它负责处理分配给它的所有Channel(通道)的所有事件。EventLoop会不断地轮询注册在其上的Channel,处理这些Channel上的IO事件,这样可以确保Channel的所有事件都在同一个线程中按顺序处理,从而避免了多线程并发带来的复杂性。一个EventLoop可能处理着多个channel的事件。一个EventLoop通常关联一个线程,而一个Netty应用可能有多个EventLoop,每个EventLoop运行在独立的线程上
  3. ChannelPipeline(通道管道):
    • ChannelPipeline是一个由一系列ChannelHandler组成的处理链。ChannelHandler如进行数据编解码、业务逻辑处理等。ChannelPipeline将这些ChannelHandler串联起来,形成一个处理链。
    • 每个Channel都有一个关联的ChannelPipeline,用于处理入站和出站的数据。
  4. ChannelHandler(通道处理器): 在Netty中,网络操作(如连接、读取数据、写入数据等)都被视为事件。当这些事件发生时,Netty会将这些事件传递给相应的事件处理器(Handler)。事件处理器就像是处理网络事件的“工人”,它们负责实现数据编解码、业务处理等。ChannelHandler可添加到ChannelPipeline中,形成一个处理链。
  5. ByteBuf(字节缓冲区): NIO里面有ByteBuffer,而Netty里面用ByteBuf,ByteBuf是对ByteBuffer的增强。ByteBuf是Netty中用于存储字节数据的缓冲区。它提供了灵活的API,支持池化、切片等高级特性,有助于提高性能和内存管理效率。
  6. ChannelFuture(通道未来): Netty 中所有的 I/O 操作都为异步的,我们不能立刻得到操作是否执行成功。不过,我们可以通过 ChannelFuture 接口的 addListener() 方法注册一个ChannelFutureListener ,当操作执行成功或者失败时,监听就会自动触发返回结果。
  7. Bootstrap(引导类): Bootstrap是Netty用于启动客户端的引导类。它负责配置和启动Netty的客户端组件,包括设置Channel类型、EventLoop组、ChannelPipeline等。
  8. ServerBootstrap(服务器引导类): ServerBootstrap是Netty用于启动服务器的引导类。它包含了一些服务器独有的配置选项,如处理客户端连接请求、设置TCP参数等。

ByteBuf与ByteBuffer区别

Netty里面有ByteBuf,而NIO里面是ByteBuffer。ByteBuf是对ByteBuffer的增强,是对字节数据的封装。
①ByteBuf是可以动态扩容的,而ByteBuffer一旦指定了容量就固定了,所以ByteBuf是对ByteBuffer的增强
②bytebuffer是读写共用一个指针,所以需要读写的切换;现在bytebuf就不用
③ByteBuf的设计允许进行零拷贝操作

Netty 服务端和客户端的启动过程了解么?

在这里插入图片描述
‌Netty服务端和客户端的启动过程‌主要涉及几个关键步骤,包括服务端的启动、客户端的启动,以及两者之间的连接建立过程。

服务端启动过程‌:
【ServerBootstrap】
【Boss NioEventLoopGroup - NioServerSocketChannel】
【Worker NioEventLoopGroup - NioServerSocketChannel】

  • 服务端首先通过ServerBootstrap类进行启动,这是Netty用于启动服务器的引导类。
  • 绑定NioEventLoopGroup,让它负责处理所有的Channel上面的事件
  • 创建NioServerSocketChannel,用于监听和接受客户端的连接请求。
  • 当有客户端连接请求时,服务端会创建一个新的NioSocketChannel,由Worker NioEventLoopGroup处理网络的读写事件。

客户端启动过程‌:
【Bootstrap】
【NioEventLoopGroup - NioSocketChannel】

  • 客户端通过Bootstrap类进行启动,这是Netty用于启动客户端的引导类。
  • 客户端绑定NioEventLoopGroup,但与服务端不同的是,客户端的EventLoopGroup主要用于处理异步操作和定时任务。
  • 客户端通过NioSocketChannel连接到服务端,这是一个双向的通信通道,用于发送和接收数据。

连接建立过程‌:

  • 服务端通过Boss NioEventLoopGroup接收客户端的连接请求,一旦连接建立,就将NioSocketChannel注册到Worker NioEventLoopGroup
  • 客户端在连接建立过程中,会调用sync()方法阻塞,直到连接成功建立。连接建立后,客户端和服务端都会调用initChannel方法,可以通过ChannelHandler处理网络事件和数据传输。
  • writeAndFlush代表发送数据,比如发送“hello”,会发送到NioEventLoopGroup,处理读事件,然后交给handler依次处理

如何优化Netty应用的性能?

《netty的关键字:Ractors+Selector+ByteBuf+线程池+编码解码器》:
参数ByteBuf线程池编码解码器等角度来说

优化Netty应用的性能涉及多个方面,以下是一些常见的优化策略:

  1. 合理使用ChannelOption: Netty提供了一些ChannelOption,用于配置底层的Socket选项。合理配置这些选项可以优化TCP连接的性能,例如设置TCP_NODELAY、SO_KEEPALIVE等。
// 启用TCP_NODELAY,禁用Nagle算法
bootstrap.option(ChannelOption.TCP_NODELAY, true);
// 启用TCP_KEEPALIVE,保持长连接
bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
tcp_nodelay 参数:
TCP 协议的包头有 20 字节,IP 协议的包头也有 20 字节,如果仅仅传输1 字节的数据在网络上传输的就有 20 + 20 + 1 = 41 字节,其中真正有用的数据只有1个字节,这对效率和带宽是极大的浪费。所以有个Nagle算法:如果是连续的小数据包,大小没有一个 MSS(Maximum SegmentSize,最大分段大小),那么这些小数据包就会在发送端暂存起来,直到小数据包累积到一个 MSS,或者收到一个Ack 为止,这种算法会造成网络延迟。
所以需要在 Socket 上开启 tcp_nodelay ,这个参数关闭了Nagle`s 算法,这样发送端就不需要等到上一个发送包的 ACK 返回直接发送新的数据包就好了。这对于强网络交互的场景来说非常的适用。
  1. 合理使用编解码器: Netty提供了一些内置的编解码器,同时也支持自定义编解码器。合理选择和配置编解码器可以减少数据的序列化和反序列化开销。
  • 内置编解码器:Netty提供了许多内置的编解码器,如StringDecoder和StringEncoder用于处理字符串,ByteToMessageDecoder和MessageToByteEncoder用于处理自定义的消息类型。通常,你会继承ByteToMessageDecoder或MessageToByteEncoder来实现自定义的编解码逻辑。
  • 第三方库:如果需要处理特定的协议(如HTTP、Protobuf、JSON等),可以使用第三方库提供的编解码器。
    • 如果对于性能要求不高,在传输数据占用带宽不大的场景下可以使用JSON作为序列化协议;
    • 如果对于性能要求比较高,那么使用 Thrift 或者 Protobuf 都可以。而Thrift 提供了配套的 RPC 框架,所以想要一体化的解决方案,你可以优先考虑Thrift;
  1. 线程
  • EventLoopGroup:合理配置EventLoopGroup的线程数,对于高并发应用,可以增加EventLoop的数量
  • 避免阻塞操作:Netty的设计是基于非阻塞IO的,阻塞操作会影响整体性能。
  1. 使用池化的ByteBuf: Netty的ByteBuf提供了内存池化的机制,通过重用ByteBuf对象,可以减少内存分配和垃圾回收的开销,提高性能。可以使用PooledByteBufAllocator来启用ByteBuf的池化。
// 启用ByteBuf的池化
bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
  1. 合理使用内存分配器: Netty支持多种内存分配器,包括堆内存和直接内存。根据应用的特性选择合适的内存分配器,例如使用UnpooledHeapByteBufAllocatorUnpooledDirectByteBufAllocator
  2. 合理处理异常和资源释放: 在Netty中,异常的处理和资源的释放非常重要。及时处理异常,释放资源可以避免内存泄漏和性能下降。
  3. 优雅关闭: Netty支持优雅关闭,确保在关闭服务器或连接时不会丢失任何正在处理的数据。通过适当地使用ChannelGroup或其他机制,可以实现连接的安全关闭。

Netty中有哪些内置的编解码器?

ByteToMessageDecoder、MessageToByteEncoder、StringDecoder和StringEncoder、ObjectDecoder和ObjectEncoder、LengthFieldBasedFrameDecoder: 根据长度字段来拆分消息,用于处理消息长度不固定的情况。

IO模型相关

同步与异步

同步与异步:从字面意思上就可以理解,异步里面有个异字,这个异字代表别人,所以同步与异步区分一个现场还是两个线程;
阻塞与非阻塞:强调等待还是不等待

同步:有一个活分为好多步骤,一个线程自己从头到尾自己把活干完(同步只有一个线程)
异步:有一个活分为好多步骤,至少有两个线程的情况下,A线程干活的情况下B线程也在干活,一起把这个活干完(异步至少有两个线程)

阻塞与非阻塞

同步与异步:从字面意思上就可以理解,异步里面有个异字,这个异字代表别人,所以同步与异步区分一个现场还是两个线程;
阻塞与非阻塞:强调等待还是不等待

阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.,把自己“挂起”等待结果的叫阻塞,还可以干别的活叫非阻塞

阻塞就是 线程执行A任务时,A任务有个停顿等待的过程,这个线程执行A时只能跟着阻塞等待不能干别的活

非阻塞就是 线程执行A任务时,A任务有个停顿等待的过程,这个线程执行A时不阻塞等待,它能干别的活

阻塞:假设你是一个餐厅的服务员,你正在等厨师做好一道菜给顾客。在厨师做菜的过程中,你什么都做不了,只能干等着,直到菜做好了你才能继续服务下一位顾客。这就是阻塞IO,你的工作(服务员的工作)被阻塞在了等待厨师做菜(IO操作)上

非阻塞:假设你是一个餐厅的服务员,你告诉厨师顾客要什么菜,然后你就去忙其他事情了(比如接待其他顾客)。每隔一段时间,你就去问一下厨师你的菜好了没有。这种方式虽然不会让你一直等着,但你需要不断地去询问,也就是“轮询”,这会消耗你的时间和精力(CPU资源)。

多路复用

多路复用:有一个selector专门负责监视多个IO操作然后及时反馈给当前线程,以便于当前线程可以专心搞其它事情(多路复用本质也是当前这一个线程在干活,所以它也是同步的,所以叫同步多路复用)

现在有一个多路复用器来帮你管理多个订餐请求。你告诉多路复用器你想要哪些餐厅的哪些菜,然后你就去忙其他事情了。多路复用器会帮你盯着这些餐厅,一旦有餐好了,他就会通知你来取。这样,你既可以处理多个订餐请求,又不需要一直等着或不断地去询问,大大提高了效率。

IO 模型

5种阻塞模型:同步阻塞I/O、同步非阻塞I/O、同步多路复用、异步阻塞(没有此情况)、异步非阻塞I/O、信号驱动 I/O

同步阻塞IO:是一个线程在处理IO请求,它处理这个客户端的IO的时候哪怕这个客户端的IO闲着它也不能处理别的客户端的IO,一个线程在干活所以是同步的,干活的时候不能干别的所以叫阻塞,所以它是同步阻塞IO

同步非阻塞IO:是一个线程在处理IO请求,它处理这个客户端的IO的时候如果这个客户端的IO闲着它可以处理别的客户端的IO,一个线程在干活所以是同步的,干活的时候可以干别的所以叫非阻塞,所以它是同步非阻塞IO

多路复用:有一个selector专门负责监视多个IO操作然后及时反馈给当前线程,以便于当前线程可以专心搞其它事情(多路复用本质也是当前这一个线程在干活,所以它也是同步的,所以叫同步多路复用)

异步阻塞(没有此情况)

异步非阻塞:有两个线程在干活,让另一个线程干活然后把结果告诉你

BIO、NIO、AIO

BIO:属于同步阻塞 IO 模型 。数据的读取写入必须阻塞在一个线程内等待其完成。

NIO:属于同步非阻塞的 I/O模型。网络通信基本都是NIO

AIO:(Asynchronous I/O)异步的IO模型。应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。目前来说 AIO 的应用还不是很广泛。Netty 之前也尝试使用过 AIO,不过又放弃了。这是因为,Netty使用了 AIO 之后,在 Linux 系统上的性能并没有多少提升。

缓冲区是什么意思?

缓冲区实质上是一个数组。在读取数据时,它是直接读到缓冲区中的。在写入数据时,它是写入到缓冲区中的。

种类:ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer

Java有哪些数据类型

这里是引用

基本数据类型sout打印出来的是数值, 引用数据类型sout打印出来的是对象的地址值
其中八种基本数据类型:byte、short、char‐‐>int‐‐>long‐‐>float‐‐>double,boolean
所占的空间大小:

	byte(1)
short(2)、char(2)
int(4)、long(8)
float(4)、double(8)

说说这 8 种基本数据类型对应的包装类型?
这八种基本类型都有对应的包装类分别为:Byte、Short、Character、Integer、Long、Float、Double、Boolean

那基本类型和包装类型有啥区别不?
Byte、Short、Character、Integer、Long、Float、Double、Boolean这些包装类的默认值是null,因为它们是对象类型。

基本类型是有默认值的:byte默认值0、short默认值0、char默认值’\u0000’、int默认值0、long默认值0L、float默认值0.0f、double默认值0.0d,boolean默认值false

零拷贝

零拷贝

传统的 IO 将一个文件通过 socket 写出,内部工作流程是这样的:

在传统的数据传输过程中,当应用程序需要从磁盘读取数据并通过网络发送时,通常需要经历以下步骤:

这里是引用

这个过程涉及了多次数据拷贝和上下文切换(用户态和内核态之间的切换),这会导致CPU资源的浪费和数据传输效率的降低。

“零拷贝”这个名字听起来很神奇,好像数据在传输过程中完全没有被拷贝一样。但实际上,这里的“零拷贝”是指减少了数据在用户空间和内核空间之间的拷贝次数,并不是没有拷贝而是减少了拷贝次数,特别是避免了将数据从内核空间拷贝到用户空间,然后再从用户空间拷贝回内核空间(如网络发送缓冲区)的过程。这样,数据的传输变得更加高效,减少了CPU的负担和内存带宽的占用。

Netty 的零拷贝了解么?

在 OS 层面上的 零拷贝通常指避免在 用户态(User-space) 与 内核态(Kernel-space) 之间来回拷贝数据。而在 Netty 层面 ,零拷贝主要体现在对于数据操作的优化。

Netty 中的零拷贝体现在以下几个方面

  1. 使用 Netty 提供的 CompositeByteBuf 类, 可以将多个ByteBuf 合并为一个逻辑上的 ByteBuf, 避免了各个 ByteBuf 之间的拷贝。
  2. ByteBuf 支持 slice 操作, 因此可以将 ByteBuf 分解为多个共享同一个存储区域的 ByteBuf, 避免了内存的拷贝。
  3. ByteBuf的duplicate操作

这里是引用
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BlackTurn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值