idou老师教你学istio30:Mixer Redis Quota Adapter 实现和机制

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了idou老师教你学istio30:Mixer Redis Quota Adapter 实现和机制相关的知识,希望对你有一定的参考价值。

  1. 配置

1.1参数

技术图片
1.2 Params.Quota

技术图片
1.3Params.Override

技术图片
1.4Params.QuotaAlgorithm

速率限制的算法:

Fixed Window 算法每个时间间隔对应一个计数器,每当有请求到来,如果此时计数器未达到配额的限定值,则计数器加 1,否则拒绝服务。当进入下一个时间间隔时,计数器失效被重置。该算法的缺点在于不能保证在任意的时间间隔内,速率都被限制在配额以下。即如果请求集中在计数器失效的时间点附近,则在该时间点附近的时间间隔内,速率最大能达到配额的两倍。

Rolling Window 算法通过对上一个时间间隔请求数和当前时间间隔已处理的请求数进行加权,实现了对任意时间间隔的速率的估算。

技术图片
图片来自

https://blog.cloudflare.com/counting-things-a-lot-of-different-things/

如上图所示,在上一分钟内处理了 42 个请求,当前这一分钟已经过去了 15 秒,处理了 18 个请求,则当前这一分钟的速率可以估算为:

rate = 42 ((60-15)/60) + 18 = 42 0.75 + 18 = 49.5

如果使用 memquota adapter,默认使用亚秒级分辨率的 rolling window 实现速率限制。

如果使用 redisquota adapter,可以配置使用 Rolling Window 算法或者 Fixed Window 算法。

技术图片
1.5例子

技术图片
上面的 redisquota handler 定义了三种不同的速率限制模式:

如果没有 override 能够匹配,默认每秒限制 500 次请求;

如果请求的 destination 是 reviews,每秒限制 1 次请求;

如果请求的 destination 是 productpage,每秒限制 2 次请求。

  1. 代码分析

2.1Mixer Server 启动时注册 Check 接口的流程

首先从 Mixer Server 的启动入口 main() 函数看起,在 istio/mixer/cmd/mixs/main.go 中:

技术图片
在 main() 函数中调用了 GetRootCmd() 方法,获取 cobra 命令树的根节点。

技术图片
在 GetRootCmd() 方法中添加了 serverCmd() 命令。

技术图片
在 serverCmd() 中定义了,当我们执行 mixs server 命令时,会调用 runServer() 函数,将 mixer 作为一个 gRPC server 启动。

技术图片
在 runServer() 中,调用了 server.New() 方法,初始化了一个具备全部功能的 Mixer server,可以开始接收流量。

技术图片
在 server.New() 方法中,调用了 newServer() 函数。

技术图片
在 newServer() 函数中,通过调用 grpc.NewServer() 方法,创建了一个 gRPC server,但是尚未注册服务,而且未启动去接收请求。然后调用了 RegisterMixerServer() 函数注册 Mixer 服务。

技术图片
在 RegisterMixerServer() 函数中,调用了上面创建的 gRPC server 的 RegisterService() 方法在该 gRPC server 中注册 Mixer 服务。该方法的参数 _Mixer_serviceDesc 是对 Mixer 服务的描述。

技术图片
在对 Mixer 服务的描述中可以看出,该服务提供了两个接口,分别是 Check 和 Report。限流功能调用的是 Check 接口。

技术图片
在处理 Check 请求的 handler 中,定义了该接口的 RPC 方法全称,并且调用了 MixerServer 接口提供的 Check() 方法。

技术图片
MixerServer 接口定义了 Mixer 服务提供的 API。Check接口在执行对服务的调用之前,预先对一些条件进行检查,并分配配额。

2.2运行时 Mixer 处理 Check 接口调用的流程

技术图片
如上图所示,在 istio/mixer/pkg/api/grpcServer.go 中实现了 MixerServer 接口。

技术图片
在 MixerServer 接口的 Check() 方法的实现中,调用了 check() 方法。

技术图片
技术图片
在 check() 方法中调用了 Dispatcher 接口的 Quota() 方法。

技术图片
如上图所示是 Dispatcher 接口的定义,Quota() 方法将请求分发到与 Quota API 相关的 adapter。

技术图片
上图所示是 Quota() 方法在运行时的实现,在实现时调用了 dispatch() 方法。

技术图片
技术图片
技术图片
在 dispatch() 方法中,通过 session 的 variety 字段区分 preprocess、check、report 或 quota 几种操作类型。如果是 quota 操作,首先匹配和 quota 的名称对应的 instance,然后调用 dispatchToHandler() 方法对匹配到的 instance 进行分发。

技术图片
在 dispatchTohandler() 方法中,调用了 ScheduleWork() 方法,注册 invokeHandler() 方法,使其在某个时间点被调用。

技术图片
技术图片
在 invokeHandler() 方法中,通过调用 DispatchQuota() 方法,将 instace 分发到 Redis Quota adapter。

2.3Mixer Server启动时注册DispatchQuota() 函数流程

技术图片
在 Mixer Server 的启动入口 main() 函数中,调用 supportedTemplates() 函数传入了支持的模板。

技术图片
supportedTemplates() 函数返回 generatedTmplRepo.SupportedTmplInfo。

技术图片
技术图片
在 generatedTmplRepo 包中,将 SupportedTmplInfo 的 DispatchQuota 字段定义为上述函数,在该函数中调用了 Redis Quota Adatper 的 HandleQuota() 方法。

2.4Redis Quota Adapter 的实现

2.4.1结构定义

首先定义 handler 结构如下:

技术图片
其中 client 字段是一个 Redis client,用于连接存放 quota 信息的 Redis 数据库;limits 字段用于存放用户配置的限制;dimensionHash 字段用于存放用户定义的 override 的 dimension 的 hash;scripts 字段用于存放两种限流算法的 Lua 脚本供 Redis 使用;getTime() 用于获取当前时间。

技术图片
builder 结构用于构建 handler,包含 quotaTypes 和 adapterConfig 两个字段。adapterConfig 字段用于存放构建 handler 时需要的参数。

技术图片
Params 结构定义如上,Quotas 字段是用户定义的限制,RedisServerUrl 字段存放 Redis server 的地址,ConnectionPoolSize 字段存放到 Redis 的最大的 idle 连接数。

技术图片
Params_Quota 结构定义如上。Name 字段是 quota 的名称;MaxAmount 是分配 quota 数量的上限;ValidDuration 是分配的 quota 有效的时间;BucketDuration 字段仅在 RollingWindow 算法中使用,是窗口滑动的时间间隔;RateLimitAlgorithm 字段是限流算法的名称,默认值是 FIXED_WINDOW;Overrides 字段定义了在某些条件下对默认规则的覆盖。

技术图片
Params_Override 结构定义如上。Dimensions 字段定义了该 override 适用的条件,MaxAmount 定义了该 override 下分配 quota 数量的上限。

2.4.2启动时的配置方法

技术图片
Validate() 方法对 builder 的参数进行校验,并检查 Redis 是否能连通,以及 Lua 脚本是否被导入 Redis。

技术图片
技术图片
Build() 方法通过 builder 构建 handler。

技术图片
getDimensionHash() 方法计算 dimension 的 hash,在 Build() 方法中被调用,用于构建 handler。

2.4.3运行时方法

在 2.3 节中提到,Redis Quota Adapter 运行时的入口是 HandleQuota() 方法,该方法可以分为 3 个部分。

技术图片
首先是调用 getKeyAndQuotaAmount() 方法,获取该 instance 适用的 key 和 maxAmount。

技术图片
getKeyAndQuotaAmount() 方法实现如上。如果该 instance 没有任何 override 能够匹配,那么 key 为 quota 的名称,maxAmount 为该 quota 默认的 maxAmount。如果匹配到了某个 override,那么 key 为 quota 的名称加上该 override 的 dimension 的 hash,maxAmount 为该 override 的 maxAmount。

技术图片
第二步是调用 Redis 中的限流算法的 Lua 脚本,根据配置信息和参数信息,获取运行结果。

技术图片
然后调用 getAllocatedTokenFromResult() 函数,将 Redis 的返回结果转换成分配的 token 数和有效时间,返回最终结果。

技术图片
getAllocatedTokenFromResult() 函数定义如上,Redis 的返回结果应该有两个值,第一个值为分配的 token 数,第二个值为 token 有效的时间。

相关服务请访问:https://support.huaweicloud.com/cce/index.html?utm_content=cce_helpcenter_2019

以上是关于idou老师教你学istio30:Mixer Redis Quota Adapter 实现和机制的主要内容,如果未能解决你的问题,请参考以下文章

idou老师教你学Istio 20 : Istio全景监控与拓扑

idou老师教你学istio2:监控能力介绍

idou老师教你学Istio 16:如何用 Istio 实现微服务间的访问控制

idou老师教你学Istio 16:如何用 Istio 实现微服务间的访问控制

idou老师教你学Istio 26:如何使用Grafana进行可视化监控

idou老师教你学Istio 08: 调用链埋点是否真的“零修改”?