6

低延迟架构体系初探

 1 year ago
source link: https://www.phodal.com/blog/explore-low-latency-architecture/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Posted by: Phodal Huang Oct. 23, 2022, 8:22 p.m.

几年前,在通信领域的技术咨询经历,让我初步了解到预分配内存管理对于性能的改善是多么的明显。最近,也从点点滴滴的金融科技的技术里,看到了高频交易所需要的低延时架构技术(国内受限于特有国情),也有点如出一辙的味道。而在未来,“元宇宙” 可能会换个新的名词,但是呢,它依旧也需要一系列的低延迟架构设计。

过程中,我发现一系列反直觉的架构模式或设计模式。于是乎,作为低延迟架构领域的 “门外汉”,便结合着自己的理解,以及手头上的资料和书籍,做一个初步的整理和调研。

低延迟技术概览

低延迟(low latency)顾名思议是计算系统或网络以最小延迟提供响应的能力。从数据的角度来考虑,如果我们想最大程度降低延迟,那么就需要关注到每一个层次的数据处理。所以,它会涉及到一系列方方面面的领域,诸如于:

  • 网络传输与硬件
    • 数据传输。如微波、光纤、网线等
    • 路由机制。
    • 网络与硬件侧。网络协议、网卡等,专用处理器等,如 FPGA,
  • 系统编程策略
    • 操作系统。(从通用型操作系统到专用型操作系统) CPU 缓存、内核调度等。
    • 语言侧。平衡高性能的语言与规模化。
    • 其它。如内存管理策略等。
  • 应用软件
    • 架构侧。事件驱动型架构,如 LMAX
    • 应用开发侧。高性能的数据结构(如集合)、通过设计模式提升性能等。
    • 可视化侧。密集计算下沉 WASM 等。
    • 设计模式。采用无锁和无阻塞设计模式,降低、消除操作系统信号延迟

当然了,受限于我在这个领域的了解有限,应该包含其它的内容,有待未来进一步探索。特别是,作为一个大学学习的是通信领域的工程师,对于网络传输与硬件侧,我还是相当有兴趣的。当然了,一些奇技淫巧并不在我们这里的讨论范围,诸如于数据包提前处理机制。

系统编程策略

操作系统

在操作系统侧,底层的模式是:基于 Linux 内核的定制型操作系统搭配特定的硬件,以及特定私有的网络协议栈。于 Linux 的网络性能不好,所以内核旁路技术的目的是:不再让内核(TCP/IP 协议栈)处理数据包。用自己实现的相同功能的代码来处理,从用户空间直接访问和控制设备内存,避免数据从设备拷贝到内核,再从内核拷贝到用户空间。所以在大量的高频交易 (HFT) 解决方案里,使用带有 OpenOnLoad 框架的 Solarflare 网卡成了一个非常好的选择。

再往上则是,同样也是围绕于 Linux 内核作其它的定制,诸如于内存分布管理、进程调度、优化数据结构以利用处理器缓存等。

语言

从现有的应用情况而言,C、C++ 基本上是在低延迟领域的代表性语言。当然,在不并需要那么严苛速度的场景下,诸如国内环境,那么 Java 也是一个非常不错的语言 —— 快速呼朋唤友。毕竟,资深的 C++ 程序员是稀缺的。

良好的设计模式 + 整洁的代码,配合上经过特制的 JVM 和架构,同样工程造价Java 程序的速度还是可以接近 C++。详细,在下一部分会展开介绍。

内存管理策略

在传统的通信领域里,一种常见管理内存的方式是:预分配内存,它可以减少分配内存所需的时间,进而提升性能。也因此,编写一个自定义的 malloc 便是这个领域的好选择,而诸如于采用 Google 的 TCMalloc,又或者是 Jemalloc 也是一个不错的选择。

既然,C/C++ 语言有内存分配的问题,那么自动 GC 的 Java 语言也有相似的问题。在 GC 的过程中,为了进行准确的计数,应用程序线程需要暂时“冻结”,也就是 GC 停顿。应对于此,通常有两个做法:无 GC 又或者是更好的 GC 算法。 而要对 JVM 进行优化并不是一件容易的事,所以直接采用诸如于 Azul Systems 的 Zing 引擎,又或者是 GraalVM,还能支持 AoT 编译的 VM —— 可以将字节码转换为机器码,以提升性能。这又造成了另外一个问题,AoT 意味着失去了自适应即时(JIT)编译,会影响到我们习惯的各种依赖注入等特性。简单来说,你可能用不了 Spring,或者你的 Spring 应用需要迁移。

这方面研究的路很长,都有待我在未来有空进一步展开研究。

从架构层面来考虑,基于事件驱动架构的无状态软件架构,诸如于 LMAX 架构这样的框架就能提供非常不错的参考。其主要由三部分组成:高并发框架(Disruptor)、事件溯源(Event Sourcing)、完全内存(in-memory)驻留风格等。其中,关于持续久化部分是相当反直觉的,既然事务性数据库是个瓶颈,那就不用数据库,将事件流记录到文件系统。以内存为中心,持久化的事件作为辅助,便是这种架构模式的一大特征。

Java 框架

回到上面说的 AoT 和 JIT 的问题,如果我们想到一个启动更快的框架,那么我们就不能使用 Spring 框架。于是乎 现代化的微服务架构框架 Micronaut、Kubernetes 原生的 Java 框架 Quarkus,提供了两种不同的思路。Micronaut 在编译时依赖流入,可以加快应用程序的启动,降低内存的使用,进而提升响应速度,也可以利用 AoT 带来的性能加强。当然,这也意味着编程模式上会有一系列的变化。

高性能数据结构

在 Java 侧,为了构建高性能的 Java 应用,还需要在大数据结构上做一些优化,诸如于集合操作(List、Set、Bag),像 Agronafastutil 便是针对于 Java Collections Framework 做的扩展。同样,高盛的 JVM 架构组在多年前贡献给 Eclipse 基金会的:Eclipse Collections,便是一个旨在提升集合操作的 Java 库。

在可视化方面,如果有大量的图表需要展示,那么就需要考虑将密集计算下沉到 WASM 的方式。在这一点上 Perspective 提供了一个非常不错的思路,使用 Rust 结合高性能的 C++ 数据结构设计,提供 GUI + WASM 的封装,以实现更快的计算速度。

数据处理。理论上,我们还应该考虑对于数据的处理,诸如于不同存储介质应对实时数据、历史数据等等。

在参考资料上,推荐一下《Developing High-Frequency Trading Systems》,它是我在编写此文时,尝试云覆盖更多维度时,所查找到的比较系统介绍的书籍。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK