上篇讲了
负载均衡------它解决的是"请求怎么分到多台服务器"。但微服务时代,问题升级了:几十个服务各管一摊,请求进来先找谁?谁有资格进?超载了怎么办?这就需要API网关 。如果把后端服务比作一栋大楼里的各个科室,那网关就是大楼门口的门卫------所有进出都得经过它,由它来决定放行、拦截还是引导到对应的科室。
原文地址
为什么需要网关
单体架构不需要网关
以前一个应用打成一个包,部署在一台服务器上,用户请求直接打到这个服务上,简单直接。没有中间层,也没有中间层的烦恼。
微服务架构的痛点
拆成几十个微服务后,事情变复杂了:
md
前端要调的接口:
/api/user/info → 用户服务(10.0.0.1:8080)
/api/order/list → 订单服务(10.0.0.2:8080)
/api/product/detail → 商品服务(10.0.0.3:8080)
/api/pay/create → 支付服务(10.0.0.4:8080)
...
前端需要知道每个服务的IP地址和端口。一旦某个服务扩容、缩容或迁移,所有前端都要跟着改代码。
而且还有一堆横切问题:鉴权逻辑要在每个服务写一遍、限流规则分散在各处难以统一调整、日志监控没有统一入口、协议格式各不相同...
网关就是来解决这些问题的------它是所有请求的统一入口,也是整个系统的第一道防线。
但也要说清楚:网关不是免费的午餐。它带来了解决上述问题的能力,同时也引入了新的代价:
- 额外的一跳延迟:每个请求多经过一层,通常增加1-5ms
- 单点风险:所有流量都经过这里,挂了就是全站挂
- 复杂度增加:多了一层就要多运维、多部署、多排查问题
所以要不要上网关,取决于你的系统规模。两三个服务直接调就行;到了十几个甚至几十个服务,网关基本是必选项。
核心职责一:路由转发
门卫的第一项工作:认识路
门卫得知道财务科在3楼、人事科在5楼、前台在1楼。网关也一样------根据请求特征把流量分发到对应的后端服务。
路由匹配方式不止一种:
| 匹配方式 | 示例 | 适用场景 |
|---|---|---|
| 路径匹配 | /api/users/* → 用户服务 |
最常用,RESTful风格 |
| Header匹配 | X-Service: order → 订单服务 |
灰度发布、A/B测试 |
| Query参数 | ?version=v2 → 新版本服务 |
功能开关、灰度 |
| 权重分配 | A服务80% / B服务20% | 金丝雀发布 |
为什么不在前端直连后端?
直连看起来省事,但问题不少:内部服务IP暴露给外部安全风险高、无法统一管控入口、服务变更时前端必须配合改代码甚至发版。
通过网关做一层抽象,后端服务扩容缩容迁移,前端完全无感知。代价是多1-5ms延迟,这个 trade-off 在绝大多数场景下都很划算。
配合服务发现效果更好:
硬编码IP的方式已经过时了。现在主流做法是网关 + 服务注册中心(如Nacos、Consul、Eureka)配合:
md
服务启动 → 注册到注册中心(我是订单服务,IP是10.0.0.2:8080)
网关需要路由 → 从注册中心拉取服务列表 → 动态转发
服务下线 → 从注册中心注销 → 网关自动感知不再转发
这样服务上下线对网关也是透明的,不需要手动改配置。
核心职责二:统一认证与授权
门卫的第二项工作:查证件
进门先看证件,没证件不让进,证件过期也不行。VIP区域还要额外检查权限等级。
典型鉴权流程:
md
请求到达网关
↓
从Header/Cookie中提取Token(JWT或SessionID)
↓
验证Token有效性(签名是否正确?是否过期?)
↓
┌── 有效 ──→ 解析出用户信息(userID、角色等)
│ ↓
│ 附加到请求Header(X-User-ID, X-User-Role)
│ ↓
│ 转发到后端业务服务
│
└── 无效 ──→ 返回 401 Unauthorized
集中鉴权 vs 各服务自己鉴权:
| 方案 | 优点 | 缺点 |
|---|---|---|
| 每个服务自己鉴权 | 解耦彻底,服务可独立部署 | 重复代码、规则改了要到处改、容易遗漏导致安全漏洞 |
| 网关统一鉴权 | 一处修改全局生效、安全策略集中管理、业务服务不用关心认证逻辑 | 网关压力增大、成为潜在瓶颈 |
实际中的最佳实践是分层处理:
- 网关层做粗粒度鉴权:Token有没有?是不是合法的?有没有基础权限?
- 业务服务层做细粒度判断:这笔订单是不是你的?你有权限删除这条评论吗?
这样既避免了在每个服务重复写认证代码,又不会把所有判断逻辑都堆到网关上。
核心职责三:限流与熔断
门卫的第三项工作:控制人流
高峰期门口要限流排队,某个科室出了状况要暂时封闭通道。网关也一样------保护后端服务不被突发流量冲垮。
限流算法怎么选:
| 算法 | 原理 | 允许突发? | 实现复杂度 | 典型场景 |
|---|---|---|---|---|
| 固定窗口 | 每秒/每分钟计数器,到期清零 | 边界处可能突发2倍 | 低 | 简单API限流 |
| 滑动窗口 | 时间窗口滑动计数,更平滑 | 基本平滑 | 中 | 精确控制 |
| 令牌桶 | 匀速产生令牌,有令牌才能通过 | 允许短时突发 | 中 | 对外接口(提升用户体验) |
| 漏桶 | 请求入桶,匀速流出 | 不允许 | 中 | 对内调用(严格保护下游) |
选型思路: 对外面向用户的接口用令牌桶------允许一定突发流量,用户体验更好(比如抢购时不能因为刚好卡在边界就被拒);对内部服务间的调用用漏桶或固定窗口------严格限制,保护下游不被压垮。
为什么在网关统一限流而不是每个服务自己限?
如果每个服务各设各的阈值,整体效果不可控------A限100 B限50 C限80,到底系统能扛多少没人说得清。在网关一处配置全局生效,后面所有服务都在保护范围内。
当然,对于超大流量的系统,光靠网关限流不够,还会配合客户端限流(验证码、滑块)、CDN边缘限流等多层手段。
熔断机制:
限流是"人太多时控制入场",熔断是"某个地方着火了赶紧封路"。
具体原理:网关持续统计每个后端服务的错误率/响应时间。当某个服务错误率超过阈值(比如50%的请求失败且持续10秒),触发熔断------后续请求不再转发到该服务,直接返回降级响应(比如返回缓存数据或默认值)。之后每隔一段时间尝试放一个请求过去探测(半开状态),如果恢复正常就关闭熔断恢复流量。
这跟家里电路保险丝一个道理:与其让故障蔓延拖垮整个系统,不如主动切断,保住大局。
核心职责四:协议转换
门卫的第四项工作:翻译
有时候来的客人说的是外语,门卫得帮忙翻译成内部能听懂的语言。
最常见的场景:HTTP ↔ gRPC
javascript
浏览器(只支持 HTTP/1.1 + JSON 文本)
↓
API网关(协议翻译层)
↓
内部微服务(HTTP/2 + gRPC + Protobuf 二进制)
为什么要这么折腾?直接都用HTTP不行吗?
行,但效率差不少。对比一下:
| 维度 | REST (HTTP/1.1 + JSON) | gRPC (HTTP/2 + Protobuf) |
|---|---|---|
| 数据格式 | 文本JSON,字段名重复 | 二进制Protobuf,紧凑编码 |
| 传输体积 | 大(JSON冗余多) | 小(通常只有JSON的1/3~1/5) |
| 解析速度 | 慢(文本解析) | 快(直接反序列化) |
| 连接模型 | 每个请求一个连接(HTTP/1.1) | 多路复用(HTTP/2) |
综合下来,内部服务间用gRPC比REST快3-5倍不是夸张。
但问题是浏览器不支持gRPC ------浏览器只能发 HTTP 请求、只能解析 JSON 文本。所以网关承担翻译工作:对外暴露标准HTTP接口,对内转成 gRPC 调用。前端开发体验不变,后端享受高性能。
除了 gRPC,网关还可能做其他协议转换:GraphQL ↔ REST、WebSocket ↔ HTTP长轮询、Thrift ↔ REST等,取决于你的技术栈。
核心职责五:日志与监控
门卫的第五项工作:登记访客
几点来的、找谁、待了多久、有没有异常情况------这些信息全记下来,方便事后追溯和分析。
作为唯一入口,网关天然适合做全链路数据采集:
- 基础信息 :
来源IP、User-Agent、请求路径、HTTP方法 - 性能数据 :
响应时间(RT)、响应状态码、响应体大小 - 关联追踪 :生成或传递
TraceID(用于在多个服务之间串联一次完整请求)
这些数据汇入ELK Stack(日志分析)、Prometheus+Grafana(监控大盘)、SkyWalking/Jaeger(分布式链路追踪)。运维人员可以在一个地方看到全局流量状况,出问题时也能快速定位是哪个环节慢了或者报错了。
BFF:Backend For Frontend
问题
一个电商商品详情页,前端可能要调6-7个后端接口才能拼出完整页面。移动端网络条件差,串行调用6次延迟叠加明显;不同端(Web/App/小程序)需要的字段还不一样------App端不需要Web端的那些营销模块数据,小程序又要比App更精简。
解决方案
BFF(Backend For Frontend)服务层位于网关之后、微服务之前,专门为特定类型的前端聚合数据:
md
Web端请求 → BFF-Web → 并行调用6个微服务 → 返回完整JSON
App端请求 → BFF-App → 同样6个服务 → 只返回必要字段(省流量)
小程序请求 → BFF-MiniApp → 同样6个服务 → 极简数据
BFF的优缺点:
| 优点 | 缺点 |
|---|---|
| 前端只调一个接口,代码简洁 | 多了一层,维护成本增加 |
| 按需裁剪返回数据,节省带宽 | BFF本身也可能成为性能瓶颈 |
| 不同端的数据差异在后端消化 | 和业务服务的边界容易模糊 |
| 后端服务变更不影响前端 | 要额外部署和运维BFF服务 |
什么时候该用BFF?
- 前端页面需要聚合3个以上后端服务的数据 → 值得考虑
- 存在Web/App/小程序等多个端且数据需求差异大 → 强烈推荐
- 前端只调一两个接口就能搞定 → 没必要,直接走网关就行
另外提一句,GraphQL可以作为BFF的替代方案------一份Schema定义按需取字段,不需要为每种前端单独写一个BFF服务。不过GraphQL也有自己的学习曲线和生态问题,这里不展开。
高可用:门卫不能倒
网关有个致命弱点:它是单点。网关挂了 = 全站不可用。普通服务挂了一个其他还能用,网关挂了所有服务都访问不了。
所以高可用对网关来说不是可选项,是必选项。
常见方案:
| 方案 | 怎么做的 | 切换速度 | 复杂度 | 适用规模 |
|---|---|---|---|---|
| 主备模式 | Keepalived + VIP漂移,一活一备 | 1-3秒 | 低 | 中小流量 |
| 双活模式 | 两个实例同时接收流量 | 无需切换 | 中 | 中大流量 |
| 集群+LB | N个实例前挂LVS/Nginx分发 | 无需切换 | 中 | 大流量 |
大多数场景主备模式就够了。超大流量(比如双十一级别)会用集群+LB的方式,前面再套一层LVS做四层分发,网关本身再做多副本。
顺便说一句,网关自身也需要限流------如果请求量超过网关的处理能力,网关自己先崩了,后面的保护措施全部失效。所以好的网关实现会在最前面给自己也设一道防线:超了就直接返回"服务繁忙",宁可拒绝也不能让自己挂掉。
一个请求的完整旅程
以打开淘宝商品详情页为例,看看一个请求经历了什么:
md
① DNS解析
www.taobao.com → 解析到网关的VIP地址
② 到达网关,依次经过:
- 提取Cookie/Token → 验证登录态
- 检查该IP/用户的请求频率 → 是否触发限流
- 匹配URL路径 /item/xxx → 路由到商品服务
- 记录日志(IP、耗时、状态码、TraceID)
③ BFF聚合(如果配了的话):
- 并行调用6个微服务:用户(收藏)、商品(详情)、价格(实时价)、
库存(是否有货)、评价(评分)、推荐(相似商品)
- 聚合成一个完整响应
④ 前端收到数据,渲染页面
用户只感觉到"嗖的一下页面出来了",背后经过了DNS→网关→BFF→多个微服务→数据库整条链路,每一步都有门卫在把关。
主流网关产品
| 产品 | 语言 | 核心特点 | QPS上限 | 适合谁 |
|---|---|---|---|---|
Nginx |
C | 反向代理之王,配置灵活,极高性能 | 10万+ | 只要LB+简单路由 |
Kong |
Lua(Nginx) | 插件丰富,社区活跃,插件市场大 | 5万+ | 微服务网关 |
APISIX |
Lua(Nginx) | 国产开源,动态配置热更新,高性能 | 10万+ | 国内团队首选 |
Spring Cloud Gateway |
Java | 响应式编程,Spring生态无缝集成 | 3万+ | Java/Spring Boot项目 |
Envoy |
C++ | 云原生设计,Service Mesh标配 | 10万+ | K8s/Istio环境 |
选型建议:
- 只要做反向代理 + 简单路由 → Nginx,够用且稳定
- 微服务网关,需要鉴权/限流/插件能力 → Kong 或 APISIX
- Spring全家桶项目 → Spring Cloud Gateway,集成最顺滑
- Kubernetes容器化环境 → Envoy,云原生路线
生产环境常见的是两层网关架构:
md
用户请求
↓
LVS/Nginx(第一层:四层分发,纯扛流量)
↓
Kong/APISIX/Spring Cloud Gateway(第二层:七层业务处理)
↓
微服务集群
第一层负责吞吐量,第二层负责业务逻辑(鉴权、限流、路由、日志)。两层各司其职,哪层出问题都不影响另一层的基本能力。
总结
回到开头那个比喻。网关就像大楼的门卫,核心就三件事:
-
统一入口 ------ 所有请求从这里进出,后端服务对前端透明。服务怎么扩、怎么迁、怎么换,前端都不用关心。
-
横向切面 ------ 鉴权、限流、日志、监控这些横切关注点在网关统一处理,业务服务专注自己的业务逻辑,不用每个人都去管"这个人有没有证件"这种事。
-
协议适配 ------ 屏蔽技术栈差异。前端用熟悉的HTTP/JSON,后端可以用gRPC、Thrift甚至任何内部协议。网关在中间做翻译,两边都舒服。
没有网关的微服务就像一栋没有前台的大楼------任何人都能直接敲任意办公室的门,既混乱又不安全。有了网关,才有秩序。
当然,门卫也不是万能的。它自身的高可用要做好,性能瓶颈要盯紧,配置变更要谨慎。毕竟,它是整栋楼的咽喉。