java中常见的限流算法详细解析

前言

以下的文章参考了一些具体的资料加深了解
B站:Java限流算法,你知道该怎么做吗?

1. 验证限流以及容器限流

突发的流量请求,系统可能会造成奔溃。可以通过集群多个服务器,所以要加以限流
生活中的比如秒杀订单或者微博热搜条等

在某种容器或者验证上也可以加以限流,具体如下

  • 合法性的验证限流:验证码、ip黑名单(防止无限次的调用)
  • 容器限流:tomcat、nginx

tomcat的限流在配置文件配置如下:
通过限制线程数的参数个数maxThreads="150"

<Connector port="8080" prptpcpl="HTTP/1.1"
	connectionTimeout="20000"
	maxThreads="150"
	redirectPort="8443">

nginx的限流配置文件配置如下:

  • 控制速率:(通过控制每秒2个并发数,队列存储4个,多了会拒绝)
limit_req_zone $binary_remote_addr zone mylimit:10m rate=2/s;
server{
	location /{
		limit_req zone=mylimit brust=4;
	}
}
  • 控制并发数:(通过控制ip以及端口号)
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn_zone $binary_name zone=perserver:10m;
server{
	limit_conn perip 10;
	limit_conn perserver 100;
}

2. 服务端限流

服务端限流:固定时间窗口算法、滑动时间窗口算法、漏桶算法、令牌桶算法

2.1 固定时间窗口

通过固定窗口,对每个窗口在固定时间内加以流量限制。
通过计数器,如果有请求,计数器加1

但是这样会造成流量分布不均匀,某一时间刻可能就已经把该窗口的数据量用完,其他时间段已经不能使用或者该时间段已经访问不了,请求都会被拒绝,这种现象称为突刺现象
时间跨度越大,临界值不好设值

2.2 滑动时间窗口

在这里插入图片描述

为此可以将上面的算法加以处理
将其每个窗口细化成好几个窗口,时间范围缩小,时间粒度控制比较小,每个小窗口都有其计数器

该小窗口如果已经结束,往右滑动获取另外一个小窗口(该算法复杂度比较高,市面少部分也会有使用)

2.3 漏桶算法

上面的水流量可以存放(满了就会拒绝·),下面的水流量可以匀速访问,以恒定的速度进行访问(类似一秒钟只能访问多少个请求)。但是处理不了短时的高并发数据量
在这里插入图片描述

主要通过redis的命令,具体如下:c1.throttle mylimit 15(桶的容量) 30(流水量) 60(时间)

实现:用队列保存请求,用ScheduledThreadPoolExecutor(支持定时任务的线程池)来定时从队列获取

2.4 令牌桶算法

漏桶算法限制请求的速率,而令牌桶算法在限制请求速率的同时还允许一定程度的数据量突发调用

以恒定的速率生成令牌,即使遇到高并发的数据量,可以生成多个令牌数据。满了之后,会丢弃或者以一定的速率进行放入。
只有真正拿到令牌的数据才会执行其操作
在这里插入图片描述

可以处理瞬时的并发数据量

public class RateLimiterExample{
	public static void main(String[] args){
	//每秒产生10个令牌(每100ms产生一个)
		RateLimiter rt=RateLimiter.create(10);
		//11个线程逐一获取
		for(int i=0;i<11;i++){
			new Thread(()->{
				//获取1个令牌
					rt.acquire();
						System.out.println(" "+Instant.now());					
			}).start();
		}
	}
}

RateLimiter的算法:

  • token bucket
    非常易于出现
    最好设置的上线的限度是攻击的2倍

借两个窗口之间的请求进行攻击的话,可能会导致整个系统崩溃掉
为此有了如下算法

  • sliding windows
    不仅记录的请求数量,还记录了所有请求的时间戳
    请求到达的时候,会移除距离这个当前节点相应单位窗口之外的所有旧请求,再来判断当前的请求与窗口请求数量(上限值是它的2倍),如果合适就接收请求。

具体算法的实现是某个ip或者用户等的限制,每个时间内的请求的上限,过期时间
这种设计模式可以通过redis很好的实现,通过设置key,设置时间限制的过期

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
令牌桶算法是一种常见限流算法,它可以控制请求的速率,防止系统被过多的请求压垮。下面是Java实现令牌桶算法的步骤和代码逻辑: 1. 定义一个令牌桶类,包含以下属性: - 最后一次令牌发放时间 - 桶的容量 - 令牌生成速度 - 当前令牌数量 2. 实现一个获取令牌的方法,该方法会在每次请求到来时被调用,具体实现如下: - 计算当前令牌数量 - 判断当前令牌数量是否足够 - 如果令牌数量不足,则拒绝请求 - 如果令牌数量足够,则领取令牌,并执行业务逻辑 3. 使用ScheduledExecutorService定时生成令牌,具体实现如下: - 每隔一段时间生成一定数量的令牌 - 如果令牌数量超过桶的容量,则不再生成令牌 下面是Java实现令牌桶算法的代码逻辑: ``` @Slf4j public class TokensLimiter { private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5); // 最后一次令牌发放时间 public long timeStamp = System.currentTimeMillis(); // 桶的容量 public int capacity = 10; // 令牌生成速度10/s public int rate = 10; // 当前令牌数量 public int tokens; public void acquire() { scheduledExecutorService.scheduleWithFixedDelay(() -> { long now = System.currentTimeMillis(); // 当前令牌数 tokens = Math.min(capacity, (int) (tokens + (now - timeStamp) * rate / 1000)); // 每隔0.5秒发送随机数量的请求 int permits = (int) (Math.random() * 9) + 1; log.info("请求令牌数:" + permits + ",当前令牌数:" + tokens); timeStamp = now; if (tokens < permits) { // 若不到令牌,拒绝 log.info("限流了"); } else { // 还有令牌,领取令牌 tokens -= permits; log.info("剩余令牌=" + tokens); } }, 1000, 500, TimeUnit.MILLISECONDS); } public static void main(String[] args) { TokensLimiter tokensLimiter = new TokensLimiter(); tokensLimiter.acquire(); } } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农研究僧

你的鼓励将是我创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值