【面试突击】深挖网络 IO 面试实战

2024-01-22 12:28

本文主要是介绍【面试突击】深挖网络 IO 面试实战,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

🌈🌈🌈🌈🌈🌈🌈🌈
欢迎关注公众号(通过文章导读关注:【11来了】),及时收到 AI 前沿项目工具及新技术的推送!

在我后台回复 「资料」 可领取编程高频电子书
在我后台回复「面试」可领取硬核面试笔记

文章导读地址:点击查看文章导读!

感谢你的关注!

🍁🍁🍁🍁🍁🍁🍁🍁

深挖网络 IO 面试实战

学前须知:

这个模块对网络 IO 这块进行深挖,深入理解了网络 IO 之后,可以跟面试官聊的有来有回,通过深入讨论,你可以展示你对网络 I/O 了解的很深入,以及你如何将这些知识应用到实际的服务器架构和性能优化中,那面试的结果一定是非常不错的

(切记不要对每一块的内容都浅尝辄止,没有技术深度是无法让面试官刮目相看的)

这个模块对网络 IO 这块进行深挖,深入理解了网络 IO 之后,可以跟面试官聊的有来有回,那面试的结果一定是非常不错的

如果每次面试官问一个问题,你都是回答了解一下,那样只会显出你对这块内容的理解太表层!

这个模块对网络 IO 这块进行深挖,深入理解了网络 IO 之后,可以跟面试官聊的有来有回,那面试的结果一定是非常不错的

如果每次面试官问一个问题,你都是回答了解一下,那样只会显出你对这块内容的理解太表层!

只要问到网络 IO 方面,那么一定会去问 Netty 的内容,因为几乎所有的网络通信,只要是用 Java 做的,都是使用 Netty 框架来进行网络通信的,所以 Netty 的东西一定要了解,如果不了解 Netty ,就像你学习 Java 不了解 SpringBoot 一样

Netty 架构原理图

问到 Netty 了,那么 NIO、BIO、AIO 肯定是要了解的,由于面试突击里之前已经写过这块的内容了,这里就不重复说了

下边说一下 Netty 的架构原理图,从整体架构学习:

Netty 处理流程:

1、BossGroup 和 WorkerGroup 都是线程组,BossGroup 负责接收客户端发送来的连接请求,NioEventLoop 是真正工作的线程,用来响应客户端的 accept 事件

2、当接收到连接建立 Accept 事件,获取到对应的 SocketChannel,封装成 NIOSocketChannel,并注册到 Worker 线程池中的某个 NioEventLoop 线程的 selector 中

3、当 Worker 线程监听到 selector 中发生自己感兴趣的事件后,就由 handler 进行处理
在这里插入图片描述

Netty 为什么这么快?

那么 Netty 作为高性能的网络 IO 框架,一定要了解 Netty 在哪些方面保证了高性能:

  1. 传输:用什么样的通道将数据发送给对方,BIO、NIO 或者 AIO,IO 模型在很大程度上决定了框架的性能。IO模型的选择

    • Netty 使用 NIO 进行网络传输,可以提供非阻塞的 IO 操作,极大提升了性能
  2. 协议:采用什么样的通信协议,HTTP 或者内部私有协议。协议的选择不同,性能模型也不同。相比于公有协议,内部私有协议的性能通常可以被设计的更优。协议的选择

    • Netty 支持丰富的网络协议,如 TCP、UDP、HTTP、HTTP/2、WebSocket 等,既保证了灵活性,又可以实现高性能
    • 并且 Netty 可以定制私有协议,避免传输不必要的数据,进一步提升性能
  3. 线程模型:数据报如何读取?读取之后的编解码在哪个线程进行,编解码后的消息如何派发,Reactor 线程模型的不同,对性能的影响也非常大。线程模型的选择

    • Netty 使用主从 Reactor 多线程模型,进一步提升性能
  4. 零拷贝:Netty 中使用了零拷贝,来提升数据传输速度

Netty 中的零拷贝了解吗?

Netty 通过零拷贝技术减少数据复制次数,提升性能!

Netty 的 零拷贝 主要在以下三个方面:

  • Netty 的接收和发送使用堆外内存(直接内存)进行 Socket 读写,不需要进行字节缓冲区的二次拷贝。

  • Netty 提供 CompositeByteBuf 组合缓冲区类,可以将多个 ByteBuf 合并为一个逻辑上的 ByteBufer,避免了各个 ByteBufer 之间的拷贝,将几个小 buffer 合并成一个大buffer的繁琐操作。

  • Netty 的文件传输使用了 FileChannel 的 transferTo 方法,该方法底层使用了 sendfile 函数实现了 cpu 零拷贝。

    sendfile 函数通过网络发送数据的流程为:

    (3次拷贝、2次上下文切换,这里上下文切换是在用户空间发起write操作,此时用户态切换为内核态,write调用完毕后又会从内核态切换回用户态)

    在这里插入图片描述

    下边画一个直观一些的图片,从 应用程序的角度 来看 Netty 的零拷贝:

    在这里插入图片描述

但是不能只了解零拷贝是怎样的,因为技术是过渡的,还要知道不使用零拷贝时,传统 IO 是怎么传输数据的:

  • 传统的 IO 操作会有 4 次拷贝,4 次用户态和内核态之间的切换,因此性能是比较低的
  • 这里说一下是 哪 4 次内核态的切换
    • 首先,应用程序去磁盘读取数据进行发送时,此时会从用户态切换到内核态,通过 DMA 拷贝将数据放到文件读取缓冲区中
    • 再从内核态切换到用户态,将文件读取缓冲区的数据通过 CPU 拷贝读取到应用进程缓冲区中
    • 再从用户态切换到内核态,将数据从应用进程缓冲区通过 CPU 拷贝放到 Socket 发送缓冲区中,之后的数据会从 Socket 发送缓冲区中通过 DMA 拷贝发送到网络设备缓冲区中
    • 操作完成之后,再从内核态切换到用户态

在这里插入图片描述

直接内存了解吗?

直接内存(也称为堆外内存)并不是虚拟机运行时数据区的一部分,直接内存的大小受限于系统的内存

在 JDK1.4 引入了 NIO 类,在 NIO 中可以通过使用 native 函数库直接分配堆外内存,然后通过存储在堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作

使用直接内存,可以避免了 Java 堆和 Native 堆中来回复制数据

在上边提到了 Netty 的零拷贝,其中有一种就是使用了 直接内存来实现零拷贝 的,直接内存的特点就是快,接下来看看为什么使用直接内存更快呢?

直接内存使用场景:

  • 有很大的数据需要存储,且数据生命周期长
  • 频繁的 IO 操作,如网络并发场景

直接内存与堆内存比较:

  • 直接内存申请空间耗费更高的性能,当频繁申请到一定量时尤为明显
  • 直接内存IO读写的性能要优于普通的堆内存,在多次读写操作的情况下差异明显

直接内存相比于堆内存,避免了数据的二次拷贝。

  • 我们先来分析不使用直接内存的情况,我们在网络发送数据需要将数据先写入 Socket 的缓冲区内,那么如果数据存储在 JVM 的堆内存中的话,会先将堆内存中的数据复制一份到直接内存中,再将直接内存中的数据写入到 Socket 缓冲区中,之后进行数据的发送

    • 为什么不能直接将 JVM 堆内存中的数据写入 Socket 缓冲区中呢?

      在 JVM 堆内存中有 GC 机制,GC 后可能会导致堆内存中数据位置发生变化,那么如果直接将 JVM 堆内存中的数据写入 Socket 缓冲区中,如果写入过程中发生 GC,导致我们需要写入的数据位置发生变化,就会将错误的数据写入 Socket 缓冲区

  • 那么如果使用直接内存的时候,我们将数据直接存放在直接内存中,在堆内存中只存放了对直接内存中数据的引用,这样在发送数据时,直接将数据从直接内存取出,放入 Socket 缓冲区中即可,减少了一次堆内存到直接内存的拷贝

在这里插入图片描述

直接内存与非直接内存性能比较:

public class ByteBufferCompare {public static void main(String[] args) {//allocateCompare(); //分配比较operateCompare(); //读写比较}/*** 直接内存 和 堆内存的 分配空间比较* 结论: 在数据量提升时,直接内存相比非直接内的申请,有很严重的性能问题*/public static void allocateCompare() {int time = 1000 * 10000; //操作次数,1千万long st = System.currentTimeMillis();for (int i = 0; i < time; i++) {//ByteBuffer.allocate(int capacity) 分配一个新的字节缓冲区。ByteBuffer buffer = ByteBuffer.allocate(2); //非直接内存分配申请}long et = System.currentTimeMillis();System.out.println("在进行" + time + "次分配操作时,堆内存 分配耗时:" +(et - st) + "ms");long st_heap = System.currentTimeMillis();for (int i = 0; i < time; i++) {//ByteBuffer.allocateDirect(int capacity) 分配新的直接字节缓冲区。ByteBuffer buffer = ByteBuffer.allocateDirect(2); //直接内存分配申请}long et_direct = System.currentTimeMillis();System.out.println("在进行" + time + "次分配操作时,直接内存 分配耗时:" +(et_direct - st_heap) + "ms");}/*** 直接内存 和 堆内存的 读写性能比较* 结论:直接内存在直接的IO 操作上,在频繁的读写时 会有显著的性能提升*/public static void operateCompare() {int time = 10 * 10000 * 10000; //操作次数,10亿ByteBuffer buffer = ByteBuffer.allocate(2 * time);long st = System.currentTimeMillis();for (int i = 0; i < time; i++) {// putChar(char value) 用来写入 char 值的相对 put 方法buffer.putChar('a');}buffer.flip();for (int i = 0; i < time; i++) {buffer.getChar();}long et = System.currentTimeMillis();System.out.println("在进行" + time + "次读写操作时,非直接内存读写耗时:" +(et - st) + "ms");ByteBuffer buffer_d = ByteBuffer.allocateDirect(2 * time);long st_direct = System.currentTimeMillis();for (int i = 0; i < time; i++) {// putChar(char value) 用来写入 char 值的相对 put 方法buffer_d.putChar('a');}buffer_d.flip();for (int i = 0; i < time; i++) {buffer_d.getChar();}long et_direct = System.currentTimeMillis();System.out.println("在进行" + time + "次读写操作时,直接内存读写耗时:" +(et_direct - st_direct) + "ms");}
}

这篇关于【面试突击】深挖网络 IO 面试实战的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/632967

相关文章

Linux系统配置NAT网络模式的详细步骤(附图文)

《Linux系统配置NAT网络模式的详细步骤(附图文)》本文详细指导如何在VMware环境下配置NAT网络模式,包括设置主机和虚拟机的IP地址、网关,以及针对Linux和Windows系统的具体步骤,... 目录一、配置NAT网络模式二、设置虚拟机交换机网关2.1 打开虚拟机2.2 管理员授权2.3 设置子

揭秘Python Socket网络编程的7种硬核用法

《揭秘PythonSocket网络编程的7种硬核用法》Socket不仅能做聊天室,还能干一大堆硬核操作,这篇文章就带大家看看Python网络编程的7种超实用玩法,感兴趣的小伙伴可以跟随小编一起... 目录1.端口扫描器:探测开放端口2.简易 HTTP 服务器:10 秒搭个网页3.局域网游戏:多人联机对战4.

Spring Boot + MyBatis Plus 高效开发实战从入门到进阶优化(推荐)

《SpringBoot+MyBatisPlus高效开发实战从入门到进阶优化(推荐)》本文将详细介绍SpringBoot+MyBatisPlus的完整开发流程,并深入剖析分页查询、批量操作、动... 目录Spring Boot + MyBATis Plus 高效开发实战:从入门到进阶优化1. MyBatis

MyBatis 动态 SQL 优化之标签的实战与技巧(常见用法)

《MyBatis动态SQL优化之标签的实战与技巧(常见用法)》本文通过详细的示例和实际应用场景,介绍了如何有效利用这些标签来优化MyBatis配置,提升开发效率,确保SQL的高效执行和安全性,感... 目录动态SQL详解一、动态SQL的核心概念1.1 什么是动态SQL?1.2 动态SQL的优点1.3 动态S

Pandas使用SQLite3实战

《Pandas使用SQLite3实战》本文主要介绍了Pandas使用SQLite3实战,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学... 目录1 环境准备2 从 SQLite3VlfrWQzgt 读取数据到 DataFrame基础用法:读

Java的IO模型、Netty原理解析

《Java的IO模型、Netty原理解析》Java的I/O是以流的方式进行数据输入输出的,Java的类库涉及很多领域的IO内容:标准的输入输出,文件的操作、网络上的数据传输流、字符串流、对象流等,这篇... 目录1.什么是IO2.同步与异步、阻塞与非阻塞3.三种IO模型BIO(blocking I/O)NI

SpringBoot使用OkHttp完成高效网络请求详解

《SpringBoot使用OkHttp完成高效网络请求详解》OkHttp是一个高效的HTTP客户端,支持同步和异步请求,且具备自动处理cookie、缓存和连接池等高级功能,下面我们来看看SpringB... 目录一、OkHttp 简介二、在 Spring Boot 中集成 OkHttp三、封装 OkHttp

Linux系统之主机网络配置方式

《Linux系统之主机网络配置方式》:本文主要介绍Linux系统之主机网络配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、查看主机的网络参数1、查看主机名2、查看IP地址3、查看网关4、查看DNS二、配置网卡1、修改网卡配置文件2、nmcli工具【通用

Python实战之屏幕录制功能的实现

《Python实战之屏幕录制功能的实现》屏幕录制,即屏幕捕获,是指将计算机屏幕上的活动记录下来,生成视频文件,本文主要为大家介绍了如何使用Python实现这一功能,希望对大家有所帮助... 目录屏幕录制原理图像捕获音频捕获编码压缩输出保存完整的屏幕录制工具高级功能实时预览增加水印多平台支持屏幕录制原理屏幕

使用Python高效获取网络数据的操作指南

《使用Python高效获取网络数据的操作指南》网络爬虫是一种自动化程序,用于访问和提取网站上的数据,Python是进行网络爬虫开发的理想语言,拥有丰富的库和工具,使得编写和维护爬虫变得简单高效,本文将... 目录网络爬虫的基本概念常用库介绍安装库Requests和BeautifulSoup爬虫开发发送请求解