这是我在Envoy架构系列中的第3篇文章。这篇文章基于以前关于Envoy的线程模型和热重启功能的帖子。如果您还没有阅读这些帖子,请先阅读。 需要指出的是,随着预演的结束,我们现在可以进入更有趣的话题!
到目前为止,Envoy所做的最重要的事情是为分布式系统的可观测性提供了一个健壮的平台。这包括统计数据、日志记录和分布式跟踪。这篇文章将集中在统计数据和Envoy是如何实现允许高容量的同时保持卓越性能的。Envoy目前支持三种不同的统计数据:
Envoy目前不支持任何浮点统计数据。
Envoy生成很多对调试分布式系统有用的数据!
Envoy统计子系统的总体目标如下:
粗略的线性吞吐量:可以与任意数量的工作线程一起扩展。另一种说法是:在稳定状态下,使用stats时应该没有跨线程争用。
在使用热重启时,状态应该在逻辑上保持一致。这意味着即使有两个Envoy进程在运行,当逻辑上认为是单个进程时,所有计数器、量规和直方图都应该是一致的。(有关这方面的更多信息,请参阅热重启这篇文章)。
统计数据应该包含在作用域内并作为一个组释放。作用域是具有公共前缀的统计数据的逻辑分组。例如:http.admin.*
。这一点很重要,因为Envoy具有动态性。Envoy支持各种管理API,如监听器发现服务(LDS)和集群发现服务(CDS) API。为了不耗尽内存,Envoy需要清理不再使用的统计数据。
统计范围应该能够重叠和正确的引用计数。这意味着如果作用域A使用一个名为foo.bar.baz
的属性,作用域B也使用foo.bar.baz
属性,那么foo.bar.baz
的属性的引用计数应该是2。这对于热重启(两个进程将在一段时间内写入相同的统计数据)和动态管理API(在一段时间内,更新的监听器或集群将引用与旧监听器或集群相同的统计数据)都是必需的。
统计数据子系统应该能够很好地执行直到数据平面处理开始时才知道的统计信息。许多统计数据本质上是“固定的”,可以在加载配置或动态API重新配置数据平面时创建(例如,cluster.foo.upstream_rq_5xx
)。这些都是低频事件。其他统计信息,例如详细的HTTP响应代码度量(例如,cluster.foo.upstream_rq_503
),在数据开始流动之前都不知道。使用“动态”的统计数据永远不会像使用“固定”的统计数据那样快,但是即使在处理每个内核每秒数千个请求的10次时,性能仍然应该是足够的。
作为一个整体,上述目标需要一个复杂的系统来满足。我们现在将深入研究这个系统是如何工作的。
图1:高级统计架构,蓝色统计数据显示了一个作用域分组。
图1显示了Envoy数据统计子系统的高级架构。它由以下几个部分组成。
stat存储是Envoy内部的一个单例对象,并提供了一个简单的接口,通过该接口,其余代码可以获得作用域、计数器、计量和直方图的句柄。调用代码负责维护所有创建的作用域的所有权语义。当作用域被销毁时,所有包含的统计数据的引用计数都会减少1。如果任何统计数据达到0引用计数,它们将被释放。
如前所述,统计数据包括计数器、量规和直方图。从终端用户的角度来看,这些接口使用起来非常简单。例如,计数器和计量都包括inc()
和dec()
方法,而只有计量包括set()
方法。程序员看不到任何潜在的存储复杂性。
为了获得高性能,使用原子CPU指令在内部缓冲所有的状态变化。在可配置的间隔内,所有计数器和计量都被冲到flusher中。注意,在当前的架构中,直方图值直接发送到接收器。下面将更详细地描述这一点。Flusher在main线程中运行。
统计数据接收器是一个接口,它接受通用的统计数据并将其转换为特定于后端的连线格式。所有接收器都使用TLS,这样在刷新输出时就不会出现争用。然而,在实践中,目前只有主线会冲掉计数器和量规。所有线程都刷新直方图。
目前Envoy只支持TCP和UDP statsd协议。statsd是一种非常简单但得到广泛支持的传输格式。在未来,很可能会实现其他本地统计数据接收器,如Prometheus、Wavefront和 InfluxDB。还要注意Envoy目前不支持维度或标签统计。这将在下面的工作部分中进一步讨论。
从操作的角度来看,能够实时地到达一个节点并转储当前状态是非常有用的。Envoy可以通过/stats
管理端点实现此功能。管理端点直接查看存储库以加载所有计数器和计量并打印它们。这个端点目前不输出任何直方图数据。这同样是由于在当前的实现中直方图值是直接写入接收器的,因此存储不知道它们。
正如已经多次提到的,Envoy目前不维护进程内直方图数据。除了开发效率之外,没有什么特别的原因;Lyft使用的statsd摄取管道提供了自己的直方图支持,并希望直方图值直接发送到它。因此,直方图值目前不能通过管理端点查看。未来我们很可能直接在Envoy内部实现HDR直方图。这一点将在下面进一步讨论。
以上所有的背景都完成了,现在是时候深入到有趣的部分:实践中是如何工作的?
图2:共享内存中单独的计数器/计量统计项
正如我们在热重启文章中已经讨论过的那样,最终,所有统计数据都存储在共享内存中,以便可以在所有进程中使用它们。图2显示了单个stat条目。它由以下几个部分组成:
http.admin.downstream_cx_active
。目前限制为128个字符。used
。这表示如果统计数据被写过,那么代码能够区分零和从未写过。Envoy不会刷新从来没有使用过的统计数据,以避免压倒性的统计后端很少使用的统计数据。图3:线程本地热重启支持的存储体系结构
图3 显示了Envoy内部使用的线程本地stat存储的设计。这个版本的商店满足了之前发布的所有设计目标。现在我们将详细介绍它的工作原理。
回顾一下,让我们看看上面的设计如何满足所有的原始目标:
虽然Envoystats子系统工作得很好,但是有几个方面在未来可以改进:
为了满足上述目标,Envoy的数据统计子系统的设计是新颖的。到目前为止,它在实践中表现得非常好,对于其他用例来说,扩展起来应该相对容易。
本文中涉及到的一些接口及实现的头文件请参考下面链接:
手机扫一扫
移动阅读更方便
你可能感兴趣的文章