限流之一二三


方案一 控制并发数

利用信号量机制(javaSemaphore类可实现)来控制并发数量,这个信号量机制类似于线程锁机制,比如服务允许线程为50,可以利用信号量机制,控制线程执行只维持20个线程同时执行。具体代码如下

 

8行获得信号量,14行释放信号量,类似锁机制。此方案就是简单,无需引入任何jar就能实现代码逻辑,只需要javaSemaphore类。

 

方案二  控制访问速率

在订单服务添加请求速率控制器,过滤请求,请求溢出服务承受能力,跳转资源紧张页面或排队页。

 

速率控制使用GoogleRateLimiterRateLimiter利用令牌桶算法实现流量控制,

令牌桶算法是一个存放固定容量令牌的桶,按照固定速率往桶里添加令牌。令牌桶算法的描述如下:

假设限制2r/s,则按照500毫秒的固定速率往桶中添加令牌;

桶中最多存放b个令牌,当桶满时,新添加的令牌被丢弃或拒绝;

令牌桶也能控制数据流读取速率,当一个n个字节大小的数据包到达,将从桶中删除n个令牌,接着数据包被发送到网络上;

如果桶中的令牌不足n个,则不会删除令牌,且该数据包将被限流(要么丢弃,要么缓冲区等待)。

 

 

 

如图,桶中按一定速率生成令牌,可以实现按请求发放令牌,当桶中令牌为空时,可以实现请求堵塞,或者直接返回false,跳转警告页。

令牌桶的另外一个好处是可以方便的改变速度. 一旦需要提高速率,则按需提高放入桶中的令牌的速率.

 

RateLimiter的主要功能就是提供一个稳定的速率,实现方式就是通过限制请求流入的速度

 

代码逻辑范例

final RateLimiter rateLimiter = RateLimiter.create(2.0);

 

void submitTasks(List tasks, Executor executor) {

    for (Runnable task : tasks) {

        rateLimiter.acquire(); // 也许需要等待 ,如果不阻塞,可使用tryAcquire方法,直接跳转页面

        executor.execute(task);

    }

}

代码逻辑大意就是,每秒生成2个令牌,每次请求过来都去领取一个令牌,如果令牌桶中令牌为空,请求阻塞或返回false做页面跳转。

web中实现可以利用拦截器实现RateLimiter 功能,拦截过载请求。

问题点:功能不算复杂,但是存在分布式问题,RateLimiter多是针对于单机实现,多机实现RateLimiter会创建多个令牌桶,拦截数会变成节点数*令牌数导致限流失败,看是否能够部署的时候做成多机单桶调用。

单机Web项目可使用过滤器实现,要初始化令牌桶,过滤器init()方法中实现令牌桶初始化。

多机要考虑我们现有框架令牌桶如何初始化。

 

 

 

 

 

 

 

方案三  控制单位时间窗口内请求数(可实现分布式限流)

 

redis + Lua做访问流量控制,此方案是针对分布式系统的限流方案,并且能实现对各个不同维度不同颗粒度的流量限制,比如,某个ip在短时间内频繁访问页面(如爬虫),可以控制其单位时间内对某一接口或者整个服务的访问次数,也可以实现对某个服务接口的单位时间的流量限制。

主要脚本及代码如下,key取的参数不同以及限制数的不同就能实现是针对用户ip或者服务端口的限流,理论上还可以实现更细化的更多维度的流量控流。

 

此方案无论部署和代码量都不是很复杂,它是利用计数器实现限流,但做不到速率流量限制,可以考虑在业务量更大的时候和方案二配合使用。

 

 

 

 

 

实现

 使用拦截器实现,redis+lua限流功能单独实现,与其他服务解耦实现。

(是否需要添加开关功能待定)

功能描述:

接口项

说明

功能

Redis+Lua限流

应用场景

大请求量的限流拦截功能

接口模块

eshop-app

拦截器名称

requestLimiterByRedisLua

关键变量说明:

变量名称

变量类型

详细说明

limiteKey

String

Key的拼接规则:Key+time(时间维度的颗粒度)

存储的key不同可以针对用户限制还是服务限制,key拼接的时间,限制到秒就是每秒限流,限制到分就是每分钟限制

limit

String

限流大小,为数字字符串

limitType

String

常量字段,1:针对用户限流,2针对服务端口限流,后期可扩展

limitIsRun

Boolean

开关字段,是否启用限流(是否需要待定)

 

 

一些其他想法

这只是在应用层解决了限流服务,解决大体量请求的方案还有其他,可以根据现有的请求量,成本和技术慢慢演化成型,后期可以在服务上抽象出一个接入层,来实现nginx+lua拦截,这是小米抢购系统初期实现的一种方式,利用lua扩展nginx的功能模块,可以在请求进入java服务层之前将请求隔离,nginx处理并发的能力比java强,这样能够更好的避免伤及服务。

数据库端也可以做优化,因为秒杀这种都是实时热点数据,短时间内会占用大量的数据库连接,数据库并发连接越多,整体处理请求能力越差,所以一般会将秒杀单独分库处理,以免让1%的热点数据影响到99%的普通业务,还可以通过合理的排队机制来减少单位时间内的数据库并发连接。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值