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 实现和机制的更多相关文章

  1. idou老师教你学Istio11 : 如何用Istio实现流量熔断

    在之前的最佳实践中,已经带大家通过一系列的实践任务领略了Istio的无穷魅力.今天,将向大家介绍如何用Istio实现流量熔断. 熔断机制是创建弹性微服务应用程序的重要模式.熔断可以帮助您自由控制故障影 ...

  2. idou老师教你学Istio 07: 如何用istio实现请求超时管理

    在前面的文章中,大家都已经熟悉了Istio的故障注入和流量迁移.这两个方面的功能都是Istio流量治理的一部分.今天将继续带大家了解Istio的另一项功能,关于请求超时的管理. 首先我们可以通过一个简 ...

  3. idou老师教你学Istio 28:istio-proxy check 的缓存

    功能概述 istio-proxy主要的功能是连接istio的控制面组件和envoy之间的交互,其中check的功能是将envoy收集的attributes信息上报给mixer,在istio中有几十种a ...

  4. idou老师教你学Istio 27:解读Mixer Report流程

    1.概述 Mixer是Istio的核心组件,提供了遥测数据收集的功能,能够实时采集服务的请求状态等信息,以达到监控服务状态目的. 1.1 核心功能 •前置检查(Check):某服务接收并响应外部请求前 ...

  5. idou老师教你学Istio :如何用istio实现监控和日志采集

    大家都知道istio可以帮助我们实现灰度发布.流量监控.流量治理等功能.每一个功能都帮助我们在不同场景中实现不同的业务.那Istio是如何帮助我们实现监控和日志采集的呢? 这里我们依然以Bookinf ...

  6. idou老师教你学Istio:如何用 Istio 实现速率限制

    使用 Istio 可以很方便地实现速率限制.本文介绍了速率限制的使用场景,使用 memquota\redisquota adapter 实现速率限制的方法,通过配置 rule 实现有条件的速率限制,以 ...

  7. idou老师教你学istio 31:Istio-proxy的report流程

    Istio-proxy的report主要是将envoy采集到的连接attributes的信息上报给控制面的mixer,它的入口在request_handler_impl.cc文件中,这里需要打开ena ...

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

    使用Grafana插件进行监控是Istio提供的监控能力之一.Istio提供丰富的监控能力,Grafana插件在Istio对Prometheus支持的基础上,为用户提供基于网页仪表面板的可视化监控效果 ...

  9. idou老师教你学Istio 25:如何用istio实现监控和日志采集

    大家都知道istio可以帮助我们实现灰度发布.流量监控.流量治理等功能.每一个功能都帮助我们在不同场景中实现不同的业务.那Istio是如何帮助我们实现监控和日志采集的呢? 这里我们依然以Bookinf ...

随机推荐

  1. Dockerfile-server1

    [root@lab2 docker-file]# cd server1/ [root@lab2 server1]# ls a.sh ddbes-server1-0.0.1-SNAPSHOT.jar D ...

  2. 【serviceaccount 和 clusterrolebinding】

    kubectl get clusterrolebinding kubectl create clusterrolebinding suosheng-rebinding --clusterrole=cl ...

  3. 【Leetcode_easy】961. N-Repeated Element in Size 2N Array

    problem 961. N-Repeated Element in Size 2N Array solution: class Solution { public: int repeatedNTim ...

  4. vue react 路由history模式刷新404问题解决方案

    vue单页因微信分享和自动登录需要,对于URL中存在’#’的地址,处理起来比较坑.用history模式就不会存在这样的问题.但是换成history模式,就会有个新的问题,就是页面刷新后,页面就无法显示 ...

  5. 客户端连接Codis集群

    新建maven webapp项目 添加相关依赖: <dependency> <groupId>redis.clients</groupId> <artifac ...

  6. LeetCode 179. 最大数(Largest Number) 21

    179. 最大数 179. Largest Number 题目描述 给定一组非负整数,重新排列它们的顺序使之组成一个最大的整数. 每日一算法2019/5/24Day 21LeetCode179. La ...

  7. 【剑指offer】面试题 25. 合并两个排序的链表

    面试题 25. 合并两个排序的链表 NowCoder 题目描述 输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则. Java 实现 ListNode Clas ...

  8. LeetCode 104. 二叉树的最大深度(Maximum Depth of Binary Tree)

    104. 二叉树的最大深度 104. Maximum Depth of Binary Tree 题目描述 给定一个二叉树,找出其最大深度. 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数. 说 ...

  9. K8S从入门到放弃系列-(13)Kubernetes集群mertics-server部署

    集群部署好后,如果我们想知道集群中每个节点及节点上的pod资源使用情况,命令行下可以直接使用kubectl top node/pod来查看资源使用情况,默认此命令不能正常使用,需要我们部署对应api资 ...

  10. 从其他数据库迁移到MySQL及MySQL特点

    从其他数据库迁移到MySQL Oracle,SQL Server迁移到MySQL 一些变化 不再使用存储过程.视图.定时作业 表结构变更,如采用自增id做主键,以及其他语法变更 业务SQL改造,不使用 ...