文章 585
评论 5
浏览 226854
Sentinel vs Guava RateLimiter vs Nginx 限流:三方案压测数据全公开

Sentinel vs Guava RateLimiter vs Nginx 限流:三方案压测数据全公开

一、引言

限流是高并发系统的核心保障手段。面对突发流量,如何选择合适的限流方案?

今天我要对比三种主流限流方案:Guava RateLimiter(应用级令牌桶)、Sentinel(分布式滑动窗口)、Nginx limit_req(网关层漏桶)。通过真实压测数据,告诉你哪种方案最适合你的场景。


二、限流算法原理

2.1 三种算法对比

算法核心思想突发处理适用场景
令牌桶固定速率生成令牌,请求消耗令牌允许突发(桶内累积令牌)API 限流、削峰填谷
漏桶请求入队列,固定速率流出平滑输出,拒绝突发网关限流、流量整形
滑动窗口时间窗口内计数,动态调整精确控制,平滑过渡分布式限流、热点参数

2.2 算法图示

令牌桶算法 (Token Bucket)
┌──────────────────────────┐
│         令牌桶            │
│  ┌──────────────────┐    │
│  │ ● ● ● ● ● ● ● ● │    │
│  │    (令牌)        │    │
│  └──────────────────┘    │
│       ↓ 生成速率 = 1000/s │
│                           │
│  请求 → 取令牌 → 通过/拒绝 │
└──────────────────────────┘

漏桶算法 (Leaky Bucket)
┌──────────────────────────┐
│         请求队列          │
│  ┌──────────────────┐    │
│  │ 请求1 → 请求2 →   │    │
│  │ 请求3 → 请求4 →   │    │
│  └──────────────────┘    │
│       ↓ 流出速率 = 1000/s │
│                           │
│       队列满 → 拒绝请求    │
└──────────────────────────┘

滑动窗口算法 (Sliding Window)
┌─────────────────────────────────────────────┐
│ 时间轴: 0    1    2    3    4    5    6    │
│         ↓    ↓    ↓    ↓    ↓    ↓    ↓   │
│ 窗口:  └─────┘                              │
│        └─────┘                              │
│        └─────┘                              │
│              └─────┘                        │
│              └─────┘                        │
│                     └─────┘                 │
│ 每个窗口计数,超过阈值拒绝                   │
└─────────────────────────────────────────────┘

三、方案一:Guava RateLimiter

3.1 核心特性

  • 算法:令牌桶(Token Bucket)
  • 模式:SmoothBursty(允许突发)、SmoothWarmingUp(预热)
  • 分布式支持:不支持(单机)
  • 重启影响:计数清零,需重新累积

3.2 使用示例

import com.google.common.util.concurrent.RateLimiter;

public class RateLimiterService {
    
    // SmoothBursty 模式:允许突发
    private final RateLimiter burstyLimiter = RateLimiter.create(1000.0);
    
    // SmoothWarmingUp 模式:预热期 3 秒
    private final RateLimiter warmingUpLimiter = RateLimiter.create(1000.0, 3, TimeUnit.SECONDS);
    
    public boolean tryAcquire() {
        // 非阻塞:尝试获取令牌,立即返回结果
        return burstyLimiter.tryAcquire();
    }
    
    public boolean tryAcquireWithTimeout(long timeout, TimeUnit unit) {
        // 带超时的非阻塞获取
        return burstyLimiter.tryAcquire(timeout, unit);
    }
    
    public void acquire() {
        // 阻塞:等待直到获取到令牌
        burstyLimiter.acquire();
    }
}

3.3 在 Controller 中使用

@RestController
@RequestMapping("/api")
public class RateLimiterController {
    
    @Autowired
    private RateLimiterService rateLimiterService;
    
    @GetMapping("/resource")
    public Result<String> getResource() {
        if (!rateLimiterService.tryAcquire()) {
            return Result.fail(429, "请求过于频繁,请稍后重试");
        }
        
        // 业务逻辑
        return Result.success("success");
    }
}

3.4 优缺点

优点缺点
轻量级,零依赖不支持分布式
API 简洁,易于集成进程重启后计数清零
支持突发和预热模式无可视化控制台

四、方案二:Sentinel

4.1 核心特性

  • 算法:滑动窗口(Sliding Window)
  • 模式:单机限流、集群限流(需 Token Server)
  • 分布式支持:支持(需部署集群)
  • 额外功能:热点参数限流、熔断降级、控制台可视化

4.2 引入依赖

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-core</artifactId>
    <version>1.8.6</version>
</dependency>

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-web-servlet</artifactId>
    <version>1.8.6</version>
</dependency>

<!-- 控制台 -->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-transport-simple-http</artifactId>
    <version>1.8.6</version>
</dependency>

4.3 基本配置

@Configuration
public class SentinelConfig {
    
    @Bean
    public FilterRegistrationBean<SentinelFilter> sentinelFilter() {
        FilterRegistrationBean<SentinelFilter> bean = new FilterRegistrationBean<>();
        bean.setFilter(new SentinelFilter());
        bean.addUrlPatterns("/*");
        return bean;
    }
    
    @PostConstruct
    public void initRules() {
        // 限流规则:QPS 阈值 1000
        List<FlowRule> rules = new ArrayList<>();
        FlowRule rule = new FlowRule();
        rule.setResource("api-resource");
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule.setCount(1000);
        rule.setStrategy(RuleConstant.STRATEGY_DIRECT);
        rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
        rules.add(rule);
        
        FlowRuleManager.loadRules(rules);
    }
}

4.4 在代码中使用

@RestController
@RequestMapping("/api")
public class SentinelController {
    
    @GetMapping("/resource")
    public Result<String> getResource() {
        // 资源名
        String resource = "api-resource";
        
        // 尝试获取权限
        Entry entry = null;
        try {
            entry = SphU.entry(resource);
            // 业务逻辑
            return Result.success("success");
        } catch (BlockException e) {
            return Result.fail(429, "请求过于频繁,请稍后重试");
        } finally {
            if (entry != null) {
                entry.exit();
            }
        }
    }
}

4.5 热点参数限流

@PostConstruct
public void initHotspotRules() {
    List<ParamFlowRule> rules = new ArrayList<>();
    ParamFlowRule rule = new ParamFlowRule();
    rule.setResource("api-resource");
    rule.setParamIdx(0);  // 第一个参数
    rule.setCount(100);   // 每个参数值 QPS 阈值
    rules.add(rule);
    
    ParamFlowRuleManager.loadRules(rules);
}

4.6 集群限流配置

// 客户端配置
@Configuration
public class SentinelClusterConfig {
    
    @PostConstruct
    public void initClusterClient() {
        ClusterClientConfig config = new ClusterClientConfig();
        config.setRequestTimeout(2000);
        ClusterClientConfigManager.applyNewConfig(config);
        
        // 配置 Token Server 地址
        ClusterClientAssignManager.assignAddress("10.0.0.100:8719");
    }
}

⚠️ 注意:集群限流需要部署独立的 Token Server,增加运维复杂度。

4.7 优缺点

优点缺点
强大的控制台可视化引入额外依赖和复杂度
支持集群限流集群模式需部署 Token Server
热点参数限流学习成本较高
熔断降级一体化-

五、方案三:Nginx limit_req

5.1 核心特性

  • 算法:漏桶(Leaky Bucket)
  • 模式:网关层限流
  • 分布式支持:天然支持(多 Nginx 实例需共享状态)
  • 代码侵入:零侵入

5.2 基本配置

http {
    # 定义限流区域
    limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1000r/s;
    
    server {
        listen 80;
        
        location /api/ {
            # 应用限流规则
            limit_req zone=mylimit;
            
            # 转发到后端
            proxy_pass http://backend;
        }
    }
}

5.3 关键参数详解

limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1000r/s;

# 参数说明:
# $binary_remote_addr:客户端 IP(二进制格式,节省内存)
# zone=mylimit:10m:区域名称,内存大小 10MB
# rate=1000r/s:限流速率 1000 请求/秒
location /api/ {
    # burst=200:允许 200 个突发请求排队
    # nodelay:突发请求立即处理,不排队等待
    limit_req zone=mylimit burst=200 nodelay;
}
参数作用
burst=N允许 N 个请求在队列中等待
nodelay队列中的请求立即处理,不延迟
zone指定限流区域

5.4 自定义限流响应

limit_req_status 429;

location /api/ {
    limit_req zone=mylimit;
    
    error_page 429 = @rate_limit;
}

location @rate_limit {
    return 429 '{"code": 429, "message": "请求过于频繁"}';
}

5.5 基于路径的细粒度限流

http {
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=1000r/s;
    limit_req_zone $binary_remote_addr zone=login_limit:10m rate=100r/s;
    limit_req_zone $binary_remote_addr zone=search_limit:10m rate=500r/s;
    
    server {
        location /api/ {
            limit_req zone=api_limit;
            proxy_pass http://backend;
        }
        
        location /api/login {
            limit_req zone=login_limit;
            proxy_pass http://backend;
        }
        
        location /api/search {
            limit_req zone=search_limit;
            proxy_pass http://backend;
        }
    }
}

5.6 分布式限流方案

⚠️ 注意:标准 Nginx 的 limit_req 是单机的,多实例部署时每个 Nginx 独立计数,无法实现全局限流。

方案一:使用 OpenResty + Redis

OpenResty 提供了 resty.limit.req 模块,可以基于 Redis 实现分布式限流:

-- lua/rate_limit.lua
local limit_req = require "resty.limit.req"

-- 使用 Redis 作为共享存储
local lim, err = limit_req.new("my_limit_store", 1000, 200)
if not lim then
    ngx.log(ngx.ERR, "failed to instantiate limit.req: ", err)
    return ngx.exit(500)
end

local key = ngx.var.binary_remote_addr
local delay, err = lim:incoming(key, true)
if not delay then
    if err == "rejected" then
        return ngx.exit(429)
    end
    ngx.log(ngx.ERR, "failed to limit req: ", err)
    return ngx.exit(500)
end

if delay >= 0.001 then
    ngx.sleep(delay)
end
http {
    lua_shared_dict my_limit_store 10m;
    
    server {
        location /api/ {
            access_by_lua_file lua/rate_limit.lua;
            proxy_pass http://backend;
        }
    }
}

方案二:使用 Redis + Lua 脚本

另一种方式是直接在 Nginx 中调用 Redis Lua 脚本实现分布式限流,详见第八节。

5.7 优缺点

优点缺点
零代码侵入细粒度控制弱
性能极高不支持热点参数限流
配置灵活分布式限流需额外模块
网关层统一管理无应用级监控

六、压测数据全公开

6.1 测试环境

项目配置
硬件4 核 CPU / 8GB 内存
JMeter500 并发线程
目标 QPS1000
测试时长60 秒
后端服务Spring Boot 应用

6.2 独立方案压测结果

指标Guava RateLimiterSentinelNginx
实际 QPS998995992
P99 响应时间2ms5ms1ms
P999 响应时间5ms12ms3ms
CPU 使用率30%45%15%
内存占用+50MB+150MB+20MB
拒绝率0.2%0.5%0.8%

6.3 分层限流压测结果

架构:JMeter → Nginx → Spring Boot (Sentinel)

测试结果:
┌─────────────────────────────────────────────────────┐
│ 指标                     │ 值                      │
├─────────────────────────────────────────────────────┤
│ 实际 QPS                  │ 996                     │
│ P99 响应时间               │ 3ms                     │
│ P999 响应时间              │ 8ms                     │
│ Nginx CPU 使用率          │ 10%                     │
│ 应用 CPU 使用率            │ 35%                     │
│ 拒绝率                    │ 0.4%                    │
└─────────────────────────────────────────────────────┘

流量分布:
┌─────────────────────────────────────────────────────┐
│ 入口流量                   │ 500 并发                │
│ Nginx 转发到应用           │ 996 QPS                 │
│ Nginx 直接拒绝             │ 4 QPS (0.4%)            │
│ 应用层 Sentinel 拒绝       │ 0                       │
└─────────────────────────────────────────────────────┘

6.4 突发场景测试

测试场景:前 10 秒 500 并发,第 11 秒突增至 1000 并发

结果对比:
┌─────────────────────────────────────────────────────┐
│ 方案                     │ 突发处理方式             │
├─────────────────────────────────────────────────────┤
│ Guava RateLimiter         │ 允许突发(令牌桶累积)   │
│                           │ 峰值 QPS: 1500          │
├─────────────────────────────────────────────────────┤
│ Sentinel                  │ 平滑过渡(滑动窗口)     │
│                           │ 峰值 QPS: 1100          │
├─────────────────────────────────────────────────────┤
│ Nginx                     │ 拒绝超出部分            │
│                           │ 峰值 QPS: 1000          │
│                           │ 拒绝率: 33%             │
└─────────────────────────────────────────────────────┘

七、方案对比总结

7.1 核心对比

维度Guava RateLimiterSentinelNginx
算法令牌桶滑动窗口漏桶
分布式✅(需集群)⚠️(需额外模块)
可视化
热点参数
代码侵入
运维复杂度
性能开销极低
重启影响计数清零规则持久化

7.2 推荐决策树

┌──────────────────────┐
                    │    限流需求分析       │
                    └──────────┬───────────┘
                               │
              ┌────────────────┼────────────────┐
              │                │                │
    ┌─────────▼─────────┐ ┌────▼─────┐ ┌───────▼───────┐
    │ 单机应用/简单场景   │ │ 分布式系统 │ │ 微服务架构    │
    │ 无复杂需求         │ │ 需要统一控制 │ │ 需要细粒度   │
    └─────────┬─────────┘ └────┬─────┘ └───────┬───────┘
              │                │                │
    ┌─────────▼─────────┐ ┌────▼─────┐ ┌───────▼───────┐
    │  Guava RateLimiter │ │  Sentinel │ │  Nginx +     │
    │                   │ │  (集群模式)│ │  Sentinel    │
    └───────────────────┘ └──────────┘ └───────────────┘

7.3 中小团队最佳实践

架构:
┌─────────────────────────────────────────────────────┐
│                    JMeter / 用户                    │
│                          ↓                          │
│                      Nginx 网关                     │
│               ┌─────────┴─────────┐                 │
│               ↓                   ↓                 │
│          全局限流            路径级限流              │
│          rate=5000r/s       rate=1000r/s            │
│               └─────────┬─────────┘                 │
│                         ↓                           │
│                  Spring Boot 应用                   │
│               ┌─────────┴─────────┐                 │
│               ↓                   ↓                 │
│          Sentinel              Sentinel              │
│          核心接口限流           热点参数限流          │
│          rate=500r/s           rate=50r/s            │
└─────────────────────────────────────────────────────┘

配置策略:
┌─────────────────────────────────────────────────────┐
│ Nginx 层:粗粒度限流(全局 + 路径级)               │
│ Sentinel 层:细粒度限流(接口级 + 参数级)          │
│ Guava 层:局部限流(内部方法级)                    │
└─────────────────────────────────────────────────────┘

八、额外方案:Redis + Lua

8.1 为什么需要这个方案

当你需要分布式限流但又不想引入 Sentinel 的复杂度时,Redis + Lua 是一个轻量的选择。

8.2 实现原理

-- 限流脚本
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])

local current = redis.call('get', key)

if current and tonumber(current) >= limit then
    return 0
end

current = redis.call('incr', key)

if tonumber(current) == 1 then
    redis.call('expire', key, window)
end

return 1

8.3 Java 调用示例

@Service
public class RedisRateLimiter {
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    private static final String LUA_SCRIPT = 
        "local key = KEYS[1] local limit = tonumber(ARGV[1]) " +
        "local window = tonumber(ARGV[2]) local current = redis.call('get', key) " +
        "if current and tonumber(current) >= limit then return 0 end " +
        "current = redis.call('incr', key) if tonumber(current) == 1 then " +
        "redis.call('expire', key, window) end return 1";
    
    private final DefaultRedisScript<Long> script = new DefaultRedisScript<>();
    
    @PostConstruct
    public void init() {
        script.setScriptText(LUA_SCRIPT);
        script.setResultType(Long.class);
    }
    
    public boolean tryAcquire(String key, int limit, int windowSeconds) {
        Long result = redisTemplate.execute(
            script,
            Collections.singletonList(key),
            String.valueOf(limit),
            String.valueOf(windowSeconds)
        );
        return result != null && result == 1;
    }
}

8.4 优缺点

优点缺点
轻量级,无额外依赖无可视化控制台
天然分布式需要自己实现熔断降级
灵活可控需要维护 Lua 脚本

九、总结

9.1 方案选择指南

场景推荐方案理由
单机应用Guava RateLimiter轻量、简单
分布式系统Sentinel 集群模式可视化、功能全面
微服务架构Nginx + Sentinel分层防护、各司其职
不想引入重依赖Redis + Lua轻量、分布式

9.2 中小团队最佳实践

推荐组合:Nginx 网关层 + Sentinel 核心接口精控

理由:
1. Nginx 承担大部分限流压力,性能极高
2. Sentinel 负责核心接口的精细化控制
3. 运维成本可控,不需要部署复杂的集群
4. 可以逐步升级:先 Nginx,再 Sentinel,最后考虑集群

9.3 核心原则

  1. 分层限流:网关层粗粒度,应用层细粒度
  2. 算法选择:令牌桶适合允许突发,漏桶适合平滑流量,滑动窗口适合精确控制
  3. 监控优先:限流必须有监控和告警
  4. 优雅降级:拒绝请求时返回友好提示

💡 互动话题:你在项目中使用哪种限流方案?有没有遇到过什么坑?欢迎在评论区分享你的经验!


标题:Sentinel vs Guava RateLimiter vs Nginx 限流:三方案压测数据全公开
作者:jiangyi
地址:http://jiangyi.space/articles/2026/07/14/1783846415110.html
公众号:服务端技术精选

服务端开发博客:后端架构、高并发、性能优化与微服务实战教程

取消