Envoy 全局限流详解gRPC Rate Limit 与配额RLQS两种架构的完整实践【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy本文基于 Envoy 官方文档《Global rate limiting》架构篇展开系统讲解 Envoy 的两种全局限流实现基于 gRPC Rate Limit 服务的逐连接/逐请求限流以及基于配额Quota/RLQS的公平共享限流。读完本文你将理解每种方案的适用场景掌握 network/HTTP 两个层级限流过滤器的配置方法、限流动作Rate Limit Action的组合规则、统计指标与运行时开关并能参考仓库中的示例配置在真实部署中落地全局限流。一、为什么需要全局限流熔断的失效场景Envoy 的分布式熔断circuit breaking在大多数场景下能有效控制分布式系统的吞吐量但文档明确指出一个典型失效场景当大量下游主机转发到少量上游主机、且平均请求延迟很低时例如对数据库服务器的连接/请求如果目标主机开始积压所有下游主机会同时把流量灌入该上游集群形成级联失败cascading failure。此时极难为每台下游主机配置一个足够紧的熔断阈值——既要保证正常流量模式下的系统正常运行又要在系统开始故障时阻止级联失败。问题的根源在于熔断是每个 Envoy 实例独立决策的N 台下游各自持有配额聚合起来的上游压力 N × 单机阈值无法表达整个集群加起来只允许 X QPS这一全局约束。全局限流Global rate limiting正是为这种场景设计的限流决策由一个集中的限流服务做出所有 Envoy 实例共享同一份配额视图。Envoy 提供两种全局限流实现逐连接或逐 HTTP 请求的限流检查Per connection or per HTTP request rate limit check每个新连接或新请求都向限流服务发起一次 gRPC 查询基于配额的限流Quota based通过周期性负载报告让多个 Envoy 实例公平共享一个全局配额。适合大规模、高 QPS 且流量在各实例间分布不均的 Envoy 部署。二、方案一基于 gRPC Rate Limit 服务的限流2.1 集成方式与参考实现Envoy 直接集成一个全局gRPC 限流服务只要服务实现了 Envoy 定义的 RPC/IDL 协议就可以接入。官方生态中有一个用 Go 编写、以 Redis 为后端的参考实现ratelimit 项目部署该服务后Envoy 的限流过滤器即可以通过 gRPC 与之通信。Envoy 侧对这一集成提供了两个层级的过滤器网络层限流过滤器network level rate limit filter对监听器上安装的每一个新连接调用一次限流服务。配置中指定一个 domain 和一组 descriptors最终效果是限制经过该监听器的连接建立速率connections per second。配置参考见 network rate limit filter 文档。HTTP 层限流过滤器HTTP level rate limit filter对监听器上的每一个新请求调用限流服务但前提是路由表中指定了该请求需要调用全局限流服务。所有发往目标上游集群的请求、以及所有从源集群到目标集群的请求都可以被限流。配置参考见 HTTP rate limit filter 文档。限流服务本身的配置见 Rate limit service 配置。2.2 与本地限流的组合两阶段限流文档特别强调Envoy 还支持本地限流local rate limiting它可以与全局限流叠加使用来降低全局限流服务的压力例如一个本地 token bucket 限流器可以吸收非常大的突发流量避免这些流量冲击全局限流服务。这样限流就变成两阶段先由 token bucket 做粗粒度的初步限流再由细粒度的全局限流完成最终的配额控制。这一架构在数据库代理等低延迟、高连接速率场景中尤其重要——否则限流服务本身会成为新的瓶颈。2.3 gRPC IDL 协议Envoy 期望限流服务支持定义在 rls.proto 中的 gRPC IDLRateLimitService.Check限流服务集群配置则遵循 config/ratelimit/v3/rls.proto。如果未配置限流服务Envoy 会使用一个null服务调用时永远返回 OK。2.4 网络层过滤器统计、运行时开关与示例网络层过滤器使用 type URLenvoy.extensions.filters.network.ratelimit.v3.RateLimit配置。每个已配置的过滤器都会在ratelimit.stat_prefix.*下输出统计名称类型说明totalCounter发给限流服务的请求总数errorCounter联系限流服务出错总数over_limitCounter限流服务返回 over limit 的总数okCounter限流服务返回 under limit 的总数cx_closedCounter因 over limit 响应而关闭的连接总数activeGauge发往限流服务的进行中请求总数failure_mode_allowedCounter出错但因failure_mode_deny为 false 而被放行的请求总数这些计数器在源码 source/extensions/filters/network/ratelimit/ratelimit.cc 中逐处递增发起检查时total_自增约 L84、服务返回超限后over_limit_自增约 L129、因超限关闭连接时cx_closed_自增约 L135并与连接关闭原因ratelimit_close_over_limit一起记录到连接统计中。运行时开关ratelimit.tcp_filter_enabled调用限流服务的连接百分比默认 100ratelimit.tcp_filter_enforcing调用限流服务并执行决策的连接百分比默认 100。可以先以 0 的 enforcing 值做影子模式观测再逐步放量执行。网络层过滤器的 descriptor 值支持基于 stream info 的替换格式化如访问日志格式变量。示例name: envoy.filters.network.ratelimit domain: foo descriptors: - entries: - key: remote_address value: %DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT% - key: foo value: bar stat_prefix: name该过滤器还会在 gRPC 限流服务的CheckResponse中填充dynamic_metadata字段时将其作为不透明的google.protobuf.Struct发出到动态元数据。2.5 HTTP 层过滤器触发条件、429 语义与故障模式HTTP 层过滤器使用 type URLenvoy.extensions.filters.http.ratelimit.v3.RateLimit配置其工作语义来自 HTTP rate limit filter 文档触发条件当请求所匹配的路由或虚拟主机带有一个或多个与过滤器阶段匹配的rate_limits配置时过滤器才会调用限流服务路由可以通过include_vh_rate_limits额外继承虚拟主机上的限流配置。一个请求可同时命中多份配置每份配置都会生成一个 descriptor 发给限流服务。限流响应任一 descriptor 返回 over limit即返回429状态码可通过rate_limited_status定制并默认添加x-envoy-ratelimited响应头可用disable_x_envoy_ratelimited_header关闭。故障模式failure mode调用限流服务出错、或限流服务返回错误时failure_mode_deny为 true 则返回 500为 false 则放行请求。源码 source/extensions/filters/http/ratelimit/ratelimit.h 中可见该逻辑构造函数保存failure_mode_deny_还支持failure_mode_deny_percent以运行时百分比做灰度切换。Retry-After 头启用enable_retry_after_header后当过滤器实际下发 429 时响应会携带Retry-After头delay-seconds形式。取值为限流服务返回的所有 over-limit 状态中最大的duration_until_reset且钳制为至少 1 秒保证所有命中规则都能等到重置。如果限流服务自己返回了Retry-After头过滤器不会覆盖。该选项默认关闭且在上游自己生成 429、未强制执行限流、配置了非 429 状态码或服务未返回 over-limit 状态时均不会发出。domain: foo enable_retry_after_header: true rate_limit_service: transport_api_version: V3 grpc_service: envoy_grpc: cluster_name: rate_limit_service统计指标输出在cluster.route target cluster.ratelimit.optional stat prefix.命名空间下包括ok、error、over_limit、failure_mode_allowed四个计数器429 或rate_limited_status配置的响应会计入该集群的常规动态 HTTP 统计。运行时开关为ratelimit.route_key.http_filter_enabled按路由上限流配置的 route_key 指定调用限流服务的请求百分比默认 100。动态元数据默认存放在envoy.filters.http.ratelimit命名空间下——这一点在源码中可以得到印证ratelimit.h 中当metadata_namespace为空串时回退到默认值envoy.filters.http.ratelimit命名空间可通过过滤器配置中的metadata_namespace字段更改。仓库中的现网示例也可供参考envoy_front_proxy.template.yaml 与 envoy_service_to_service.template.yaml 都演示了将envoy.filters.http.ratelimit插入 HTTP 过滤器链、并将限流服务集群指向ratelimit的完整配置形态。2.6 Rate Limit Action 组合构造复杂 descriptor每个路由/虚拟主机上的rate_limits中的 action 都会填充一个 descriptor 条目条目向量按配置顺序组成一个完整 descriptor。仓库自带的 rate-limit-routes.yaml 展示了五种典型路由可以直接复用到自己的 bootstrap 中以下截取其中前两条路由的rate_limits片段- match: prefix: /route0 route: host_rewrite_literal: upstream.com cluster: upstream_com rate_limits: - actions: - source_cluster: {} - generic_key: descriptor_value: some_value0示例 1组合/route0的 action 列表是source_clustergeneric_key生成的 descriptor 为(generic_key, some_value0) (source_cluster, from_cluster)示例 2条件性 action/route1的 action 列表为source_clusterremote_addressgeneric_key。如果请求没有设置x-forwarded-forremote_addressaction 不产生条目整个 descriptor 不生成、不会调用限流服务如果请求设置了x-forwarded-for则生成(generic_key, some_value1) (remote_address, trusted address from x-forwarded-for) (source_cluster, from_cluster)2.7 Limit Override用动态元数据覆盖服务端的静态限额rate limit action可以携带一个limitoverride其值会被追加到 descriptor 中发给限流服务覆盖服务端的静态配置。override 的值可以从动态元数据中按指定metadata_key查找取值失败或键不存在时override 配置被忽略。/route2的配置摘自 rate-limit-routes.yamlrate_limits: - actions: - generic_key: descriptor_value: some_value2 limit: dynamic_metadata: metadata_key: key: test.filter.key path: - key: test被查找的动态元数据值必须是一个包含整数字段requests_per_unit和字符串字段unit可解析为RateLimitUnit枚举的结构体例如test.filter.key: test: requests_per_unit: 42 unit: HOUR此时 descriptor 中会追加42 请求/小时的限额覆盖。2.8 Descriptor 扩展用任意请求属性作为描述符值限流 descriptor 支持通过扩展机制扩展。envoy.extensions.rate_limit_descriptors.expr.v3.Descriptor扩展允许使用任何请求属性 作为描述符值- actions: - extension: name: custom typed_config: type: type.googleapis.com/envoy.extensions.rate_limit_descriptors.expr.v3.Descriptor descriptor_key: my_descriptor_name text: request.method此外HTTP 匹配输入函数matching input functions也可以作为 descriptor 生产者例如- actions: - extension: name: custom typed_config: type: type.googleapis.com/envoy.type.matcher.v3.HttpRequestHeaderMatchInput header_name: x-header-name上述配置产生 key 为custom、值取自请求头x-header-name的条目。若头不存在则不产生该条目、不生成 descriptor若头存在但为空串则生成 descriptor 但不添加该条目。三、方案二基于配额Quota的限流3.1 协议与当前可用服务配额式全局限流只能作用于 HTTP 请求。Envoy 会对请求进行分桶bucketize并通过 HTTP Rate Limit Quota 过滤器配置向限流配额服务请求配额分配。配额服务需实现 rlqs.proto 中定义的 gRPC IDLRateLimitQuotaService。需要注意文档中明确标注的前提该配额式限流服务的开源参考实现目前不可用当前可与 Google Cloud Rate Limit Service 配合使用文档中的 TODO 注明待参考实现与 GCP 文档可用后再补充链接。选择该方案时需先确认自己的限流服务端可用性。3.2 工作原理分配、上报与再平衡配额式限流与方案一每请求查询的根本区别在于周期性负载上报 服务端主动推送RLQS 向每个连接的 Envoy 实例分配配额quota assignment过滤器按配置的reporting_interval周期性上报每个桶的请求速率RLQS 据此再平衡各 Envoy 实例间的配额分配——这正是公平共享的实现机制适合流量在 Envoy 实例间分布不均的大规模部署配额分配变化时RLQS主动推送新分配到 Envoy无需 Envoy 轮询。关键生命周期语义初始状态所有 Envoy 的配额分配初始为空。当请求第一次匹配到某个桶时过滤器才向 RLQS 请求配额。等待初始分配期间的行为由no_assignment_behavior决定可以立即放行所有请求也可以拒绝直到收到配额分配。分配过期配额分配可以带 TTLassignment_time_to_liveRLQS 预期会在 TTL 到期前更新。若 TTL 过期仍未收到更新过滤器可配置为继续使用最后一次分配或回退到expired_assignment_behavior中预定义的值。未匹配兜底请求若不匹配任何 matcher则应用bucket_matchers中on_no_match字段配置的catch all桶如果未配置on_no_match所有未匹配请求不限流fail-open。桶定义覆盖桶定义可以被虚拟主机或路由配置覆盖更具体的定义完全覆盖较不具体的定义RateLimitQuotaOverride。故障模式与 RLQS 的连接失败时若尚未收到配额则回退到no_assignment_behavior若已有配额但到期前无法重连则回退到expired_assignment_behavior。若某个桶在预定时间内始终未收到初始分配时间由过滤器实现决定该桶最终会被从内存中清除后续请求将重新初始化该桶并重启上报。3.3 完整配置示例三个桶与不同的初始行为仓库自带的 rate-limit-quota-filter-configuration.yaml 是一个可直接参考的完整示例启用 3 个桶桶name: prod-rate-limit-quota匹配deployment: prod头的请求等待配额分配期间放行所有请求ALLOW_ALL桶name: staging-rate-limit-quota匹配deployment: staging头的请求等待配额分配期间拒绝所有请求DENY_ALL桶name: default-rate-limit-quota兜底桶分配过期后回退到1000 RPS的固定限额。- name: envoy.filters.http.rate_limit_quota typed_config: type: type.googleapis.com/envoy.extensions.filters.http.rate_limit_quota.v3.RateLimitQuotaFilterConfig rlqs_server: envoy_grpc: cluster_name: rate_limit_quota_service domain: acme-services bucket_matchers: matcher_list: matchers: - predicate: single_predicate: input: name: request-headers typed_config: type: type.googleapis.com/envoy.type.matcher.v3.HttpRequestHeaderMatchInput header_name: deployment value_match: exact: prod on_match: action: name: prod-bucket typed_config: type: type.googleapis.com/envoy.extensions.filters.http.rate_limit_quota.v3.RateLimitQuotaBucketSettings bucket_id_builder: bucket_id_builder: name: string_value: prod-rate-limit-quota reporting_interval: 60s no_assignment_behavior: fallback_rate_limit: blanket_rule: ALLOW_ALL - predicate: single_predicate: input: name: request-headers typed_config: type: type.googleapis.com/envoy.type.matcher.v3.HttpRequestHeaderMatchInput header_name: deployment value_match: exact: staging on_match: action: name: staging-bucket typed_config: type: type.googleapis.com/envoy.extensions.filters.http.rate_limit_quota.v3.RateLimitQuotaBucketSettings bucket_id_builder: bucket_id_builder: name: string_value: staging-rate-limit-quota reporting_interval: 60s no_assignment_behavior: fallback_rate_limit: blanket_rule: DENY_ALL # The catch all bucket settings on_no_match: action: name: default-bucket typed_config: type: type.googleapis.com/envoy.extensions.filters.http.rate_limit_quota.v3.RateLimitQuotaBucketSettings bucket_id_builder: bucket_id_builder: name: string_value: default-rate-limit-quota reporting_interval: 60s deny_response_settings: http_status: code: 429 no_assignment_behavior: fallback_rate_limit: blanket_rule: ALLOW_ALL expired_assignment_behavior: fallback_rate_limit: requests_per_time_unit: requests_per_time_unit: 1000 time_unit: SECOND要点bucket_id_builder支持按请求属性如请求头值动态生成桶 ID也支持基于配置的静态生成reporting_interval控制负载上报周期示例为 60s。3.4 自定义拒绝响应HTTP 与 gRPC 状态配额过滤器支持通过deny_response_settings自定义超配请求的拒绝响应HTTP 请求通过http_status配置状态码默认 429gRPC 请求可选设置grpc_status精确指定 gRPC 状态码和消息不设置时 Envoy 会从 HTTP 状态码推导。默认行为gRPC 状态由 HTTP 状态推导deny_response_settings: http_status: code: 429显式指定 gRPC 状态deny_response_settings: http_status: code: 429 grpc_status: code: 8 # RESOURCE_EXHAUSTED message: Quota exhausted四、两种方案选型与落地要点维度gRPC Rate Limit方案一Rate Limit Quota / RLQS方案二协议rls.protoRateLimitService.Checkrlqs.protoRateLimitQuotaService作用层级网络层逐连接 HTTP 层逐请求仅 HTTP 请求决策模型每次连接/请求同步查询服务端分配配额周期性上报负载并再平衡对限流服务压力与连接/请求速率成正比可叠加本地限流缓解上报频率由reporting_interval决定压力与 QPS 解耦适用场景通用场景有现成的 ratelimit 参考服务大规模、高 QPS、实例间负载不均的部署开源服务端官方 Go Redis 参考实现可用目前需配合 Google Cloud Rate Limit Service落地时的通用建议均源自上述文档限流服务本身也要有兜底两个方案都定义了明确的故障回退failure_mode_deny/no_assignment_behavior/expired_assignment_behavior上线前应明确限流服务不可用时是放行还是拒绝先影子后执行利用ratelimit.tcp_filter_enforcing、ratelimit.route_key.http_filter_enabled等运行时百分比开关灰度放量用统计验证方案一检查ratelimit.stat_prefix.*网络层与cluster.cluster.ratelimit.*HTTP 层计数器方案二结合桶级上报确认各 Envoy 实例的负载分布。本文所有结论均基于当前仓库中的全局限流架构文档、限流服务配置文档及网络层/HTTP 层过滤器文档、rls.proto、rlqs.proto 与 source/extensions/filters/http/ratelimit、source/extensions/filters/network/ratelimit 下的源码实现适用前提为使用 v3 API 的 Envoy。【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考