单机服务限流是指在单个服务器实例中,通过技术手段限制单位时间内的请求量,防止服务因过载而崩溃。它就像给单台机器装了一个 "流量阀门",只允许指定数量的请求通过。单机限流可以直接使用 Google Guava 自带的限流工具类 RateLimiter
RateLimiter
可以从非阻塞式限流,阻塞定时式限流,阻塞限流三种方式分析:
不管是什么方式,引入依赖都是第一步。
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.0.1-jre</version>
</dependency>
一、非阻塞式限流
核心特点:当请求量超过限制时,直接拒绝多余的请求,不等待。也可以这么形象的理解为,游乐园的某个项目,每小时只能容纳 100 人,满了就直接告诉后面的人 "今日名额已满",不会让他们排队。
@RestController
public class RateLimitController {
//允许每秒钟发放两个通行证
RateLimiter rateLimiter = RateLimiter.create(2.0);
//非阻塞限流
@GetMapping("/tryAcquire")
public String tryAcquire(Integer count){
if(rateLimiter.tryAcquire(count)) //当前请求每秒钟消耗count个令牌 如果count==1说明每秒钟可以通过两个请求
{
System.out.println("success ,rate is " + rateLimiter.getRate());
return "success";
}else{
System.out.println("fail ,rate is " + rateLimiter.getRate());
return "fail";
}
}
}
二、阻塞定时限流
核心特点:当请求量超过限制时,让请求等待一段时间,如果在规定时间内拿到 "名额" 就处理,否则拒绝。就好像餐厅等位,告知顾客 "最多等 10 分钟",10 分钟内有空位就安排入座,否则建议顾客离开。
@GetMapping("/tryAcquireWithTimeOut")
public String tryAcquireWithTimeOut(Integer count , Integer timeout){
if(rateLimiter.tryAcquire(count,timeout, TimeUnit.SECONDS)) //当前请求每秒钟消耗count个令牌 如果count==1说明每秒钟可以通过两个请求
{
System.out.println("success ,rate is " + rateLimiter.getRate());
return "success";
}else{
System.out.println("fail ,rate is " + rateLimiter.getRate());
return "fail";
}
}
三、阻塞式限流
核心特点:当请求量超过限制时,让多余的请求等待,直到有 "名额" 再处理,不会直接拒绝。就像超市收银台,一次只能处理 1 个顾客,如果后面有人来,就必须排队等待,直到前面的人处理完。
@GetMapping("/acquire")
public String acquire(Integer count){
rateLimiter.acquire(count);
System.out.println("success ,rate is " + rateLimiter.getRate());
return "success";
}
需要注意的是,以上的方案都不是真正的分布式限流,仅仅是局限于当前服务器上,只能保障单个实例的请求处理,无法解决分布式集群的 “全局流量管控” 问题。若要在分布式场景下实现真正的限流,必须跳出 “本地状态” 的局限,通过中间件共享全局限流状态(如用 Redis 统计全集群请求量、用分布式 Semaphore 控制全局资源),才能避免 “单机合规但集群过载” 的风险。