上一篇 下一篇 分享链接 返回 返回顶部

如何在香港服务器中实现分布式限流?Nginx+Lua vs Redis Token Bucket实测对比

发布人:Minchunlin 发布时间:2025-07-28 20:30 阅读量:866


我一直为一家跨境电商平台提供后端架构支持,主要服务器部署在香港,既为了靠近亚洲用户,也为了享受香港网络自由和较低的跨境访问延迟。随着平台在东南亚、港澳台市场逐渐起量,我们面临的一个核心挑战是:如何在高并发访问场景下,实现稳定高效的分布式限流策略?

最初,我尝试在单台应用服务器上通过内存限流(如Guava RateLimiter)控制API速率,但很快发现其在多实例部署下几乎失效。用户请求可能打到任意节点,每个节点维护的计数器彼此独立,导致整个集群层面失控。

最终,我决定在香港服务器上部署两个分布式限流方案进行实测对比:

  • 方案 A:基于 Nginx + Lua 的 Local Token Bucket
  • 方案 B:基于 Redis 的 Centralized Token Bucket

下面是我在实际部署、调优、压测中的完整过程与最终结论。

一、需求与场景定义

我们的核心限流目标包括:

  • 全局限流:整个系统每秒最多处理 X 个请求(比如 QPS 2000)。
  • 用户级限流:每个用户每秒最多发送 Y 个请求(比如每秒 10 个)。
  • IP 限流:防止恶意 IP 短时间内大量访问。

此外,还有一些非功能性要求:

  • 分布式可扩展:能在多节点、多实例部署下统一管理速率;
  • 高性能:不能引入明显延迟;
  • 容错性好:即便某个限流组件宕机,不应导致请求全部失败。

二、方案 A:Nginx + Lua 实现本地令牌桶限流

技术选型

  • OpenResty(Nginx + LuaJIT)
  • lua-resty-limit-traffic 模块(官方推荐)
  • 本地内存存储令牌桶,基于共享内存 lua_shared_dict

实现逻辑

lua_shared_dict limit_req_store 10m;

init_by_lua_block {
    local limit_req = require "resty.limit.req"
    limiter, err = limit_req.new("limit_req_store", 10, 20) -- rate=10/s, burst=20
}

access_by_lua_block {
    local key = ngx.var.binary_remote_addr
    local delay, err = limiter:incoming(key, true)
    if not delay then
        return ngx.exit(429)
    end
    if delay >= 0.001 then
        ngx.sleep(delay)
    end
}

优点

  • 毫秒级处理速度,非常轻量;
  • Nginx 层限流,防止非法请求进入业务层;
  • 易于部署在香港本地服务器,无需外部依赖。

缺点

  • 局部限流:在多台 Nginx 实例中,令牌桶是本地的;
  • 不支持全局视角的限流,用户分散到不同实例上,限流精度降低;
  • 难以实现动态调整规则。

三、方案 B:Redis Token Bucket 实现全局限流

技术选型

  • Redis 7.x(部署在香港阿里云实例,主从哨兵集群)
  • Lua 脚本实现令牌桶算法
  • 应用层或 Nginx Lua 通过 Redis 统一请求分发控制

Lua 脚本核心逻辑(Redis Token Bucket)

local key = KEYS[1]
local rate = tonumber(ARGV[1])
local capacity = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
local requested = tonumber(ARGV[4])

local last_tokens = tonumber(redis.call("GET", key .. ":tokens")) or capacity
local last_refreshed = tonumber(redis.call("GET", key .. ":ts")) or now

local delta = math.max(0, now - last_refreshed)
local new_tokens = math.min(capacity, last_tokens + (delta * rate))
if new_tokens < requested then
    return 0
else
    redis.call("SET", key .. ":tokens", new_tokens - requested)
    redis.call("SET", key .. ":ts", now)
    return 1
end

每次请求前调用 Redis 判断是否可以放行,Redis 脚本原子性保障一致性。

优点

  • 真正分布式全局限流:所有实例共享 Redis 作为速率源;
  • 精度高,动态配置灵活;
  • 支持多级策略:IP、用户、API 维度分别建桶。

缺点

  • Redis 成为瓶颈,高并发下需要部署 Redis Cluster;
  • 请求延迟较 Nginx + Lua 稍高(约 0.3~0.6ms);
  • 需处理 Redis 网络超时、单点故障等容错逻辑。

四、压测对比(香港本地服务器)

我分别在两套部署环境中进行压测(AB 工具 + K6 + 自研脚本),测试环境为香港 3 台 ECS 实例,1Gbps 内网带宽。

指标 Nginx + Lua Redis Token Bucket
平均响应时间 0.4ms 1.2ms
吞吐量(请求/s) 14,000 9,500
限流精度(±1s) 中(±10~15%) 高(±1~2%)
多实例一致性
可动态调整限速规则 否(需 reload) 是(热更新配置)
容错性 高(完全本地) 中(依赖 Redis 连通性)

五、最终方案:混合架构

最后我们采用了一个 混合限流架构:

  • Nginx + Lua 限流作为第一道防线,防止恶意流量;
  • Redis Token Bucket 实现核心业务限流,确保策略一致性;
  • 结合 Kong / APISIX 支持更灵活的限流插件;
  • 统一配置中心动态推送规则(基于 etcd + Lua);
  • Prometheus + Grafana 实时监控各层限流状态和命中率。

六、总结与建议

从这次在香港服务器上实测来看:

  • 小型部署、边缘网关、防火墙式限流推荐 Nginx + Lua;
  • 多节点大规模分布式系统推荐 Redis Token Bucket 或 Kafka 方案;
  • 限流只是手段,底层服务弹性能力同样重要。

限流没有银弹,唯有根据业务场景权衡取舍,才能实现真正稳定、高效、可扩展的流控架构。

如果你也在香港服务器环境下部署系统,并面临类似限流挑战,欢迎和我一起交流架构实践与调优策略。

目录结构
全文