2024年Java最全P8大牛带你细谈架构中的限流与计数器的实现方式(1),360°深入了解spring

最后

笔者已经把面试题和答案整理成了面试专题文档

image

image

image

image

image

image

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

今天挑一个最简单的计数器方式来讲讲限流

3. 解决方案

计数器的解决方式是最简单最容易实现的一种解决方案,假设有一个接口,要求1分钟的访问量不能超过10次

这样当有任何请求过来,我可以让计数器+1;如果这个计数器的值大于10,而且和第一次的请求相比,时间间隔在1分钟以内,那么久能说明该请求访问过多。

如果这个请求与第一次请求的访问时间之间的间隔超过了1分钟,那么该计数器的值久还是限流范围之内,接下来久只要重置计数器就好;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.atomic.AtomicInteger;

public class EnjoyCountLimit {

private int limtCount = 60;// 限制最大访问的容量

AtomicInteger atomicInteger = new AtomicInteger(0); // 每秒钟 实际请求的数量

private long start = System.currentTimeMillis();// 获取当前系统时间

private int interval = 60*1000;// 间隔时间60秒

public boolean acquire() {

long newTime = System.currentTimeMillis();

if (newTime > (start + interval)) {

// 判断是否是一个周期

start = newTime;

atomicInteger.set(0); // 清理为0

return true;

}

atomicInteger.incrementAndGet();// i++;

return atomicInteger.get() <= limtCount;

}

static EnjoyCountLimit limitService = new EnjoyCountLimit();

public static void main(String[] args) {

ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();

for (int i = 1; i < 100; i++) {

final int tempI = i;

newCachedThreadPool.execute(new Runnable() {

public void run() {

if (limitService.acquire()) {

System.out.println(“你没有被限流,可以正常访问逻辑 i:” + tempI);

} else {

System.out.println(“你已经被限流呢 i:” + tempI);

}

}

});

}

}

}

这个计数器的限流方式很简单吧,但这样问题吗?好好想想……

还是以60运行访问10次请求为例,在第一次0-58秒之内,没有访问请求,在59秒之内突然来了10次请求,这个时候会做什么,由于已经到了1分钟计数器会重置。

这个时候第二次的1秒内(1分0秒)又了10请求,这个时候是不是就在2秒之内有20个请求被放行了呢?(59秒,1分0秒),如果某个服务器的访问量只能是10次请求,那这种限流方式已经导致服务器挂了;

4. 滑动窗口计数器

前面已经知道简单的计数器的实现方式,也知道他会出现的一些问题,虽然这些问题举得有些极端,但还是有更好得解决方案,这方案就是使用滑动窗口计数器

滑动窗口计数器得原理是在没错请求过来得时候,先判断前面N个单位内得总访问量是否操过得阈值,并且在当前得时间单位得请求数上+1

举例来说,要求1分钟的访问量不能超过10次

可以把1分钟看成是6个10秒钟的时间,0-9秒的访问数记录到第一个格子,10-19秒的访问数记录数记录到第二个格子以此内推,每次统计将6个格子里面的数据求和,如果超过了10次就不允许访问。

import java.util.concurrent.atomic.AtomicInteger;

public class EnjoySlidingWindow {

private AtomicInteger[] timeSlices;

/* 队列的总长度 */

private final int timeSliceSize;

/* 每个时间片的时长 */

private final long timeMillisPerSlice;

/* 窗口长度 */

private final int windowSize;

/* 当前所使用的时间片位置 */

private AtomicInteger cursor = new AtomicInteger(0);

public static enum Time {

MILLISECONDS(1),

SECONDS(1000),

MINUTES(SECONDS.getMillis() * 60),

HOURS(MINUTES.getMillis() * 60),

DAYS(HOURS.getMillis() * 24),

WEEKS(DAYS.getMillis() * 7);

private long millis;

Time(long millis) {

this.millis = millis;

}

public long getMillis() {

return millis;

}

}

public EnjoySlidingWindow(int windowSize, Time timeSlice) {

this.timeMillisPerSlice = timeSlice.millis;

this.windowSize = windowSize;

// 保证存储在至少两个window

this.timeSliceSize = windowSize * 2 + 1;

init();

}

/**

  • 初始化

*/

private void init() {

AtomicInteger[] localTimeSlices = new AtomicInteger[timeSliceSize];

for (int i = 0; i < timeSliceSize; i++) {

localTimeSlices[i] = new AtomicInteger(0);

}

timeSlices = localTimeSlices;

}

private int locationIndex() {

long time = System.currentTimeMillis();

return (int) ((time / timeMillisPerSlice) % timeSliceSize);

}

/**

  • 对时间片计数+1,并返回窗口中所有的计数总和

  • 该方法只要调用就一定会对某个时间片进行+1

  • @return

*/

public int incrementAndSum() {

int index = locationIndex();

int sum = 0;

// cursor等于index,返回true

// cursor不等于index,返回false,并会将cursor设置为index

int oldCursor = cursor.getAndSet(index);

if (oldCursor == index) {

// 在当前时间片里继续+1

sum += timeSlices[index].incrementAndGet();

面试结束复盘查漏补缺

每次面试都是检验自己知识与技术实力的一次机会,面试结束后建议大家及时总结复盘,查漏补缺,然后有针对性地进行学习,既能提高下一场面试的成功概率,还能增加自己的技术知识栈储备,可谓是一举两得。

以下最新总结的阿里P6资深Java必考题范围和答案,包含最全MySQL、Redis、Java并发编程等等面试题和答案,用于参考~

重要的事说三遍,关注+关注+关注!

历经30天,说说我的支付宝4面+美团4面+拼多多四面,侥幸全获Offer

image.png

更多笔记分享

历经30天,说说我的支付宝4面+美团4面+拼多多四面,侥幸全获Offer

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

注+关注!**

[外链图片转存中…(img-YPjbqS9L-1714868198914)]

[外链图片转存中…(img-bpxnRw1y-1714868198914)]

更多笔记分享

[外链图片转存中…(img-xs7BSB2S-1714868198915)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值