轻淘客网站建设,wordpress挂马,wordpress固定链接精简,郑州seo排名收费Netty基础知识
什么是Netty#xff1f;
Netty 是一款用于高效开发网络应用的 NIO 网络框架#xff0c;它大大简化了网络应用的开发过程#xff1b; 封装了JDK底层的NIO模型#xff0c;提供高度可用的API#xff0c;用于快速开发高性能服务端和客户端#xff1b;精心设计…Netty基础知识
什么是Netty
Netty 是一款用于高效开发网络应用的 NIO 网络框架它大大简化了网络应用的开发过程 封装了JDK底层的NIO模型提供高度可用的API用于快速开发高性能服务端和客户端精心设计的 Reactor 线程模型支持高并发海量连接自带编解码器解决拆包和粘包问题用户只关心业务逻辑即可自带各种协议栈让你处理任何一种通用协议几乎都不用亲自动手。
Netty对比Java NIO有哪些优势
易用性 使用 JDK NIO 编程需要了解很多复杂的概念比如 Channels、Selectors、Sockets、Buffers 等编码复杂程度令人发指Netty 在 NIO 基础上封装了更加人性化的 API统一的 API阻塞/非阻塞 大大降低了开发者的上手难度Netty 提供了很多开箱即用的工具例如常用的行解码器、长度域解码器等而这些在 JDK NIO 中都需要你自己实现。 稳定性 Netty 更加可靠稳定修复和完善了 JDK NIO 较多已知问题例如臭名昭著的 select 空转导致 CPU 消耗 100%TCP 断线重连keep-alive 检测等问题 可扩展性 一个是可定制化的线程模型用户可以通过启动的配置参数选择 Reactor 线程模型另一个是可扩展的事件驱动模型将框架层和业务层的关注点分离。大部分情况下开发者只需要关注 ChannelHandler 的业务逻辑实现。 更低的资源消耗 对象池复用技术。 Netty 通过复用对象避免频繁创建和销毁带来的开销零拷贝技术。 除了操作系统级别的零拷贝技术外Netty 提供了更多面向用户态的零拷贝技术例如 Netty 在 I/O 读写时直接使用 DirectBuffer从而避免了数据在堆内存和堆外内存之间的拷贝。
Netty与Tomcat的区别是什么
Netty 和 Tomcat 最大的区别在于对通信协议的支持 Tomcat 是一个 HTTP Server它主要解决 HTTP 协议层的传输而 Netty 不仅支持 HTTP 协议还支持 SSH、TLS/SSL 等多种应用层的协议而且能够自定义应用层协议。Tomcat 需要遵循 Servlet 规范HTTP协议的请求/响应模型然而 Netty 与 Tomcat 侧重点不同所以不需要受到 Servlet 规范的约束可以最大化发挥 NIO 特性如果仅仅需要一个 HTTP 服务器那么推荐使用 Tomcat。术业有专攻Tomcat 在这方面的成熟度和稳定性更好。但如果要做面向 TCP 的网络应用开发那么 Netty 才是最佳选择。
什么是 Reactor 线程模型 上图是主从Reactor多线程模型 MainReactor 只负责监听连接建立事件SubReactor 只负责监听读写事件Reactor 主线程负责通过 Acceptor 对象处理 MainReactor 监听到的连接建立事件当Acceptor 完成网络连接的建立之后MainReactor 会将建立好的连接分配给 SubReactor 进行后续监听当一个连接被分配到一个 SubReactor 之上时会由 SubReactor 负责监听该连接上的读写事件。当有新的读事件OP_READ发生时SubReactor就会调用对应的 Handler 读取数据然后分发给 Worker 线程池中的线程进行处理并返回结果。待处理结束之后Handler 会根据处理结果调用 send 将响应返回给客户端当然此时连接要有可写事件OP_WRITE才能发送数据。 Reactor的工作流程主要分为四步 连接注册Channel 建立后注册至 Reactor 线程中的 Selector 选择器事件轮询轮询 Selector 选择器中已注册的所有 Channel 的 I/O 事件事件分发为准备就绪的 I/O 事件分配相应的处理线程任务处理Reactor 线程还负责任务队列中的非 I/O 任务每个 Worker 线程从各自维护的任务队列中取出任务异步执行。
Netty的架构与核心主件
Netty的工作模型是什么 Netty 抽象出两组线程池BossGroup 专门用于接收客户端的连接WorkerGroup 专门用于网络的读写BossGroup 和 WorkerGroup 类型都是 NioEventLoopGroup相当于一个事件循环组其中包含多个事件循环NioEventLoopNioEventLoop 表示一个不断循环的、执行处理任务的线程每个 NioEventLoop 都有一个Selector 对象与之对应用于监听绑定在其上的连接这些连接上的事件由 Selector 对应的这条线程处理每个 Boss NioEventLoop 会监听 Selector 上连接建立的 accept 事件然后处理 accept 事件与客户端建立网络连接生成相应的 NioSocketChannel 对象一个 NioSocketChannel 就表示一条网络连接。之后会将 NioSocketChannel 注册到某个 Worker NioEventLoop 上的 Selector 中。每个 Worker NioEventLoop 会监听对应 Selector 上的 read/write 事件当监听到 read/write 事件的时候会通过 Pipeline 进行处理。一个 Pipeline 与一个 Channel 绑定在 Pipeline 上可以添加多个 ChannelHandler每个 ChannelHandler 中都可以包含一定的逻辑例如编解码等。Pipeline 在处理请求的时候会按照我们指定的顺序调用 ChannelHandler。
Netty的逻辑架构是怎样的有哪些核心组件 Netty 可分为网络通信层、事件调度层、服务编排层每一层各司其职。网络通信层核心组件包含BootStrap、ServerBootStrap、Channel Bootstrap客户端启动器只绑定一个 EventLoopGroupServerBootStrap服务端启动器监听本地端口会绑定两个 EventLoopGroup分别是 Boss 和 WorkerChannel可以理解为是对Socket的封装一个Channel代表一条新连接对于数据的读写都可以在这条连接上操作 事件调度层核心组件包含EventLoopGroup、EventLoop EventLoopGroup的本质是线程池组一个 EventLoopGroup 往往包含一个或者多个 EventLoop。EventLoop 用于处理 Channel 生命周期内的所有 I/O 事件如 accept、connect、read、write 等 I/O 事件EventLoop 的本质是线程池每个 EventLoop 负责处理多个 Channel 服务编排层核心组件包括 ChannelPipeline、ChannelHandler、ChannelHandlerContext ChannelPipeline负责组装各种 ChannelHandler内部通过双向链表将不同的 ChannelHandler 链接在一起。ChannelHandler字面含义上可以反映出ChannelHandler是用来操作Channel的ChannelPipeline与ChannelHandler组成了一种责任链模式一般ChannelHandler是直接面对开发者的数据的编码解码等都是由ChannelHandler完成ChannelHandlerContext用于保存 ChannelHandler 上下文通过 ChannelHandlerContext 我们可以知道 ChannelPipeline 和 ChannelHandler 的关联关系。
Netty组件的工作流程是怎样的 服务端启动初始化时有 Boss EventLoopGroup 和 Worker EventLoopGroup 两个组件其中 Boss 负责监听网络连接事件。当有新的网络连接事件到达时则将 Channel 注册到 Worker EventLoopGroupWorker EventLoopGroup 会被分配一个 EventLoop 负责处理该 Channel 的读写事件。每个 EventLoop 都是单线程的通过 Selector 进行事件循环当客户端发起 I/O 读写事件时服务端 EventLoop 会进行数据的读取然后通过 Pipeline 触发各种监听器进行数据的加工处理客户端数据会被传递到 ChannelPipeline 的第一个 ChannelInboundHandler 中数据处理完成后将加工完成的数据传递给下一个 ChannelInboundHandler当数据写回客户端时会将处理结果在 ChannelPipeline 的 ChannelOutboundHandler 中传播最后到达客户端。
EventLoop 是一种什么模型
EventLoop 这个概念其实并不是 Netty 独有的它是一种事件等待和处理的程序模型可以解决多线程资源消耗高的问题。每当事件发生时应用程序都会将产生的事件放入事件队列当中然后 EventLoop 会轮询从队列中取出事件执行或者将事件分发给相应的事件监听者执行。事件执行的方式通常分为立即执行、延后执行、定期执行几种。在Netty 中EventLoop的实现类叫做NioEventLoop是 Reactor 线程模型的事件处理引擎。
Netty 的无锁化设计体现在哪
当accept事件触发时事件会被注册到WorkerEventLoopGroup 中的一个 NioEventLoop 上由于每个请求的Channel都只与一个NioEventLoop绑定所以说 Channel 生命周期的所有事件处理都是线程独立的不同的 NioEventLoop 线程之间不会发生任何交集NioEventLoop 完成数据读取后会调用绑定的 ChannelPipeline 进行事件传播数据在传播过程中由具体的ChannelHandler处理整个过程是串行化执行没有线程安全问题。
Netty 是如何解决 JDK epoll 空轮询的 Bug 的
Selector每次执行 select 操作之前记录当前时间 currentTimeNanos然后计算本次select的截止时间deadline根据当前时间与截止时间比较如果超时结束本次select轮询操作如果没有超时且任务队列中出现任务需要处理结束select轮询开始处理任务如果没有超时且任务队列没有任务 调用NIO底层的select方法进行阻塞会一直阻塞到截止时间同时记录轮询次数。阻塞可以被外部任务唤醒阻塞结束后如果阻塞时间小于截止时间说明阻塞被提前唤醒如果唤醒没有任务说明可能触发了空轮询的BugNetty会对空轮询次数进行统计当次数达到一定阈值512时重建Selector将老的Selector上的Channel注册到新Selector上。
ChannelPipeline中的Inbound事件与Outbound事件的区别是什么
ChannelPipeline是一个双向链表结构头尾分别维护了Head节点与Tail节点用户自定义的ChannelHandler会被插入到Head与Tail之间Inbound事件与Outbound事件是ChannelPipeline最主要的两种事件传播方式两者的主要区别是事件类型与传播顺序传播顺序 Inbound事件的传播顺序为Head - h1 - h2 - h3 - TailOutbount事件的传播顺序正好相反 Tail - h3 - h2 - h1 - Head 事件类型 Inbound事件一般指应用程序被动接收的事件由外部触发例如接收了新的I/O事件Tail节点会做一些收尾工作如资源释放等Outbount一般由应用程序主动触发例如应用程序从socket读取或写入数据head节点会执行操作系统底层的api完成具体的动作。
ChannelPipeline中异常传播的顺序是什么
异常传播顺序与ChannelHandler的注册顺序一致与Inbound和Outbound无关。
Netty的编解码
什么叫做拆包与粘包
TCP 传输协议是面向流的没有数据包界限。客户端向服务端发送数据时可能将一个完整的报文拆分成多个小报文进行发送也可能将多个报文合并成一个大的报文进行发送。因此就有了拆包和粘包。
Netty如何解决半包与粘包问题的
FixedLengthFrameDecoder 用来解决固定大小数据包的粘包问题LineBasedFrameDecoder 适合对文本进行按行分包DelimiterBasedFrameDecoder 适合按特殊字符作为分包标记的场景LengthFieldBasedFrameDecoder 可以支持复杂的自定义协议分包等等。
内存管理与ByteBuf
JVM堆内内存与堆外内存的区别是什么
堆内内存由 JVM GC 自动回收内存降低了 Java 用户的使用难度堆外内存不受 JVM 管理使用后需要手动释放如果使用不当容易造成内存泄漏且排查问题会比较困难当进行网络 I/O 操作、文件读写时堆内内存都需要转换为堆外内存然后再与底层设备进行交互所以直接使用堆外内存可以减少一次内存拷贝堆外内存可以实现进程之间、JVM 多实例之间的数据共享。
Netty的零拷贝指的是什么
DirectByteBuffer Netty提供的DirectByteBuffer直接将数据分配到堆外内存中避免在 Socket 读写时缓冲数据在堆外与堆内进行频繁复制 CompositeByteBuf 对于传统的ByteBuffer如果需要将两个ByteBuffer中的数据组合到一起需要首先创建一个sizesize1size2大小的新的数组然后将两个数组中的数据拷贝到新的数组中Netty利用CompositeByteBuf可以避免这种内存拷贝因为CompositeByteBuf并没有真正将多个Buffer组合起来而是保存了它们的引用从而实现了零拷贝 FileRegion Netty 使用 FileRegion 实现文件传输FileRegion 底层封装了 FileChannel#transferTo() 方法可以将文件缓冲区的数据直接传输到目标 Channel避免内核缓冲区和用户态缓冲区之间的数据拷贝这属于操作系统级别的零拷贝。
Netty如何回收堆外内存
首先Netty是通过DirectByteBuffer对象分配堆外内存的在堆内存放的 DirectByteBuffer 对象并不大仅仅包含堆外内存的地址、大小等属性同时还会创建对应的 Cleaner 对象这个Cleaner是专门用来回收堆外内存的Cleaner是JAVA四种引用类型中PhantomReference虚引用的子类PhantomReference不能单独使用必须与ReferenceQueue联合使用ReferenceQueue 用于保存需要回收的 Cleaner 对象当JVM发生 GC 时DirectByteBuffer 对象被回收此时 Cleaner 对象不再有任何引用关系然后被添加到ReferenceQueue中并执行clean方法clean() 方法主要做两件事情 将 Cleaner 对象从 Cleaner 链表中移除调用 unsafe.freeMemory 方法清理堆外内存 总体来说Netty是通过虚引用的特性将堆外内存对象与堆内内存对象联系起来然后在JVM GC时进行同步回收
JDK NIO 的 ByteBuffer 有什么缺陷
ByteBuffer 分配的长度是固定的无法动态扩缩容所以很难控制需要分配多大的容量ByteBuffer 只能通过 position 获取当前可操作的位置因为读写共用的 position 指针所以需要频繁调用 flip、rewind 方法切换读写状态对使用者不友好容易出错。
Netty 的 ByteBuf 有什么优势
容量可以按需动态扩展类似于 StringBuffer读写采用了不同的指针读写模式可以随意切换不需要调用 flip 方法通过内置的复合缓冲类型可以实现零拷贝支持引用计数支持缓存池。
Netty 的 ByteBuf有哪些分类
Pooled与Unpooled池化与非池化 Pooled池化方式每次分配内存时都会从系统预先分配好的一段内存中来取Unpooled非池化方式每次分配内存时都会调用系统API向操作系统申请内存创建ByteBuf Unsafe与非Unsafe Unsafe会先计算数据的内存地址偏移量通过unsafe对象的native API来操作数据非Unsafe不依赖JDK的unsafe对象它是通过数组下标方式来获取数据或者是通过JDK 底层的ByteBuffer API进行读写一般而言unsafe方式效率更高一些 Heap与Direct Heap代表堆上内存分配会被JVM GC管理Direct代表堆外内存分配调用JDK底层API进行分配系统内存效率更高但不受GC直接控制需要手动释放内存。
Netty 的内存规格是怎样的 ChunkNetty中所有内存都是以Chunk为单位分配的一个Chunk有16M例如当前需要1M内存那么就需要向系统申请一个Chunk单位的内存然后再从这个Chunk中进一步划分PageChunk的划分单位为Page一个Page有8K那么一个Chunk就可以划分出2048个PageSubPage有时候我们需要的内存远达不到一个Page的大小那么Netty根据实际需要对Page进一步划分成SubPage。
Netty 的内存池是如何设计的
Netty的内存池分四种内存规格管理内存分别为 Tiny、Small、Normal、HugePoolChunk 负责管理 8K 以上的内存分配PoolSubpage 用于管理 8K 以下的内存分配。当申请内存大于 16M 时不会经过内存池直接分配。设计了本地线程缓存机制 PoolThreadCache用于提升内存分配时的并发性能。用于申请 Tiny、Small、Normal 三种类型的内存时会优先尝试从 PoolThreadCache 中分配PoolChunk 使用伙伴算法管理 Page以二叉树的数据结构实现是整个内存池分配的核心所在每调用 PoolThreadCache 的 allocate() 方法到一定次数会触发检查 PoolThreadCache 中缓存的使用频率使用频率较低的内存块会被释放线程退出时Netty 会回收该线程对应的所有内存。
Netty 的对象池是如何设计的
Netty 为了避免多线程竞争问题每个线程都会持有各自的 Recycler 对象池内部通过 FastThreadLocal 来实现每个线程的私有化Recycler 有两个重要的组成部分Stack 和 WeakOrderQueue从 Recycler 获取对象时优先从 Stack 中查找如果 Stack 没有可用对象会尝试从 WeakOrderQueue 迁移部分对象到 Stack 中Recycler 回收对象时分为同线程对象回收和异线程对象回收两种情况同线程回收直接向 Stack 中添加对象异线程回收向 WeakOrderQueue 中的 Link 添加对象对象回收都会控制回收速率每 8 个对象会回收一个其他的全部丢弃。