这种限制接口调用次数的方式,我们通常称之为限流,那么为什么要做限流呢,一般有两种原因:

1. 首先是防止服务提供方被大量的请求击垮

我们开发一个项目,最理想的状况是有多少请求,都可以正常地响应,但是在现在的互联网环境,我们很难评估用户的增长,很难评估访问量有多少,甚至有些时候会遇到恶意攻击;那么相比于项目被流量击垮,【限制流量,只满足部分访问的正常响应】要好一些。

简单说就是:满足所有请求 > 满足部分请求 > 项目被击垮,所有请求无法响应。

2. 计费

现在很多平台对外开发的接口,并不全是免费的,比如普通会员每天只能调用 1000 次接口,高级会员每天可以调用 10 万次接口,或者按照调用量计费。

那么如何限制服务接口的调用次数呢?

使用限流算法

通常我们可以通过限流算法达到限制接口调用次数,比如计数器法、滑动窗口法、漏桶算法、令牌桶算法,这里我们就用令牌桶算法举例。

令牌桶算法,我们可以看做有一个桶,桶里面有 N 个令牌,并且系统会以一个恒定的速度往桶里投放令牌,每次处理之前先要获取令牌,如果获取不到的话,就拒绝服务;在这里我们使用 Google 出品的 Guava 工具库,里面提供了一个开箱即用的令牌桶 RateLimiter。

如图,我们编写了一个简单的接口,省略了业务逻辑,只返回一个字符串;我们设置 RateLimiter.create(2),表示每秒不超过 2 个任务被提交。

让我们用接口工具模拟一下并发调用:

他强任他强,我自巍然不动。因为我们使用了限流算法,每秒只处理 2 个请求,所以从日志中我们可以看到这样的效果:每秒只有两条日志。

分布式架构下的限流

因为使用开源的组件,限流的实现看起来非常简单,但是这里也有一个比较大的问题,就是实例中是一个应用包,但在实际的项目中,我们通常会是用集群部署的方式,将我们的应用部署在多台机器上,那么这时候该如何限流呢?

每台服务器上的应用自己控制自己的响应数量?比如每天只能调 100 次,那部署 10 台的话,总量就变成了 1000 次了;

反推?因为每天总量只能调 100 次,部署 10 台,那就是每台每天只能调 10 次?这是个很差的办法,先不说流量一定可以平均分配到每台机器上,如果有一台机器挂掉了,是不是今天只能支持调用 90 次了?

通常的解决方案,可以把令牌桶中的令牌,不要放在本地,而是放在一个公共的地方,比如 Redis 中,每次请求过来,就计算是否超过限制的总量,如果未超过,则正常处理,如果已超过,则返回错误信息。

具体做法是,用 Redis 中的 key-100 作为令牌桶,其中 100 表示一分钟可以调用 100 次,每次处理前对 value 进行减 1,返回的值大于 0 表示可以处理;每分钟将 value 设置回 100;或计数累加,开始是 0 ,不断累加,最后超过单位时间的总量限制;

不过这个方法要有一个定时任务,去设置令牌的数量,另外这种方法是不能应对突发流量的,比如前 59 秒一次请求也没有,第 60 秒来了 100 次,第 61 秒进入了一个新的周期,又来了 100 次请求 ,这样实际上是在两秒内处理了 200 次请求。

另外一种方案是使用 Redis 中的有序队列 Sorted Set ,存储近 100 次的调用时间,每次有新请求的时候,对比队列中第一个元素的时间和当前时间,如果相差超过 1 分钟,表示还没有超过流量限制,进行处理,并将第一个元素压出队列,将新的请求时间压入队列。

为什么需要限流

按照服务的调用方,可以分为以下几种类型服务

1.与用户打交道的服务

比如web服务、对外API,这种类型的服务有以下几种可能导致机器被拖垮:

  • 用户增长过快
  • 热点事件,如抢购、秒杀
  • 爬虫请求,爬取数据等
  • 恶意请求

这些情况都是无法预知的,不知道什么时候会有10倍甚至20倍的流量打进来,如果真碰上这种情况,扩容是根本来不及的(弹性扩容都是虚谈,一秒钟你给我扩一下试试)

2.对内的RPC服务

一个服务A的接口可能被BCDE多个服务进行调用,在B服务发生突发流量时,直接把A服务给调用挂了,导致A服务对CDE也无法提供服务。 这种情况时有发生,解决方案有两种: 每个调用方采用线程池进行资源隔离和使用限流手段对每个调用方进行限流。

常用限流算法

常见的限流算法有:计数器、令牌桶、漏桶。

1、计数器算法,采用计数器实现限流有点简单粗暴,一般我们会限制一秒钟的能够通过的请求数,比如限流qps为100,算法的实现思路就是从第一个请求进来开始计时,在接下去的1s内,每来一个请求,就把计数加1,如果累加的数字达到了100,那么后续的请求就会被全部拒绝。等到1s结束后,把计数恢复成0,重新开始计数。

具体的实现可以是这样的:对于每次服务调用,可以通过 AtomicLong#incrementAndGet()方法来给计数器加1并返回最新值,通过这个最新值和阈值进行比较。

这种实现方式,相信大家都知道有一个弊端:如果我在单位时间1s内的前10ms,已经通过了100个请求,那后面的990ms,只能眼巴巴的把请求拒绝,我们把这种现象称为“突刺现象”。

2、漏桶算法,为了消除"突刺现象",可以采用漏桶算法实现限流,漏桶算法这个名字就很形象,算法内部有一个容器,类似生活用到的漏斗,当请求进来时,相当于水倒入漏斗,然后从下端小口慢慢匀速的流出。不管上面流量多大,下面流出的速度始终保持不变。

不管服务调用方多么不稳定,通过漏桶算法进行限流,每10毫秒处理一次请求。因为处理的速度是固定的,请求进来的速度是未知的,可能突然进来很多请求,没来得及处理的请求就先放在桶里,既然是个桶,肯定是有容量上限,如果桶满了,那么新进来的请求就丢弃。

在算法实现方面,可以准备一个队列,用来保存请求,另外通过一个线程池定期从队列中获取请求并执行,可以一次性获取多个并发执行。

这种算法,在使用过后也存在弊端:无法应对短时间的突发流量。

3、令牌桶算法,从某种意义上讲,令牌桶算法是对漏桶算法的一种改进,桶算法能够限制请求调用的速率,而令牌桶算法能够在限制调用的平均速率的同时还允许一定程度的突发调用。

在令牌桶算法中,存在一个桶,用来存放固定数量的令牌。算法中存在一种机制,以一定的速率往桶中放令牌。每次请求调用需要先获取令牌,只有拿到令牌,才有机会继续执行,否则选择选择等待可用的令牌、或者直接拒绝。

放令牌这个动作是持续不断的进行,如果桶中令牌数达到上限,就丢弃令牌,所以就存在这种情况,桶中一直有大量的可用令牌,这时进来的请求就可以直接拿到令牌执行,比如设置qps为100,那么限流器初始化完成一秒后,桶中就已经有100个令牌了,这时服务还没完全启动好,等启动完成对外提供服务时,该限流器可以抵挡瞬时的100个请求。所以,只有桶中没有令牌时,请求才会进行等待,最后相当于以一定的速率执行。

实现思路:可以准备一个队列,用来保存令牌,另外通过一个线程池定期生成令牌放到队列中,每来一个请求,就从队列中获取一个令牌,并继续执行。

幸运的是,通过Google开源的guava包,我们可以很轻松的创建一个令牌桶算法的限流器。

集群限流

前面讨论的几种算法都属于单机限流的范畴,但是业务需求五花八门,简单的单机限流,根本无法满足他们。

比如为了限制某个资源被每个用户或者商户的访问次数,5s只能访问2次,或者一天只能调用1000次,这种需求,单机限流是无法实现的,这时就需要通过集群限流进行实现。

如何实现?为了控制访问次数,肯定需要一个计数器,而且这个计数器只能保存在第三方服务,比如redis+lua。

服务入口限流

这个就比较常见了,比如使用Nginx,在Nginx中通过配置实现请求频率的限制。

微服务如何限制接口调用次数相关推荐

  1. 如何让控件span的id调用ajax_微服务架构之「 调用链监控 」

    「 调用链监控 」是在微服务兴起后才有的一种新流行的监控模式.因为在我们传统单体应用的项目中,不存在服务链/调用链的概念,所以也就根本没有调用链监控的需求了. 当我们开始微服务架构之后,我们的很多服务 ...

  2. 微服务之间的最佳调用方式

    上一篇:3600万中国人在抖音"上清华" 0.2T架构师学习资料干货分享 茉莉花,别名:茉莉,拉丁文名:Jasminum sambac (L.) Ait,木犀科.素馨属直立或攀援灌 ...

  3. Spring Cloud 微服务开放平台接口

    Spring Cloud 微服务开放平台接口 github源码地址: https://github.com/spring-cloud/spring-cloud-security 前言: 什么是开放平台 ...

  4. 微服务架构之「 调用链监控 」

    「 调用链监控 」是在微服务兴起后才有的一种新流行的监控模式.因为在我们传统单体应用的项目中,不存在服务链/调用链的概念,所以也就根本没有调用链监控的需求了. 当我们开始微服务架构之后,我们的很多服务 ...

  5. 微服务之Feign远程调用

    微服务之Feign远程调用 1.微服务之间一般都是业务分离,会创建一个独立的模块来存放接口,方便以后调用,以及防止代码的冗余和降低耦合性 2.导入feign的依赖 <dependency> ...

  6. httpclient依赖_.NetCore 3.1高性能微服务架构:封装调用外部服务的接口方法HttpClient客户端思路分析...

    护士节快乐 众所周知,微服务架构是由一众微服务组成,项目中调用其他微服务接口更是常见的操作.为了便于调用外部接口,我们的常用思路一般都是封装一个外部接口的客户端,使用时候直接调用相应的方法.webse ...

  7. 商品微服务添加api接口

    编写跳转controller 在learn-goods-web中编写controller,接收请求,并跳转到商品详情页: @Controller @RequestMapping("item& ...

  8. 微服务采用何种远程调用方式?

    无论是微服务还是SOA,都面临着服务间的远程调用.那么服务间的远程调用方式有哪些呢? 常见的远程调用方式有以下几种: RPC:Remote Produce Call远程过程调用,类似的还有RMI.自定 ...

  9. 设置失败重新发起_微服务架构下请求调用失败了怎么办!

    微服务架构相比单体架构,服务的调用从同一台机器内部的本地调用变成了不同机器之间的远程方法调用,但是这个过程也引入了两个不确定的因素: - 调用的执行是在服务提供者一端,即使服务消费者本身是正常的,服务 ...

最新文章

  1. 多线程共享全局变量以及锁机制
  2. boost::ptr_set相关的测试程序
  3. 交换机与路由器主要功能的区别和联系
  4. 信息学奥赛一本通 1080:余数相同问题 2022.1.29 AC
  5. [Tjoi2016Heoi2016]排序[01序列]
  6. 2013 Multi-University Training Contest 4
  7. String.format()用法
  8. bzoj 3450: Tyvj1952 Easy(概率DP)
  9. 分享:Python中的位运算符
  10. 深入了解基于容器的红帽PaaS和OpenShift
  11. 电脑控制手机 易语言也可以实现颜色比较功能哦
  12. axure怎么存html文件怎么打开,axure 保存html文件怎么打开
  13. 实用:旋转矩阵与方向余弦矩阵(DCM)
  14. CVPR 2021 Exploring Simple Siamese Representation Learning
  15. 如何开启WIN10卓越性能模式
  16. SpringBoot和Mybatis-Plus
  17. Springboot + vue 实现导出word
  18. epics安装css,EPICS-synApps/areaDetector安装
  19. 在制作蓝牙小车app中遇到的坑
  20. cadence 17.4 + LP Wizard 10.5

热门文章

  1. Java 银行卡和身份证号根据校验位验证是否合法
  2. 小米wifi模块连接Iot你必须要知道的几件事
  3. QQ如何设置会话窗默认使用腾讯视频播放视频文件
  4. n刀切蛋糕问题(最多切多少块c语言)
  5. Echarts图表可视化
  6. BUI树形控件TreeList的异步加载
  7. Matlab做电路系统设计仿真
  8. Android Edittext 软键盘输入法回车键改成下一步Next
  9. 博弈论——《取石子》《取石子游戏》
  10. vue中使用js控制button的disabled属性