场景
1.为了防止第三方无节制的调用打垮我们的系统,需要对重要的请求进行限流。
2.服务方对我们调用的接口有每分钟限流
场景1通常已经有比较成熟的框架支持,只需要简单的配置即可,即可在系统中引入限流机制如alibaba sentinel,spring clound hystrix
场景2服务方对我们限流,为了能正确获得服务结果,我们也需要对我们的请求进行限流发起。
常见的几种限流方案
计数限流、固定窗口、滑动窗口、漏桶算法、令牌桶
最简单的方案
非上面限流算法,既然对方限流我们,那我们只要一直请求,请求结果如果是限制请求,那么我们sleep一段时间,再继续发起请求
public class WeTask{
public static void main(String[] args) {
// 伪代码
task(1);
}
public Static void task(int para) {
Boolean res = getResponse();
if (!res) {
//限流了
Thread.sleep(1000);
// 继续发起上次请求
task(para);
}
// 处理结果
dosomething()
}
}
令牌桶通过信号量实现
由于是我们限制自己的发起请求,对方限制我们的结果,因此根据对方给的限制每分钟300个请求的限制,使用令牌桶,来控制我们的请求。
代码里省略了很多细节,主要为了理解信号量怎么实现令牌桶,通过acquire、release两个方法实现往桶里取、放。
public class WeTask{
@autowire
Semaphore apiSemaphore;
@Bean(name = "apiSemaphore")
public Semaphore apiSemaphore() {
Semaphore apiSemaphore = new Semaphore(40, true);
scheduledThreadPoolExecutor.scheduleAtFixedRate(() -> {
try {
// 每10s往桶里放允许的请求数
log.info("release ...40");
apiSemaphore.release(40);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}, -1, 10, TimeUnit.SECONDS);
return apiSemaphore;
}
public static void main(String[] args) {
// 伪代码
task(1);
}
public Static void task(int para) {
apiSemaphore.acquire();
Boolean res = getResponse();
//伪代码 还要再次判断可能限流(不信任原则,对方可能会调整限流量)
// 处理结果
dosomething()
}
}
涉及到的API
scheduledThreadPoolExecutor: 定时任务线程池和Timer类似,但是比他强大,支持多线程的定时任务
Semaphore: 信号量,内部类继承实现AQS,支持公平、非公平锁
其他相关知识点参考之前的:栅栏和闭锁
https://blog.csdn.net/u013565163/article/details/87310700