微服务之服务治理:Envoy 全局 gRPC 限速服务 lyft/ratelimit 详解
应用微服务架构后,你是否遇到过这些问题?后台资源被大量请求淹没耗尽、客户端持续发起请求直至服务宕机、分布式系统因超时而雪崩……随着微服务实施水平的不断深化,服务治理已经成为企业关注的一大核心话题。
在这篇文章中,才云科技首席客户成功官 Keon 将基于才云的成功实践,分享 Envoy 如何利用 lyft/ratelimit 实现快速、高性能和可靠的全局速率限制。
发布 | 才云 Caicloud
作者 | Keon(才云首席客户成功官)
01
gRPC ratelimit service
-
网络级别的速率限制过滤器 :ratelimit 过滤器安装在 listener 上,Envoy 将为每个新连接调用 ratelimit 限速服务,这样能限制每秒该 listener 上建立的连接数 -
HTTP 级别速率限制过滤器 :该过滤器安装在服务路由上,该过滤器可以限制到目标 upstream cluster 的所有请求速率,也可以限制不同来源的到目标 upstream cluster 的请求速率
ratelimit 架构
配置 ratelimit service
-
启用 ratelimit filter
Envoy ratelimit service 配置
Envoy ratelimit filter 配置
外部限速服务
github.com/envoyproxy/go-control-plane/envoy/service/ratelimit/v2/rls.pb.go
envoyproxy/go-control-plane/envoy/service/ratelimit/v2/rls.proto
RateLimitServiceClient
-
RateLimitServiceServer
02
lyft/ratelimit
lyft/ratelimit 配置文件
一个 Redis 实例,针对所有限速
两个 Redis 实例,一个实例针对每秒限速,另一个实例针对其他限速
INCRBY:
INCRBY key increment
,如果 key 存在,则将 key 中储存的数字加上指定的增量 increment;如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再加上指定的增量 incrementEXPIRE:
EXPIRE key seconds
给定 key 设置生存时间,当 key 过期时(生存时间为 0),它会被自动删除
lyft/ratelimit 实现分析
cacheKey 设计
domain_key_value_key_value_..._divider
对齐的时间”组成了 cacheKey。注意,cacheKey 对应的 value 为在该时间段已经使用的 token 数。
/demo
路径的限流配置 (限制每小时只有 500个 token,只能访问该路径 500 次):
/demo
路径时,ratelimit 服务将根据 domain、request.Descriptor、访问时间来生成 cacheKey:dev_version_v1_1577890800
。
pipelineAppend
cacheKey 对应的值加上 hitsAddend(一般情况下为1,表示访问一次;当然我们可以 request 自行定义 hitsAddend 的值);如果 cacheKey 不存在则新增 cacheKey,初始值为 hitsAddend
设置 cacheKey 的过期时间,过期 cacheKey 将从 Redis 中删除
PipeResponse
PipeResp
第一步,将 pipeline 中(pending 数组)命令全部执行完
第二步,将所有命令执行结果取出来,保存到 completed 数组
-
第三步,返回 pipeline 中下一个命令的执行结果(这里其实为刚刚执行完的 pipeline 的第一个命令的结果)
pipelineFetch
-
Redis INCRBY 命令 -
Redis EXPIRE 命令
DoLimit 分析
Usecase
/INCRBY dev_rate_v1_1577890800 1
命令更新 Redis cacheKey dev_rate_v1_1577890800
。
由于 Redis 之前不存在
cacheKey dev_rate_v1_1577890800
,所以新增该 cacheKey,并初始化其值为 1:
/INCRBY dev_rate_v1_1577890800 1
命令执行完后,返回最新的cacheKey dev_rate_v1_1577890800
的值。ratelimit service 收到该值,发现访问次数并没有超过limit:300
,所以访问请求到达 Envoy 后进入 router 过滤器到达业务服务,并成功返回业务结果。
/INCRBY dev_rate_v1_1577890800 1
命令更新 Redis cacheKey dev_rate_v1_1577890800
,Redis 将cacheKey dev_rate_v1_1577890800
的值加了 1 后,返回 300 给 ratelimit:
limit:300
,所以访问请求到达 Envoy 后进入 router 过滤器到达业务服务,并成功返回业务结果。
/INCRBY dev_rate_v1_1577890800 1
命令更新 Redis cacheKey dev_rate_v1_1577890800
,Redis 将cacheKey dev_rate_v1_1577890800
的值加了 1 后,返回 301 给 ratelimit:
limit:300
,所以返回超过访问限制的响应结果给 Envoy,Envoy 然后拒绝了客户的请求,错误码为 429,拒绝原因为超过访问限制,类似下图结果:
03
思考
可用性和持久化问题
首先,由于业务对 ratelimit 服务的需要,都要求 ratelimit 服务高可用性和 Redis 服务的高可用性
但是,per_second_limits 对 Redis 可用性要求非常高,Redis 一旦不可用,那么从不可用恢复到可用或者 Redis 重启花费的时间,一般会达到或者超过秒级别,这样存储在 Redis 中的 cacheKey 都过期了,所以 Redis 持久化几乎是没用的;除非 Redis 从故障恢复的时间上限在毫秒级别
per_second_limits 对于 ratelimit 服务的可用性要求也非常高,类似于 Redis,因为 ratelimit 服务的故障也可能导致存储在 Redis 中的 cacheKey 都过期
per_minute_limits 对 Redis 可用性要求也很高,只有在 Redis 从故障恢复的时间上限在秒级别,Redis 持久化才有意义。同理,对 ratelimit 服务可用性要求也很高
对于 per_hour_limits,在 Redis 故障恢复时间不超过 1 个小时的情况,Redis 持久化有意义。同理,ratelimit 服务也一样
对于 per_day_limits,在 Redis 故障恢复时间不超过 1 天的情况,Redis 持久化有意义。同理,ratelimit 服务也一样
从另外一个角度来分析,如果业务对 ratelimit 不是要求非常实时,对于 per_hour_limits 和 per_day_limits,Redis 即使不使用持久化存储,Redis 故障恢复后数据丢失也不会对业务造成什么影响。
ratelimit 是无状态的,状态都在 Redis 上,所以 ratelimit 可以部署多个实例,访问相同的 Redis;Redis 虽然是有状态的,但是如果我们可以接受状态丢失,那么多个 ratelimit 服务访问一个单实例 Redis 也足够了。
Redis 性能
当客户端通过以太网访问 Redis 服务器,并且操作的数据大小一直小于以太网数据包的大小(大约 1500 字节)时,通过管道机制聚合多个命令在一次请求中处理能够明显提高效率。实际上,处理 10 字节、100 字节或 1000 字节的查询,几乎会产生相同的吞吐量。
如何设计可扩展的速率限制算法
一种好的方式是使用“set-then-get”方法,依靠以非常高效的方式实现锁的原子操作,使你可以快速增加并检查计数器值,而不会阻塞原子操作。
在高度分布式系统中,你可能希望完全避免使用集中式数据存储来存储速率限制之类的快速变化的数据。CRDT(无冲突复制数据类型)和存储桶加权可能是一种更有效的策略。
跟踪每个节点可能会导致竞争条件出现。如果节点集群知道其他节点和集群的相对负载,则可以使用此值对隔离的速率限制器进行加权,并且可以用发布/订阅机制在节点之间广播仅需要共享的数据。
如果允许存在一定的方差,则让节点根据集群的大小同步其“令牌权重”意味着节点可以在内存中管理速率限制,甚至都不需要数据存储来跟踪。
至少在我们看来,在大多数限速场景中,极端精确度通常并不那么重要,我们更关心限速服务的可扩展性,这个扩展性不用考虑扩展数据层去处理计数器。
参考文献
[1] ratelimit filter configuration
[2] global ratelimiting
[3] envoy config rate limit service
[4] Rate limit service v2 api proto
[5] Common rate limit components proto
[6] Understanding EnvoyProxy’s Rate Limiting
[7] Using Envoy as Sidecar Proxy’s Microservice Mode-5.rate limiter
[8] Envoy gRPC and Rate Limiting
[9] Scaling your API with rate limiters
[10] 服务接口 API 限流 Rate Limit
[11] Envoy 的配置文件完全展开介绍
[12] 高可用 Redis 服务架构分析与搭建
[13] Redis 哨兵模式(sentinel)
[14] Redis benchmarks
[15] How to Design a Scalable Rate Limiting Algorithm (Reddit)
[16] How to Design a Scalable Rate Limiting Algorithm (Blog)
[17] Envoy Custom Auth Ratelimiter Example
[18] SRE Resiliency Bolt on Rate Limiting using Envoy
[19] Envoy gRPC and Rate Limiting
[20] 知其所以然 redis 的原子性
[21] 一文看懂 Redis 的持久化原理
完
关于才云科技
杭州才云科技有限公司(Caicloud)是一家基于容器技术和人工智能,引领新一代智能云计算平台和 AI 服务的公司。才云科技独家研发的基于 Kubernetes 的企业级智能容器云平台 Caicloud Compass (获 CNCF KCSP 认证)、基于 TensorFlow 的人工智能中台 Caicloud Clever 及开箱即用的 AI 模型解决方案 Caicloud Cabernet 现已在国内 500 强企业成功落地,并在电商、金融、新零售、制造、运营商、教育等行业均有成熟解决方案。才云科技总部位于中国杭州,在北京、上海、成都、深圳、南京设有分支机构。