网站首页 > java教程 正文
前言
笔者在一家规模还算可以的互联网公司工作,参与优惠券、红包等营销领域的系统的开发,这些系统都是和用户利益密切相关,贴近用户的需求,故备受用户关注,流量极高,俗称“高并发”。如何确保系统的平稳?这就涉及到一个电商公司必备的利器——限流组件,下面来详细描述下实现原理。
实现
如何实现一个限流方法呢,并且是秒级的呢?听起来挺高大上的,其实没那么复杂,使用本地缓存guava,采用计数器算法,写少量代码,即可做到!
package com.example.limit.utils;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 计数器算法实现限流
*/
public class LimitUtils {
private final static LoadingCache<String, AtomicInteger> limiter = CacheBuilder
.newBuilder()
.maximumSize(Integer.MAX_VALUE)
.expireAfterWrite(1, TimeUnit.SECONDS)
.build(new CacheLoader<String, AtomicInteger>() {
@Override
public AtomicInteger load(String key) {
return new AtomicInteger(0);
}
});
/**
* 是否被限流
*
* @param channelName 渠道
* @param limitSize 限额
* @return
* @throws ExecutionException
*/
public static boolean isLimited(String channelName, Integer limitSize) throws ExecutionException {
return limiter.get(channelName).incrementAndGet() > limitSize;
}
}
以上,展示了计数器算法实现限流的方法,要点有以下几点:
1.计数采用原子整数类AtomicInteger
为了确保限流准确无误,我们不能采用简单的int类型,需要采用原子操作类AtomicInteger,它是一个原子操作,在高并发多线程情况下,能准确计数,不会出现重复计数的等情况。
2.使用本地缓存LoadingCache
限流计算,在实际场景中,qps是十分高的,如何确保性能,只能采用本地缓存,不能采用外置的Redis等。不过有个缺陷,就是本地缓存不能共享计数值,故只能单机限流,每台机器各自限流各自的,比较依赖rpc的负载均衡。
测试
下面我们写个Service,利用这个限流的utils,在多线程下做下测试,看是否能正确限流
首先是定义个接口方法
package com.example.limit.demo;
import java.util.concurrent.ExecutionException;
public interface LimitService {
/**
* 测试方法
*
* @param channelName 渠道
* @param limitSize 限额
* @return
* @throws ExecutionException
*/
String doSomething(String channelName, Integer limitSize) throws ExecutionException;
}
接着是接口方法的实现类
package com.example.limit.demo;
import com.example.limit.utils.LimitUtils;
import org.springframework.stereotype.Service;
import java.util.concurrent.ExecutionException;
@Service
public class LimitServiceImpl implements LimitService {
@Override
public String doSomething(String channelName, Integer limitSize) throws ExecutionException {
if (LimitUtils.isLimited(channelName, limitSize)) {
return "调用失败";
} else {
return "调用成功---";
}
}
}
最后是多线程情况下测试
package com.example.limit;
import com.example.limit.demo.LimitService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
@SpringBootTest
class LimitApplicationTests {
@Autowired
LimitService limitService;
@Test
void contextLoads() throws IOException {
exec("线程1", "a", 3);
exec("线程2", "a", 3);
// 等待线程执行完
System.in.read();
}
void exec(String threadName, String channelName, Integer limitSize) {
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
System.out.println(Thread.currentThread().getName() + limitService.doSomething(channelName, limitSize));
if (i == 5) {
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}, threadName).start();
}
}
结果分析
1.在限流方面,我指定方法的限流为3/s。
2.在线程方面,我发起2个线程,每个线程发起10次请求,共20次
3.在时间方面,我确保每个线程执行的时间超过1秒。
结果可以看到,2秒内,20次请求,只有6次请求成功。
这是符合我们的限流预定的!
其他
本文采用了Guava+AtomicInteger,实现一个计数器限流算法,虽然简单,但在实际场景中,还是经常用到!下次和他人谈起限流方法,可以刚起来了!
猜你喜欢
- 2025-09-19 常见的几种简单限流算法_常见的几种简单限流算法有哪些
- 2025-09-19 常用限流算法与Guava RateLimiter源码解析
- 2025-09-19 常见的4种限流算法与实现_限流方法原理
- 2025-09-19 API能力开放平台设计思路剖析_开放api技术
- 2025-09-19 Sentinel 是什么?它是如何实现限流的?
- 2025-09-19 限流算法面面观:Sentinel与Gateway的差异揭秘
- 2025-09-19 在Spring Boot集成Bucket4j实现接口限流操作
- 2025-09-19 Spring Boot3 中实现单节点限流操作全解析
- 2025-09-19 一文搞懂!7种常见的限流方式!_限流的几种方式
- 2025-09-19 Java API Gateway动态限流,流量控制还能这么玩?一线实战全解析!
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)