服务接口的流量控制策略:分流、降级、限流等。本文讨论下限流策略,虽然降低了服务接口的访问频率和并发量,却换取服务接口和业务应用系统的高可用。

限流算法

常用的限流算法有漏桶算法、令牌桶算法。

 1、漏桶算法

漏桶(Leaky Bucket)算法思路很简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水(接口有响应速率),当水流入速度过大会直接溢出(访问频率超过接口响应速率),然后就拒绝请求,可以看出漏桶算法能强行限制数据的传输速率。示意图如下:

可见这里有两个变量,一个是桶的大小,支持流量突发增多时可以存多少的水(burst),另一个是水桶漏洞的大小(rate)。

因为漏桶的漏出速率是固定的参数,所以,即使网络中不存在资源冲突(没有发生拥塞),漏桶算法也不能使流突发(burst)到端口速率。因此,漏桶算法对于存在突发特性的流量来说缺乏效率。

2、令牌桶算法

令牌桶算法(Token Bucket)和 Leaky Bucket 效果一样但方向相反的算法,更加容易理解。

随着时间流逝,系统会按恒定1/QPS时间间隔(如果QPS=100,则间隔是10ms)往桶里加入Token(想象和漏洞漏水相反,有个水龙头在不断的加水),如果桶已经满了就不再加了。新请求来临时,会各自拿走一个Token,如果没有Token可拿了就阻塞或者拒绝服务。

令牌桶的另外一个好处是可以方便的改变速度。一旦需要提高速率,则按需提高放入桶中的令牌的速率。一般会定时(比如100毫秒)往桶中增加一定数量的令牌, 有些变种算法则实时的计算应该增加的令牌的数量。

ReteLimiter完成限流、抢购场景实现

RateLimiter是Guava提供的基于令牌桶算法的实现类,API使用简单,并且根据系统的实际情况来调整生成Token的速率。

maven引入:

<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>18.0</version>
</dependency>

代码:

package com.lsq.einterview;import com.google.common.util.concurrent.RateLimiter;public class RateLimiterTest {/*简单使用Ratelimiter 实现限流功能,例:限制2秒钟只能有一个任务通过*/public static void main(String[] args) {//每秒通过0.5,所以2秒只能有一个通过RateLimiter rateLimiter = RateLimiter.create(0.5);for (int i = 0; i < 10; i++) {double acquire = rateLimiter.acquire();System.out.println("任务执行,等待时间:"+acquire);}}
}

结果:

rateLimiter.acquire()返回的是本次执行等待的时间,我们可以清楚地看到,第一次无需等待,后边都需要等待两秒才可以执行到,

rateLimiter.acquire()该方法会阻塞线程,直到令牌桶中能取到令牌为止才继续向下执行。

抢购实现1

我们模拟抢购商品,总共有35件商品,模拟我们服务器只能承受住20个并发,超过20个服务器会挂掉,我们用RateLimiter来进行限流,

我们没有传参数,当然实际场景复杂很多。

package com.lsq.einterview.controller;import com.google.common.util.concurrent.RateLimiter;
import com.lsq.einterview.domain.APIResponse;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController()
@RequestMapping("/api")
public class RushBuyController {private static final RateLimiter rateLimiter = RateLimiter.create(20);private static int productCount=35;private static final Object o=new Object();/*** 我们模拟抢购商品,模拟我们服务器只能承受住20个并发,超过10个服务器会挂掉,* 我们用rateLimiter来进行限流,* 我们也没有传参数,当然实际场景复杂很多。* @return 响应体*/@RequestMapping(value = "/rushBuy2Product" , produces = "application/json;charset=utf-8")public APIResponse rushBuy2Product() {System.out.println("进入抢购:等待时间为:" + rateLimiter.acquire());boolean b = productStock();if (b){System.out.println("抢购成功");}else {System.out.println("抢购失败");}return new APIResponse<>().success();}/*** 抢购商品的库存* @return*/private boolean productStock(){if (productCount==0){return false;}synchronized (o){if (productCount>0){--productCount;System.out.println("剩余库存为:"+productCount);return true;}else {return false;}}}
}

使用Jmeter测试开启100个线程进行测试:

统计下商品抢购成功的数据,正好是35个抢购成功。

我们看到在开始进入的请求不需要等待,直接执行,后面进来的需要等待的时间慢慢变长,因为拿不到令牌。

但是这种方法有个很明显的缺陷,在实际并不适合使用,因为用户请求进来,拿不到令牌就需要等待执行,体验十分差。所以下面我们介绍另一种方式。

抢购实现2

由于RateLimiter是属于单位时间内生成多少个令牌的方式,譬如0.1秒生成1个,那抢购就要看运气了,你刚好是在刚生成1个时进来了。

那么你就能抢到,在这0.1秒内其他的请求就算白瞎了,只能寄希望于下一个0.1秒,而从用户体验上来说,不能让他在那一直阻塞等待。

所以就需要迅速判断,该用户在某段时间内,还有没有机会得到令牌,这里就需要使用TryAcquire(long timeout、TimeUnit、Unit)方法,指定一个超时时间,一旦判断出在timeout时间内还无法取得令牌,就返回false。

注意,这里并不是真正的等待了timeout时间,而是被判断为即便过了timeout时间,也无法取得令牌。这个是不需要等待的。

package com.lsq.einterview.controller;import com.google.common.util.concurrent.RateLimiter;
import com.lsq.einterview.domain.APIResponse;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.concurrent.TimeUnit;@RestController()
@RequestMapping("/api")
public class RushBuyController {private static final RateLimiter rateLimiter = RateLimiter.create(20);private static int productCount=35;private static final Object o=new Object();/*** 我们模拟抢购商品,模拟我们服务器只能承受住20个并发,超过10个服务器会挂掉,* 我们用rateLimiter来进行限流,* 我们也没有传参数,当然实际场景复杂很多。* @return 响应体*/@RequestMapping(value = "/rushBuy2Product" , produces = "application/json;charset=utf-8")public APIResponse rushBuy2Product() {boolean isAcquire = rateLimiter.tryAcquire(1, TimeUnit.SECONDS);if (!isAcquire){System.out.println("进入抢购:在1s内无法拿到令牌,直接返回");return new APIResponse<>().fail("抢购失败");}boolean b = productStock();if (b){System.out.println("抢购成功");}else {System.out.println("抢购失败");}return new APIResponse<>().success();}/*** 抢购商品的库存* @return*/private boolean productStock(){if (productCount==0){return false;}synchronized (o){if (productCount>0){--productCount;System.out.println("剩余库存为:"+productCount);return true;}else {return false;}}}
}

结果:

在我们的所有日志文件中统计:

  • 抢购成功:35;

  • 抢购失败:23;

  • 无法拿到令牌,未参与抢购:42;

  • 总数为我们100个线程。

总结

抢购实现的方式有很多种,在抢购2案例中,按照固定的单位时间进行分割,每个单位时间产生一个令牌,可供购买。

我们可以想到平常参与抢购秒杀时,有时候你网速很快,但总是抢不到,现在明白了吧。

真正的抢购不是这么简单,难度也复杂得多。所以不只是在代码中限流能实现的。

瞬间的流量洪峰会冲垮服务器的负载,当几十万人抢购几件商品时,连接口都请求不进来,更别提接口里的令牌分配了。

如何应对大流量冲击?相关推荐

  1. java 大流量高并发_【BAT面试题】如何应对大流量、高并发??

    这是一道BAT大厂的面试题 所谓高并发指的是:在同时或极短时间内,有大量的请求到达服务端,每个请求都需要服务端耗费资源进行处理,并做出相应的反馈. 常用的高并发处理的思路与手段 从服务端视角看高并发 ...

  2. 程序员如何应对双十一购物的大流量冲击?

    作者 | LLLSQ 责编| 胡巍巍 双十一就要来了!在双十一这样的大流量场景中,抢购.下单量会非常大,如果业务应用系统的负载能力有限,非预期的请求,就会给系统带来很大压力,从而拖垮业务应用系统. 那 ...

  3. 大流量冲击下,腾讯QQ客户端如何保障春节红包活动的用户体验?

    导语 |  2020春节红包"鼓力全开"已顺利闭幕,本文针对今年腾讯QQ春节红包客户端在活动配置.错峰.数据上报.资源预下载和柔性策略五个方面进行探讨和总结,希望能和大家在后续春节 ...

  4. 网站流量过大服务器不行,网站如何应对突然的大流量访问?

    对于一些网站,特别是电商和报名网站,这些网站大多数时候网站的访问人数都是比较稳定的,但是在过节.报名或者是一些特殊时候,网站会突然增加很多访问者.那么,网站面对这些突然出现的大量流量该怎么做呢? 网站 ...

  5. 阿里云高级专家朱小平:如何打造应对超大流量的负载均衡

    大流量高并发互联网应用实践在线峰会官网:https://yq.aliyun.com/activity/112 峰会统一报名链接:http://yq.aliyun.com/webinar/join/49 ...

  6. 阿里为啥值4万亿?看它如何应对亿级高并发大流量?如何保障高可用和稳定性,就知道了!...

    作者:丁浪,目前在创业公司担任高级技术架构师.曾就职于阿里巴巴大文娱和蚂蚁金服.具有丰富的稳定性保障,全链路性能优化的经验.架构师社区特邀嘉宾! 阅读本文,你将会收获: 高并发.大流量场景的常见问题和 ...

  7. 阿里如何应对亿级高并发大流量?如何保障高可用和稳定性!

    作者:丁浪,目前在创业公司担任高级技术架构师.曾就职于阿里巴巴大文娱和蚂蚁金服.具有丰富的稳定性保障,全链路性能优化的经验.架构师社区特邀嘉宾! 阅读本文,你将会收获: 高并发.大流量场景的常见问题和 ...

  8. 【华为云技术分享】弹性负载均衡服务助力企业应对高并发流量冲击

    摘要:弹性负载均衡(Elastic Load Balance 简称ELB)将访问流量均衡分发到多台弹性云服务器,扩展应用系统对外的服务能力,实现更高水平的应用程序容错性能. 如今,随着互联网规模和消费 ...

  9. 如何应对大促流量洪峰?揭秘京东技术人的备战手册

    又是一年11.11,大促备战也如期而至,但今年11.11前的大长假给我们备战的准备带来了很大挑战.如何在时间紧.任务重的情况下快速让团队进入状态?如何梳理繁杂的系统风险点.准备资源?作为京东大促的技术 ...

最新文章

  1. CVPR 2020丨UDVD:用于可变退化的统一动态卷积超分辨率网络
  2. IE8的项目在IE11下 一些功能无法实现的解决方案
  3. Packagist / Composer 中国全量镜像
  4. linux——线程(1)
  5. IM、RTC技术两生花,看融云如何打造“IM+RTC+Push”一站式通信云服务
  6. 简单的print函数的实现
  7. vs2010如何发布窗体应用程序到服务器,C++ CLR 使用(VS2012,VS2013,VS2015)编写Windows窗体应用程序...
  8. html彩色条,html5 canvas彩色流动线条动画特效
  9. linux 字符 拨号上网,LINUX下用ADSL拨号上网
  10. 1T免费全能空间 注册即可开通
  11. 《基础水文数据库》应用软件-水文预报中PA值计算
  12. 软件开发新技术(工具及相关技术)
  13. 不安装DBC2000安装架设传奇服务端的方法
  14. 【前端三分钟】锚点自动跟随滚动定位
  15. if 判断条件为纯数字
  16. android 异形图片布局,杜蕾斯一张图说明异形全面屏手机进化史!刘海屏到水滴屏很形象...
  17. Arduino LED 闪烁 教程
  18. Linux使用split命令切割大型日志文件 保留文件结尾删除前面的内容
  19. 基于opencv视觉库,通过numpy进行像素矩阵处理,压缩图片、做像素图
  20. java正则替换特殊符号

热门文章

  1. 苹果6s出现连接不上服务器未响应,苹果6s的蜂窝移动数据打开没反应怎么办
  2. SEO工作思路怎么写,为什么建站前要设计思路?
  3. linux xargs
  4. 中国楼市房贷真相:只有傻瓜才自掏腰包!
  5. MUR20060CT-ASEMI快恢复模块MUR20060CT
  6. 用c#实现拍拍抢拍精灵实现过程--核心代码--腾讯qq拍拍网秒杀器代码【欢迎转载】
  7. PeekMessage与GetMessage区别
  8. c语言学生学籍管理程序,C语言实现简单学籍管理系统
  9. Android中实现平铺图片
  10. 通过这一篇文章,可以把Java中的类加载器了解的七七八八了