网关是怎么当"门卫"的?
当前位置:点晴教程→知识管理交流
→『 技术文档交流 』
|
| 匹配方式 | 示例 | 适用场景 |
|---|---|---|
| 路径匹配 | /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)配合:
服务启动 → 注册到注册中心(我是订单服务,IP是10.0.0.2:8080)
网关需要路由 → 从注册中心拉取服务列表 → 动态转发
服务下线 → 从注册中心注销 → 网关自动感知不再转发
这样服务上下线对网关也是透明的,不需要手动改配置。
进门先看证件,没证件不让进,证件过期也不行。VIP区域还要额外检查权限等级。
典型鉴权流程:
请求到达网关
↓
从Header/Cookie中提取Token(JWT或SessionID)
↓
验证Token有效性(签名是否正确?是否过期?)
↓
┌── 有效 ──→ 解析出用户信息(userID、角色等)
│ ↓
│ 附加到请求Header(X-User-ID, X-User-Role)
│ ↓
│ 转发到后端业务服务
│
└── 无效 ──→ 返回 401 Unauthorized
集中鉴权 vs 各服务自己鉴权:
| 方案 | 优点 | 缺点 |
|---|---|---|
| 每个服务自己鉴权 | 解耦彻底,服务可独立部署 | 重复代码、规则改了要到处改、容易遗漏导致安全漏洞 |
| 网关统一鉴权 | 一处修改全局生效、安全策略集中管理、业务服务不用关心认证逻辑 | 网关压力增大、成为潜在瓶颈 |
实际中的最佳实践是分层处理:
这样既避免了在每个服务重复写认证代码,又不会把所有判断逻辑都堆到网关上。
高峰期门口要限流排队,某个科室出了状况要暂时封闭通道。网关也一样——保护后端服务不被突发流量冲垮。
限流算法怎么选:
| 算法 | 原理 | 允许突发? | 实现复杂度 | 典型场景 |
|---|---|---|---|---|
| 固定窗口 | 每秒/每分钟计数器,到期清零 | 边界处可能突发2倍 | 低 | 简单API限流 |
| 滑动窗口 | 时间窗口滑动计数,更平滑 | 基本平滑 | 中 | 精确控制 |
| 令牌桶 | 匀速产生令牌,有令牌才能通过 | 允许短时突发 | 中 | 对外接口(提升用户体验) |
| 漏桶 | 请求入桶,匀速流出 | 不允许 | 中 | 对内调用(严格保护下游) |
选型思路: 对外面向用户的接口用令牌桶——允许一定突发流量,用户体验更好(比如抢购时不能因为刚好卡在边界就被拒);对内部服务间的调用用漏桶或固定窗口——严格限制,保护下游不被压垮。
为什么在网关统一限流而不是每个服务自己限?
如果每个服务各设各的阈值,整体效果不可控——A限100 B限50 C限80,到底系统能扛多少没人说得清。在网关一处配置全局生效,后面所有服务都在保护范围内。
当然,对于超大流量的系统,光靠网关限流不够,还会配合客户端限流(验证码、滑块)、CDN边缘限流等多层手段。
熔断机制:
限流是"人太多时控制入场",熔断是"某个地方着火了赶紧封路"。
具体原理:网关持续统计每个后端服务的错误率/响应时间。当某个服务错误率超过阈值(比如50%的请求失败且持续10秒),触发熔断——后续请求不再转发到该服务,直接返回降级响应(比如返回缓存数据或默认值)。之后每隔一段时间尝试放一个请求过去探测(半开状态),如果恢复正常就关闭熔断恢复流量。
这跟家里电路保险丝一个道理:与其让故障蔓延拖垮整个系统,不如主动切断,保住大局。
有时候来的客人说的是外语,门卫得帮忙翻译成内部能听懂的语言。
最常见的场景:HTTP ↔ gRPC
浏览器(只支持 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(分布式链路追踪)。运维人员可以在一个地方看到全局流量状况,出问题时也能快速定位是哪个环节慢了或者报错了。
一个电商商品详情页,前端可能要调6-7个后端接口才能拼出完整页面。移动端网络条件差,串行调用6次延迟叠加明显;不同端(Web/App/小程序)需要的字段还不一样——App端不需要Web端的那些营销模块数据,小程序又要比App更精简。
BFF(Backend For Frontend)服务层位于网关之后、微服务之前,专门为特定类型的前端聚合数据:
Web端请求 → BFF-Web → 并行调用6个微服务 → 返回完整JSON
App端请求 → BFF-App → 同样6个服务 → 只返回必要字段(省流量)
小程序请求 → BFF-MiniApp → 同样6个服务 → 极简数据
BFF的优缺点:
| 优点 | 缺点 |
|---|---|
| 前端只调一个接口,代码简洁 | 多了一层,维护成本增加 |
| 按需裁剪返回数据,节省带宽 | BFF本身也可能成为性能瓶颈 |
| 不同端的数据差异在后端消化 | 和业务服务的边界容易模糊 |
| 后端服务变更不影响前端 | 要额外部署和运维BFF服务 |
什么时候该用BFF?
另外提一句,GraphQL可以作为BFF的替代方案——一份Schema定义按需取字段,不需要为每种前端单独写一个BFF服务。不过GraphQL也有自己的学习曲线和生态问题,这里不展开。
网关有个致命弱点:它是单点。网关挂了 = 全站不可用。普通服务挂了一个其他还能用,网关挂了所有服务都访问不了。
所以高可用对网关来说不是可选项,是必选项。
常见方案:
| 方案 | 怎么做的 | 切换速度 | 复杂度 | 适用规模 |
|---|---|---|---|---|
| 主备模式 | Keepalived + VIP漂移,一活一备 | 1-3秒 | 低 | 中小流量 |
| 双活模式 | 两个实例同时接收流量 | 无需切换 | 中 | 中大流量 |
| 集群+LB | N个实例前挂LVS/Nginx分发 | 无需切换 | 中 | 大流量 |
大多数场景主备模式就够了。超大流量(比如双十一级别)会用集群+LB的方式,前面再套一层LVS做四层分发,网关本身再做多副本。
顺便说一句,网关自身也需要限流——如果请求量超过网关的处理能力,网关自己先崩了,后面的保护措施全部失效。所以好的网关实现会在最前面给自己也设一道防线:超了就直接返回"服务繁忙",宁可拒绝也不能让自己挂掉。
以打开淘宝商品详情页为例,看看一个请求经历了什么
① 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环境 |
选型建议:
生产环境常见的是两层网关架构:
用户请求
↓
LVS/Nginx(第一层:四层分发,纯扛流量)
↓
Kong/APISIX/Spring Cloud Gateway(第二层:七层业务处理)
↓
微服务集群
第一层负责吞吐量,第二层负责业务逻辑(鉴权、限流、路由、日志)。两层各司其职,哪层出问题都不影响另一层的基本能力。
回到开头那个比喻。网关就像大楼的门卫,核心就三件事:
统一入口 —— 所有请求从这里进出,后端服务对前端透明。服务怎么扩、怎么迁、怎么换,前端都不用关心。
横向切面 —— 鉴权、限流、日志、监控这些横切关注点在网关统一处理,业务服务专注自己的业务逻辑,不用每个人都去管"这个人有没有证件"这种事。
协议适配 —— 屏蔽技术栈差异。前端用熟悉的HTTP/JSON,后端可以用gRPC、Thrift甚至任何内部协议。网关在中间做翻译,两边都舒服。
没有网关的微服务就像一栋没有前台的大楼——任何人都能直接敲任意办公室的门,既混乱又不安全。有了网关,才有秩序。
当然,门卫也不是万能的。它自身的高可用要做好,性能瓶颈要盯紧,配置变更要谨慎。毕竟,它是整栋楼的咽喉。