微服务之服务治理(二):一文掌握 Envoy 限速配置

2020-02-28

Envoy 可以集成一个全局的 gRPC ratelimit 服务,它支持如下两个特性:

  • 网络级别的速率限制过滤器:ratelimit 过滤器安装在 listener 上,Envoy 将为每个新连接调用 ratelimit 限速服务,这样能限制每秒该 listener 上建立的连接数;
  • HTTP 级别速率限制过滤器:该过滤器安装在服务路由上,既可以限制到目标 upstream cluster 的所有请求速率,也可以限制不同来源的到目标 upstream cluster 的请求速率。

所以,Envoy ratelimit 配置主要涉及三块内容:

  • global ratelimit service 配置
  • 网络级别 ratelimit filter 配置
  • HTTP 级别 ratelimit filter 配置


01 global ratelimit service 配置

随着 Envoy 版本的迭代升级,global ratelimit service 的配置方式也在发生变化。


Envoy v1.9.0 之前的版本

对于 1.9.0 版本以前的 Envoy,global ratelimit service 配置是放在 bootstrap 进行的,filter 中仅描述 domain 和 descriptor:

envoy_role_id_user_user_id_jackey_1579188960


Envoy v1.9.0 之后的版本(包括 v1.9.0)

而在 1.9.0 版本后,我们可以分别在网络级别的 ratelimit filter 和 HTTP 级别的 ratelimit filter 上进行配置。

由于本文针对 Envoy v1.11.1 进行分析,所以我们更关注网络级别和 HTTP 级别的 ratelimit filter 配置。按照 1.9.0 版本后的 ratelimit filter 配置,其实我们可以分别为网络级别和 HTTP 级别配置独立的 ratelimit service。


02 网络级别 ratelimit filter 配置

对于网络级别 ratelimit filter 配置,以 static_resources 为例,从 bootstrap 到 listener network level ratelimit filter 的配置如下图所示:


它的特点是限制单位时间内 listener 产生的连接数。每次 listener 接收到新连接创建的请求时,Envoy 根据网络级别 ratelimit filter 中的 domain 和 descriptors 向 rate_limit_service 发送请求,让 rate_limit_service 判断是否超过配额限制。


网络级别 ratelimit 示例


将 descriptor entries 组成 descriptor

根据官方文档,descriptors 由多个 entries 组成,每个 entries 构成一个 descriptor。顾名思义,每个 entries 由多个 entry 组成,而一个 entry 就是 key value 对,所以每个 entries 根据其内部所有的 key value 组成一个 descriptor,而每个 entries 根据内部多个 entry 排列顺序的不同,将构成不同的 descriptor。

该请求对应的多个限速 descriptor 会连同 domain 一起发送给 ratelimit service。在 ratelimit service 内,每个 descriptor 和 domain 都会生成一个用来向 Redis 查询当前单位时间内访问次数的 key。任何一个 descriptor 代表的限速规则不通过,都会拒绝该请求通过

上述 descriptors 有两个 entries,所以将生成两个类似 descriptor:

Envoy 将 domain 和 descriptors 发送给 ratelimit 之后,ratelimit 将生成两个类似 key:

可参考网络级别 ratelimit 示例。


03 HTTP 级别 ratelimit filter 配置

网络级别的 ratelimit filter 主要限制单位时间内到目标路径的请求速率,对于 HTTP 级别的 ratelimit filter,限速服务和限速规则是分开配置的:

限速服务在 envoy.http_connection_manager 的 http_filters 进行配置,需要配置 domain 和 ratelimit grpc service。

限速规则在 envoy.http_connection_manager 的 virtual_hosts 上或者 virtual_hosts 内的 route 上配置。


以下是 http_connection_manager -> http_filters 中 ratelimit service 的配置格式:

已知限速规则(descriptors)有两个地方可以配置:virtual host 和 virtual host 内的 route

对于一个请求,过滤阶段匹配到的所有 virtual host level 限速规则都会作用到该请求上。如果该请求还匹配到了 route level 的一些限速规则,那么这些限速规则也会作用到该请求上。

所以一个请求可能会匹配到多个限速规则,Envoy 会将该请求匹配到的所有限速规则(virtual host level 限速规则和 route level 限速规则)和 domain 信息发送到 ratelimit 服务做限速判断,任何一条限速规则不通过都会拒绝该请求通过。

对于这点,官网原文是这样描述的:


For a request, all virtual host level configurations that match the filter stage setting will be applied. If there are route specific configurations that match the filter stage setting, those will be applied in addition. More than one configuration can apply to a request. Each configuration results in a descriptor being sent to the rate limit service. Rate limits are enacted based on the result of logically ‘or’-ing the response from each descriptor.


virtual host level 或者 virtual host 内 route level 的限速规则配置格式如下所示:


ratelimit action 分析

ratelimit action (route.RateLimit.Action) 有以下几种类型:

  • source_cluster
  • destination_cluster
  • request_headers
  • remote_address
  • generic_key
  • header_value_match

source_cluster

action 指定为 source_cluster 的配置如下:

上述配置中 source_cluster 类型的 action ,过滤器将产生一个如下 descriptor entry:

其中 "<local service cluster>" 为该 Envoy 所在的 cluster name,来源于 Envoy 启动时 --service-cluster 选择指定的值。

destination_cluster

action 指定为 destination_cluster 的配置如下:

上述配置中 destination_cluster 类型的 action ,过滤器将产生一个如下 descriptor entry:

其中,"<routed target cluster>" 表示根据路由匹配规则该请求将发往的 upstream cluster 的 cluster name。而 upstream cluster 的选择由当前路由规则决定:


路由规则中 routed cluster 配置不同,那么 "<routed target cluster>" 得到的 upstream cluster name 也不同。

  • 如果配置为 cluster 比如   cluster: XXXX ,那么   "<routed target cluster>"  就   XXXX

  • 如果配置为 cluster_header,比如 cluster_header: X-Envoy-Destination-Cluster,那么通过读取该请求 header 中 X-Envoy-Destination-Cluster 的值,比如 X-Envoy-Destination-Cluster: YYYY,并且 YYYY 名字的 upstream cluster 存在,那么 "<routed target cluster>" 就是 YYYY。如果不存在则返回 404 错误;
  • 如果配置为 weighted_clusters,如上述配置实例所示,那么 upstream cluster 就是 Envoy 当前根据权重选择出来的,比如选中的 upstream cluster 为 ZZZZ-02,那么 "<routed target cluster>" 就为 ZZZZ-02

request_headers

action 指定为 request_headers 的配置如下:

上述配置中 request_headers 类型的 action ,过滤器将产生一个如下 descriptor entry:"<header_value_queried_from_header>" 为该请求 header 中 X-Envoy-ClientID 对应的值。如果 X-Envoy-ClientID: foo,则 "<header_value_queried_from_header>" 为 foo

remote_address

action 指定为 destination_cluster 的配置如下:

上述配置中 remote_address 类型的 action ,过滤器将产生一个如下 descriptor entry:

其中,"<trusted address from x-forwarded-for>" 的值为该请求 header 中 x-forwarded-for 的值,例如如果 x-forwarded-for: 50.0.0.1,那么 "<trusted address from x-forwarded-for>" 就为 50.0.0.1

generic_key

action 指定为 generic_key 的配置如下:

上述配置中 generic_key 类型的 action ,过滤器将产生一个如下 descriptor entry:

上述配置实例中,得到的 descriptor entry 就是 (generic_key, bar)

header_value_match

action 指定为 header_value_match 的配置如下:

上述配置中 header_value_match 类型的 action ,过滤器将产生一个如下 descriptor entry:

比如上述 header_value_match 表示限速访客的请求速率,当请求 header 中出现 X-RoleID: Visitor 时,表示该用户的角色为访客,那么根据 header_value_match 限速规则,将生成一个如下 descriptor entry:

如果请求 header 中没有出现 X-RoleID: Visitor,比如出现 X-RoleID: User,那么将不会生成任何 descriptor entry。


HTTP 级别 ratelimit 示例



将 actions 组合成 descriptor

如前文所述,descriptor 由多个 descriptor entry 构成,即 entries 构成一个 descriptor。entries 内各 entry 的组成顺序不同,也将构成不同的 descriptor。每个 descriptor 和 domain 将生成一个 ratelimit service 向 Redis 查询的一个 key。

我们通过以下例子来说明。

上述 actions 将产生类似如下 descriptors:

也就是说针对当前请求,Envoy 会将 rate_limits actions 对应的 4 个 descriptor 发送给 ratelimit service 做限速判断,ratelimit service 生成 4 个 key:

这也意味只有所有上述 descriptor 描述的限速规则通过,该请求才能继续下去。


04 lyft/ratelimit service descriptor 匹配规则

我们知道,通过 Envoy 发送过来的 domain 和 descriptors,我们可以生成查询 Redis 记录的 key。但是如果 Envoy 发送过来的请求中,有一个 descriptorXXX,但是 ratelimit 配置文件中没有完全匹配的 descriptorXXX,该怎么处理呢?

首先,lyft ratelimit 要求查询的 descriptor 元组数量和 ratelimit 配置的元组数量要一样

假设 RateLimitRequest 中的 descriptor 是二元组的:

那么 ratelimit 的配置的 descriptor 也需要二元组:

其次,N 个元组全部按照顺序完全相等,那么这就是匹配的

再次,对于 N 个元组,虽然没有全部按顺序完全相等,但是前 N-1 个元组按照顺序完全相等,ratelimit 配置的 descriptor 最后一个元组只有 key 没有 value,而两边第 N 个元组 key 又相等,那么这也是匹配的

举个例子,Envoy 发送过来的 RateLimitRequest 如下所示:

ratelimit 中没有完全相等的配置,但是如果有如下配置,就会匹配如下配置:

最后,如果 RateLimitRequest 中的 descriptor 在 ratelimit service 配置中找不到匹配项,那么默认不受访问限制

以上就是我对 Envoy 限速配置的理解和解读,篇幅所限,有些细节不再展开。如有疑问或想法,欢迎关注才云 Caicloud 公众号,并在留言区评论指出!

参考文献

[1] global ratelimit

[2] http ratelimit filter

[3] RateLimitDescriptor

[4] rate limit: configuration/filter enhancements

[5] Network level Rate Limit Service works, but not HTTP level

[6] keontang ratelimit demo

[7] lyft ratelimit


点击体验,开启谷歌级数字化之旅
立即体验
立即咨询