[转帖]字节跳动开源 Shmipc:基于共享内存的高性能 IPC

字节,跳动,开源,shmipc,基于,共享内存,高性能,ipc · 浏览次数 : 0

小编点评

**Shmipc 文档摘要** **设计原理** Shmipc 是一个基于内存共享的异步 IPC 框架,它采用共享内存和内存管制技术来实现高性能的 IPC 操作。 **实现细节** * Shmipc 使用 go 的插件机制来实现跨进程共享内存。 * 它使用 mmap 文件系统来实现内存共享。 * Shmipc 支持定时同步机制和轮询同步机制。 **使用方法** * Shmipc 可以用于各种 IPC 场景,例如 Log SDK 与 Log Agent,Metrics SDK 与 Metrics Agent 等。 * 它提供了多种方式来设置共享内存地址,例如 memfd 和 memfd。 * Shmipc 支持多种同步机制,例如 go 的插件机制和 mmap 文件系统。 **性能** * Shmipc 的性能受内存共享和内存管制技术的优化。 * 它可以用于各种场景,例如离线场景和延迟有极致场景。 **结论** Shmipc 是一个高效的 IPC 框架,它提供了多种方法来实现跨进程共享内存、内存管制和同步机制。它适用于各种 IPC 场景,例如 Log SDK 与 Log Agent,Metrics SDK 与 Metrics Agent 等。

正文

https://maimai.cn/article/detail?fid=1780832041&efid=WeW8ji-LiPaXA8QER_Q1YQ

 

简介

CloudWeGo - Shmipc 是字节跳动服务框架团队研发的高性能进程间通讯库,它基于共享内存构建,具有零拷贝的特点,同时它引入的同步机制具有批量收割 IO 的能力,相对于其他进程间通讯方式能明显提升性能。在字节内部,Shmipc 应用于 Service Mesh 场景下,mesh proxy 进程与业务逻辑进程、与通用 sidecar 进程的通讯, 在大包场景和 IO 密集型场景能够取得了显著的性能收益。

开源社区关于这方面的资料不多,Shmipc 的开源希望能为社区贡献一份力量,提供一份参考。本文主要介绍 Shmipc 的一些主要的设计思路以及后续的演进规划。

go 版本实现:

http://github.com/cloudwego/shmipc-go

设计细节:

http://github.com/cloudwego/shmipc-spec






 

项目背景

在字节,Service Mesh 在落地的过程中进行了大量的性能优化工作,其中 Service Mesh 的流量劫持是通过,mesh proxy 与微服务框架约定的地址进行进程间通讯来完成,性能会优于开源方案中的 iptables。但常规的优化手段已不能带来明显的性能提升。于是我们把目光放到了进程间通讯上,Shmipc 由此诞生。

设计思路

零拷贝

在生产环境中比较广泛使用的进程间通讯方式是 unix domain socket 与 TCP loopback(localhost:$PORT),两者从 benchmark 看性能差异不大。从技术细节看,都需要将通讯的数据在用户态和内核态之间进行拷贝。在 RPC场景下,一次 RPC 流程中在进程间通讯上会有四次的内存拷贝,Request 路径两次, Response 路径两次。

虽然现代 CPU 上进行顺序的 copy 非常快,但如果我们能够消除这多达四次的内存拷贝,在大包场景下也能在一定程度上节省 CPU 使用。而基于共享内存通讯零拷贝的特性,我们可以很容易达成这一点。但为了达到零拷贝的效果,围绕共享内存本身,还会产生有许多额外的工作,比如:

  1. 深入微服务框架的序列化与反序列化。我们希望当 Request 或 Response 序列化完成时,对应的二进制数据已经存在共享内存中。而不是序列化到一块非共享内存的 buffer 中,然后再拷贝到共享内存 buffer。
  2. 实现一种进程同步机制。当一个进程把数据写入共享内存后,另外一个进程并不知道,因此需要同步机制进行通知。
  3. 高效的内存分配和回收。保证跨进程的共享内存的分配和回收机制的开销足够低,避免其掩盖零拷贝的特性带来的收益。

同步机制

分场景考虑:

  1. 按需实时同步。适用于在线场景,对时延极其敏感,每次写入操作完成后都通知对端进程。Linux 下,可做选择的比较多,TCP loopback、unix domain socket、event fd 等。event fd的 benchmark 性能会略好,但跨进程传递 fd 会引入过多复杂性,其带来的性能提升在 IPC 上不太明显,复杂性与性能中间的权衡需要慎重考虑。在字节,我们选择了 unix domain socket 来进行进程同步。
  2. 定时同步。适用于离线场景,对时延不敏感。通过高间隔的 sleep 访问共享内存中自定义的标志位来鉴别是否有数据写入。但注意 sleep 本身也需要系统调用,开销大于 unix domain socket 的读写。
  3. 轮询同步。适用于时延非常敏感,CPU不那么敏感的场景。可以通过单核轮询共享内存中的自定义标志位来完成。

总的来说按需实时同步和定期同步需要系统调用来完成,轮询同步不需要系统调用,但需要常态跑满一个 CPU 核心。

批量收割 IO

在线场景中按需实时同步,每次数据写入都需要进行一次进行进程同步(下图中的4),虽然延迟问题解决了,但在性能上,需要交互的数据包需要大于一个比较大的阈值,零拷贝带来的收益才能突显。因此在共享内存中构造了一个 IO 队列的来完成批量收割 IO,使其在小包 IO 密集场景也能显现收益。核心思想是:当一个进程把请求写入 IO队列后,会给另外一个进程发通知来处理。那么在下一个请求进来时(对应下图中的 IOEvent 2~N,一个 IOEvent 可以独立描述一个请求在共享内存中的位置),如果对端进程还在处理 IO 队列中的请求,那么就不必进行通知。因此,IO越密集,批处理效果就越好。

另外就是离线场景中,定时同步本身就是批量处理 IO 的,批处理的效果能够有效减少进程同步带来的系统调用,sleep 间隔越高,进程同步的开销就越低。

对于轮询同步则不需要考虑批量收割 IO,因为这个机制本身是为了减少进程同步开销。而轮询同步直接占满一个 CPU 核心,相当于默认把同步机制的开销拉满以获取极低的同步延迟。

性能收益

Benchmark

其中X 轴为数据包大小,Y轴为一次 Ping-Pong 的耗时,单位为微秒,越小越好。可以看到在小包场景下,Shmipc 相对于 unix domain socket 也能获得一些收益,并且随着包大小越大性能越好。

数据源:git clone http://github.com/cloudwego/shmipc-go && go test -bench=BenchmarkParallelPingPong -run BenchmarkParallelPingPong

生产环境

在字节生产环境的 Service Mesh 生态中,我们在 3000+ 服务、100w+ 实例上应用了 Shmipc。不同的业务场景显现出不同的收益,其中收益最高的风控 业务降低了整体24%的资源使用,当然也有无明显收益的甚至劣化的场景出现。但在大包和 IO 密集型场景均能显现出显著收益。

采坑记录

在字节实际落地的过程中我们也踩了一些坑,导致一些线上事故,比较具有参考价值。

  1. 共享内存泄漏。IPC 过程共享内存分配和回收涉及到两个进程,稍有不慎就容易发生共享内存的泄漏。问题虽然非常棘手,但只要能够做到泄漏时主动发现,以及泄漏之后有观测手段可以排查即可。

    1. 主动发现。可以通过增加一些统计信息然后汇总到监控系统来做到主动发现,比如总分配和总回收的内存大小。
    2. 观测手段。在设计共享内存的布局时增加一些元信息,使得在发生泄漏之后,我们可以通过内置的 debug 工具dump 泄漏时刻的共享内存来进行分析。能够知道所泄漏的内存有多少,里面的内容是什么,以及和这部分内容相关的一些元信息。
  2. 串包。串包是最头疼的问题,出现的原因是千奇百怪的,往往造成严重后果。我们曾在某业务上发生串包事故,出现的原因是因为大包导致共享内存耗尽,fallback 到常规路径的过程中设计存在缺陷,小概率出现串包。排查过程和原因并不具备共性,可以提供更多的参考是增加更多场景的集成测试和单元测试将串包扼杀在摇篮中。

  3. 共享内存踩踏。应该尽可能使用 memfd 来共享内存,而不是 mmap 文件系统中的某个路径。早期我们通过 mmap 文件系统的路径来共享内存,Shmipc 的开启和共享内存的路径由环境变量指定,启动过程由引导进程注入应用进程。那么存在一种情况是应用进程可能会 fork 出一个进程,该进程继承了应用进程的环境变量并且也集成了 Shmipc,然后 fork 的进程和应用进程 mmap 了同一块共享内存,发现踩踏。在字节的事故场景是应用进程使用了 golang 的 plugin 机制从外部加载 .so 来运行,该 .so 集成了 Shmipc,并且跑在应用进程里,能看到所有环境变量,于是就和应用进程 mmap 了同一片共享内存,运行过程发生未定义行为。

  4. Sigbus coredump。早期我们通过 mmap /dev/shm/路径(tmpfs)下的文件来共享内存,应用服务大都运行在 docker 容器实例中。容器实例对 tmpfs 有容量限制(可以通过 df -h 观测),这会使得 mmap 的共享内存如果超过该限制就会出现 Sigbus,并且 mmap 本身不会有任何报错,但在运行期,使用到超过限制的地址空间时才会出现 Sigbus 导致应用进程崩溃。解决方式和第三点一样,使用 memfd 来共享内存。

后续演进

  1. 整合至微服务 RPC 框架 CloudWeGo/Kitex。
  2. 整合至微服务 HTTP 框架 CloudWeGo/Hertz。
  3. 开源 Rust 版本的 Shmipc 并整合至 Rust RPC 框架 CloudWeGo/Volo。
  4. 开源 C++ 版本的 Shmipc。
  5. 引入定时同步机制适用于离线场景。
  6. 引入轮询同步的同步机制适用于对延迟有极致要求的场景。
  7. 赋能其他 IPC 场景, 比如 Log SDK 与 Log Agent, Metrics SDK 与 Metrics Agent 等。

总结

希望本文能让大家对于 Shmipc 有一个初步的了解,知晓其设计原理,更多实现细节以及使用方法请参考文章开头给出的项目地址。欢迎各位感兴趣的同学向 Shmipc 项目提交 Issue 和 PR,共同建设 CloudWeGo 开源社区,也期望 Shmipc 在 IPC 领域助力越来越多开发者和企业构建高性能云原生架构。

与[转帖]字节跳动开源 Shmipc:基于共享内存的高性能 IPC相似的内容:

[转帖]字节跳动开源 Shmipc:基于共享内存的高性能 IPC

https://maimai.cn/article/detail?fid=1780832041&efid=WeW8ji-LiPaXA8QER_Q1YQ 简介 CloudWeGo - Shmipc 是字节跳动服务框架团队研发的高性能进程间通讯库,它基于共享内存构建,具有零拷贝的特点,同时它引入的同步机

[转帖]HTTP 框架 Hertz 实践入门:性能测试指南

https://maimai.cn/article/detail?fid=1767401397&efid=R2_kM5y-yEUDCK88FZWrGA 干货不迷路2021 年 9 月 8 日,字节跳动宣布正式开源 CloudWeGo。CloudWeGo 是一套字节跳动内部微服务中间件集合,具备高性能

[转帖]字节跳动的云原生技术历程演进

https://maimai.cn/article/detail?fid=1751463745&efid=56tCo_A4kKDBGGJfOu608A 以 Kubernetes 为代表的云原生技术底座支撑了字节跳动业务的快速发展。从微服务场景开始,Kubernetes 逐渐演化统一支撑了字节内部的大

[转帖]突破 etcd 限制!字节自研 K8s 存储 KubeBrain

https://my.oschina.net/u/5632822/blog/5596911 KubeBrain 是字节跳动针对 Kubernetes 元信息存储的使用需求,基于分布式 KV 存储引擎设计并实现的、可以取代 etcd 的元信息存储系统,目前支撑着线上超过 20,000 节点的超大规模

[转帖]深入分析 OpenJDK G1 FullGC 原理

https://my.oschina.net/u/6150560/blog/7928455 欢迎关注【字节跳动 SYS Tech】公众号。字节跳动 SYS Tech 聚焦系统技术领域,与大家分享前沿技术动态、技术创新与实践、行业技术热点分析等内容。 导读 本文主要从代码层面介绍 OpenJDK G1

[转帖]图解:什么是红黑树?

https://zhuanlan.zhihu.com/p/273829162 注:本文比较硬核但是很值得大家花心思看完,看完你一定会有所收获的 红黑树是面试中一个很经典也很有难度的知识点,网传字节跳动面试官最喜欢问这个问题。很多人会觉得这个知识点太难,不想花太多功夫去了解,也有人会认为这个数据结构在

[转帖]nginx 白名单处理

Nginx如何配置根据cookie或header自定义字段进行跳转 一、根据cookie中的值来判断跳转 $http_cookie这个关键词是读取cookie的全部信息,那么我们是需要判断cookie中包含的某个字段的值,比如我设置openid=5,我该如何获取那么,我们可以通过$cookie_op

[转帖]字节换算

https://www.cnblogs.com/huazhixu/p/16544808.html 1B (byte 字节); 1KB(Kilobyte 千字节) = 2^10 B = 1024 B; 1MB(Megabyte 兆字节) = 2^10 KB = 1024 KB = 2^20 B; 1G

[转帖]关于字节序(大小端)的一点想法

https://www.zhihu.com/people/bei-ji-85/posts 今天在一个技术群里有人问起来了,当时有一些讨论(不完全都是我个人的观点),整理一下: 为什么网络字节序(多数情况下)是大端? 早年设备的缓存很小,先接收高字节能快速的判断报文信息:包长度(需要准备多大缓存)、地

[转帖]【JVM】字节码执行引擎

引入 class文件就是字节码文件,是由虚拟机执行的文件。也就是java语言和C & C++语言的区别就是,整个编译执行过程多了一个虚拟机这一步。这个在 类文件结构 中已经解释。上一节讲了虚拟机是如何加载一个class的,这一节就讲解虚拟机是如何执行class文件的。 运行时栈帧结构 1.定义 栈是