本地限流因为使用简单,执行高效,可以算是最常用的了
在 Envoy 里,local rate limit 可以用于不同的 Envoy Filter 扩展阶段,从而实现不同行为的限流:
- HTTP Filter,限制 HTTP 请求频率
- Network Filter,限制 TCP 建连频率
- Listener Filter,限制TLS 握手频率
配置用法
以常用的 HTTP localrate 为例来感受一下配置:
1 | http_filters: |
Envoy local ratelimit 采用令牌桶算法,限流的核心配置是这三个:
max_tokens
桶的最大token
容量,也是初始的默认值tokens_per_fill
每个周期增加的token
数量fill_interval
填充token
的时间周期
那么,上面配置的效果就是:
- 一个最大容量为 1000 的桶,每 2s 往桶里加 1000 个
token
- 每来一个请求,消耗桶里一个
token
,如果桶里没有token
了,就会拦截请求
对于拦截请求的响应,还可以配置:
- 响应状态码,默认是 429
- 和响应的请求头
细粒度限流
上面的示例是一个粗粒度的策略,针对这个 listener 端口上的所有请求。
如果想实现一个细粒度的,比如域名级,或者接口级的呢?
也是可以做到的,ratelimit 也支持 route 级别的配置:
1 | routes: |
如果是同一个路由内,还想实现更细粒度限流呢?
Envoy 还有路由级别的 descriptors,比如:
1 | - match: {prefix: "/foo"} |
可以根据请求中的特征(上面示例是一个请求 header),实现路由内,不同的限流配置
实现机制
从配置方式理解,Envoy 的实现也不需要很复杂
在配置解析阶段,配置中的每一个
token_bucket
,就会生成一个RateLimitTokenBucket
桶实例在请求处理阶段,通过
route/descriptors
匹配到token_bucket
之后,就从对应的桶里取值即可
在 1.31.0 之前,是每个桶都有一个定时器,每 fill_interval
给桶里注入 tokens_per_fill
的令牌
在 1.31.0 里,做了个优化,不再依赖定时器
原因是,定时器运行在 main thread,而 main thread 在 Envoy 里面还有一个重要职责就是加载来自控制面的配置,当配置量很大的时候,可能会长时间的阻塞这些 timer,就影响限流了
与 NGINX 对比
在 NGINX 里,与 Envoy local rate limit 最接近的就是 limit_req
了
首先是限流算法上的区别,Envoy 是令牌桶算法,而 NGINX 是漏桶算法
漏桶算法可以实现流量整形,比如上面示例的 1000/2s 在 NGINX 会被处理为 500/s,也就是每 2ms 放行一个请求,从而让请求更均匀的转发给后端应用
而令牌桶算法的限制值,更接近人类的理解,X 时间放行 Y 个请求
动态限流 key
不过,在我看来,最大的区别还是,NGINX 可以指定动态限流 key,从而实现每个 IP 限制 10/s 这样的效果,而 Envoy 却做不到。
从实现机制上来说,NGINX 是基于 shared memory zone 来实现的,可以灵活的开辟大内存来实现很多 key 的存储,同时,基于 lru 的淘汰算法,也可以保证内存可控
shared memory zone 是 NGINX 内部的一个通用的底层组件,而 Envoy 还没有这类基建
从使用场景上来说,NGINX 主要用于南北向接入网关,防 DDOS/cc 攻击需求强烈,而基于 IP 的限流策略则是最常用的防攻击策略
而 Envoy 多用于东西向场景,流量来源多是可信的,限流策略主要是用于保护后端服务不被打爆,所以对于这种动态 key 的诉求相对没那么强烈
集群限流
Envoy 的 local ratelimit 虽然是本地限流,但是也可以配置为集群级别的限流效果
1.31.0 新增了 local_cluster_rate_limit
的配置,可以让 token
的限流值,按照 Envoy 集群数量来平均分配,并且随着 Envoy 实例数量自动调整
当前,这里有个假设前提是,流量在 Envoy 节点上是均匀的
NGINX 的 shared memory zone 是单机内共享的,不过商业版里,也增加了一个跨机同步的机制,这样也可以实现集群级别的限流
最后
个人观点,简单点评几句:
Envoy 的本地限流,好的是应用场景多,HTTP 请求,TCP 连接,TLS 握手 都可以用
NGINX 呢,优势是动态限流 key,用来防 cc,应对公网不可信的流量,确实很香
嗯,至少目前如此,后面 Envoy 加上动态限流 key,也不是太大的问题