接入层设计(一):API网关选型与限流策略

各位同学,今天我们聊聊接入层。说白了,就是用户请求到达咱们人脸识别服务之前,最先碰到的那个“守门员”。

我见过不少团队,一上来就怼业务逻辑,网关随便搭一个。结果呢?线上流量一冲,服务直接雪崩。嗯,这个坑我踩过,所以今天咱们把网关选型、限流策略、路由负载均衡这三件事掰开揉碎讲清楚。

为什么接入层这么重要?

人脸识别API有个特点:单次请求计算重、耗时长,而且经常要跟第三方(比如公安库、活体检测SDK)打交道。如果接入层不做防护,恶意刷接口、突发流量、慢连接耗尽连接池……随便一个都能让后端集群瘫痪。

接入层要干三件事:

  • 流量管控——谁可以来,来多少,超了怎么办
  • 请求分发——把请求扔给最合适的后端节点
  • 协议转换——统一入口,后端爱用啥协议用啥

你想想看,这三件事要是没做好,后面再牛的算法模型也白搭。

API网关选型:Kong vs Nginx

市面上网关很多,但高并发场景下,我主要看两个:Kong 和 Nginx。不是其他不好,是这两个生态最成熟,坑最少。

对比维度 Kong Nginx
核心定位 API网关(带管理面) 反向代理服务器
扩展方式 插件(Lua/Go/JS) 模块(C/Lua)
管理API 自带RESTful Admin API 需自行开发
性能 略低于Nginx(约5-10%) 极致,单机10万+连接
适用场景 多团队、多服务、需动态路由 纯反向代理、静态路由

我个人习惯:如果团队规模大、服务多、需要频繁调整路由策略,选Kong。它自带限流、鉴权、日志插件,省去很多开发时间。但如果只是做流量入口,后端就两三个服务,Nginx完全够用,而且性能更极致。

我的经验:曾经有个项目,用Nginx做网关,后来业务膨胀到20多个微服务,每次加路由都要改配置reload,烦死了。后来切到Kong,用Admin API动态管理,爽多了。所以选型要看未来半年的规模。

限流策略:令牌桶 vs 漏桶

限流是接入层的核心。人脸识别API尤其需要——一次识别可能要几百毫秒,如果并发冲上来,数据库连接池、GPU资源全都会被打满。

两种经典算法:

令牌桶算法

原理很简单:一个桶,每秒往里面放固定数量的令牌。请求来了,拿一个令牌就走。桶里令牌用完了,请求就排队或者直接拒绝。

优点:允许突发流量。比如桶容量100,每秒放10个令牌,那瞬间可以处理100个请求,然后慢慢消化。

缺点:突发可能把后端打懵。我遇到过,桶设太大,瞬间流量把数据库连接池撑爆了。

漏桶算法

这个更“死板”。桶底下有个洞,每秒漏固定数量的水(请求)。不管上面倒多少水,下面流出的速度是恒定的。

优点:输出流量平滑,后端永远感受不到突发。

缺点:无法应对突发,用户体验差。比如用户瞬间上传10张照片,可能前9张都要排队。

特性 令牌桶 漏桶
突发处理 支持(桶容量内) 不支持
输出平滑度 一般 平滑
实现复杂度 中等 简单
适用场景 大部分业务API 数据库写入、第三方调用
避坑指南:我曾经在一个人脸比对服务上用了漏桶,结果用户反馈“上传照片后要等5秒才有结果”。查了半天,发现漏桶把突发请求全排队了。后来换成令牌桶,桶容量设为QPS的2倍,问题解决。所以,人脸识别这种有突发特征的场景,优先令牌桶。

限流实现:Nginx + Lua 示例

用Nginx做限流,最常用的就是 limit_req 模块(漏桶)和 lua-resty-limit-traffic(令牌桶)。

下面是一个令牌桶的Lua实现片段:

-- 令牌桶限流,基于lua-resty-limit-traffic
local limit = require "resty.limit.count"

-- 创建限流器:每秒10个令牌,桶容量20
local lim, err = limit.new("my_limit_store", 10, 20)
if not lim then
    ngx.log(ngx.ERR, "failed to instantiate: ", err)
    return ngx.exit(500)
end

-- 检查当前请求
local delay, err = lim:incoming("api:v1:face:compare", true)
if not delay then
    if err == "rejected" then
        return ngx.exit(429)  -- 请求过多
    end
    ngx.log(ngx.ERR, "failed to limit: ", err)
    return ngx.exit(500)
end

if delay >= 0.001 then
    -- 需要等待,但这里我们选择直接拒绝
    -- 实际可以ngx.sleep(delay)做排队
    return ngx.exit(429)
end

这段代码我直接用在生产环境过。注意那个 true 参数,表示“如果超限就拒绝,不排队”。人脸识别场景,排队意义不大,用户等不起。

请求路由与负载均衡

网关拿到请求后,要决定扔给谁。这里有两个关键点:

  • 路由规则:根据URL、Header、甚至请求体内容做分发
  • 负载均衡:在同组后端里选一个最合适的节点

路由策略

人脸识别API常见的路由方式:

  • 按版本路由:/v1/face/compare → 旧模型集群;/v2/face/compare → 新模型集群
  • 按渠道路由:根据 X-App-Id 头部分发到不同资源池
  • 按请求类型路由:1:N识别走专用集群,1:1比对走通用集群

负载均衡算法

算法 特点 人脸识别场景适用性
轮询 简单,不考虑后端负载 低,后端性能差异大时容易倾斜
最少连接 选当前连接数最少的节点 中等,适合长连接场景
一致性哈希 相同请求落到相同节点 高,人脸特征缓存命中率提升明显
加权响应时间 根据历史响应时间动态调整权重 高,能自动避开慢节点

我建议:人脸识别场景,优先用一致性哈希。为什么呢?因为人脸特征提取结果可以缓存。同一个用户反复请求,落到同一台机器,缓存命中率能从20%提升到70%以上。这个优化,比加机器还管用。

整体架构图

下面这张图,把今天讲的内容串起来了。你可以看到请求从客户端进来,经过网关的限流、路由、负载均衡,最后到达后端服务集群。

人脸识别API接入层架构 客户端请求 API网关(Kong / Nginx) 限流 · 路由 · 负载均衡 · 协议转换 限流策略 请求路由 负载均衡 令牌桶 / 漏桶 版本 · 渠道 · 请求类型 一致性哈希 · 最少连接 人脸检测集群 特征提取集群 比对检索集群 一致性哈希路由确保相同用户落到相同节点,提升特征缓存命中率 令牌桶限流允许突发,但桶容量建议设为QPS的2倍以内
核心要点:接入层不是简单的“转发”,而是整个系统的第一道防线。网关选型看团队规模,限流策略看业务特征,路由和负载均衡要结合缓存命中率来设计。这三件事做好了,后端服务才能安心处理业务逻辑。