API网关架构设计:部署模式与高可用

做API网关,我最怕听到的一句话是:「网关挂了,所有服务都访问不了。」

这不是危言耸听。网关作为流量的统一入口,一旦宕机,整个系统就瘫痪了。所以我今天想跟你聊聊,网关到底该怎么部署,才能既扛得住流量,又经得起故障。

部署模式的三种选择

先说说部署模式。不同规模的系统,选择完全不同。我见过不少团队,上来就搞多活集群,结果运维成本比业务系统还高。其实,合适才是最好的。

部署模式 适用场景 优势 劣势
单机部署 开发测试、小流量场景 部署简单、调试方便 单点故障、无法扩容
集群部署 生产环境、中等规模 横向扩展、故障转移 需要负载均衡、配置复杂
多活部署 超大流量、跨机房场景 就近接入、极致可用性 成本高、数据一致性难保证

1. 单机部署

说白了就是一台机器跑网关。我最初做第一个网关项目时,就这么干的。调路由、配限流、测插件,都在一台机器上搞。方便是真方便,但上线后提心吊胆。机器一挂,万事皆休。

注意:生产环境不要用单机。所谓「开发一时爽,上线火葬场」,说的就是这个。

2. 集群部署

这是生产环境最通用的模式。前面挂一个负载均衡器(Nginx、HAProxy或云厂商的SLB),后面挂多个网关实例。流量分散到各个节点。

我习惯用Nginx做负载均衡,配置很简单:

upstream gateway_cluster {
    server 10.0.1.1:8080 weight=5;
    server 10.0.1.2:8080 weight=5;
    server 10.0.1.3:8080 weight=5;
}

server {
    listen 80;
    location / {
        proxy_pass http://gateway_cluster;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

这里有个小细节:健康检查。不加健康检查,负载均衡器会把请求打到已宕机的节点上。我见过一个项目,因为忘了配健康检查,高峰期网关某节点挂了,结果一半的请求超时。嗯,后来加了定期探测,才算安稳。

3. 多活部署

这个就高级了。比如阿里云的API网关,就是多活架构。说白了,就是在不同机房甚至不同城市都部署一套网关集群,通过DNS智能解析或全局负载均衡(GSLB)把流量分发到最近的机房。

多活的好处很明显:一个机房光缆被挖断了,流量自动切到另一个机房。但代价也不小——你得解决数据同步的问题。比如同一个用户的限流计数,在多个机房之间如何保持一致?

我建议:初期别碰多活。等你流量真到了那个量级,再考虑。

高可用的三个关键

部署模式只是第一步。真正让网关高可用的,是下面三个机制。我在项目中踩过不少坑,把它们总结出来。

1. 健康检查与自动摘除

网关实例如果挂了,负载均衡器要能自动把它踢出集群。这是基础中的基础。

通常的做法是暴露一个/health端点,返回网关自身及依赖组件的状态:

GET /health
{
    "status": "UP",
    "components": {
        "redis": "UP",
        "database": "UP",
        "upstream_services": {
            "user-service": "UP",
            "order-service": "DEGRADED"
        }
    }
}

健康检查的频率很重要。我习惯设5秒一次,连续3次失败就摘除。太频繁会浪费资源,太慢又延误故障切换。

2. 无状态设计

为什么要强调无状态?因为只有无状态,才能任意水平扩展。网关实例挂了,换个新的顶上,不影响业务。

有状态的例子:把用户的校验结果缓存在本地内存里。这样节点挂了,缓存就丢了。换一个节点,又是全量校验,压力全在认证服务上。

一个小技巧:把状态外移到共享存储(Redis、Memcached)。这样每个网关节点都是平等的,扩缩容随意。

3. 优雅关闭与热加载

网关更新配置或发布版本时,不能「一刀切」。直接关掉进程,正在处理的请求就全断了。

优雅关闭的流程:先停止接受新请求,等待正在处理的请求完成(或超时),再关闭连接。我在Nginx里用过graceful shutdown,在Kubernetes里用preStop hook实现:

lifecycle:
  preStop:
    exec:
      command: ["/bin/sh", "-c", "sleep 5 && /usr/sbin/nginx -s quit"]

等待5秒是为了给K8s Service一点时间,把Pod从endpoints里摘除。否则边关闭边还往里发请求,就会报错。

实战中的选型建议

讲这么多,你可能会问:我该选哪种?

我给你三个原则:

  • 初创期(日均PV < 100万):单机+冷备。一台主网关,一台备用机。主挂了手动切。省钱省心。
  • 成长期(日均PV 100万-1000万):集群部署,3-5个节点。加负载均衡和健康检查。这个阶段我踩过最大的坑是:健康检查只查网关自身,没查上游依赖。结果网关活着,但Redis挂了,所有限流都失效。
  • 成熟期(日均PV > 1000万):考虑多活。这时候你已经有专门的运维团队了,可以搞。
核心观点:高可用不是靠「多副本」就能搞定的。你还需要无状态设计、健康检查、优雅关闭这三个支柱。少一个,都可能出大问题。

架构总览

下面这张图,把今天讲的核心内容串起来了。你可以看到流量怎么进来,负载均衡怎么分发,网关集群怎么处理,以及高可用机制在哪里起作用。

API网关架构设计总览 API网关架构设计总览 客户端请求 DNS / 全局负载均衡 负载均衡器 (SLB/Nginx/HAProxy) 网关实例 1 网关实例 2 网关实例 3 路由 · 认证 · 限流 路由 · 认证 · 限流 路由 · 认证 · 限流 共享存储 (Redis/DB) 无状态设计 健康检查 & 自动摘除 优雅关闭 & 热加载

总结一下今天的重点:

  • 部署模式看量级,别跟风搞多活。
  • 高可用的三大支柱:无状态、健康检查、优雅关闭。
  • 健康检查要检得全,别只看网关自己。
  • 无状态才能自由扩缩,有状态就往共享存储上搬。

我曾经在某次大促前,因为健康检查没配全,导致网关集群有一半节点虽然在运行,但连不上Redis,限流全部失效。那晚我守在监控屏前,手抖着切流。从那以后,我所有的健康检查都至少包含三个维度:自身、依赖、连通性。嗯,这都是血泪教训。