Netty为什么高效,为什么这么受欢迎?

文章目录

  • 前言
  • Netty 解决的问题
    • 简化网络编程
    • 粘包和拆包
  • 高性能的设计
    • 多线程调度
    • 零拷贝
  • 总结

前言

上篇文章通过 Java NIO 的处理流程与 Netty 的总体流程比较,并结合 Netty 的源码,可以更加清晰地理解Netty。本文将结合源码详细解析Netty的高效和强大功能的设计原理,学习 Netty 是如何实现其卓越的性能和功能特性,也希望可以在日后工作中利用到 Netty 的设计思想。

Netty 解决的问题

我们先看看使用 Netty 在网络编程中帮助我们解决了什么问题

简化网络编程

首先基于 Netty 初次编码的直观体验来讲,开发者不用手动处理网络通信细节,包括线程管理、I/O 处理、协议解析等,可以专注于业务逻辑的实现。也正是因为如此,在学习 Netty 时比较抽象难懂 。

下面对比一下 Java NIO 实现的简单的服务端和 Netty 实现的 HTTP 服务端,如下图,可以看到 Java NIO 的代码大概有 80 行,而且还没有实现 HTTP 协议,并且还是单线程,没有复杂的线程管理,更不用说性能什么的,而 Netty 实现的代码只有 30 多行,其中的差别一目了然。
在这里插入图片描述

粘包和拆包

我们一般说粘包和拆包都是说 TCP 协议的问题,因为当用户消息通过 UDP 协议传输时,操作系统不会对消息进行拆分,所以发送出去的一条 UDP 报文就是完整的用户消息,也就是每个 UDP 报文就是用户消息的边界。而当用户消息通过 TCP 协议传输时,消息可能会被操作系统分组成多个的 TCP 报文进行传输,这个时候接收方收到多个报文后,由于不知道消息的边界,也就无法读出一个有效的用户消息。

举个例子,当发送方准备发送 「Hi」和「I am Erdan」这两个消息,由于MTU限制、缓冲区的大小等条件,可能会出现几种情况:

第一种情况,两条消息分到一个报文中,像这样:
在这里插入图片描述
第二种情况,「I am Erdan」中的部分消息随「Hi」被分到一个报文中,像这样:
在这里插入图片描述
还可能会有第三、四…种情况,而当接收方接收到第一种情况时我们称之为粘包,第二种情况称之为拆包。并且上面的种种情况可以表明不能认为一个用户消息对应一个 TCP 报文,正因为这样,所以 TCP 是面向字节流的协议。

解决粘包和拆包的根本手段就是找出消息的边界,有几种方式:

  • 固定消息长度,这种方式灵活性不高,实际中很少用。
  • 特殊字符作为边界,HTTP 是一个非常好的例子,通过设置回车符、换行符作为 HTTP 报文协议的边界。在这里插入图片描述
  • 自定义消息结构:消息头消息体,可以自定义一个消息结构,由包头和数据组成,其中包头包是固定大小的,而且包头里有一个字段来说明紧随其后的数据有多大。

而 Netty 提供了固定长度解码器(FixedLengthFrameDecoder行分隔符解码器(LineBasedFrameDecoder分隔符解码器(DelimiterBasedFrameDecoder、**基于长度字段的解码器(LengthFieldBasedFrameDecoder)**几种方式来解决粘包问题,可以结合 Netty 的 ChannelPipeline 来使用。

除此之外 Netty 也提供了 HTTP、WebSocket、TCP、UDP几种协议的编解码器,这也是 Netty 灵活扩展强大之处。

高性能的设计

Netty 除了帮助开发人员解决了一些问题,还提高了网络编程性能,体现如下:

多线程调度

在网络编程中如果使用单线程来处理,即便是IO多路复用,吞吐和性能也是会有局限的。而 Netty 中通过 EventLoopGroup 管理线程池,每个线程就是一个 EventLoop,而 EventLoop 内部有一个 Selector 负责处理一个或多个 Channel 的注册、读写和其他事件。所以 Netty 通过 EventLoopGroupEventLoopSelector 的配合工作,实现了高效的并发处理能力。
在这里插入图片描述

既然是多线程处理,肯定要去考虑线程安全以确保程序的正确性。而 Netty 是如何保障线程安全的?Netty 通过使用管道(ChannelPipeline)和处理器(ChannelHandler)的方式来实现数据的处理和流转,而 ChannelHandler 会被分配给一个 EventLoop 处理,而 EventLoop 内部的数据结构和状态都是线程封闭的,不会被其他线程访问或修改。所以 Netty 通过合理地设计组件之间的关系,通过单线程执行、无锁设计等方式保证了在高并发情况下的线程安全性。

零拷贝

在传统的网络编程中,数据在进行网络传输之前需要从应用层缓冲区复制到操作系统内核的缓冲区,然后再从内核的缓冲区复制到网络设备的缓冲区。这种复制操作会增加 CPU 的负载和内存的开销,如下图
在这里插入图片描述

而 Netty 利用零拷贝技术来减少数据复制的次数,提高了数据传输的效率。零拷贝通过将数据从内核空间直接传输到网络适配器,避免了数据在内核空间和用户空间之间的复制,从而减少了CPU的负担。

在这里插入图片描述
具体体现在以下几个方面:

  • 零拷贝文件传输:Netty 的 FileRegion 接口提供了直接在文件系统和网络之间传输数据的功能。通过使用零拷贝技术,数据可以直接从磁盘读取并发送到网络设备,避免了中间的缓冲区拷贝,提高了文件传输的性能。

  • 零拷贝内存传输:Netty 的 ByteBuf 类型支持零拷贝的内存传输。当数据在应用程序和内核之间传输时,Netty 使用直接内存缓冲区(Direct ByteBuffer)来避免额外的数据拷贝操作,提高了内存传输的效率。

通过以上方式,Netty 实现了零拷贝技术在网络编程中的应用,提高了数据传输的效率和性能。这使得 Netty 在处理大量数据传输和高并发场景下具有更好的性能表现。

总结

总的来说,Netty 不论在功能、性能以及稳定性来讲都是一款很nice的网络编程框架,很多知名的项目都将 Netty 作为其网络通信的底层框架,比如Apache Kafka、Elasticsearch、gRPC、Dubbo等。熟悉这些框架的开发者通常都具备高并发开发经验,并且掌握 Netty 是理解这些框架的重要基础之一。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/76743.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

spring源码分析bean的生命周期(下)

doGetBean()执行过程 createBean()执行过程 一、DependsOn注解 spring创建对象之前会判断类上是否加了DependsOn注解,加了会遍历然后会添加到一个map中,spring会先创建DependsOn注解指定的类 二、spring类加载器 在合并BeanDefinition,确定…

【校招VIP】java语言类和对象之map、set集合

考点介绍: map、set集合相关内容是校招面试的高频考点之一。 map和set是一种专门用来进行搜索的容器或者数据结构,其搜索效率与其具体的实例化子类有关系。 『java语言类和对象之map、set集合』相关题目及解析内容可点击文章末尾链接查看! …

网络通信原理传输层TCP三次建立连接(第四十八课)

ACK :确认号 。 是期望收到对方的下一个报文段的数据的第1个字节的序号,即上次已成功接收到的数据字节序号加1。只有ACK标识为1,此字段有效。确认号X+1SEQ:序号字段。 TCP链接中传输的数据流中每个字节都编上一个序号。序号字段的值指的是本报文段所发送的数据的第一个字节的…

20W IP网络吸顶喇叭 POE供电吸顶喇叭

SV-29852T 20W IP网络吸顶喇叭产品简介 产品用途: ◆室内豪华型吸顶喇叭一体化网络音频解码扬声器,用于广播分区音频解码、声音还原作用 ◆应用场地如火车站、地铁、教堂、工厂、仓库、公园停车场等;室内使用效果均佳。 产品特点&#xff…

ARM体系结构学习笔记:过程调用标准AAPC、 ARM32调用约定、ARM64调用约定

参数传递的本质: 将上层函数变量复制一份, 传给下层函数. 过程调用标准AAPC(Arm Architecture Procedure Call) 有了标准, 才能够进行C调用汇编或者汇编调用C ARM32调用约定 参数是不同位数传参情况, 额外的参数被caller进行入栈, callee进行出栈 寄存器传参 寄存器返回 汇编…

OpenCV importerror:dll load failed

从预编译的二进制文件安装OpenCV,从github下载opencv-4.8.0-windows.exe 编译好的文件。按照官方文档拖入cv2.pyd文件。 https://docs.opencv.org/4.8.0/d5/de5/tutorial_py_setup_in_windows.html 使用pycharm运行时,出现报错,importerror…

leetcode做题笔记85最大矩形

给定一个仅包含 0 和 1 、大小为 rows x cols 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。 示例 1: 思路一:单调栈 int maximalRectangle(char** matrix, int matrixSize, int* matrixColSize){int dp[matrixSize…

Vue项目商品购物车前端本地缓存逻辑(适用H5/ipad/PC端)——前端实现购物车删除商品、购物车增减数量,清空购物车功能

一、需求 1、用户选择商品,自动回显在购物车列表中; 2、同个商品追加,购物车列表数量叠加; 3、开启赠送,选中的商品,在购物车中另增一条数据,且购物车列表价格显示为0;其实际价格在…

16、Flink 的table api与sql之连接外部系统: 读写外部系统的连接器和格式以及FileSystem示例(1)

Flink 系列文章 1、Flink 部署、概念介绍、source、transformation、sink使用示例、四大基石介绍和示例等系列综合文章链接 13、Flink 的table api与sql的基本概念、通用api介绍及入门示例 14、Flink 的table api与sql之数据类型: 内置数据类型以及它们的属性 15、Flink 的ta…

安装docker配置镜像加速器,容器等

1.安装docker服务,配置镜像加速器 2.下载系统镜像(Ubuntu、 centos) 3.基于下载的镜像创建两个容器 (容器名一个为自己名字全拼,一个为首名字字母) 4.容器的启动、 停止及重启操作 5.怎么查看正在运行的容器…

无需公网IP——搭建web站点

文章目录 概述使用 Raspberry Pi Imager 安装 Raspberry Pi OS设置 Apache Web 服务器测试 web 站点安装静态样例站点将web站点发布到公网安装 Cpolar内网穿透cpolar进行token认证生成cpolar随机域名网址生成cpolar二级子域名将参数保存到cpolar配置文件中测试修改后配置文件配…

基于PaddleOCR2.7.0发布WebRest服务测试案例

基于PaddleOCR2.7.0发布WebRest服务测试案例 #WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. #警告:这是一个开发服务器。不要在生产部署中使用它。请改用生产WSGI服务器。 输出结果…