常見分布式限流方案
點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”
優(yōu)質(zhì)文章,第一時(shí)間送達(dá)
限流分類
合法性驗(yàn)證限流
容器限流
服務(wù)端限流
常見的6種限流方案
1.Tomcat 使用 maxThreads 設(shè)置請(qǐng)求線程數(shù)來(lái)實(shí)現(xiàn)限流
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
maxThreads="150"
redirectPort="8443" />
2.nginx通過(guò) limit_req_zone 和 burst 來(lái)實(shí)現(xiàn)速率限流
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
server {
location / {
limit_req zone=mylimit;
}
}
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
server {
location / {
limit_req zone=mylimit burst=4;
}
}
3.nginx通過(guò) limit_conn_zone 和 limit_conn 兩個(gè)指令控制并發(fā)連接的總數(shù)
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn_zone $server_name zone=perserver:10m;
server {
...
limit_conn perip 10;
limit_conn perserver 100;
}
4.服務(wù)端使用時(shí)間窗口算法借助 Redis 的zset實(shí)現(xiàn)限流
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.3.0</version>
</dependency>
import redis.clients.jedis.Jedis;
public class RedisLimit {
// Redis 操作客戶端
static Jedis jedis = new Jedis("127.0.0.1", 6379);
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 15; i++) {
boolean res = isPeriodLimiting("java", 3, 10);
if (res) {
System.out.println("正常執(zhí)行請(qǐng)求:" + i);
} else {
System.out.println("被限流:" + i);
}
}
// 休眠 4s
Thread.sleep(4000);
// 超過(guò)最大執(zhí)行時(shí)間之后,再?gòu)陌l(fā)起請(qǐng)求
boolean res = isPeriodLimiting("java", 3, 10);
if (res) {
System.out.println("休眠后,正常執(zhí)行請(qǐng)求");
} else {
System.out.println("休眠后,被限流");
}
}
/**
* 限流方法(滑動(dòng)時(shí)間算法)
* @param key 限流標(biāo)識(shí)
* @param period 限流時(shí)間范圍(單位:秒)
* @param maxCount 最大運(yùn)行訪問(wèn)次數(shù)
* @return
*/
private static boolean isPeriodLimiting(String key, int period, int maxCount) {
long nowTs = System.currentTimeMillis(); // 當(dāng)前時(shí)間戳
// 刪除非時(shí)間段內(nèi)的請(qǐng)求數(shù)據(jù)(清除老訪問(wèn)數(shù)據(jù),比如 period=60 時(shí),標(biāo)識(shí)清除 60s 以前的請(qǐng)求記錄)
jedis.zremrangeByScore(key, 0, nowTs - period * 1000);
long currCount = jedis.zcard(key); // 當(dāng)前請(qǐng)求次數(shù)
if (currCount >= maxCount) {
// 超過(guò)最大請(qǐng)求次數(shù),執(zhí)行限流
return false;
}
// 未達(dá)到最大請(qǐng)求數(shù),正常執(zhí)行業(yè)務(wù)
jedis.zadd(key, nowTs, "" + nowTs); // 請(qǐng)求記錄 +1
return true;
}
}
5.服務(wù)端使用漏桶算法借助 Redis-Cell 來(lái)實(shí)現(xiàn)限流
> cl.throttle mylimit 15 30 60
1)(integer)0 # 0 表示獲取成功,1 表示拒絕
2)(integer)15 # 漏斗容量
3)(integer)14 # 漏斗剩余容量
4)(integer)-1 # 被拒絕之后,多長(zhǎng)時(shí)間之后再試(單位:秒)-1 表示無(wú)需重試
5)(integer)2 # 多久之后漏斗完全空出來(lái)
6.服務(wù)端使用令牌算法借助Google 的 guava 包來(lái)實(shí)現(xiàn)限流
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.2-jre</version>
</dependency>
import com.google.common.util.concurrent.RateLimiter;
import java.time.Instant;
/**
* Guava 實(shí)現(xiàn)限流
*/
public class RateLimiterExample {
public static void main(String[] args) {
// 每秒產(chǎn)生 10 個(gè)令牌(每 100 ms 產(chǎn)生一個(gè))
RateLimiter rt = RateLimiter.create(10);
for (int i = 0; i < 11; i++) {
new Thread(() -> {
// 獲取 1 個(gè)令牌
rt.acquire();
System.out.println("正常執(zhí)行方法,ts:" + Instant.now());
}).start();
}
}
}
版權(quán)聲明:本文為博主原創(chuàng)文章,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接和本聲明。
本文鏈接:
https://blog.csdn.net/qq_36427244/article/details/117388495


評(píng)論
圖片
表情
