分布式限流

分布式限流

为什么需要限流

在互联网应用中,我们的系统会面临一个重大的挑战,那就是大流量高并发访问,比如∶天猫双十一、京东618、秒杀、抢购促销等,这些都是典型的大流量高并发场景。
短时间内巨大的访问流量,我们如何让系统在处理高并发的同时还能保证自身系统的稳定?有人说,增加机器即可,但是如果加机器也不够怎么办?而且我们也不能无限制地添加机器,我们的服务资源总是有限的,在有限的资源下,我们要应对这种高并发大流量访问,就不得不采取一些其他措施来保护我们的后端服务系统,比如缓存、异步,降级、限流、静态化等;
我们今天要给大家介绍的是面对高并发系统,我们经常会采用的一个策略:限流;

什么是限流

限流在我们的系统中指的是∶在高并发场景下对高并发访问或请求进行限速来保护我们的系统,一旦达到我们限制的速度则可以∶

  1. 拒绝服务(提示友好的信息或者跳转到错误提示页);
  2. 排队或等待(比如秒杀)
  3. 降级(返回默认数据)。

所以限流也就是对请求进行限速,比如10r/s,即每秒只允许10个请求,这样就限制了请求的速度﹔限流,归根结底就是在一定频率上进行量的限制。

我们今天主要介绍限流技术;
1、对稀缺资源的秒杀、抢购;
2、对数据库的高并发读写操作,比如下单、瞬间往数据库插入大量的数据等;

限流可以说是处理高并发问题的利器,有了限流就不用担心瞬间高峰流量压垮系统服务或服务雪崩,[最终做到有损服务而不是不服务﹔
当然限流要评估好、测试好,否则会导致正常流量访问也被限流了,这样会有损用户体验,引起用户抱怨甚至投诉﹔

实现限流的方案

使用nginx接入层限流

Nginx中使用ngx_http_limit_req_module模块来限制的访问频率,在nginx.conf配置文件中可以使用limit_req_zone命令及limit_req命令限制请求速率;

API网关限流

j继承ZuulFilter写一个filter,并使用google的guava库的RateLimiter实现令牌桶限流。

private static final Ratelimiter rateLimiter = RateLimiter.create(1):

//尝试获取合牌
boolean token = ratelimiter.tryAcquire();
if (!token) {
    //没有拿到合牌,那么就不能访问
    ctx.setsendzuulResponse(false);
    ctx.setResponsestatuscode(401);
    ctx.addzuulResponseHeader( " content-type" , "text/html;charset=utf-8");
    ctx.setResponseBody("非法访问");
}

也可以使用spring-cloud-zuul-ratelimite
https://github.com/marcosbarbero/spring-cloud-zuul-ratelimit

使用Redis+Lua脚本限流

分布式限流最关键的是要将限流服务做成全局的,可以采用redis+lua 技术进往实现,通过这种技术可以实现高并发和高性能的限流。Lua是一种轻量小巧的脚本编程语言,用标准C语言编写并以源代码形式开放,其设计目的是为了嵌入到应用程序中,为应用程序提供灵活的扩展和定制功能。

local key = KEYS[1] --限流KEY
local limit = tonumber(ARGV[1]) --限流大小10
local current = tonumber(redis.call( 'get', key) or "o")if current + 1 > limit then --如果超出限流大小
    return 0 --被限流了
else--请求数+1,并设置2秒过期
    redis.call( "INCRBY" , key,"1")
    redis.call( "expire" , key ,"2")
    return 1 --没有被限流了,可以正常访问
end

redisscript = new DefaultRedisscript<List>();
redisscript.setResultType(List.class) ;
redisscript.setscriptsource(new ResourceScriptsource(new ClassPathResource("limit.lua")));


string key = "ip:" + system.currentTimelillis()/1000;
List<string> keyList = Lists.newArrayList(key);
//调用脚本并执行
List result = stringRedisTemplate.execute(redisscript,keyList,string.valueof(value));
system.out.println("1ua脚本执行结果:" + result);

使用Nginx+lua(OpenResty)

常见的限流算法

计数器法

它是限流算法中最简单粗暴,也最容易的一种算法,比如我们要求某一个接口1分钟内的请求不能超过60次,我们可以在开始时设置一个计数器,每次请求,该计数器+1如果该计数器的值大于60并且与第一次请求的时间间隔在1分钟内,那么说明请求过多,如果该请求与第一次请求的时间间隔大于1分钟,并且该计数器的值还在限流范围内,那么重置该计数器;

漏桶算法

维基百科:https://en.wikipedia.org/wiki/Leaky_,bucket
漏桶算法的思路,水(请求)先进入到漏桶里,漏桶以恒定的速度流出,当水流入速度过大会直接溢出,可以看出漏桶算法能强行限制数据的传输速率。

令牌桶算法

维基百科:https://en.wikipedia.org/wiki/Token_bucket
令牌桶算法的原理是系统会以一个恒定的速度往桶里放入令牌,而如果请求需要被处理,则需要先从桶里获取一个令牌,当桶里没有令牌可取时,则拒绝服务。
Guava框架提供了令牌桶算法实现( Google ) ,可直接拿来使用,使用Guava框架的RateLimiter类即可创建一个令牌桶限流器,比如设置每秒放置令牌数为5个,那么RateLimiter 对象就可以保证1秒内不会放入超过5个令牌,并且以固定速率进行放置令牌,达到平滑输出的效果。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值