netty基础知识梳理和总结

news/2025/2/23 8:29:11

目录标题

    • 由来
    • netty整体结构
        • 核心功能
          • 可扩展的事件模型
          • 统一的通信 API
          • 零拷贝机制与字节缓冲区
        • 传输服务
        • 协议支持
    • netty的IO模型
    • netty核心组件
        • Channel
        • EventLoop、EventLoopGroup
        • ChannelHandler
        • ChannelPipeline
        • Bootstrap
        • Future
    • netty的bytebuf
      • bytebuf的内部构造
      • bytebuf的使用模式
      • ByteBuf的释放
    • netty的零拷贝
      • Kafka的零拷贝
      • **1. Kafka 的零拷贝**
        • **定义**
        • **实现原理**
        • **优点**
        • **缺点**

由来

Netty是由JBOSS公司提供的一个java开源框架
是一个基于NIO的客户、服务器端编程框架,用以快速开发高性能、高可靠性的网络服务器和客户端程序。

netty整体结构

在这里插入图片描述

核心功能
可扩展的事件模型
  • 提供灵活且可扩展的事件处理机制,适应多种应用场景。
统一的通信 API
  • 统一接口:无论是 HTTP 还是 Socket,均使用统一的 API,简化了操作流程。
  • 易用性:通过一致的接口设计,降低了开发复杂度。
零拷贝机制与字节缓冲区
  • 高效数据传输:采用零拷贝机制,减少数据在内存中的拷贝次数,提升性能。
  • 字节缓冲区:优化数据存储和读取效率。
传输服务
  • Socket,基于TCP
  • Datagram(数据报),基于UDP
  • HTTP 协议
  • In-VM Pipe(管道协议)
协议支持
  • HTTP 与 WebSocket:支持标准的 HTTP 和 WebSocket 协议。
  • SSL 安全套接字协议:提供安全的通信保障。
  • Google Protobuf:支持高效的序列化框架。
  • 压缩支持
    • 支持 zlibgzip 压缩,优化传输效率。
  • 大文件传输:支持高效的大文件传输能力。
  • RTSP(实时流传输协议):支持 TCP/IP 协议体系中的应用层协议,适用于实时流媒体场景。
  • 二进制协议支持:支持自定义二进制协议,并提供完整的单元测试。

netty的IO模型

Netty就是使用Java的NIO实现了Reactor线程模型是其实现高性能的一个核心点。
Netty是基于NIO2的IO模型(IO多路复用模型,非循环进行read的系统调用的原始NOBlock IO模型)。

  • 常见的Reactor线程模型有三种
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

Netty就是结合了NIO的特点,应用了Reactor线程模型所实现的。

在这里插入图片描述

  • 在Netty模型中,负责处理新连接事件的是BossGroup,负责处理其他事件的是WorkGroup。Group就是线程池的概念。
  • NioEventLoop表示一个不断循环的执行处理任务的线程,用于监听绑定在其上的读/写事件。NioEventLoop就是Group里面的工作线程。
  • 通过Pipeline(管道)执行业务逻辑的处理,Pipeline中会有多个ChannelHandler,真正的业务逻辑是在ChannelHandler中完成的。

netty核心组件

Channel

可以理解为是socket连接,在客户端与服务端连接的时候就会建立一个Channel。
常用的 Channel 类型:

  • NioSocketChannel,NIO的客户端 TCP Socket 连接。
  • NioServerSocketChannel,NIO的服务器端 TCP Socket 连接。
  • NioDatagramChannel, UDP 连接。
  • NioSctpChannel,客户端 Sctp 连接。
  • NioSctpServerChannel,Sctp 服务器端连接,这些通道涵盖了 UDP 和 TCP 网络 IO 以及文件IO。
EventLoop、EventLoopGroup
  • 在 Netty 中每个 Channel 都会被分配到一个 EventLoop。一个 EventLoop 可以服务于多个 Channel。
  • 每个 EventLoop 会占用一个 Thread,同时这个 Thread 会处理 EventLoop 上面发生的所有 IO 操作和事件
  • EventLoopGroup 是用来生成 EventLoop 的
ChannelHandler

ChannelHandler对使用者而言,可以说是最重要的组件了,因为对于数据的入站和出站的业务逻辑的编写都是在ChannelHandler中完成的。
在这里插入图片描述

  • ChannelInboundHandler 入站事件处理器
  • ChannelOutBoundHandler 出站事件处理器

例如:在 入站事件处理器中可以实现了channelRead方法,获取到客户端传来的数据
ChannelHandler只能完成编码解码处理、读写操作中的一种,所以ChannelHandler都是成组出现来完成功能。

ChannelPipeline

一个Channel包含了一个ChannelPipeline,而ChannelPipeline中维护了一个ChannelHandler的列表。
ChannelHandlerContext进行维护ChannelHandler与Channel和ChannelPipeline之间的映射关系。
在这里插入图片描述
ChannelHandler按照加入的顺序会组成一个双向链表

  • 入站事件从链表的head往后传递到最后一个ChannelHandler
  • 出站事件从链表的tail向前传递,直到最后一个ChannelHandler
  • 两种类型的ChannelHandler相互不会影响。
Bootstrap

Bootstrap是引导的意思,它的作用是配置整个Netty程序,将各个组件都串起来,最后绑定端口、启动Netty服务。
Netty中提供了2种类型的引导类,一种用于客户端(Bootstrap),而另一种(ServerBootstrap)用于服务器。
它们的区别在于:

  • ServerBootstrap 只需要绑定一个监听连接的端口,而 Bootstrap 则是需要远程节点的IP和端口
  • 客户端Bootstrap 只需要一个EventLoopGroup来实现消息的读取和写入,而ServerBootstrap则需要两个EventLoopGroup,一个来实现处理新连接事件,一个用来处理消息的读取和写入等事件。
Future

Future提供了一种在操作完成时通知应用程序的方式。这个对象可以看作是一个异步操作的结果的占位符,它将在未来的某个时刻完成,并提供对其结果的访问。

netty的bytebuf

bytebuf的内部构造

ByteBuf的三个指针:

  • readerIndex(读指针)
    指示读取的起始位置, 每读取一个字节, readerIndex自增累加1。 如果readerIndex 与writerIndex 相等,ByteBuf 不可读 。
  • writerIndex(写指针)
    指示写入的起始位置, 每写入一个字节, writeIndex自增累加1。如果增加到 writerIndex 与capacity() 容量相等,表示 ByteBuf 已经不可写。
  • maxCapacity(最大容量)
    指示ByteBuf 可以扩容的最大容量, 如果向ByteBuf写入数据时, 容量不足, 可以进行扩容

当从 ByteBuf 读取时,它的 readerIndex(读索引)将会根据读取的字节数递增。
同样,当写 ByteBuf 时,它的 writerIndex(写索引) 也会根据写入的字节数进行递增。

ByteBuf内部空间结构:
在这里插入图片描述

  • byteBuf.readByte(),内部通过移动readerIndex进行读取
  • byteBuf.writeInt(i);
  • discardReadBytes(),可以将已经读取的数据进行丢弃处理,就可以回收已经读取的字节空间,可写空间就会变大
  • clear() ,重置readerIndex 、 writerIndex 为0,需要注意的是,重置并没有删除真正的内容

bytebuf的使用模式

  • 堆缓冲区(HeapByteBuf)
    • 优点:内存的分配和回收速度比较快,可以被JVM自动回收
    • 缺点:进行socket的IO读或者写时,需要额外做一次内存复制,,将堆内存对应的缓冲区复制到内核Channel中,性能下降。
  • 直接缓冲区(DirectByteBuf),非堆内存
    • 优点:它写入或从Socket Channel中读取时,由于减少了一次内存拷贝(零拷贝),速度比堆内存块
    • 缺点:内存的分配和回收速度慢,并且如果不注意内存回收,会导致内存泄漏
  • 复合缓冲区,顾名思义就是将上述两类缓冲区聚合在一起。
    • 复合缓冲区的核心思想是通过逻辑上的组合,而不是物理上的合并,来管理多个缓冲区
    • 工作原理
      • 数据的逻辑组合:通过维护一个内部的组件列表(components),记录每个子缓冲区的引用和偏移量。每个子缓冲区可以是堆缓冲区或直接缓冲区。
      • 统一的读写接口:复合缓冲区对外提供了一个统一的读写接口,用户可以通过索引访问整个缓冲区的内容,而不需要关心底层是由哪些子缓冲区组成的。
    • 使用场景:在网络协议中,可能需要将头部(通常较小,适合堆缓冲区)和负载(通常较大,适合直接缓冲区)分开存储,但又希望以统一的方式操作。

ByteBuf的释放

  • 手动释放,就是在使用完成后,调用ReferenceCountUtil.release(byteBuf); 进行释放
  • 自动释放
    • 入站的TailHandler:Netty的ChannelPipleline的流水线的末端是TailHandler,默认情况下如果每个入站处理器Handler都把消息往下传,TailHandler会释放掉ReferenceCounted类型的消息。
    • 继承SimpleChannelInboundHandler,类中包含一个onUnhandledInboundMessage方法,会释放缓存
    • HeadHandler的出站释放,类似于入站

netty的零拷贝

所谓的零拷贝,就是取消用户空间与内核空间之间的数据拷贝操作,应用进程每一次的读写操作,都可以通过一种方式,让应用进程向用户空间写入或者读取数据,就如同直接向内核空间写入或者读取数据一样,再通过 DMA 将内核中的数据拷贝到网卡,或将网卡中的数据 copy 到内核。

Netty零拷贝主要体现在三个方面:

  • Netty的接收和发送ByteBuffer是采用DIRECT BUFFERS,使用堆外的直接内存(内存对象分配在JVM中堆以外的内存)进行Socket读写,不需要进行字节缓冲区的二次拷贝。如果采用传统堆内存(HEAP BUFFERS)进行Socket读写,JVM会将堆内存Buffer拷贝一份到直接内存中,然后写入Socket中。
  • Netty提供了组合Buffer对象,也就是CompositeByteBuf 类,可以将 ByteBuf 分解为多个共享同一个存储区域的 ByteBuf,避免了内存的拷贝。
  • Netty的文件传输采用了FileRegion 中包装 NIO 的 FileChannel.transferTo() 方法,它可以直接将文件缓冲区的数据发送到目标Channel,避免了传统通过循环write方式导致的内存拷贝问题。(类似于kafka的零拷贝)

Kafka的零拷贝

Kafka 和 Netty 都使用了零拷贝技术,但它们的应用场景和实现方式有所不同。以下是对两者的区别、优缺点的详细分析:


1. Kafka 的零拷贝

定义

Kafka 的零拷贝是指在数据传输过程中,尽量减少数据在用户空间和内核空间之间的拷贝次数。它主要依赖于操作系统提供的 sendfile 系统调用(Linux 环境下)来实现。

实现原理
  • 当 Kafka 从磁盘读取数据并发送到网络时,传统的方式需要经过多次数据拷贝:
    1. 数据从磁盘读取到内核缓冲区。
    2. 数据从内核缓冲区拷贝到用户空间缓冲区。
    3. 数据从用户空间缓冲区再拷贝回内核缓冲区(用于网络传输)。
    4. 数据通过网络接口发送出去。
  • 使用零拷贝后:
    • 数据直接从内核缓冲区通过 sendfile 系统调用传输到网络接口,避免了用户空间的参与,减少了两次拷贝操作。
优点
  1. 性能提升:减少了数据拷贝次数和上下文切换,显著提高了吞吐量。
  2. 降低 CPU 开销:避免了用户空间和内核空间之间的数据拷贝,降低了 CPU 的负担。
  3. 适合大数据传输:特别适用于 Kafka 这种需要频繁进行大文件传输的场景。
缺点
  1. 灵活性较低:零拷贝要求数据是连续存储的,且不能对数据进行修改。如果需要对数据进行处理(如加密、压缩等),则无法直接使用零拷贝。
  2. 依赖操作系统支持:需要底层操作系统提供 sendfile 或类似机制的支持。

http://www.niftyadmin.cn/n/5863199.html

相关文章

Ubuntu 下 nginx-1.24.0 源码分析 - ngx_atoi 函数

ngx_atoi 声明在 src/core/ngx_string.h ngx_int_t ngx_atoi(u_char *line, size_t n); 定义在 src/core/ngx_string.c ngx_int_t ngx_atoi(u_char *line, size_t n) {ngx_int_t value, cutoff, cutlim;if (n 0) {return NGX_ERROR;}cutoff NGX_MAX_INT_T_VALUE / 10;cutlim…

MySql数据库运维学习笔记

数据库运维常识 DQL、DML、DCL 和 DDL 是 SQL(结构化查询语言)中的四个重要类别,它们分别用于不同类型的数据库操作,下面为你简单明了地解释这四类语句: 1. DQL(数据查询语言,Data Query Langu…

网页请求腾讯云环境的云函数

背景&#xff1a;uniapp&#xff0c;做一个管理后台 需求&#xff1a;在PC端网页请求云环境的云函数 npm npm install cloudbase/js-sdk -S 在APP中&#xff0c;封装匿名登陆&#xff0c;因为未登录时无法请求云函数 app.vue <script>import Vue from vueimport cl…

libxls库的编译以及基于Visual studio的配置

最近有一个需求在windows处理xls&#xff0c;所以就需要libxls这个库&#xff0c;调查了一下&#xff0c;基于C的库的解析情况如下&#xff1a; 所以最理想的就是Libxlsd个开源的方案 基于历史整理的 libxls 在 MinGW 下的编译步骤 前提条件 系统&#xff1a;Windows&#…

Visual Studio Code 2025 安装与高效配置教程

一、软件简介与下载 1. Visual Studio Code 是什么&#xff1f; Visual Studio Code&#xff08;简称VS Code&#xff09;是微软推出的免费开源代码编辑器&#xff0c;支持 智能代码补全、Git集成、插件扩展 等功能&#xff0c;适用于前端开发、Python、Java等多种编程场景。…

巧妙实现右键菜单功能,提升用户操作体验

在动态交互式图库中&#xff0c;右键菜单是一项能够显著提升用户操作便捷性的功能。它的设计既要响应用户点击位置&#xff0c;又需确保菜单功能与数据操作紧密结合&#xff0c;比如删除图片操作。以下将通过一段实际代码实现&#xff0c;展示从思路到实现的详细过程。 实现右键…

对计算机中缓存的理解和使用Redis作为缓存

使用Redis作为缓存缓存例子缓存的引入 Redis缓存的实现 使用Redis作为缓存 缓存 ​什么是缓存&#xff0c;第一次接触这个东西是在考研学习408的时候&#xff0c;计算机组成原理里面学习到Cache缓存&#xff0c;用于降低由于内存和CPU的速度的差异带来的延迟。它是在CPU和内存…

[SQL] 事务的四大特性(ACID)

&#x1f384;事务的四大特性 以下就是事务的四大特性&#xff0c;简称ACID。 原子性&#x1f4e2;事务时不可分割的最小操作单元&#xff0c;要么全部成功&#xff0c;要么全部失败。一致性&#x1f4e2;事务完成后&#xff0c;必须使所有的数据都保持一致隔离性&#x1f4e2…