Envoy 介绍之(八)
Contents
本文档基于 v1.28, 原文地址: https://www.envoyproxy.io/docs/envoy/v1.28.0/intro/intro
Envoy 官网介绍文档的中文翻译(请求的生命周期)
以下我们将描述一个请求通过 Envoy 代理时的生命周期中的事件。 我们首先描述 Envoy 如何适应请求路径,然后描述从下游到达 Envoy 代理后发生的内部事件。 我们将跟踪请求,直到相应的上游分发和响应路径。
术语
Envoy的代码库和文档中用到了以下术语:
- 集群(Cluster):一组端点,代表一个逻辑服务。Envoy会将请求转发给这些端点。
- 下游(Downstream):与Envoy建立连接的实体。这可以是一个本地应用程序(在sidecar模型中)或网络节点。 在非sidecar模型中,它是一个远程客户端。
- 端点(Endpoints):实现逻辑服务的网络节点。这些节点被组合成集群。在集群中的端点是Envoy代理的上游。
- 过滤器(Filter):处理请求的某个方面的模块,位于连接或请求处理管道中。 它可以被视为由Unix管道连接的小工具(过滤器)的集合。
- 过滤器链(Filter chain):一系列过滤器,它们按顺序执行。
- 监听器(Listeners):负责绑定到IP/端口、接受新的TCP连接(或UDP数据报)并协调请求处理下游方面的Envoy模块。
- 上游:当转发服务请求时,Envoy连接到的端点(网络节点)。 这可以是一个本地应用程序(在sidecar模型中)或网络节点。 在非sidecar模型中,它对应于远程后端。
网络拓扑
请求如何在包括Envoy在内的网络组件中流动,取决于网络的拓扑结构。Envoy可以用于各种网络拓扑。 下面我们将重点关注Envoy的内部操作,但在此部分中,我们将简要介绍Envoy如何与网络的其他部分相关联。
Envoy最初是一个服务网格边车代理,将负载均衡、路由、可观察性、安全性和发现服务从应用程序中分离出来。 在服务网格模型中,请求通过Envoy作为网关流经网络。请求通过入口或出口监听器到达Envoy:
入口监听器从服务网格中的其他节点接收请求,并将它们转发给本地应用程序。本地应用程序的响应通过Envoy返回到下游节点。
出口监听器接收来自本地应用程序的请求,并将它们转发到网络中的其他节点。 这些接收节点通常也会运行Envoy,并使用其入口监听器接收请求。
Envoy 可用于服务网格之外的各种配置。例如,它还可以充当内部负载均衡器:
或者作为网络边缘的入口/出口代理:
在实践中,经常使用这些的混合,其中 Envoy 在服务网格、边缘和内部负载均衡器中发挥作用。 一个请求路径可能会遍历多个 Envoy。
Envoy 可以配置为多层拓扑,以实现可扩展性和可靠性,请求首先通过边缘 Envoy,然后再通过第二个 Envoy 层:
在上述所有情况下,请求将从下游通过 TCP、UDP 或 Unix 域套接字到达特定 Envoy。 Envoy 将通过 TCP、UDP 或 Unix 域套接字向上游转发请求。下面我们重点关注单个 Envoy 代理。
配置
Envoy是一个非常容易扩展的平台。这导致了可能的请求路径组合的爆炸性增长,具体取决于以下因素:
- L3/L4协议,例如TCP、UDP、Unix域套接字。
- L7协议,例如HTTP/1、HTTP/2、HTTP/3、gRPC、Thrift、Dubbo、Kafka、Redis和各种数据库。
- 传输套接字,例如明文、TLS、ALTS。
- 连接路由,例如PROXY协议、原始目的地、动态转发。
- 身份验证和授权。
- 断路器和异常检测的配置和激活状态。
- 许多其他用于网络、HTTP、监听器、访问日志记录、健康检查、跟踪和统计扩展的配置。
一次专注于一个配置很有帮助,因此此示例涵盖了以下内容:
- 下游和上游都通过TCP连接使用TLS的HTTP/2请求。
- HTTP连接管理器作为唯一的网络过滤器。
- 假设的CustomFilter和路由器过滤器作为HTTP过滤器链。
- 文件系统访问日志记录。
- Statsd接收器。
- 具有静态端点的单个集群。
为了简单起见,我们假设使用静态引导配置文件。
|
|
高级架构
Envoy中的请求处理路径有两个主要部分:
监听器子系统, 负责处理下游请求处理。它还负责管理下游请求生命周期以及客户端的响应路径。下游HTTP/2编解码器位于此处。
集群子系统, 负责选择和配置到端点的上游连接。这是集群和端点健康状况、负载均衡和连接池知识的所在之处。上游HTTP/2编解码器位于此处。
这两个子系统通过HTTP路由器过滤器进行桥接,将HTTP请求从下游转发到上游。
我们使用监听器子系统 和集群子系统 这两个术语来指代由顶级ListenerManager和ClusterManager类创建的模块和实例类组。 我们将在下面讨论许多组件,这些组件在请求之前和请求过程中由这些管理系统实例化, 例如监听器、过滤器链、编解码器、连接池和负载均衡数据结构。
Envoy具有基于事件的多线程模型。 主线程负责服务器生命周期、配置处理、统计信息等,而一些工作线程处理请求。 所有线程都围绕事件循环(libevent)运行,对于给定的下游TCP连接(包括其上的所有复用流),在其生命周期中,将由一个工作线程精确处理。 每个工作线程维护自己的TCP连接池到上游端点。 UDP处理使用SO_REUSEPORT,使内核始终将源/目标IP:端口元组哈希到相同的工作线程。 对于给定的工作线程,UDP过滤器状态是共享的,过滤器负责提供所需的会话语义。 这与我们下面讨论的面向连接的TCP过滤器不同,其中过滤器状态存在于每个连接和HTTP过滤器的每个请求上。
工作线程很少共享状态,并以非常简单的并行方式运行。这种线程模型使得能够扩展到非常高的核心数CPU。
请求流
概述
使用上述配置示例简要概述请求和响应的生命周期:
- 由工作线程上的Envoy监听器接受来自下游的TCP连接。
- 创建并运行监听器过滤器链。它可以提供SNI和其他TLS前的信息。 一旦完成,监听器将匹配一个网络过滤器链。 每个监听器可以有多个过滤器链,它们根据目标IP CIDR范围、SNI、ALPN、源端口等的组合进行匹配。 在这种情况下,传输套接字(TLS传输套接字)与该过滤器链关联。
- 在网络读取时,TLS传输套接字解密从TCP连接读取的数据到解密的数据流以供进一步处理。
- 创建并运行网络过滤器链。对于HTTP来说,最重要的过滤器是HTTP连接管理器,它是链中的最后一个网络过滤器。
- HTTP连接管理器中的HTTP/2编解码器将来自TLS连接的解密数据流拆分为多个独立流。每个流处理单个请求和响应。
- 对于每个HTTP流,创建并运行下游HTTP过滤器链。 请求首先通过CustomFilter,该过滤器可能读取和修改请求。 最重要的HTTP过滤器是位于HTTP过滤器链末端的路由器过滤器。 当对路由器过滤器调用decodeHeaders时,选择路由并选择一个集群。 该集群中的请求头将转发到上游端点。 路由器过滤器从集群管理器中获取与匹配集群相关的HTTP连接池进行此操作。
- 执行集群特定的负载均衡以找到端点。 检查集群的断路器以确定是否允许新的流。 如果端点的连接池为空或容量不足,则创建与该端点的新连接。
- 对于每个流,创建并运行上游HTTP过滤器链。 默认情况下,这仅包括发送数据到适当编解码器的CodecFilter, 但如果集群配置了上游过滤器链,则将为每个流创建和运行该过滤器链, 包括为重试和阴影请求创建和运行单独的过滤器链。
- 上游端点连接的HTTP/2编解码器通过单个TCP连接将请求流的任何其他流与该上游进行多路复用和帧化。
- 上游端点连接的TLS传输套接字加密这些字节,并将它们写入上游连接的TCP套接字。
- 请求(包括头部、可选正文和尾部)被代理到上游,响应被代理到下游。 响应通过HTTP过滤器的顺序与请求相反,从编解码器过滤器开始,遍历任何上游过滤器, 然后通过路由器过滤器并经过CustomFilter,最后发送到下游。
- 当响应完成时,流将被销毁。后请求处理将更新统计信息、写入访问日志并完成跟踪跨度。
我们将在以下部分详细阐述这些步骤。
1. 监听器 TCP Accept
监听器管理器负责接收表示监听器的配置,并实例化一些绑定到各自IP/端口的Listener实例。监听器可以处于以下三种状态之一:
预热(Warming):监听器正在等待配置依赖项(例如路由配置、动态秘钥)。监听器尚未准备好接受TCP连接。
活动(Active):监听器绑定到其IP/端口并接受TCP连接。
排空(Draining):监听器不再接受新的TCP连接,但其现有的TCP连接在一段时间内仍被允许继续。
每个工作线程为其配置的每个监听器维护自己的Listener实例。
每个监听器可以通过 SO_REUSEPORT 或共享单个绑定到此端口的套接字来绑定到相同的端口。
当新的TCP连接到达时,内核决定哪个工作线程将接受该连接,并且该工作线程的Listener的Server::ConnectionHandlerImpl::ActiveTcpListener::onAccept()
回调将被调用。
2. 监听器过滤器链和网络过滤器链匹配
工作线程的Listener然后创建并运行监听器过滤器链。过滤器链是通过应用每个过滤器的过滤器工厂创建的。工厂知道过滤器的配置,并为每个连接或流创建过滤器的新实例。
对于我们的TLS监听器配置,监听器过滤器链由TLS检查器过滤器(envoy.filters.listener.tls_inspector)组成。此过滤器检查初始TLS握手并提取服务器名称(SNI)。然后,SNI可用于过滤器链匹配。尽管TLS检查器在监听器过滤器链配置中显式出现,但Envoy还能够在需要SNI(或ALPN)时自动插入此过滤器。
TLS检查器过滤器实现了 ListenerFilter接口。
所有过滤器接口,无论是监听器还是网络/HTTP,都要求过滤器为特定的连接或流事件实现回调。对于 ListenerFilter
,这是:
|
|
onAccept()
允许过滤器在 TCP accept 处理期间运行。回调返回的FilterStatus
控制监听器过滤器链将如何继续。监听器过滤器可以暂停过滤器链,然后稍后恢复,例如响应于对另一个服务的RPC。
从监听器过滤器和连接属性中提取的信息随后用于匹配过滤器链,提供将用于处理连接的网络过滤器链和传输套接字。
- TLS传输套接字解密
Envoy通过TransportSocket扩展接口提供可插拔的传输套接字。 传输套接字遵循TCP连接的生命周期事件,并读取/写入网络缓冲区。传输套接字必须实现的一些关键方法如下:
|
|
当TCP连接上有数据可用时,Network::ConnectionImpl::onReadReady()``通过
SslSocket::doRead()调用TLS传输套接字。 传输套接字然后在TCP连接上执行TLS握手。 当握手完成后,`SslSocket::doRead()
向负责管理网络过滤器链的`Network::FilterManagerImpl``实例提供解密的字节流。
重要的是要注意,没有任何操作,无论是TLS握手还是过滤器管道的暂停,都是真正的阻塞操作。 由于Envoy是基于事件的,任何需要额外数据才能完成处理的情况都会导致早期事件完成,并将CPU让给其他事件。 当网络提供更多可读数据时,一个读取事件将触发TLS握手的恢复。
4. 网络过滤器链处理
与监听器过滤器链一样,Envoy通过 Network::FilterManagerImpl
实例化一系列网络过滤器,从它们的过滤器工厂中。
每个新连接都有一个新的实例。
网络过滤器,就像传输套接字一样,遵循TCP生命周期事件,并在传输套接字可用数据时被调用。
网络过滤器以管道的形式组成,与每个连接一个的传输套接字不同。网络过滤器有三种类型:
ReadFilter 实现
onData()
,当从连接中可用数据时调用(由于某些请求)。WriteFilter 实现
onWrite()
,当数据即将写入连接时调用(由于某些响应)。实现ReadFilter和WriteFilter的Filter。
关键过滤器方法的方法签名如下:
|
|
与监听器过滤器一样,FilterStatus
允许过滤器暂停过滤器链的执行。例如,如果需要查询限速服务,限速网络过滤器将返回Network::FilterStatus::StopIteration
从onData()
,并在查询完成后调用continueReading()
。
处理HTTP的监听器的最后一个网络过滤器是HTTP连接管理器(HCM)。它负责创建HTTP/2编解码器和管理HTTP过滤器链。在我们的示例中,这是唯一的网络过滤器。使用多个网络过滤器的示例网络过滤器链如下所示:
在响应路径上,网络过滤器链按与请求路径相反的顺序执行。
5. HTTP/2 codec 解码
Envoy中的HTTP/2编解码器基于nghttp2。它由HCM使用来自TCP连接的明文字节(经过网络过滤器链转换后)调用。编解码器将字节流解码为一系列HTTP/2帧,并将连接解复用为多个独立的HTTP流。流复用是HTTP/2的关键功能,与HTTP/1相比提供了显著的性能优势。每个HTTP流处理一个请求和响应。
编解码器还负责处理HTTP/2设置帧以及流和连接级别的流量控制。
编解码器负责抽象HTTP连接的细节,向HTTP连接管理器和HTTP过滤器链呈现标准的连接视图,该连接分为多个流,每个流具有请求/响应头/正文/尾部。无论协议是HTTP/1、HTTP/2还是HTTP/3,这都是正确的。
6. HTTP过滤器链处理
对于每个HTTP流,HCM实例化一个下游HTTP过滤器链, 遵循上述监听器和网络过滤器链的模式。
有三种HTTP过滤器接口:
StreamDecoderFilter与请求处理回调。
StreamEncoderFilter与响应处理回调。
StreamFilter 实现
StreamDecoderFilter
和StreamEncoderFilter
。
查看解码器过滤器接口:
|
|
与在连接缓冲区和事件上操作不同,HTTP过滤器遵循HTTP请求的生命周期,例如,decodeHeaders()
方法接受HTTP头部作为参数,而不是字节缓冲区。返回的 FilterStatus
与网络和监听器过滤器一样,提供了管理过滤器链控制流的能力。
当HTTP/2编解码器提供HTTP请求的头部时,这些头部首先传递给 CustomFilter 中的 decodeHeaders()
方法。如果返回的
FilterHeadersStatus
为 Continue
,HCM然后将头部(可能由CustomFilter修改)传递给路由器过滤器。
解码器和编解码器过滤器在请求路径上执行。编码器和编解码器过滤器在响应路径上执行,方向相反。考虑以下示例过滤器链:
请求路径看起来是:
响应路径看起来是:
当在路由过滤器上调用decodeHeaders()
时,路由选择将最终确定并选择一个集群。在HTTP过滤器链执行开始时,HCM从其RouteConfiguration
中选择一条路由。这被称为缓存的路由。过滤器可以修改头部并要求HCM清除路由缓存并重新评估路由选择。过滤器还可以通过setRoute
回调直接设置此缓存的路由选择。当路由器过滤器被调用时,路由被确定。所选路由的配置将指向上游集群名称。路由器过滤器然后要求ClusterManager
为集群创建一个HTTP连接池。这涉及到下一部分中讨论的负载均衡和连接池。
由此产生的HTTP连接池用于在路由器中构建一个UpstreamRequest
对象,该对象封装了上游HTTP请求的HTTP编码和解码回调方法。一旦在HTTP连接池中的连接上分配了一个流,请求头部将通过调用UpstreamRequest::encodeHeaders
转发到上游端点。
路由器过滤器负责从HTTP连接池中分配的流上的上游请求生命周期管理的所有方面。它还负责请求超时、重试和亲和性。
路由器过滤器还负责创建和运行上游HTTP过滤器链 <arch_overview_http_filters>。 默认情况下,上游过滤器将在路由器过滤器收到头部后立即开始运行,但是C++过滤器可以暂停,直到需要检查上游流或连接时才建立上游连接。 默认情况下,上游过滤器链通过集群配置进行配置,因此例如,被遮蔽的请求可以为主集群和遮蔽的集群设置单独的上游过滤器链。 另外,由于上游过滤器链位于路由器过滤器上游,因此每个重试尝试都会运行一次, 允许每个重试进行头部操作并包含有关上游流和连接的信息。 与下游过滤器不同,上游过滤器不能更改路由。
7. 负载均衡
每个集群都有一个负载均衡器, 当新请求到达时,它会选择一个端点。 Envoy支持各种负载均衡算法,例如加权轮询、Maglev、最少负载、随机。 负载均衡器通过组合静态引导配置、DNS、动态xDS(CDS和EDS发现服务)以及主动/被动健康检查来获取其有效分配。 有关Envoy中负载均衡如何工作的更多详细信息,请参阅负载均衡文档。
一旦选择了端点,将从该端点的连接池, 查找用于转发请求的连接。 如果没有到主机的连接,或者所有连接都已达到其最大并发流限制, 则将建立新的连接并将其放置在连接池中,除非该集群的最大连接数的断路器已跳闸。 如果配置了连接的最大生命周期流限制并且已达到,则将在池中分配新的连接,并将受影响的HTTP/2连接排空。 还会检查其他断路器,例如到集群的最大并发请求。请参阅 断路器 和连接池 以获取更多详细信息。
8. HTTP/2编解码器编码
选定的连接的HTTP/2编解码器通过单个TCP连接将请求流与任何其他流向相同上游的流进行复用。这与HTTP/2编解码器解码相反。
与下游HTTP/2编解码器一样,上游编解码器负责将Envoy的标准HTTP抽象,即通过单个连接进行多流复用,具有请求/响应头/正文/尾部,并将其映射到HTTP/2的具体细节,生成一系列HTTP/2帧。
9. TLS传输套接字加密
上游端点连接的TLS传输套接字对来自HTTP/2编解码器的字节进行加密,并将它们写入TCP套接字以进行上游连接。与 TLS传输套接字解密 一样,在我们的示例中,集群配置了提供TLS传输安全的传输套接字。上游和下游传输套接字扩展具有相同的接口。
10. 响应路径和HTTP生命周期
由头部和可选的正文和尾部组成的请求被代理到上游,响应被代理到下游。 响应按照与请求相反的顺序通过HTTP和网络过滤器。
在解码器/编码器请求生命周期事件的各种回调中,HTTP过滤器将被调用,例如在转发响应尾部或流式传输请求正文时。 同样,在请求期间,读写网络过滤器也将有其各自的回调被调用,因为数据在两个方向上继续流动。
随着请求的进展,端点的异常检测状态也会得到修订。
当上游响应到达其流的末尾时,即收到带有设置了end-stream的响应头部/正文或尾部时,请求完成。
这由outer::Filter::onUpstreamComplete()
处理。
请求可能会提前终止。这可能是由于以下原因(但不限于):
- 请求超时。
- 上游端点流重置。
- HTTP过滤器流重置。
- 断路器触发。
- 上游资源不可用,例如缺少一个路由的集群。
- 没有健康端点。
- DoS保护。
- HTTP协议违规。
- 来自HCM或HTTP过滤器的本地回复。例如,速率限制HTTP过滤器返回429响应。
如果发生其中任何一种情况,Envoy可能会发送一个内部生成的响应(如果上游响应头尚未发送), 或者如果响应头已经转发到下游,则会重置流。 有关解释这些早期流终止的更多信息,请参阅Envoy调试常见问题解答。
11. 请求后处理
一旦请求完成,流将被销毁。以下操作也会发生:
Author itsfinn
LastMod 2024-01-13