1.2 选型对比:自研与云 API 网关的账
场景:上云 API 网关还是自己写
需求清单定好后,团队吵了一周。一派主张直接上云厂商的 API 网关产品,开箱即用,控制台点一点就能上线;另一派坚持用 PHP 自研,理由是我们鉴权规则定制多,还要和内部的灰度发布、配额账单打通。真正的分歧其实不是技术,是成本与控制权的权衡——我们把两边的账都算了一遍,用数据代替直觉,会议室才安静下来。
云 API 网关的优势很直接:路由、鉴权、限流、监控全是现成的,标准协议一应俱全,厂商帮你兜底可用性,接入只要半天。但我们的场景有两个硬约束:一是调用方配额要和我们 SaaS 的计费系统联动,客户欠费自动降级、充值自动恢复,云产品做不到这么贴业务;二是灰度发布要按调用方维度放量,先放开三家试点客户再全量,原生网关的灰度普遍按流量比例切,颗粒度不够细。还有第三条没说出口的顾虑:我们全部资产已经押在腾讯云上,再把入口绑死在某个具体产品上,未来迁移成本会更高——自研薄网关反而留了后路。
方案:四项对比,一笔账
我们把四个维度摆上桌面逐项打分,这张表后来在评审会上说服了所有人。注意最后一行不是打分,而是我们真实算过的钱:
| 对比项 | 云 API 网关 | PHP 自研 |
|---|---|---|
| 上线周期 | 3 天 | 3 周 |
| 鉴权定制 | 仅支持标准模式 | 完全可定(含业务账单联动) |
| 限流维度 | 服务级为主 | 可精确到每个 appkey |
| 灰度方式 | 按流量比例 | 按调用方白名单放量 |
| 运维成本 | 厂商兜底 | 全部自己背 |
| 月调用费用 | 按次收费 | 仅 CVM 与 Redis 成本 |
再算经济账。当时我们的调用量是每月 8000 万次且每三个月翻一倍。云 API 网关按调用次数收费,这意味着流量越涨账单越贵,且与业务收入不挂钩——淡季赔钱、旺季更贵。自研的固定成本是两台 CVM 加一个 Redis 实例,加上开发投入约三周人力;自研在调用量大的情况下边际成本趋近于零。团队本身就是 PHP 栈,网关代码和业务系统同一个语言同一个仓库,code review、灰度发布、告警值班全都复用现有流程,维护门槛低。这笔账算完,自研划算。
这笔账里还有一项被我们刻意放大汇报:调用量每三个月翻倍意味着按次付费的云网关账单也每三个月翻倍,而自研成本固定。把未来两年的账画成两条曲线放在评审材料里,财务出身的老板当场就站到了自研这边。算账不仅是算今天,更是给决策者画出明天的曲线。
我们也给自研划了边界,避免滑向深渊——网关只做薄层,不内置任何业务逻辑,不做可视化控制台(配置直接改契约文件加 git review),不做插件市场。这三条边界保住了三周上线的承诺。
方案补充:留一条退路
自研不等于和云产品一刀两断,我们的策略是薄网关加云底座:转发、鉴权、限流这些薄层自己写,但计算资源、数据库、日志、监控全部用云服务。云服务器 CVM 负责网关部署(文档入口 cloud.tencent.com/document/product/213),云数据库 Redis 承载令牌桶与缓存(文档入口 cloud.tencent.com/document/product/239),日志服务 CLS 接全量调用日志,云监控盯告警。这样即便自研代码出问题,底座的稳定性仍然由厂商兜底,相当于站在云的肩膀上做薄层。
同时我们为最坏情况准备了降级开关:网关若因自身故障雪崩,一键把 Nginx 直连后端绕过 PHP 网关,牺牲鉴权限流保住接口可用。这个开关后来真用过一次(详见第三篇熔断那节),算是给自研留的保险丝。退路要在上线前就准备好,而不是出事再临时翻文档。
配置:先能跑,再跑得好
选型定了自研,我们没急着造轮子,而是先用 PHP 把最小可用版本跑起来:一个入口文件,一段路由表,一个鉴权函数。框架我们选了轻量路线,只引入两个关键库——guzzlehttp/psr7 处理请求响应(PSR-7 接口后面整个中间件链都靠它),predis/predis 连 Redis。核心入口骨架如下:
<?php
// public/index.php:网关唯一入口
require __DIR__ . '/../vendor/autoload.php';
$psrRequest = GuzzleHttp\Psr7\ServerRequest::fromGlobals();
$gateway = new App\Gateway();
$response = $gateway->handle($psrRequest);
(new GuzzleHttp\Psr7\ResponseEmitter($response))->emit();
中间件链按固定顺序执行,顺序本身就是经验的结晶:限流必须挡在鉴权之后(先知道是谁再谈限多少),缓存必须挡在鉴权之后(否则缓存了别人的鉴权结果就乱套了),追踪放在最前面保证每个请求都有 ID 可查:
<?php
// 网关中间件链:顺序不可随意调换
public function handle($request): ResponseInterface
{
return (new App\Pipeline())
->send($request)
->through([
App\Middleware\TraceMiddleware::class, // 生成请求 ID
App\Middleware\AuthMiddleware::class, // 鉴权
App\Middleware\RateLimitMiddleware::class, // 限流
App\Middleware\GrayRouteMiddleware::class, // 灰度路由
App\Middleware\CacheMiddleware::class, // 响应缓存
])
->then(App\Middleware\ProxyMiddleware::class) // 转发到后端
->execute();
}
踩坑:高估了自己,低估了运维
自研第一个月我们就被现实教育了。最痛的一点:云网关的监控告警是白送的,自研的一切都得自己造,QPS 曲线、错误率告警、调用方排行榜、慢接口统计,全是活。有一阵子我们戏称自己不是在写网关,是在写一个简易 APM。如果团队能力偏运维薄弱,或调用量小(月调用量千万级以下),建议不要自研,直接选云 API 网关,把人力留给业务——能用钱买来的稳定性,别用命去拼。
另一个隐性成本是升级。云网关厂商帮你滚动升级、修安全漏洞;自研的网关每个依赖的版本都要自己盯,PHP 大版本升级、composer 依赖安全公告,全靠流程兜底。我们的做法是把依赖版本检查加进每周例会清单,雷打不动。还有心理建设:选自研前先问自己一句——凌晨三点爬起来改 PHP 路由代码,行不行。如果全组只有一个能干的人,答案是不行。
第三个坑是过度自信的复用。我们一开始觉得网关代码简单,没写单元测试,结果限流算法的对错全靠线上试,第一次灰度就因为令牌桶计算偏差误伤了一家大客户。从那以后网关核心模块强制覆盖测试,压测环境跑通才允许上线。
实际执行时我们把三周拆成三个里程碑:第一周中间件链跑通核心转发与鉴权;第二周限流、缓存、灰度全量补齐;第三周监控、告警、压测联调。每周末做一次 demo 演示给非技术同事看,逼自己把可工作的软件摆在最前。这个节奏比计划晚了四天,主要是监控部分超预期,但总体可控。
变更保护上再补一条经验:所有限流、熔断、鉴权策略的变更,必须在压测环境灌一轮真实流量回放再上线。我们用 CLS 的历史调用日志抽样子集做回放集,每次改网关先跑十分钟回放比对响应,后来误伤救回过好几次。回放集的建设成本只有一天,却把网关变更的事故率压到接近零,这是自研团队最容易忽视的高性价比投入。
顺带分享一个团队心态的转变。自研之前我们觉得云产品贵,自研之后才明白贵的是人时:三周开发、持续维护、告警值班、版本升级,这些隐性成本从来不在最初的预算表上。后来我们向管理层汇报时把人时折算成了工资,自研第一年的总账其实高于云网关,是第二年开始才反超的,理由是流量持续增长摊薄了边际成本。各位做汇报时也可以用同样的算法,数据比口号有说服力。
小结
自研与否的核心是两笔账:经济账(调用量决定边际成本)与控制权账(定制化需求决定替代难度)。月调用量大、鉴权限流需要和业务系统深度联动、团队是 PHP 栈——满足这三条自研划算,否则选云产品,别为了省小钱给自己找大麻烦。自研的代价是运维与监控全自己背,开工前要有心理准备。预算上先留出百分之三十的缓冲,我们最终花在监控与高可用上的精力远超核心转发本身,这一点所有想自研的团队有必要提前知道。
还有一个视角值得补充:自研网关的隐性收益是团队成长。这套系统跑下来,团队里每个人都对限流、缓存与可观测性有了实战级理解,后来业务线出性能问题,网关出来的人个个都能顶上。这笔能力账不会出现在任何预算表上,但它真实地改变了团队的战斗力。招人时这也成了我们的招牌——履历上写着自研过整套 API 网关,面试官会多聊半小时。
