限流 Rhino
①、常见的方式: heapdump.cn/article/548…
- 令牌桶方式:以固定的速率往桶中添加令牌,处理速度取决于桶中令牌数
- 漏桶方式:按照固定的速率流出请求,流入速率不限制。可以积攒请求取决于桶容量
- 计数器方式:采用Semaphore信号量。如果超过阈值信号量,则进入队列阻塞等待
- 滑动窗口方式:
采用滑动窗口限流方法实现
public class DynamicWindowLimit {
private long limit = 1;
private final int windowNum = 5;
private final int windowTime = 1000;
private LinkedList<Entry> windowList = Lists.newLinkedList();
public DynamicWindowLimit() {
while (true) {
new Thread(() -> {
if (windowList.size() == windowNum) {
// 删除最左的一个格子
windowList.poll();
}
// 窗口向右移动新增
windowList.offer(new Entry(System.currentTimeMillis() / 1000));
try {
Thread.sleep(windowTime / windowNum);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}).start();
}
}
public boolean tryAcquire() {
// 先判断当前属于哪个小窗口
Entry entry = getCurWindow();
entry.setCount(entry.getCount() + 1);
return getSumRequest() >= limit;
}
public long getSumRequest() {
return windowList.stream().mapToLong(Entry::getCount).sum();
}
public Entry getCurWindow() {
if (windowList.isEmpty()) {
while (true) {
if (windowList.isEmpty()) {
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
return windowList.getLast();
}
class Entry {
long count;
long time;
public Entry(long time) {
this.time = time;
}
public long getCount() {
return count;
}
public void setCount(long count) {
this.count = count;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
}
public static void main(String[] args) {
DynamicWindowLimit dynamicWindowLimit = new DynamicWindowLimit();
for (int i = 0; i < 3; i++) {
if (dynamicWindowLimit.tryAcquire()) {
System.out.println("请求可以执行...");
} else {
System.out.println("请求被限流!!!");
}
}
}
}
②、限流的指标一般有哪些?
- QPS、响应时间、错误率。
③、怎么实现集群维度的限流?
- 单机模式下:每个应用启动的时候都会启动一个
token-server
来实现单机的限流策略 - 集群模式下:会有一个单独
token-server
服务端,其他的应用里引入的是token-client
,应用判断是否限流时是需要向server
请求判断的,server管控整个集群所有的client对资源的访问是否达到限流配置
采用的是令牌桶的方式
④、Sentinel
sentinelguard.io/zh-cn/docs/…