惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
S
Securelist
GbyAI
GbyAI
The Register - Security
The Register - Security
B
Blog
Recorded Future
Recorded Future
D
DataBreaches.Net
C
Cybersecurity and Infrastructure Security Agency CISA
A
About on SuperTechFans
C
CERT Recently Published Vulnerability Notes
T
The Blog of Author Tim Ferriss
Vercel News
Vercel News
Google DeepMind News
Google DeepMind News
S
Schneier on Security
S
SegmentFault 最新的问题
Martin Fowler
Martin Fowler
T
Tenable Blog
T
The Exploit Database - CXSecurity.com
阮一峰的网络日志
阮一峰的网络日志
宝玉的分享
宝玉的分享
AWS News Blog
AWS News Blog
L
Lohrmann on Cybersecurity
Spread Privacy
Spread Privacy
N
News | PayPal Newsroom
Engineering at Meta
Engineering at Meta
T
Tor Project blog
The Hacker News
The Hacker News
量子位
酷 壳 – CoolShell
酷 壳 – CoolShell
MongoDB | Blog
MongoDB | Blog
Cyberwarzone
Cyberwarzone
Security Archives - TechRepublic
Security Archives - TechRepublic
爱范儿
爱范儿
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
C
Cyber Attacks, Cyber Crime and Cyber Security
T
Threatpost
WordPress大学
WordPress大学
Google Online Security Blog
Google Online Security Blog
G
GRAHAM CLULEY
Google DeepMind News
Google DeepMind News
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Attack and Defense Labs
Attack and Defense Labs
N
Netflix TechBlog - Medium
SecWiki News
SecWiki News
Hacker News: Ask HN
Hacker News: Ask HN
M
MIT News - Artificial intelligence
Scott Helme
Scott Helme
Microsoft Security Blog
Microsoft Security Blog
H
Help Net Security
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org

土法炼钢兴趣小组的算法知识备份

国密算法与国密 TLS 系列索引 【系统架构设计】架构质量属性:不只是"高可用高性能" 【系统架构设计百科】告警策略:如何避免"狼来了" 【系统架构设计】CQRS:读写分离的架构哲学 【系统架构设计】空间架构:极端扩展场景的解法 【系统架构设计】微服务架构深度审视:优势、代价与适用边界 【系统架构设计】扩展性原理:水平、垂直与对角扩展 【系统架构设计】无状态设计:扩展的第一步也是最难的一步 【系统架构设计】缓存架构:从本地到分布式的多级缓存体系 【系统架构设计】管道与过滤器:Unix 哲学的架构表达 【系统架构设计】复杂性管理:架构的核心战场 【系统架构设计】消息队列架构:异步解耦的设计与陷阱 【系统架构设计】CDN 架构:全球加速的设计原理 【系统架构设计】连接池设计:被忽视的性能杀手 【系统架构设计】弹性设计模式:熔断器、舱壁与超时 【系统架构设计】高可用设计模式:冗余、故障转移与仲裁 【系统架构设计】容量规划:从拍脑袋到数据驱动 【系统架构设计】数据库扩展:分库分表的工程实践与替代方案 【系统架构设计】SLO 工程:可靠性的量化管理 【系统架构设计】性能建模:用数学思维分析系统瓶颈 【系统架构设计】混沌工程:主动验证系统的韧性 【系统架构设计】零拷贝与内存映射:数据搬运的极致优化 【系统架构设计】线程模型:从 thread-per-request 到协程 【系统架构设计】容灾架构:多活与灾备设计 【系统架构设计】数据库性能模式:索引、查询与连接管理 【系统架构设计】数据建模:从关系范式到文档模型的真实权衡 【系统架构设计】吞吐量优化:批处理、流水线与并发模型 【系统架构设计】流处理架构:从批处理到实时的范式迁移 【系统架构设计】搜索引擎架构:倒排索引之上的系统设计 【系统架构设计】时序数据架构:监控与 IoT 的存储设计 【系统架构设计】数据迁移与版本化:在线不停机的数据演进 【系统架构设计】数据湖与数据仓库:分析架构的演进路线 【系统架构设计】应用层数据一致性模式:在正确性与性能之间走钢丝 【系统架构设计】多模数据库选型:Polyglot Persistence 的工程实践 【系统架构设计】服务发现与注册:动态拓扑的基础设施 【系统架构设计】配置管理架构:从配置文件到配置中心 【系统架构设计】全链路压测:大规模系统的性能验证 【系统架构设计】幂等性设计:分布式环境下的安全重试 【系统架构设计】契约测试与 Schema 演进:服务间的信任协议 【系统架构设计】长连接与推送架构:WebSocket、SSE 与 MQTT 【系统架构设计】延迟分析:从 P50 到 P999 的全链路追踪 【系统架构设计百科】DDD 战术模式:聚合、实体与值对象 【系统架构设计百科】防腐层与开放主机服务:系统集成的 DDD 方案 【系统架构设计百科】领域事件与事件风暴:从业务到架构的桥梁 【系统架构设计百科】CQRS + Event Sourcing 完整实战:从领域建模到部署 【系统架构设计百科】DDD 与微服务:用领域模型划分服务边界 【系统架构设计】DDD 战略设计:限界上下文与上下文映射 【系统架构设计百科】认证架构:从 Session 到 JWT 到 OIDC 【系统架构设计】API 设计哲学:REST vs GraphQL vs gRPC 的真实权衡 排序算法专题:从 TimSort 到并行排序 【密码学百科】国密算法体系:SM2/SM3/SM4/SM9 全景解读 【密码学百科】承诺方案:Pedersen 承诺、向量承诺与多项式承诺 【密码学百科】不经意传输与隐私信息检索:OT、OT 扩展与 PIR 【密码学百科】门限密码学:门限签名、门限解密与分布式密钥生成 完美哈希:从理论到 gperf 实践 【密码学百科】安全多方计算:从 Yao 的混淆电路到实用 MPC 【密码学百科】同态加密:从 Paillier 到全同态加密(FHE) 【密码学百科】零知识证明系统:zk-SNARKs、zk-STARKs 与 Bulletproofs 【密码学百科】概率论与密码分析:生日攻击、差分分析与线性分析 【密码学百科】计算复杂性与归约:密码安全性证明的基石 【密码学百科】秘密共享:Shamir 方案、VSS 与安全多方计算入口 【密码学百科】椭圆曲线代数:Weierstrass 方程、点群运算与曲线选择 【密码学百科】离散对数与配对密码学:从 DLP 到 BLS 签名 【密码学百科】格密码数学基础:SVP、LWE 与格基约化 【密码学百科】抽象代数:群、环、域的密码学视角 【密码学百科】有限域算术:GF(2^n) 运算与在 AES/ECC 中的应用 【密码学百科】数论进阶:二次剩余、椭圆曲线上的 Weil 配对 【密码学百科】密码学简史:从凯撒密码到量子时代 【密码学百科】威胁模型与安全目标:CIA 三要素之外 【密码学百科】Kerckhoffs 原则与现代密码设计哲学 【密码学百科】随机性:密码学的基石 【密码学百科】信息论入门:熵、完美保密与 Shannon 定理 【密码学百科】分组密码原理:Feistel 网络与 SPN 结构 【密码学百科】AES 逐步拆解:SubBytes 到 MixColumns 的数学 【密码学百科】分组密码工作模式全览:ECB/CBC/CTR/OFB/CFB 【密码学百科】流密码:RC4 的兴衰与 ChaCha20 的崛起 【密码学百科】密码学哈希函数:MD5→SHA-2→SHA-3 的进化之路 【密码学百科】MAC 与 HMAC:消息认证的正确姿势 【密码学百科】认证加密(AEAD):GCM、ChaCha20-Poly1305 与 OCB 【密码学百科】密钥派生函数:HKDF、PBKDF2、Argon2 与密码存储 【密码学百科】公钥密码的数论基础:模运算、群、原根 【密码学百科】RSA 从原理到攻击:教科书 RSA 为什么不安全 【密码学百科】Diffie-Hellman 密钥交换与离散对数问题 【密码学百科】椭圆曲线密码学(ECC):从几何直觉到点群运算 【密码学百科】数字签名:ECDSA、EdDSA 与 Schnorr 签名 【密码学百科】现代密钥交换:X25519、ECDHE 与前向保密 【密码学百科】混合加密与 KEM/DEM 范式:ECIES 与 HPKE 【密码学百科】填充方案:PKCS#1 v1.5、OAEP 与 PSS 【密码学百科】TLS 协议全解析:从握手到 0-RTT 【密码学百科】PKI 与数字证书:信任链的构建与崩塌 【密码学百科】密码认证协议:从 SRP 到 OPAQUE 【密码学百科】零知识证明入门:如何证明你知道而不泄露 【密码学百科】安全信道构造:Noise 协议框架与 Signal 协议 【密码学百科】密钥管理工程:HSM、KMS 与密钥生命周期 【密码学百科】侧信道攻击:从时序攻击到功耗分析 【密码学百科】密码学实现陷阱:三层漏洞分类、审计工具链与系统性预防 密码敏捷性:如何设计可升级的密码系统 【密码学百科】OpenSSL/BoringSSL 架构剖析:ENGINE、Provider 与 FIPS 模块 【Linux 网络子系统深度拆解】内核网络调优方法论:从基准测试到生产验证 排序基准测试:用数据说话
【系统架构设计】API 网关设计:入口层的职责边界
Liao Tonglang · 2026-04-13 · via 土法炼钢兴趣小组的算法知识备份

2021 年某电商平台大促期间,由于所有外部流量直接打到后端的 200 多个微服务上,缺乏统一的入口管控,导致恶意刷单流量绕过了分散在各服务中的限流逻辑,在 30 秒内击穿了库存服务和订单服务。事后复盘发现,如果存在一个统一的 API 网关(API Gateway),在入口层完成流量识别、限流和认证,这次故障完全可以避免。

微服务架构将单体应用拆分为数十甚至数百个独立服务后,客户端与服务之间的交互复杂度呈指数级增长。每个服务都需要独立处理认证、限流、日志、协议转换等横切关注点(Cross-cutting Concerns),不仅造成大量重复代码,还使得运维团队无法从全局视角管控流量。API 网关正是为解决这一问题而生的基础设施组件——它作为系统的唯一入口,承担流量治理的核心职责。

但网关该做多少事?路由、限流、认证、协议转换——哪些该放在网关,哪些该留给服务自身?这条职责边界划得不好,网关要么成为什么都做的”胖网关”(Fat Gateway),要么退化为一个简单的反向代理而失去存在的意义。本文将系统探讨这个核心问题。

推荐先阅读 API 设计,了解 RESTful 与 gRPC 接口设计的基本原则,它是理解网关路由与协议转换的前提。

没有网关的世界

在没有网关的架构中,客户端需要直接与各个微服务通信:

客户端 → 用户服务(:8001)
客户端 → 订单服务(:8002)
客户端 → 支付服务(:8003)
客户端 → 商品服务(:8004)

这种架构带来的问题:

第一,客户端耦合。 客户端需要知道每个服务的地址和端口,任何服务的地址变更都需要客户端配合修改。当服务数量从 10 个增长到 100 个时,客户端的维护成本变得不可接受。 第二,横切关注点分散。 认证、限流、日志采集、链路追踪等功能需要在每个服务中重复实现。一旦限流策略需要调整,运维团队必须逐一修改所有服务的配置。

第三,协议不统一。 内部服务可能使用 gRPC、Thrift 等高效二进制协议,但移动端和浏览器通常只支持 HTTP/JSON。没有网关,协议转换的责任就落在了每个面向外部的服务上。

第四,安全边界模糊。 没有统一入口意味着每个服务都暴露在公网上,攻击面急剧扩大。

网关的定位

API 网关本质上是一个反向代理(Reverse Proxy),但它不仅仅做请求转发。它是系统与外部世界的唯一接触点,承担着”入口层”的所有职责:

graph TB
    subgraph 外部
        C1[Web 客户端]
        C2[移动客户端]
        C3[第三方系统]
    end

    GW[API 网关]

    subgraph 内部服务集群
        S1[用户服务]
        S2[订单服务]
        S3[支付服务]
        S4[商品服务]
        S5[搜索服务]
    end

    C1 -->|HTTPS| GW
    C2 -->|HTTPS| GW
    C3 -->|HTTPS| GW

    GW -->|gRPC| S1
    GW -->|gRPC| S2
    GW -->|HTTP| S3
    GW -->|gRPC| S4
    GW -->|HTTP| S5

    style GW fill:#f96,stroke:#333,stroke-width:2px

网关将客户端与内部服务解耦,客户端只需要知道网关地址即可。网关负责将请求路由到正确的后端服务,并在这个过程中执行各种策略。

二、网关的核心职责

网关的职责可以分为以下几个层次,从最基础到最上层依次为:

2.1 路由与负载均衡

这是网关最基本的职责。根据请求的路径、方法、头部信息等将请求分发到对应的后端服务。

# 以 APISIX 路由配置为例
routes:
  - uri: /api/v1/users/*
    upstream:
      type: roundrobin
      nodes:
        "user-service-1:8080": 1
        "user-service-2:8080": 1
        "user-service-3:8080": 1

  - uri: /api/v1/orders/*
    upstream:
      type: chash
      key: $arg_user_id
      nodes:
        "order-service-1:8080": 1
        "order-service-2:8080": 1

常见的负载均衡策略:

策略 适用场景 缺点
轮询(Round Robin) 服务实例配置一致 不考虑实例负载差异
加权轮询(Weighted Round Robin) 实例配置不同 权重需要人工调整
一致性哈希(Consistent Hashing) 需要会话亲和性 节点变化时部分请求迁移
最少连接(Least Connections) 请求处理时间差异大 需要维护连接计数

2.2 限流与熔断

网关是实施限流最有效的位置,因为它是所有流量的必经之路。限流的具体算法将在第五节详细讨论。

2.3 认证与鉴权

在网关层完成身份认证(Authentication)和权限校验(Authorization),后端服务无需重复处理。详见第六节。

2.4 协议转换

网关可以将外部的 HTTP/JSON 请求转换为内部的 gRPC 调用,或将 HTTP 长轮询升级为 WebSocket 连接。这使得内部服务可以自由选择最适合的通信协议,而不受客户端能力的限制。

2.5 请求/响应改写

网关可以对请求和响应进行改写,包括:

  • 添加、修改或删除请求头
  • 请求体的格式转换
  • 响应数据的裁剪和聚合
  • API 版本兼容性适配

2.6 可观测性

网关是采集全局指标的最佳位置:

  • 访问日志(Access Log): 记录每个请求的来源、路径、响应码、延迟等信息
  • 指标采集(Metrics): 暴露 Prometheus 格式的指标,包括 QPS、延迟分布、错误率
  • 链路追踪(Distributed Tracing): 注入 Trace ID,串联完整的请求链路

职责边界原则

并非所有事情都应该放在网关上做。核心原则是:网关只做与业务逻辑无关的横切关注点。

应该放在网关 不应该放在网关
路由分发 业务数据校验
全局限流 业务规则执行
身份认证 复杂的数据聚合
TLS 终止 数据库访问
协议转换 业务异常处理
访问日志 状态机流转
IP 黑白名单 领域事件发布
请求 ID 注入 业务缓存策略

三、Kong / Envoy / APISIX 架构对比

当前主流的开源 API 网关方案有三个:Kong、Envoy 和 Apache APISIX。它们的架构设计思路差异显著。

3.1 Kong 架构

Kong 基于 OpenResty(Nginx + LuaJIT)构建,通过插件系统(Plugin System)扩展功能。

┌─────────────────────────────────────┐
│            Kong Gateway             │
│                                     │
│  ┌──────────┐    ┌───────────────┐  │
│  │ Nginx    │    │ Plugin Chain  │  │
│  │ (worker) │───▶│ auth → rate   │  │
│  │          │    │ → log → ...   │  │
│  └──────────┘    └───────────────┘  │
│         │                           │
│  ┌──────┴──────┐                    │
│  │  Admin API  │                    │
│  └──────┬──────┘                    │
└─────────┼───────────────────────────┘
          │
  ┌───────┴───────┐
  │  PostgreSQL / │
  │  Cassandra    │
  └───────────────┘

存储层: Kong 使用 PostgreSQL 或 Cassandra 存储路由、服务、插件等配置。PostgreSQL 适用于中小规模部署,Cassandra 适用于多数据中心同步的大规模场景。

插件系统: Kong 的核心竞争力在于其丰富的插件生态。插件分为多个阶段(Phase)执行:

-- Kong 插件示例:自定义请求头注入
local CustomHeaderHandler = { PRIORITY = 1000, VERSION = "1.0.0" }

function CustomHeaderHandler:access(conf)
    kong.service.request.set_header("X-Request-Start", tostring(ngx.now() * 1000))
    if conf.inject_consumer then
        local consumer = kong.client.get_consumer()
        if consumer then
            kong.service.request.set_header("X-Consumer-ID", consumer.id)
        end
    end
end

function CustomHeaderHandler:header_filter(conf)
    local start = tonumber(kong.request.get_header("X-Request-Start"))
    if start then
        local duration = ngx.now() * 1000 - start
        kong.response.set_header("X-Processing-Time", tostring(duration) .. "ms")
    end
end

return CustomHeaderHandler

局限性: Kong 的配置变更依赖数据库轮询,延迟通常在秒级。在需要毫秒级配置生效的场景下,这一点可能不可接受。

3.2 Envoy 架构

Envoy 由 Lyft 开发,使用 C++ 编写,是云原生生态中最重要的数据平面(Data Plane)代理。

graph LR
    subgraph Envoy 进程
        L[Listener] --> FC[Filter Chain]
        FC --> R[Router]
        R --> C[Cluster Manager]
        C --> U1[Upstream Host 1]
        C --> U2[Upstream Host 2]
    end

    CP[控制平面] -->|xDS API| L
    CP -->|xDS API| C

    style CP fill:#9cf,stroke:#333

xDS API: Envoy 的核心设计理念是将数据平面与控制平面(Control Plane)分离。控制平面通过 xDS(x Discovery Service)协议族动态下发配置:

xDS 协议 全称 用途
LDS Listener Discovery Service 监听器配置
RDS Route Discovery Service 路由规则
CDS Cluster Discovery Service 后端集群信息
EDS Endpoint Discovery Service 具体服务实例地址
SDS Secret Discovery Service TLS 证书和密钥

过滤器链(Filter Chain): Envoy 的请求处理通过过滤器链完成。每个过滤器可以读取、修改或终止请求。过滤器分为三类:

  • 监听器过滤器(Listener Filter): 处理连接级别的逻辑,如 TLS 检测
  • 网络过滤器(Network Filter): 处理 L3/L4 层逻辑,如 TCP 代理
  • HTTP 过滤器(HTTP Filter): 处理 L7 层逻辑,如路由、认证、限流

双重角色: Envoy 既可以作为边缘代理(Edge Proxy)部署在网关位置,也可以作为 Sidecar 代理部署在每个服务旁边,同一套过滤器配置可以在两种模式下复用。

3.3 APISIX 架构

Apache APISIX 同样基于 OpenResty 构建,但在配置存储上选择了 etcd 而非关系型数据库。

┌─────────────────────────────────┐
│          APISIX Node            │
│                                 │
│  ┌─────────┐   ┌────────────┐  │
│  │  Nginx  │   │  Lua 插件  │  │
│  │ Worker  │──▶│  Pipeline  │  │
│  └─────────┘   └────────────┘  │
│       │                         │
│  ┌────┴────┐                    │
│  │ Radixtree│  ← 高性能路由匹配│
│  │ Router  │                    │
│  └─────────┘                    │
└────────┬────────────────────────┘
         │ watch
    ┌────┴────┐      ┌───────────┐
    │  etcd   │      │ Dashboard │
    │ Cluster │◀─────│   (Web)   │
    └─────────┘      └───────────┘

etcd 存储的优势:

  • 实时性: APISIX 通过 etcd 的 watch 机制实现毫秒级配置生效,远快于 Kong 的数据库轮询
  • 一致性: etcd 基于 Raft 协议保证强一致性
  • 无单点: etcd 集群天然高可用

高性能路由: APISIX 使用基数树(Radixtree)实现路由匹配,时间复杂度为 O(k)(k 为路径长度),在路由规则数量增长时性能几乎不变。

三大网关对比总结

维度 Kong Envoy APISIX
开发语言 Lua(OpenResty) C++ Lua(OpenResty)
配置存储 PostgreSQL / Cassandra xDS(控制平面) etcd
配置生效延迟 秒级(DB 轮询) 亚秒级(xDS 推送) 毫秒级(etcd watch)
插件生态 非常丰富(商业 + 社区) 较丰富(WASM 扩展) 丰富(Lua + WASM)
插件开发语言 Lua / Go C++ / WASM Lua / Java / Go / WASM
控制平面 Kong Manager(商业版) Istio / 自研 Dashboard(内置)
云原生集成 Kubernetes Ingress Controller Service Mesh 标配 Kubernetes Ingress Controller
性能(单核 QPS) 约 15,000 约 25,000 约 20,000
部署复杂度 中等(需数据库) 较高(需控制平面) 低(仅需 etcd)
适用场景 传统 API 管理 Service Mesh / 高性能边缘 高性能 API 网关
社区活跃度 非常高
商业支持 Kong Inc. Envoy 代理由多家提供 API7.ai

选型建议:

  • 如果团队已经使用 Kubernetes 和 Istio,Envoy 是天然的选择
  • 如果需要丰富的开箱即用插件和商业支持,Kong 是成熟之选
  • 如果追求高性能和配置实时生效,APISIX 值得优先考虑

四、BFF 模式详解

4.1 什么是 BFF

BFF(Backend for Frontend)是一种在网关与后端服务之间插入一层专属聚合服务的架构模式。每种客户端类型拥有自己的 BFF 层,负责针对该客户端的特定需求进行数据聚合和裁剪。

graph TB
    Web[Web 客户端] --> BFF_Web[Web BFF]
    Mobile[移动客户端] --> BFF_Mobile[Mobile BFF]
    MiniApp[小程序客户端] --> BFF_Mini[小程序 BFF]

    subgraph API 网关层
        GW[API 网关]
    end

    Web --> GW
    Mobile --> GW
    MiniApp --> GW
    GW --> BFF_Web
    GW --> BFF_Mobile
    GW --> BFF_Mini

    subgraph 后端微服务
        US[用户服务]
        OS[订单服务]
        PS[商品服务]
        RS[推荐服务]
    end

    BFF_Web --> US
    BFF_Web --> OS
    BFF_Web --> PS
    BFF_Mobile --> US
    BFF_Mobile --> PS
    BFF_Mobile --> RS
    BFF_Mini --> US
    BFF_Mini --> OS

4.2 为什么需要 BFF

不同客户端的需求差异: Web 端的商品详情页可能需要展示完整的商品规格、评价、关联推荐等信息;移动端由于屏幕限制和带宽敏感,只需要核心字段和缩略图;小程序可能需要特殊的分享卡片数据。如果让后端服务直接满足所有客户端的需求,API 设计将变得臃肿且难以演进。

聚合需求: 一个页面的渲染通常需要来自多个服务的数据。例如,订单详情页需要同时调用订单服务、用户服务、商品服务和物流服务。这个聚合逻辑放在客户端会导致多次网络往返;放在后端服务会造成服务间的耦合;放在网关又违反了”网关不做业务逻辑”的原则。BFF 正是处理这种聚合需求的合适位置。

4.3 BFF 的 Go 实现示例

package main

import (
    "context"
    "encoding/json"
    "log"
    "net/http"
    "sync"
    "time"
)

// OrderDetail 是 Mobile BFF 返回给客户端的聚合数据结构
type OrderDetail struct {
    OrderID     string      `json:"order_id"`
    Status      string      `json:"status"`
    UserName    string      `json:"user_name"`
    UserAvatar  string      `json:"user_avatar"`
    ProductName string      `json:"product_name"`
    ProductImg  string      `json:"product_img"`
    TotalPrice  int64       `json:"total_price"`
    CreatedAt   time.Time   `json:"created_at"`
}

// 各后端服务的客户端接口
type OrderService interface {
    GetOrder(ctx context.Context, orderID string) (*Order, error)
}

type UserService interface {
    GetUser(ctx context.Context, userID string) (*User, error)
}

type ProductService interface {
    GetProduct(ctx context.Context, productID string) (*Product, error)
}

type MobileBFF struct {
    orderSvc   OrderService
    userSvc    UserService
    productSvc ProductService
}

func (bff *MobileBFF) HandleOrderDetail(w http.ResponseWriter, r *http.Request) {
    ctx, cancel := context.WithTimeout(r.Context(), 3*time.Second)
    defer cancel()

    orderID := r.URL.Query().Get("order_id")
    if orderID == "" {
        http.Error(w, "order_id is required", http.StatusBadRequest)
        return
    }

    // 第一步:获取订单基础信息
    order, err := bff.orderSvc.GetOrder(ctx, orderID)
    if err != nil {
        http.Error(w, "failed to get order", http.StatusInternalServerError)
        return
    }

    // 第二步:并发获取用户信息和商品信息
    var (
        user    *User
        product *Product
        userErr error
        prodErr error
        wg      sync.WaitGroup
    )

    wg.Add(2)

    go func() {
        defer wg.Done()
        user, userErr = bff.userSvc.GetUser(ctx, order.UserID)
    }()

    go func() {
        defer wg.Done()
        product, prodErr = bff.productSvc.GetProduct(ctx, order.ProductID)
    }()

    wg.Wait()

    if userErr != nil || prodErr != nil {
        http.Error(w, "failed to fetch related data", http.StatusInternalServerError)
        return
    }

    // 第三步:组装面向移动端的精简响应
    detail := OrderDetail{
        OrderID:     order.ID,
        Status:      order.Status,
        UserName:    user.Name,
        UserAvatar:  user.AvatarSmall, // 移动端使用小图
        ProductName: product.Name,
        ProductImg:  product.ThumbnailURL, // 移动端使用缩略图
        TotalPrice:  order.TotalPrice,
        CreatedAt:   order.CreatedAt,
    }

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(detail)
}

4.4 BFF 与网关的边界

职责 网关 BFF
路由分发
全局限流
身份认证
数据聚合
字段裁剪
客户端适配
协议转换 ✓(通用) ✓(业务相关)

关键区分:网关处理的是与业务无关的横切关注点,BFF 处理的是与特定客户端相关的业务聚合逻辑。

五、限流算法在网关中的实现

限流(Rate Limiting)是网关最重要的流量治理能力之一。下面详细分析两种常见算法及其在网关中的实现。

5.1 令牌桶算法

令牌桶(Token Bucket)算法以固定速率向桶中添加令牌,每个请求消耗一个令牌。桶满时新令牌被丢弃,桶空时请求被拒绝。这种算法允许一定程度的突发流量(Burst)。

package ratelimit

import (
    "sync"
    "time"
)

// TokenBucket 实现令牌桶限流算法
type TokenBucket struct {
    mu         sync.Mutex
    capacity   int64   // 桶的容量(最大令牌数)
    tokens     float64 // 当前令牌数
    refillRate float64 // 每秒填充速率
    lastRefill time.Time
}

func NewTokenBucket(capacity int64, refillRate float64) *TokenBucket {
    return &TokenBucket{
        capacity:   capacity,
        tokens:     float64(capacity),
        refillRate: refillRate,
        lastRefill: time.Now(),
    }
}

// Allow 判断是否允许请求通过
func (tb *TokenBucket) Allow() bool {
    tb.mu.Lock()
    defer tb.mu.Unlock()

    now := time.Now()
    elapsed := now.Sub(tb.lastRefill).Seconds()
    tb.lastRefill = now

    // 补充令牌
    tb.tokens += elapsed * tb.refillRate
    if tb.tokens > float64(tb.capacity) {
        tb.tokens = float64(tb.capacity)
    }

    // 尝试消耗一个令牌
    if tb.tokens >= 1 {
        tb.tokens--
        return true
    }

    return false
}

5.2 滑动窗口算法

滑动窗口(Sliding Window)算法将时间划分为多个小窗口,统计当前时间点往前一个完整周期内的请求数。相比固定窗口,它避免了窗口边界处的突发问题。

package ratelimit

import (
    "sync"
    "time"
)

// SlidingWindowCounter 实现滑动窗口计数器
type SlidingWindowCounter struct {
    mu          sync.Mutex
    limit       int64         // 窗口内允许的最大请求数
    window      time.Duration // 窗口大小
    slots       int           // 窗口划分的槽数
    slotSize    time.Duration // 每个槽的大小
    counts      []int64       // 每个槽的计数
    currentSlot int           // 当前槽索引
    lastUpdate  time.Time     // 上次更新时间
}

func NewSlidingWindowCounter(
    limit int64,
    window time.Duration,
    slots int,
) *SlidingWindowCounter {
    return &SlidingWindowCounter{
        limit:      limit,
        window:     window,
        slots:      slots,
        slotSize:   window / time.Duration(slots),
        counts:     make([]int64, slots),
        lastUpdate: time.Now(),
    }
}

// Allow 判断是否允许请求通过
func (sw *SlidingWindowCounter) Allow() bool {
    sw.mu.Lock()
    defer sw.mu.Unlock()

    now := time.Now()
    sw.advanceSlots(now)

    // 统计所有槽的总计数
    var total int64
    for _, c := range sw.counts {
        total += c
    }

    if total >= sw.limit {
        return false
    }

    sw.counts[sw.currentSlot]++
    return true
}

// advanceSlots 根据时间流逝推进槽位,清零过期槽
func (sw *SlidingWindowCounter) advanceSlots(now time.Time) {
    elapsed := now.Sub(sw.lastUpdate)
    slotsToAdvance := int(elapsed / sw.slotSize)

    if slotsToAdvance <= 0 {
        return
    }

    if slotsToAdvance >= sw.slots {
        // 超过一个完整窗口,清零所有槽
        for i := range sw.counts {
            sw.counts[i] = 0
        }
        sw.currentSlot = 0
    } else {
        // 逐个推进,清零过期槽
        for i := 0; i < slotsToAdvance; i++ {
            sw.currentSlot = (sw.currentSlot + 1) % sw.slots
            sw.counts[sw.currentSlot] = 0
        }
    }

    sw.lastUpdate = now
}

5.3 分布式限流

在网关多实例部署的场景下,需要使用分布式限流。通常基于 Redis 实现:

package ratelimit

import (
    "context"
    "fmt"
    "time"

    "github.com/redis/go-redis/v9"
)

// DistributedRateLimiter 基于 Redis 的分布式滑动窗口限流器
type DistributedRateLimiter struct {
    rdb    *redis.Client
    limit  int64
    window time.Duration
}

func NewDistributedRateLimiter(
    rdb *redis.Client,
    limit int64,
    window time.Duration,
) *DistributedRateLimiter {
    return &DistributedRateLimiter{
        rdb:    rdb,
        limit:  limit,
        window: window,
    }
}

// Allow 使用 Redis Sorted Set 实现精确的滑动窗口限流
func (rl *DistributedRateLimiter) Allow(
    ctx context.Context,
    key string,
) (bool, error) {
    now := time.Now().UnixMicro()
    windowStart := now - rl.window.Microseconds()
    redisKey := fmt.Sprintf("ratelimit:%s", key)

    // 使用 Lua 脚本保证原子性
    script := redis.NewScript(`
        -- 移除窗口外的过期记录
        redis.call('ZREMRANGEBYSCORE', KEYS[1], '-inf', ARGV[1])
        -- 统计当前窗口内的请求数
        local count = redis.call('ZCARD', KEYS[1])
        if count < tonumber(ARGV[2]) then
            -- 未超限,添加当前请求
            redis.call('ZADD', KEYS[1], ARGV[3], ARGV[3])
            redis.call('PEXPIRE', KEYS[1], ARGV[4])
            return 1
        end
        return 0
    `)

    result, err := script.Run(
        ctx,
        rl.rdb,
        []string{redisKey},
        windowStart,            // ARGV[1]: 窗口起始时间
        rl.limit,               // ARGV[2]: 限流阈值
        now,                    // ARGV[3]: 当前时间戳
        rl.window.Milliseconds(), // ARGV[4]: 过期时间
    ).Int64()

    if err != nil {
        return false, err
    }

    return result == 1, nil
}

5.4 限流策略的选择

算法 突发支持 精确度 内存开销 分布式难度 适用场景
令牌桶 支持 中等 中等 允许突发的 API 限流
漏桶 不支持 中等 平滑流量整形
固定窗口 边界突发 简单场景
滑动窗口 部分支持 中等 精确计数的 API 限流

六、网关的认证与安全

6.1 JWT 验证

网关层的 JWT(JSON Web Token)验证是最常见的认证方式。网关只负责验证 Token 的签名和有效期,然后将解码后的用户信息通过请求头传递给后端服务。

package middleware

import (
    "fmt"
    "net/http"
    "strings"

    "github.com/golang-jwt/jwt/v5"
)

type Claims struct {
    UserID   string   `json:"user_id"`
    Username string   `json:"username"`
    Roles    []string `json:"roles"`
    jwt.RegisteredClaims
}

type JWTAuthMiddleware struct {
    publicKey  *rsa.PublicKey
    issuer     string
    skipPaths  map[string]bool
}

func (m *JWTAuthMiddleware) Handle(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if m.skipPaths[r.URL.Path] {
            next.ServeHTTP(w, r)
            return
        }

        authHeader := r.Header.Get("Authorization")
        if authHeader == "" {
            http.Error(w, "missing authorization header", http.StatusUnauthorized)
            return
        }

        parts := strings.SplitN(authHeader, " ", 2)
        if len(parts) != 2 || parts[0] != "Bearer" {
            http.Error(w, "invalid authorization format", http.StatusUnauthorized)
            return
        }

        claims := &Claims{}
        token, err := jwt.ParseWithClaims(
            parts[1], claims,
            func(token *jwt.Token) (interface{}, error) {
                if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
                    return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
                }
                return m.publicKey, nil
            },
            jwt.WithIssuer(m.issuer),
            jwt.WithExpirationRequired(),
        )

        if err != nil || !token.Valid {
            http.Error(w, "invalid token", http.StatusUnauthorized)
            return
        }

        // 将用户信息注入请求头,传递给后端服务
        r.Header.Set("X-User-ID", claims.UserID)
        r.Header.Set("X-Username", claims.Username)
        r.Header.Set("X-User-Roles", strings.Join(claims.Roles, ","))
        // 清除原始 Authorization 头,避免 Token 泄露到后端
        r.Header.Del("Authorization")

        next.ServeHTTP(w, r)
    })
}

6.2 OAuth2 代理模式

对于需要与第三方身份提供商(Identity Provider)集成的场景,网关可以作为 OAuth2 代理(OAuth2 Proxy),代替后端服务完成整个 OAuth2 流程:

sequenceDiagram
    participant C as 客户端
    participant GW as API 网关
    participant IDP as 身份提供商
    participant S as 后端服务

    C->>GW: 请求受保护资源
    GW->>GW: 检查 Session / Token
    alt 未认证
        GW->>C: 302 重定向到 IDP
        C->>IDP: 用户登录
        IDP->>C: 302 重定向回网关(携带 code)
        C->>GW: 携带 authorization code
        GW->>IDP: 用 code 换取 access_token
        IDP->>GW: 返回 access_token
        GW->>GW: 创建 Session,缓存 Token
    end
    GW->>S: 转发请求(注入用户信息头)
    S->>GW: 返回业务数据
    GW->>C: 返回响应

6.3 安全防护

除认证外,网关还承担以下安全职责:

IP 黑白名单: 基于 IP 地址或 CIDR 段进行访问控制。

WAF 基础能力: 检测和拦截常见的 Web 攻击,如 SQL 注入、XSS、路径穿越等。但完整的 WAF 能力通常由专用设备承担,网关只做基础防护。

TLS 终止(TLS Termination): 网关负责 HTTPS 的加解密,后端服务之间可以使用明文通信(在可信网络内),减少后端的加密开销。

CORS 处理: 跨域资源共享(Cross-Origin Resource Sharing)策略统一在网关管控:

# APISIX CORS 插件配置示例
plugins:
  cors:
    allow_origins: "https://example.com,https://app.example.com"
    allow_methods: "GET,POST,PUT,DELETE,OPTIONS"
    allow_headers: "Content-Type,Authorization,X-Request-ID"
    expose_headers: "X-RateLimit-Remaining,X-RateLimit-Reset"
    max_age: 86400
    allow_credential: true

七、网关的高可用设计

网关是系统的单一入口,一旦网关不可用,整个系统对外不可达。因此,网关的高可用(High Availability)设计至关重要。

7.1 多实例部署与负载均衡

网关本身应该是无状态的(Stateless),这样才能通过水平扩展(Scale Out)来提高可用性:

                    ┌──────────────┐
                    │   DNS / VIP  │
                    └──────┬───────┘
                           │
                    ┌──────┴───────┐
                    │   L4 负载均衡  │
                    │ (LVS / NLB) │
                    └──────┬───────┘
                           │
              ┌────────────┼────────────┐
              │            │            │
        ┌─────┴─────┐┌────┴─────┐┌────┴─────┐
        │ 网关实例 1 ││ 网关实例 2 ││ 网关实例 3 │
        └─────┬─────┘└────┬─────┘└────┬─────┘
              │            │            │
              └────────────┼────────────┘
                           │
                    ┌──────┴───────┐
                    │  后端服务集群  │
                    └──────────────┘

L4 负载均衡器 位于网关前方,将流量分发到多个网关实例。常用方案包括:

  • 云厂商 NLB(Network Load Balancer): 全托管,自动弹性扩缩
  • LVS(Linux Virtual Server): 性能极高,运维成本高
  • DNS 轮询: 简单但生效慢,不适合故障快速切换

7.2 健康检查与优雅关闭

L4 负载均衡器需要对网关实例进行健康检查,及时摘除故障节点。健康检查分为两类:

  • 存活检查(Liveness): 进程是否还在运行。失败意味着进程需要重启。
  • 就绪检查(Readiness): 是否可以接收流量。在启动初始化阶段或滚动更新的排水阶段返回不就绪。

网关实例在滚动更新或缩容时,必须优雅关闭(Graceful Shutdown),确保正在处理中的请求不会被中断。标准流程:

func startGateway(handler http.Handler) {
    srv := &http.Server{
        Addr:         ":8080",
        Handler:      handler,
        ReadTimeout:  15 * time.Second,
        WriteTimeout: 15 * time.Second,
        IdleTimeout:  60 * time.Second,
    }

    go func() {
        log.Printf("gateway listening on %s", srv.Addr)
        if err := srv.ListenAndServe(); err != http.ErrServerClosed {
            log.Fatalf("listen error: %v", err)
        }
    }()

    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit

    // 先标记为不就绪,等负载均衡器感知后再关闭
    healthChecker.SetReady(false)
    time.Sleep(5 * time.Second)

    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()
    if err := srv.Shutdown(ctx); err != nil {
        log.Printf("forced shutdown: %v", err)
    }
}

7.3 配置中心高可用

网关依赖的配置存储也必须是高可用的:

  • Kong + PostgreSQL: 使用主从复制 + 自动故障转移(如 Patroni)
  • APISIX + etcd: 部署 3 或 5 节点的 etcd 集群
  • Envoy + 控制平面: 控制平面本身也需要多副本部署

当配置中心不可用时,网关应该能够使用本地缓存的配置继续工作,这是网关高可用的最后一道防线。

八、工程案例:某金融科技公司的网关演进

8.1 背景

某金融科技公司的核心业务系统由 120 个微服务组成,日均 API 调用量约 8 亿次,峰值 QPS 达到 5 万。系统经历了三个阶段的网关演进。

8.2 第一阶段:Nginx + Lua 自研网关

最初团队使用 Nginx 作为反向代理,配合自研的 Lua 脚本实现基础的路由和限流功能。

问题:

  • 路由配置以静态文件形式管理,每次变更需要重新加载 Nginx,存在短暂的连接中断
  • 限流逻辑硬编码在 Lua 脚本中,修改限流策略需要重新部署
  • 缺乏统一的监控和日志采集,排查问题困难
  • 认证逻辑分散在各个服务中,存在实现不一致的安全风险

8.3 第二阶段:迁移到 APISIX

团队评估了 Kong、Envoy 和 APISIX 后,最终选择 APISIX,原因包括:

  • etcd 的 watch 机制可以实现路由的毫秒级生效,满足金融场景对配置变更实时性的要求
  • 内置的 Dashboard 降低了运维门槛
  • Lua 插件机制与原有的自研 Lua 脚本兼容性好,迁移成本低
  • 性能测试显示在同等硬件条件下,APISIX 的 QPS 比 Kong 高约 30%

迁移架构:

graph TB
    subgraph 外部流量
        CLB[云负载均衡器]
    end

    subgraph 网关集群(6 节点)
        A1[APISIX-1]
        A2[APISIX-2]
        A3[APISIX-3]
        A4[APISIX-4]
        A5[APISIX-5]
        A6[APISIX-6]
    end

    subgraph 配置中心
        E1[etcd-1]
        E2[etcd-2]
        E3[etcd-3]
    end

    subgraph 限流存储
        R1[Redis Cluster]
    end

    subgraph 后端服务
        SG1[用户服务组]
        SG2[交易服务组]
        SG3[风控服务组]
        SG4[支付服务组]
    end

    CLB --> A1
    CLB --> A2
    CLB --> A3
    CLB --> A4
    CLB --> A5
    CLB --> A6

    A1 --> E1
    A2 --> E2
    A3 --> E3

    A1 --> R1
    A2 --> R1

    A1 --> SG1
    A2 --> SG2
    A3 --> SG3
    A4 --> SG4

实施步骤:

  1. 部署 APISIX 集群与 etcd 集群
  2. 将原有 Nginx 路由规则逐条迁移到 APISIX
  3. 使用灰度切流(Canary Release)逐步将流量从 Nginx 切换到 APISIX
  4. 在 APISIX 上启用 JWT 认证插件,统一所有服务的认证逻辑
  5. 配置分布式限流,基于 Redis Cluster 实现跨网关实例的全局限流
  6. 接入 Prometheus + Grafana 监控体系

8.4 第三阶段:引入 BFF 层

随着移动端、Web 端和开放平台三条业务线的分化,团队引入了 BFF 层:

  • Mobile BFF: 负责移动端的数据聚合和裁剪,优化移动端的数据传输量
  • Web BFF: 负责 Web 管理后台的复杂查询聚合
  • Open API BFF: 负责开放平台的接口适配和版本管理

8.5 成果

指标 迁移前 迁移后
路由变更生效时间 30 秒(Nginx reload) 50 毫秒(etcd watch)
P99 延迟(网关层) 12 毫秒 3 毫秒
认证逻辑代码行数 12,000 行(分散在 40 个服务中) 200 行(统一在网关插件中)
限流策略调整耗时 2 小时(需逐服务部署) 5 分钟(Dashboard 配置)
故障排查平均耗时 45 分钟 10 分钟
网关可用性 99.95% 99.99%

九、网关反模式与最佳实践

9.1 反模式

反模式一:胖网关(Fat Gateway)

将业务逻辑放入网关,例如在网关中执行订单价格计算、库存扣减等操作。这导致网关变得臃肿且难以维护,每次业务逻辑变更都需要重新部署网关——而网关的变更影响全局。

// 错误示范:在网关插件中实现业务逻辑
function access(conf)
    -- 网关不应该直接查询数据库
    local db = connect_database()
    local user = db:query("SELECT * FROM users WHERE id = ?", user_id)
    -- 网关不应该执行业务规则
    if user.vip_level > 3 and order.amount > 10000 then
        apply_vip_discount(order)
    end
end

反模式二:业务逻辑泄露(Business Logic Leak)

网关路由规则中隐含了业务逻辑。例如,根据用户类型将请求路由到不同的服务版本——这本质上是一个业务决策,不应该由网关做出。

反模式三:单体网关(Monolithic Gateway)

所有团队共享一个网关配置,任何团队的配置变更都可能影响其他团队。正确的做法是按团队或业务域划分网关,或至少在逻辑上隔离不同团队的路由配置。

反模式四:忽视网关自身的可观测性

只关注后端服务的监控,忽略了网关本身的内存、CPU、连接数等指标。网关作为全局入口,自身的性能问题会被放大到所有服务。

9.2 最佳实践

实践一:保持网关的无状态性。 网关不应该在内存中维护任何会话状态。所有需要持久化的数据(限流计数、会话信息)都应该存储在外部组件中。

实践二:分层限流。 在网关层实施全局限流(防止系统过载),在服务层实施细粒度限流(按业务场景调整)。两层限流各司其职。

实践三:统一认证,分散鉴权。 身份认证(你是谁)在网关完成,权限校验(你能做什么)由各服务自行处理。因为权限规则与业务逻辑紧密相关,放在网关中会导致胖网关。

请求流程:
客户端 → 网关(验证 JWT,提取用户身份)
      → 后端服务(根据用户角色检查操作权限)

实践四:灰度发布能力。 网关应该支持按比例、按用户标签、按地域等维度进行灰度路由,这是微服务系统安全发布的基础能力:

# APISIX 灰度路由配置示例
routes:
  - uri: /api/v1/orders/*
    plugins:
      traffic-split:
        rules:
          - match:
              - vars:
                  - ["http_x_user_group", "==", "beta"]
            weighted_upstreams:
              - upstream:
                  type: roundrobin
                  nodes:
                    "order-service-v2:8080": 1
                weight: 1
          - weighted_upstreams:
              - upstream:
                  type: roundrobin
                  nodes:
                    "order-service-v1:8080": 1
                weight: 1

实践五:超时与重试策略。 网关应该为每个路由配置合理的超时时间和重试策略。重试只应该针对幂等请求(GET、PUT、DELETE),非幂等请求(POST)的重试需要谨慎处理。

实践六:协议转换的边界。 通用协议转换(如 TLS 终止、HTTP/2 到 HTTP/1.1 降级)放在网关;业务相关的协议适配(如将 REST 请求映射到特定的 gRPC 方法)放在 BFF 或服务自身。

9.3 协议转换:REST 到 gRPC

网关层的 REST 到 gRPC 转换是常见需求。以 Envoy 的 gRPC-JSON 转码器(Transcoder)为例:

# Envoy gRPC-JSON 转码配置
http_filters:
  - name: envoy.filters.http.grpc_json_transcoder
    typed_config:
      "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder
      proto_descriptor: "/etc/envoy/proto.pb"
      services:
        - "user.UserService"
      print_options:
        add_whitespace: true
        always_print_primitive_fields: true
        preserve_proto_field_names: true

对应的 Proto 定义:

syntax = "proto3";
package user;
import "google/api/annotations.proto";

service UserService {
    rpc GetUser(GetUserRequest) returns (GetUserResponse) {
        option (google.api.http) = { get: "/api/v1/users/{user_id}" };
    }
    rpc CreateUser(CreateUserRequest) returns (CreateUserResponse) {
        option (google.api.http) = { post: "/api/v1/users" body: "*" };
    }
}

message GetUserRequest { string user_id = 1; }
message GetUserResponse {
    string user_id = 1;
    string username = 2;
    string email = 3;
    int64 created_at = 4;
}
message CreateUserRequest {
    string username = 1;
    string email = 2;
    string password = 3;
}
message CreateUserResponse { string user_id = 1; }

客户端发送标准的 HTTP/JSON 请求,Envoy 自动将其转换为 gRPC 调用并转发给后端的 gRPC 服务,再将 gRPC 响应转回 JSON 返回给客户端。整个过程对客户端完全透明。

9.4 网关选型决策树

在实际选型中,可以按照以下决策路径进行判断:

graph TD
    Q1{是否已使用 Service Mesh?}
    Q1 -->|是| A1[Envoy 作为入口网关]
    Q1 -->|否| Q2{是否需要丰富的商业插件?}
    Q2 -->|是| A2[Kong Enterprise]
    Q2 -->|否| Q3{是否需要毫秒级配置生效?}
    Q3 -->|是| A3[APISIX]
    Q3 -->|否| Q4{团队是否有 C++ 能力?}
    Q4 -->|是| A4[Envoy + 自研控制平面]
    Q4 -->|否| Q5{是否需要低运维成本?}
    Q5 -->|是| A5[云厂商托管网关]
    Q5 -->|否| A6[Kong 开源版]

参考资料

  1. Chris Richardson, “Microservices Patterns”, Manning Publications, 2018, Chapter 8: External API Patterns
  2. Sam Newman, “Building Microservices”, 2nd Edition, O’Reilly, 2021, Chapter 7: Build
  3. Kong 官方文档, https://docs.konghq.com/
  4. Envoy 官方文档, https://www.envoyproxy.io/docs/envoy/latest/
  5. Apache APISIX 官方文档, https://apisix.apache.org/docs/
  6. Matt Klein, “Introduction to Modern Network Load Balancing and Proxying”, 2017
  7. “Pattern: API Gateway / Backends for Frontends”, https://microservices.io/patterns/apigateway.html
  8. Gupta, A. “API Gateway vs. Service Mesh”, NGINX Blog, 2020
  9. 云原生社区, “APISIX 与 Kong 的对比分析”, 2022
  10. Google Cloud, “API Gateway Design Patterns”, https://cloud.google.com/architecture/api-gateway

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-04-21 · architecture / security

【身份与访问控制工程】API Gateway、BFF 与边界认证授权

API Gateway 能做粗粒度的认证鉴权,但细粒度授权必须下沉到服务层。本文梳理 Gateway/BFF/Service 三层认证职责边界,讨论 Token 验证位置选择、RFC 8693 Token Exchange、服务间身份传播(JWT/mTLS/SPIFFE),以及 Istio、Kong、APISIX 的认证授权能力对比。

2026-04-13 · architecture

【系统架构设计】Service Mesh:Sidecar 的代价与无 Sidecar 的未来

2023 年,某头部电商平台在全量接入 Istio 后发现:每个 Pod 的内存占用增加了 40-70 MB,p99 延迟从 12 ms 上升到 18 ms,整个集群每月多出数万美元的计算成本。这并非个例。CNCF 2024 年度调查显示,超过 60% 的受访企业已在生产环境中使用或评估服务网格(Service Mes…

2026-04-13 · architecture

【系统架构设计】什么是软件架构:从代码结构到系统决策

软件架构的定义至少有三种主流理解,每种指向不同的关注点。本文对比 Booch、Fowler、Johnson 三家定义,分析架构决策的不可逆性特征,拆解 Krutchten 4+1 视图模型,回答一个核心问题:架构和设计的边界到底在哪里。

2026-04-13 · architecture

【系统架构设计】架构质量属性:不只是"高可用高性能"

需求评审时写下的'高可用、高性能、高并发',到了架构设计阶段几乎无法落地——因为它们不是可执行的需求。本文从 SEI/CMU 的质量属性理论出发,用 stimulus-response 场景模型把模糊需求变成可量化、可验证的架构约束,并拆解属性之间的冲突与联动关系。