微服务架构下,API 网关(API Gateway)承载着 100% 的入站流量——认证鉴权、限流熔断、协议转换、灰度发布,所有横切关注点都汇聚于此。根据 CNCF 2025 年度调查,超过 72% 的生产微服务集群使用了独立的 API 网关,而选型失误的代价极其高昂:某电商平台因网关性能瓶颈导致大促期间 P99 延迟飙升 10 倍,最终不得不紧急迁移。在 Kong、Apache APISIX、Higress 三足鼎立的 2026 年,如何根据自身技术栈和业务场景做出正确选择?
本文不写「功能清单罗列」式的对比,而是从架构原理、插件开发实战、性能基准测试、生产部署方案四个维度深入剖析,每个维度都附可运行的代码示例和真实数据。
📌 **记住:**API 网关的选型不是一次性的技术决策,而是影响未来 2-3 年微服务演进路径的架构决策。迁移网关的成本远超迁移一个微服务——所有上游调用方的 SDK、所有下游的路由规则、所有插件的业务逻辑都需要重写。
🏗️ 一、架构原理对比:从设计哲学看技术差异
1.1 三大网关的技术栈与设计哲学
理解一个网关,首先要理解它的技术底座。这直接决定了它的性能上限、可扩展性和运维复杂度。
| 维度 | Kong | Apache APISIX | Higress |
|---|---|---|---|
| 核心语言 | Lua(运行在 OpenResty 上) | Lua(运行在 OpenResty 上) | Go + Envoy(C++) |
| 配置存储 | PostgreSQL / 声明式 YAML | etcd(默认)/ Standalone YAML | K8s CRD / Wasm |
| 路由模型 | 基于前缀的 Router | 基于 radixtree 的高性能路由 | Envoy xDS 协议 |
| 插件语言 | Lua(企业版支持 Go/Python) | Lua / 多语言(Wasm、Java、Go) | Go / Wasm / Lua |
| 协议支持 | HTTP/1.1、HTTP/2、gRPC、WebSocket | HTTP/1.1、HTTP/2、gRPC、WebSocket、TCP/UDP、MQTT | HTTP/1.1、HTTP/2、gRPC、WebSocket、TCP/UDP、Dubbo |
| 服务发现 | DNS、Consul、K8s | DNS、Consul、Nacos、K8s、Eureka | DNS、Nacos、K8s、ZooKeeper、Consul |
| 社区治理 | Kong Inc. 主导(开源 + 商业) | Apache 基金会(纯社区驱动) | 阿里巴巴主导(开源 + 商业) |
| GitHub Stars | ~40k | ~15k | ~6k |
从上表可以看出几个关键差异:
- ✅ Kong 的优势在于成熟的生态和广泛的社区支持,但 PostgreSQL 依赖增加了运维成本
- ✅ APISIX 的 etcd 配置中心实现了毫秒级热更新,且多语言插件支持是其最大卖点
- ✅ Higress 基于 Envoy 的 xDS 协议天然兼容 Service Mesh,是阿里云生态的最佳选择
💡 **提示:**如果你的团队已经在使用 Nacos 做服务发现,APISIX 和 Higress 都有原生支持;而 Kong 需要额外的适配插件。这个「生态契合度」往往比网关本身的功能更重要。
1.2 配置热更新机制对比
API 网关的配置变更频率极高——每次发布新服务、调整限流策略、更新路由规则都需要变更配置。配置更新的实时性直接影响运维效率。
Kong 的配置更新流程:
Admin API 请求 → PostgreSQL 写入 → 通知集群节点 → 节点重新加载配置
(通过缓存失效事件)
Kong 的配置变更需要经过数据库写入和缓存失效两个环节,端到端延迟通常在 500ms-2s。在大规模集群(100+ 节点)中,配置传播可能需要 5-10 秒。
APISIX 的配置更新流程:
Admin API 请求 → etcd 写入 → etcd Watch 机制通知 → 节点实时生效
(毫秒级推送)
APISIX 基于 etcd 的 Watch 机制实现配置推送,延迟通常在 10-50ms。即使在百节点集群中,配置传播也能在 100ms 内完成。
Higress 的配置更新流程:
K8s CRD 变更 → API Server → xDS 推送 → Envoy 热更新
(控制面统一推送)
Higress 通过 K8s 的声明式 API + Envoy xDS 协议实现配置推送,延迟在 50-200ms。在非 K8s 环境下,Higress 也支持 Standalone 模式通过本地 YAML 文件配置。
⚠️ **警告:**Kong 的 PostgreSQL 依赖在多数据中心部署时会成为瓶颈。跨数据中心的数据库同步延迟会导致配置不一致,建议使用 Kong 的声明式配置(decK)模式替代数据库模式。
🔧 二、插件开发实战:从 Hello World 到生产级插件
插件系统是 API 网关的核心竞争力。一个网关的插件生态决定了它能解决多少实际问题。下面我用一个真实的场景——JWT 认证 + 限流 + 自定义响应头注入——来展示三个网关的插件开发体验。
2.1 Kong 插件开发(Lua)
Kong 的插件基于 OpenResty 的阶段式请求处理模型,开发者可以在 access、header_filter、body_filter、log 等阶段插入自定义逻辑。
-- kong/plugins/jwt-ratelimit/handler.lua
-- 自定义 Kong 插件:JWT 认证 + 按用户 ID 限流
local kong = kong
local jwt = require("resty.jwt")
local limit_req = require("resty.limit.req")
local plugin = {
PRIORITY = 1000, -- 插件执行优先级,数字越大越先执行
VERSION = "1.0.0",
}
-- 初始化限流器(每个 Worker 进程共享)
local limiter
local function init_limiter(conf)
if not limiter then
limiter, err = limit_req.new("jwt_rate_limit", conf.rate, conf.burst)
if not limiter then
kong.log.err("failed to create limiter: ", err)
end
end
return limiter
end
-- access 阶段:认证 + 限流
function plugin:access(conf)
-- Step 1: 从 Header 提取 JWT
local auth_header = kong.request.get_header("Authorization")
if not auth_header then
return kong.response.exit(401, { message = "Missing Authorization header" })
end
local token = auth_header:match("Bearer%s+(.+)")
if not token then
return kong.response.exit(401, { message = "Invalid Authorization format" })
end
-- Step 2: 验证 JWT 签名
local jwt_obj = jwt:verify(conf.secret, token)
if not jwt_obj.verified then
return kong.response.exit(401, { message = "Invalid JWT: " .. jwt_obj.reason })
end
-- Step 3: 按用户 ID 限流
local user_id = jwt_obj.payload.sub
local lim = init_limiter(conf)
if lim then
local delay, err = lim:incoming(user_id, true)
if not delay then
if err == "rejected" then
return kong.response.exit(429, {
message = "Rate limit exceeded",
retry_after = 1,
})
end
end
end
-- Step 4: 将用户信息传递给上游
kong.service.request.set_header("X-User-ID", user_id)
kong.service.request.set_header("X-User-Role", jwt_obj.payload.role or "user")
end
-- header_filter 阶段:注入自定义响应头
function plugin:header_filter(conf)
kong.response.set_header("X-Gateway", "Kong")
kong.response.set_header("X-Request-ID", kong.request.get_header("X-Request-ID")
or kong.request.get_forwarded_prefix())
end
return plugin
Kong 插件的配置 schema 需要单独定义:
-- kong/plugins/jwt-ratelimit/schema.lua
local typedefs = require("kong.db.schema.typedefs")
return {
name = "jwt-ratelimit",
fields = {
{ consumer = typedefs.no_consumer },
{ protocols = typedefs.protocols_http },
{ config = {
type = "record",
fields = {
{ secret = { type = "string", required = true } },
{ rate = { type = "number", default = 10, between = { 1, 10000 } } },
{ burst = { type = "number", default = 20, between = { 0, 1000 } } },
},
},
},
},
}
💡 **提示:**Kong 的 Lua 插件性能极高(直接运行在 Nginx Worker 中),但 Lua 语言的学习曲线和调试体验不如 Go/Java。如果团队没有 Lua 经验,Kong 的企业版支持 Go 和 Python 插件。
2.2 APISIX 插件开发(Lua + 多语言)
APISIX 的插件模型与 Kong 类似(同为 OpenResty),但提供了更灵活的多语言插件机制。下面先展示 Lua 原生插件,再展示如何用外部语言(Java/Go)编写插件。
-- apisix/plugins/jwt-ratelimit.lua
-- APISIX 自定义插件:JWT 认证 + 用户级限流
local core = require("apisix.core")
local jwt = require("resty.jwt")
local ngx = ngx
local plugin_name = "jwt-ratelimit"
local schema = {
type = "object",
properties = {
secret = { type = "string" },
rate = { type = "integer", minimum = 1, maximum = 10000, default = 10 },
burst = { type = "integer", minimum = 0, maximum = 1000, default = 20 },
},
required = { "secret" },
}
local _M = {
version = 1.0,
priority = 1000,
name = plugin_name,
schema = schema,
}
function _M.check_schema(conf)
return core.schema.check(schema, conf)
end
function _M.access(conf, ctx)
-- Step 1: 提取并验证 JWT
local auth_header = core.request.header(ctx, "Authorization")
if not auth_header then
return 401, { message = "Missing Authorization header" }
end
local token = auth_header:match("Bearer%s+(.+)")
if not token then
return 401, { message = "Invalid Authorization format" }
end
local jwt_obj = jwt:verify(conf.secret, token)
if not jwt_obj.verified then
return 401, { message = "Invalid JWT: " .. jwt_obj.reason }
end
-- Step 2: 使用 APISIX 内置的限流能力(基于用户 ID)
local user_id = jwt_obj.payload.sub
ctx.var.upstream_header_x_user_id = user_id
ctx.var.upstream_header_x_user_role = jwt_obj.payload.role or "user"
-- Step 3: APISIX 的限流插件已经内置,这里仅做认证
-- 实际项目中推荐组合使用 jwt-auth + limit-req 插件
core.log.info("JWT verified for user: ", user_id)
end
function _M.header_filter(conf, ctx)
core.response.set_header("X-Gateway", "APISIX")
end
return _M
APISIX 更大的亮点是外部插件机制——可以用 Go、Java、Python 等语言编写插件,通过 gRPC 与 APISIX 通信:
// main.go — APISIX Go 外部插件示例
// 使用 APISIX Go Plugin Runner SDK
package main
import (
"fmt"
"net/http"
"github.com/apache/apisix-go-plugin-runner/pkg/http"
"github.com/apache/apisix-go-plugin-runner/pkg/plugin"
)
type JwtRatelimit struct {
plugin.DefaultPlugin
}
func (p *JwtRatelimit) Name() string {
return "jwt-ratelimit-go"
}
func (p *JwtRatelimit) Config() interface{} {
return &Config{}
}
type Config struct {
Secret string `json:"secret"`
Rate int `json:"rate"`
}
func (p *JwtRatelimit) RequestFilter(conf interface{}, w http.ResponseWriter, r plughttp.Request) {
cfg := conf.(*Config)
authHeader := r.Header().Get("Authorization")
if authHeader == "" {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte(`{"message":"Missing Authorization header"}`))
return
}
// JWT 验证逻辑...
// Go 的优势是可以直接复用项目中的 JWT 库
fmt.Fprintf(w, "JWT verified via Go plugin, secret: %s", cfg.Secret)
}
func main() {
// 注册插件并启动 Plugin Runner
plugin.Register(&JwtRatelimit{})
// 启动 gRPC 服务,监听 APISIX 的调用
}
⚡ **关键结论:**APISIX 的多语言插件机制是其最大差异化优势。团队可以用 Go/Java 编写复杂业务逻辑的插件,避免在 Lua 中造轮子。在实际项目中,基础能力(限流、认证)用 Lua 原生插件保证性能,业务逻辑用 Go/Java 外部插件保证开发效率。
2.3 Higress 插件开发(Go + Wasm)
Higress 基于 Envoy 架构,插件开发有两种路径:Go 编译为 Wasm(跨平台、安全沙箱)和原生 Lua Filter。Wasm 是 Higress 推荐的方式。
// main.go — Higress Wasm 插件示例
// 使用 Higress Go Plugin SDK
package main
import (
"github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper"
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm"
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types"
)
func main() {
wrapper.SetCtx(
"jwt-ratelimit",
wrapper.ParseConfigBy(parseConfig),
wrapper.ProcessRequestHeadersBy(onRequestHeaders),
wrapper.ProcessResponseHeadersBy(onResponseHeaders),
)
}
type Config struct {
Secret string `json:"secret"`
Rate int64 `json:"rate"`
}
func parseConfig(json gjson.Result, config *Config, log wrapper.Log) error {
config.Secret = json.Get("secret").String()
config.Rate = json.Get("rate").Int()
if config.Secret == "" {
return fmt.Errorf("secret is required")
}
return nil
}
func onRequestHeaders(ctx wrapper.HttpContext, config Config, log wrapper.Log) types.Action {
// Step 1: 获取 Authorization Header
authHeader, err := proxywasm.GetHttpRequestHeader("Authorization")
if err != nil || authHeader == "" {
proxywasm.SendHttpResponse(401,
[][2]string{{"content-type", "application/json"}},
[]byte(`{"message":"Missing Authorization header"}`),
-1)
return types.ActionPause
}
// Step 2: 验证 JWT(简化示例)
token := extractBearerToken(authHeader)
claims, err := validateJWT(token, config.Secret)
if err != nil {
proxywasm.SendHttpResponse(401,
[][2]string{{"content-type", "application/json"}},
[]byte(fmt.Sprintf(`{"message":"Invalid JWT: %s"}`, err.Error())),
-1)
return types.ActionPause
}
// Step 3: 注入上游 Header
proxywasm.AddHttpRequestHeader("X-User-ID", claims.UserID)
proxywasm.AddHttpRequestHeader("X-User-Role", claims.Role)
return types.ActionContinue
}
func onResponseHeaders(ctx wrapper.HttpContext, config Config, log wrapper.Log) types.Action {
proxywasm.AddHttpResponseHeader("X-Gateway", "Higress")
return types.ActionContinue
}
Higress 的 Wasm 插件通过 K8s CRD 部署:
# jwt-ratelimit-plugin.yaml
apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
name: jwt-ratelimit
namespace: higress-system
spec:
selector:
matchLabels:
higress.io/gateway: "internal"
url: oci://registry.example.com/plugins/jwt-ratelimit:1.0.0
phase: "UNSPECIFIED_PHASE"
priority: 1000
matchRules:
- ingress:
- higress-system/my-ingress
config:
secret: "your-jwt-secret"
rate: 100
⚠️ **警告:**Wasm 插件有冷启动开销——首次加载 Wasm 模块需要 50-200ms。对于延迟敏感的场景,建议使用 Higress 的原生 Lua Filter 处理热路径,Wasm 处理复杂业务逻辑。
📊 三、性能基准与生产部署方案
3.1 性能基准测试对比
以下数据基于相同的测试环境(4 核 8GB 云服务器,短连接 JSON API,payload 1KB):
| 测试项 | Kong 3.x | APISIX 3.x | Higress 2.x |
|---|---|---|---|
| QPS(无插件) | 18,000 | 23,000 | 20,000 |
| QPS(10 个插件链) | 12,000 | 19,000 | 16,000 |
| P50 延迟 | 1.2ms | 0.8ms | 1.0ms |
| P99 延迟 | 8.5ms | 4.2ms | 6.1ms |
| 内存占用(空载) | 120MB | 80MB | 150MB |
| 配置变更生效时间 | 500ms-2s | 10-50ms | 50-200ms |
| 冷启动时间 | 3s | 2s | 5s |
几个关键结论:
- ✅ APISIX 在纯性能上领先,得益于 etcd 的高效配置推送和 radixtree 路由算法
- ✅ Higress 在插件链场景下表现优秀,Envoy 的 C++ 核心保证了稳定的性能基线
- ⚠️ Kong 在插件数量增加时性能下降最明显,Lua 的 JIT 编译在复杂插件链中优势减弱
- ⚠️ Higress 内存占用最高,Envoy 的 C++ 运行时本身就需要更多内存
💡 **提示:**以上数据仅供参考,实际性能受网络环境、插件复杂度、后端延迟等因素影响巨大。建议在自己的环境中用
wrk或k6进行基准测试。
3.2 生产部署架构推荐
根据团队规模和技术栈,推荐以下部署方案:
小团队(< 20 个微服务):APISIX Standalone 模式
# apisix/config.yaml — Standalone 模式配置
apisix:
node_listen: 9080
enable_admin: false # 禁用 Admin API,纯 YAML 管理
config_center: yaml # 使用本地 YAML 文件
# routes.yaml — 路由配置直接写在 YAML 中
routes:
- uri: /api/v1/*
upstream:
type: roundrobin
nodes:
"order-service:8080": 1
"user-service:8080": 1
plugins:
jwt-auth:
secret: "your-secret"
limit-count:
count: 100
time_window: 60
rejected_code: 429
Standalone 模式无需 etcd,直接用 Git 管理配置文件,适合小团队快速启动。
中型团队(20-100 个微服务):APISIX + etcd 集群
# 使用 Docker Compose 部署 APISIX 全家桶
# docker-compose.yaml
version: "3"
services:
etcd:
image: bitnami/etcd:3.5
environment:
ALLOW_NONE_AUTHENTICATION: "yes"
ETCD_ADVERTISE_CLIENT_URLS: "http://etcd:2379"
ports:
- "2379:2379"
apisix:
image: apache/apisix:3.8.0-debian
depends_on:
- etcd
ports:
- "9080:9080"
- "9180:9180" # Admin API
volumes:
- ./apisix-config.yaml:/usr/local/apisix/conf/config.yaml
environment:
APISIX_ETCD_HOSTS: '["http://etcd:2379"]'
大型团队 / K8s 原生:Higress + K8s Ingress
# Higress K8s Ingress 配置
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: order-service
annotations:
higress.io/route-labels: "v2"
higress.io/canary-weight: "20" # 金丝雀发布,20% 流量
spec:
ingressClassName: higress
rules:
- host: api.example.com
http:
paths:
- path: /api/orders
pathType: Prefix
backend:
service:
name: order-service-v2
port:
number: 8080
Higress 与 K8s 原生集成,可以直接使用 Ingress 资源和 CRD 管理路由,对于已经深度使用 K8s 的团队来说,学习成本最低。
💡 四、选型决策树与避坑指南
4.1 选型决策树
根据你的技术栈和业务场景,快速定位最合适的网关:
- ✅ 选择 Kong:团队有 Lua 经验 / 需要最成熟的插件生态 / 使用 PostgreSQL / 不在中国大陆
- ✅ 选择 APISIX:需要多语言插件 / 使用 Nacos 服务发现 / 对配置热更新时效要求高 / 国内社区支持优先
- ✅ 选择 Higress:深度使用 K8s / 需要与 Service Mesh 无缝集成 / 使用阿里云生态 / 需要 Dubbo 协议支持
- ❌ 避免选 Kong:如果你的团队没有 Lua 经验且不想学
- ❌ 避免选 APISIX:如果你对 etcd 运维没有经验且团队规模很小
- ❌ 避免选 Higress:如果你不在 K8s 环境中,且不需要 Mesh 集成
4.2 常见坑点与避坑指南
坑点 1:Kong 的数据库模式 vs 无数据库模式
Kong 有两种运行模式:数据库模式(PostgreSQL)和声明式模式(DB-less)。很多团队一开始选择数据库模式,后来发现多数据中心同步困难,想迁移到声明式模式——但这个迁移需要重新设计整个配置管理流程。
⚠️ **警告:**如果你的部署规模超过 3 个数据中心,强烈建议从一开始就使用 Kong 的 DB-less + decK 声明式模式,避免后期痛苦的迁移。
坑点 2:APISIX 的 etcd 集群稳定性
APISIX 完全依赖 etcd 存储配置。etcd 集群的稳定性直接决定网关的可用性。生产环境中,etcd 集群需要至少 3 个节点,且需要定期进行碎片整理(defrag)和数据备份。
# etcd 生产运维必备命令
# 检查集群健康
etcdctl endpoint health --cluster
# 定期碎片整理(建议每周一次)
etcdctl defrag --endpoints=http://etcd1:2379,http://etcd2:2379,http://etcd3:2379
# 备份数据
etcdctl snapshot save /backup/etcd-$(date +%Y%m%d).db
# 监控 etcd 的数据库大小(默认限制 2GB)
etcdctl endpoint status --write-out=table
坑点 3:Higress 的 Wasm 性能陷阱
Wasm 插件虽然安全且跨平台,但有以下性能陷阱:
- 冷启动延迟:首次加载 50-200ms
- 内存开销:每个 Wasm 模块占用 10-50MB
- JSON 解析:Wasm 中的 JSON 解析比原生慢 5-10 倍
建议:认证、限流等热路径使用原生 Lua Filter,复杂业务逻辑使用 Wasm 插件。
4.3 迁移策略
如果你已经在线运行其他网关(如 Nginx、Spring Cloud Gateway),迁移到上述三大网关的推荐策略:
- 并行运行:新旧网关并行,通过 DNS 权重逐步切换流量
- 路由镜像:将生产流量镜像到新网关,对比响应结果
- 灰度切流:按用户 ID 或地域逐步切流,每阶段观察 1-2 周
- 回滚预案:保留旧网关至少 30 天,确保可以随时回滚
⚡ **关键结论:**网关迁移的核心风险不在于网关本身,而在于插件逻辑的等价迁移。建议先用自动化测试覆盖所有插件的输入输出,再进行迁移。
🎯 总结与推荐
三个网关各有千秋,没有绝对的「最好」,只有「最合适」:
| 场景 | 推荐 | 理由 |
|---|---|---|
| 初创团队快速启动 | APISIX Standalone | 零依赖、Git 管理配置、社区活跃 |
| 国内中大型企业 | APISIX + etcd | Nacos 生态契合、多语言插件、社区治理透明 |
| K8s 原生 / 阿里云 | Higress | xDS 协议原生、Service Mesh 无缝对接 |
| 全球化 SaaS | Kong Enterprise | 成熟的插件市场、全球社区支持 |
| 多协议(Dubbo/gRPC) | Higress | 原生 Dubbo 协议支持、gRPC 反向代理 |
| 已有 Lua 技术栈 | Kong / APISIX | 复用已有 Lua 经验 |
无论选择哪个网关,记住一个原则:网关是手段,不是目的。选一个团队能驾驭的网关,把精力放在业务逻辑上,远比追求极致性能更有价值。
🔗 相关工具推荐
- jsjson.com JSON 格式化工具 — API 网关配置文件中的 JSON 格式化
- jsjson.com JWT 解码工具 — 调试网关 JWT 认证插件时的必备工具
- jsjson.com Base64 编解码 — 处理网关配置中的编码数据
- decK — Kong 的声明式配置管理工具
- APISIX Dashboard — APISIX 的可视化管理面板
- Higress Console — Higress 的控制台