[转帖]这种本机网络 IO 方法,性能可以翻倍!

这种,本机,网络,io,方法,性能,可以,翻倍 · 浏览次数 : 0

小编点评

## UDS vs. 127.0.0.1 本机网络 IO 连接性能比较分析 本文通过深入分析 Unix Domain Socket 的内部工作原理,与基于 127.0.0.1 的本机网络 IO 沟通方式进行性能对比测试,为用户提供使用 UDS 的性能提升建议。 **1. 使用 UDS 的优势** * 与传统 socket 相似,简化连接建立流程。 * 减少系统调用,提升性能。 * 减少开销,降低延迟。 **2. UDS 的连接建立过程** * 客户端先创建自己的 socket。 * 调用 `connect` 函数建立连接。 * 与服务器建立连接关系后,放到服务器的接收队列中。 * 服务端通过 `__skb_queue_tail` 将 skb 放到接收队列中。 **3. UDS 的发送过程** * 直接将数据写到接收方的接收队列中。 * 使用 `sendto` 系统调用将数据发送出去。 **4. 性能对比** * 在小包情况下,UDS 的性能大约是 TCP 的两倍多。 * 在包体达到 100 KB 以后,UDS 的延迟 24 微秒左右,仍比 TCP 还要低。 **5. 总结** * 在本机网络 IO 场景下,使用 Unix Domain Socket 可以获得显著的性能提升。 * UDS 的连接建立流程简化,发送过程高效,降低开销。 * 性能测试结果表明,UDS 的性能约为 TCP 的两倍,为用户提供性能提升的绝佳选择。 **其他说明** * 本文仅提供 UDS 的基本原理分析,实际实现细节可参考内核代码。 * 性能测试结果可能因硬件、软件版本等因素影响,实际性能可能有所不同。

正文

https://www.bilibili.com/read/cv16902163?spm_id_from=333.999.0.0

 

 

大家好,我是飞哥!

很多读者在看完《127.0.0.1 之本机网络通信过程知多少 ?》这一篇后,让我讲讲 Unix Domain Socket。好了,今天就安排!

在本机网络 IO 中,我们讲到过基于普通 socket 的本机网络通信过程中,其实在内核工作流上并没有节约太多的开销。该走的系统调用、协议栈、邻居系统、设备驱动(虽然说对于本机网络 loopback 设备来说只是一个软件虚拟的东东)全都走了一遍。其工作过程如下图

图片

那么我们今天来看另外一种本机网络 IO 通信方式 -- Unix Domain Socket。看看这种方式在性能开销上和基于 127.0.0.1 的本机网络 IO 有没有啥差异呢。

本文中,我们将分析 Unix Domain Socket 的内部工作原理。你将理解为什么这种方式的性能比 127.0.0.1 要好很多。最后我们还给出了实际的性能测试对比数据。

相信你已经迫不及待了,别着急,让我们一一展开细说!

一、使用方法

Unix Domain Socket(后面统一简称 UDS) 使用起来和传统的 socket 非常的相似。区别点主要有两个地方需要关注。

第一,在创建 socket 的时候,普通的 socket 第一个参数 family 为 AF_INET, 而 UDS 指定为 AF_UNIX 即可。

第二,Server 的标识不再是 ip 和 端口,而是一个路径,例如 /dev/shm/fpm-cgi.sock。

其实在平时我们使用 UDS 并不一定需要去写一段代码,很多应用程序都支持在本机网络 IO 的时候配置。例如在 Nginx 中,如果要访问的本机 fastcgi 服务是以 UDS 方式提供服务的话,只需要在配置文件中配置这么一行就搞定了。

如果 对于一个 UDS 的 server 来说,它的代码示例大概结构如下,大家简单了解一下。只是个示例不一定可运行。

基于 UDS 的 client 也是和普通 socket 使用方式差不太多,创建一个 socket,然后 connect 即可。

二、连接过程

总的来说,基于 UDS 的连接过程比 inet 的 socket 连接过程要简单多了。客户端先创建一个自己用的 socket,然后调用 connect 来和服务器建立连接。

在 connect 的时候,会申请一个新 socket 给 server 端将来使用,和自己的 socket 建立好连接关系以后,就放到服务器正在监听的 socket 的接收队列中。这个时候,服务器端通过 accept 就能获取到和客户端配好对的新 socket 了。

总的 UDS 的连接建立流程如下图。

图片

内核源码中最重要的逻辑在 connect 函数中,我们来简单展开看一下。unix 协议族中定义了这类 socket 的所有方法,它位于 net/unix/af_unix.c 中。

我们找到 connect 函数的具体实现,unix_stream_connect。

主要的连接操作都是在这个函数中完成的。和我们平常所见的 TCP 连接建立过程,这个连接过程简直是太简单了。没有三次握手,也没有全连接队列、半连接队列,更没有啥超时重传。

直接就是将两个 socket 结构体中的指针互相指向对方就行了。就是 unix_peer(newsk) = sk 和 unix_peer(sk) = newsk 这两句。

当关联关系建立好之后,通过 __skb_queue_tail 将 skb 放到服务器的接收队列中。注意这里的 skb 里保存着新 socket 的指针,因为服务进程通过 accept 取出这个 skb 的时候,就能获取到和客户进程中 socket 建立好连接关系的另一个 socket。

怎么样,UDS 的连接建立过程是不是很简单!?

三、发送过程

看完了连接建立过程,我们再来看看基于 UDS 的数据的收发。这个收发过程一样也是非常的简单。发送方是直接将数据写到接收方的接收队列里的。

图片

我们从 send 函数来看起。send 系统调用的源码位于文件 net/socket.c 中。在这个系统调用里,内部其实真正使用的是 sendto 系统调用。它只干了两件简单的事情,

第一是在内核中把真正的 socket 找出来,在这个对象里记录着各种协议栈的函数地址。第二是构造一个 struct msghdr 对象,把用户传入的数据,比如 buffer地址、数据长度啥的,统统都装进去. 剩下的事情就交给下一层,协议栈里的函数 inet_sendmsg 了,其中 inet_sendmsg 函数的地址是通过 socket 内核对象里的 ops 成员找到的。大致流程如图。

图片

在进入到协议栈 inet_sendmsg 以后,内核接着会找到 socket 上的具体协议发送函数。对于 Unix Domain Socket 来说,那就是 unix_stream_sendmsg。我们来看一下这个函数

和复杂的 TCP 发送接收过程相比,这里的发送逻辑简单简单到令人发指。申请一块内存(skb),把数据拷贝进去。根据 socket 对象找到另一端,直接把 skb 给放到对端的接收队列里了

接收函数主题是 unix_stream_recvmsg,这个函数中只需要访问它自己的接收队列就行了,源码就不展示了。所以在本机网络 IO 场景里,基于 Unix Domain Socket 的服务性能上肯定要好一些的。

四、性能对比

为了验证 Unix Domain Socket 到底比基于 127.0.0.1 的性能好多少,我做了一个性能测试。在网络性能对比测试,最重要的两个指标是延迟和吞吐。我从 Github 上找了个好用的测试源码:https://github.com/rigtorp/ipc-bench。我的测试环境是一台 4 核 CPU,8G 内存的 KVM 虚机。

在延迟指标上,对比结果如下图。

图片

可见在小包(100 字节)的情况下,UDS 方法的“网络” IO 平均延迟只有 2707 纳秒,而基于 TCP(访问 127.0.0.1)的方式下延迟高达 5690 纳秒。耗时整整是前者的两倍。

在包体达到 100 KB 以后,UDS 方法延迟 24 微秒左右(1 微秒等于 1000 纳秒),TCP 是 32 微秒,仍然高一截。这里低于 2 倍的关系了,是因为当包足够大的时候,网络协议栈上的开销就显得没那么明显了。

再来看看吞吐效果对比。

图片

在小包的情况下,带宽指标可以达到 854 M,而基于 TCP 的 IO 方式下只有 386 M。数据就解读到这里。

五、总结

本文分析了基于 Unix Domain Socket 的连接创建、以及数据收发过程。其中数据收发的工作过程如下图。

图片

相对比本机网络 IO 通信过程上,它的工作过程要清爽许多。其中 127.0.0.1 工作过程如下图。

图片

我们也对比了 UDP 和 TCP 两种方式下的延迟和性能指标。在包体不大于 1KB 的时候,UDS 的性能大约是 TCP 的两倍多。所以,在本机网络 IO 的场景下,如果对性能敏感,飞哥建议你使用 Unix Domain Socket。

与[转帖]这种本机网络 IO 方法,性能可以翻倍!相似的内容:

[转帖]这种本机网络 IO 方法,性能可以翻倍!

https://www.bilibili.com/read/cv16902163?spm_id_from=333.999.0.0 大家好,我是飞哥! 很多读者在看完《127.0.0.1 之本机网络通信过程知多少 ?》这一篇后,让我讲讲 Unix Domain Socket。好了,今天就安排! 在本机

[转帖]重写Nacos服务发现逻辑动态修改远程服务IP地址

https://www.cnblogs.com/changxy-codest/p/14632574.html 背景 还是先说下做这个的背景,开发环境上了K8S,所有的微服务都注册在K8S内的Nacos,注册地址为K8S内部虚拟IP,K8S内的服务之间相互调用没有问题,但是本机开发联调调用其他微服务就

[转帖]jumpserver (Linux资产管理快速入门)

准备工作 准备三台虚拟机,一台作为jumpserver的服务端,两台作为测试端。 一、安装好jump server后,输入IP地址登录 [192.168.2.111为本机测试地址] 二、创建用户组 这里话我们就可以先创建一个 test 的测试组作为测试。 三、创建资产 点击页面左侧的 资产管理 -

[转帖]Linux文件分发脚本,只需一条命令将你的文件分发到各个服务器上

https://zhuanlan.zhihu.com/p/438457921 背景 在运维或在日常工作生活中,我们经常会把一个文件拷贝到其它服务器上,或同时分发到多个服务器上,甚至要求目标机将文件放在相同的路径下,方便程序进一步调用。 遇到这种问题,我们通常的做法是使用scp或rsync命令把文件拷

[转帖]Jmeter性能测试:高并发分布式性能测试

一、为什么要进行分布式性能测试 当进行高并发性能测试的时候,受限于Jmeter工具本身和电脑硬件的原因,无法满足我们对大并发性能测试的要求。基于这种场景下,我们就需要采用分布式的方式来实现我们高并发的性能测试要求。 二、分布式性能测试原理 要进行分布式性能测试,我们首先要先一台机器作为主控机(Con

[转帖](三)固态硬盘的接口与协议

https://zhuanlan.zhihu.com/p/415817808 目前市场上的固态硬盘简单来说共有SATA、PCIE、M.2和U.2四种接口类型。 早期,为了更适应于超极本这类超薄设备的使用环境,针对便携设备开发的mSATA(mini SATA)接口应运而生。你可以把它看作标准SATA接

[转帖]超全超详细的HTTP状态码大全

本部分余下的内容会详细地介绍 HTTP 1.1中的状态码。这些状态码被分为五大类:100-199 用于指定客户端应相应的某些动作。200-299 用于表示请求成功。300-399 用于已经移动的文件并且常被包含在定位头信息中指定新的地址信息。400-499 用于指出客户端的错误。500-599 用于

[转帖]Access-Control-Allow-Origin跨域解决及详细介绍

重要声明:本文章仅仅代表了作者个人对此观点的理解和表述。读者请查阅时持自己的意见进行讨论。 本文更新不及时,建议到原文地址浏览:跨域解决及详细介绍。 首先,跨域不是问题。是一种安全机制。 这是你在开发时、上线前就必须提前考虑到的安全问题并且采取合适的手段去避免这个问题带来的程序错误。不过通常情况下,

【转帖】Linux开发工具 — readelf、objdump、hexdump

本博文的主要内容是:1)readelf工具查看ELF文件的信息;2)hexdump工具查看这块内存;3)objdump工具对文件进行反汇编。 前一段时间对Linux不熟,所以很多命令不知道。学习C时候需要偶尔看一下汇编用来理解。我喜欢用问题的形式来学习和总结。 1. 如何看一个程序代码变量的存储布局

[转帖]TiDB 数据库核心原理与架构 [TiDB v6](101)笔记

https://www.jianshu.com/p/01e49a93f671 description: "本课程专为将在工作中使用 TiDB 数据库的开发人员、DBA 和架构师设计。 本门课侧重于 TiDB 数据库的架构和设计原则,这是未来管理、开发、性能调整和故障排除的基础。在学习本课程前,您需要