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 次请求。
2. 代码分析
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老师教你学Istio11 : 如何用Istio实现流量熔断
在之前的最佳实践中,已经带大家通过一系列的实践任务领略了Istio的无穷魅力.今天,将向大家介绍如何用Istio实现流量熔断. 熔断机制是创建弹性微服务应用程序的重要模式.熔断可以帮助您自由控制故障影 ...
- idou老师教你学Istio 07: 如何用istio实现请求超时管理
在前面的文章中,大家都已经熟悉了Istio的故障注入和流量迁移.这两个方面的功能都是Istio流量治理的一部分.今天将继续带大家了解Istio的另一项功能,关于请求超时的管理. 首先我们可以通过一个简 ...
- idou老师教你学Istio 28:istio-proxy check 的缓存
功能概述 istio-proxy主要的功能是连接istio的控制面组件和envoy之间的交互,其中check的功能是将envoy收集的attributes信息上报给mixer,在istio中有几十种a ...
- idou老师教你学Istio 27:解读Mixer Report流程
1.概述 Mixer是Istio的核心组件,提供了遥测数据收集的功能,能够实时采集服务的请求状态等信息,以达到监控服务状态目的. 1.1 核心功能 •前置检查(Check):某服务接收并响应外部请求前 ...
- idou老师教你学Istio :如何用istio实现监控和日志采集
大家都知道istio可以帮助我们实现灰度发布.流量监控.流量治理等功能.每一个功能都帮助我们在不同场景中实现不同的业务.那Istio是如何帮助我们实现监控和日志采集的呢? 这里我们依然以Bookinf ...
- idou老师教你学Istio:如何用 Istio 实现速率限制
使用 Istio 可以很方便地实现速率限制.本文介绍了速率限制的使用场景,使用 memquota\redisquota adapter 实现速率限制的方法,通过配置 rule 实现有条件的速率限制,以 ...
- idou老师教你学istio 31:Istio-proxy的report流程
Istio-proxy的report主要是将envoy采集到的连接attributes的信息上报给控制面的mixer,它的入口在request_handler_impl.cc文件中,这里需要打开ena ...
- idou老师教你学Istio 26:如何使用Grafana进行可视化监控
使用Grafana插件进行监控是Istio提供的监控能力之一.Istio提供丰富的监控能力,Grafana插件在Istio对Prometheus支持的基础上,为用户提供基于网页仪表面板的可视化监控效果 ...
- idou老师教你学Istio 25:如何用istio实现监控和日志采集
大家都知道istio可以帮助我们实现灰度发布.流量监控.流量治理等功能.每一个功能都帮助我们在不同场景中实现不同的业务.那Istio是如何帮助我们实现监控和日志采集的呢? 这里我们依然以Bookinf ...
随机推荐
- 容器版jenkins安装并且实现使用宿主机docker命令,采用的是docker outside deocker,带jdk、添加maven、git
docker版jekins使用宿主机docker命令 docker版jekins安装,实现CI/CD,也就是实现在容器里面使用宿主机docker命令,这样方式为:docker outside deoc ...
- 【Leetcode_easy】1137. N-th Tribonacci Number
problem 1137. N-th Tribonacci Number solution: class Solution { public: int tribonacci(int n) { ) ; ...
- JS扩展Array.prototype引发的问题及解决方法
遇到的问题 一上班收到个bug,写的表单联动插件在ie里面会出现js源码,当时有点意外,从没出现过这问题. 问题的原由 为什么会出现一个function呢?其它调用的插件的页面为什么没有这问题? 控制 ...
- Docker - 在CentOS7.5中升级Docker版本
1 - 检查当前版本 [root@localhost ~]# uname -a Linux localhost.localdomain 3.10.0-957.el7.x86_64 #1 SMP Thu ...
- mousedown、mouseup、click事件之间的关系及执行顺序
三个事件的触发时机 mousedown 当鼠标指针移动到元素上方,并按下鼠标按键(左.右键均可)时,会发生 mousedown 事件.与 click 事件不同,mousedown 事件仅需要按键被 ...
- 023 Android 自定义Toast控件
1.Toast自定义控件工具类 package com.example.administrator.test62360safeguard.Utils; import android.content.C ...
- [C++] 例题 2.7.1 用栈实现简易计算器
目录 前置技能 栈 (stack) 中缀表达式 (InfixExp) 与后缀表达式 (PostfixExp) 需求描述 概要设计 函数详细设计 中缀转后缀 infix_to_postfix(strin ...
- python — 线程
目录 1.线程基础知识 2 Thread 类 3 锁 4 队列 1.线程基础知识 1.1 进程与线程的区别 进程: 创建进程 时间开销大 销毁进程 时间开销大 进程之间切换 时间开销大 线程: 线程是 ...
- Python习题001
作业1 * 用条件语句写一个BMI(体重除以身高的平方)指数* 低于18.5:过轻* 18.5 - 25 正常* 25 - 28 过重* 28 - 32 肥胖* 高于32 严重肥胖 weight = ...
- 在论坛中出现的比较难的sql问题:39(动态行转列 动态日期列问题)
原文:在论坛中出现的比较难的sql问题:39(动态行转列 动态日期列问题) 最近,在论坛中,遇到了不少比较难的sql问题,虽然自己都能解决,但发现过几天后,就记不起来了,也忘记解决的方法了. 所以,觉 ...