MonkeyCode构建API网关:从零到Kong/Nginx的全链路实战
所有微服务的统一入口:认证、限流、日志、灰度。API网关做对了,后端才能真正专心做业务。
API网关是什么?
客户端请求
↓
┌─────────────────┐
│ API Gateway │ ← 所有请求的单一入口
│ ┌───────────┐ │
│ │ 认证鉴权 │ │
│ │ 限流熔断 │ │
│ │ 路由转发 │ │
│ │ 日志监控 │ │
│ │ 协议转换 │ │
│ │ 灰度发布 │ │
│ └───────────┘ │
└─────────────────┘
↓ ↓ ↓
用户服务 订单服务 支付服务
方案对比:选型指南
| 方案 | 适用规模 | 性能 | 配置方式 | 学习成本 |
|---|---|---|---|---|
| Nginx + Lua | 中小型(<100QPS) | 极高 | conf文件 | 高 |
| Kong | 中大型 | 高 | REST API + DB | 中 |
| APISIX | 大型(云原生) | 高 | Admin API | 中 |
| Traefik | K8s原生 | 中 | 注解/ConfigMap | 低 |
| 自研(Go/Node) | 特定需求 | 中 | 代码 | 高 |
实战一:Nginx + Kong(最成熟方案)
Kong + Docker Compose一键启动
# docker-compose.kong.yml
version: '3.8'
services:
kong-database:
image: postgres:15
environment:
POSTGRES_USER: kong
POSTGRES_PASSWORD: kongpass
POSTGRES_DB: kong
volumes: [pgdata:/var/lib/postgresql/data]
healthcheck:
test: ["CMD", "pg_isready", "-U", "kong"]
networks: [kong-net]
kong:
image: kong:3.4
depends_on: [kong-database]
environment:
KONG_DATABASE: postgres
KONG_PG_HOST: kong-database
KONG_PG_USER: kong
KONG_PG_PASSWORD: kongpass
KONG_ADMIN_LISTEN: 0.0.0.0:8001
KONG_PROXY_LISTEN: 0.0.0.0:8000
KONG_LOG_LEVEL: info
ports: ["8000:8000", "8443:8443", "8001:8001"]
networks: [kong-net]
konga:
image: pantsel/konga:latest
depends_on: [kong]
environment:
NODE_ENV: production
DB_ADAPTER: postgres
DB_HOST: kong-database
DB_PORT: 5432
DB_USER: kong
DB_PASSWORD: kongpass
DB_DATABASE: kong
ports: ["1337:1337"]
networks: [kong-net]
volumes:
pgdata:
networks:
kong-net:
driver: bridge
Kong Admin配置路由和服务
# 1. 创建上游服务(user-service)
curl -X POST http://localhost:8001/services/ \
-d "name=user-service" \
-d "url=http://user-service:8001" \
-d "port=8001" \
-d "protocol=http"
# 2. 添加路由
curl -X POST http://localhost:8001/services/user-service/routes/ \
-d "name=user-routes" \
-d "paths[]=/api/users" \
-d "methods=GET,POST,PUT,DELETE" \
-d "strip_path=false"
# 3. 配置JWT认证插件
curl -X POST http://localhost:8001/services/user-service/plugins/ \
-d "name=jwt" \
-d "config.uri_param_names=jwt" \
-d "config.cookie_names=jwt" \
-d "config.claims_to_verify=exp"
# 4. 配置限流插件(每个IP每分钟100次)
curl -X POST http://localhost:8001/routes/user-routes/plugins/ \
-d "name=rate-limiting" \
-d "config.minute=100" \
-d "config.policy=redis" \
-d "config.redis_host=redis" \
-d "config.redis_port=6379" \
-d "config.hide_client_headers=false"
# 5. 配置IP黑白名单
curl -X POST http://localhost:8001/routes/user-routes/plugins/ \
-d "name=ip-restriction" \
-d "config.allow=127.0.0.1,10.0.0.0/8"
实战二:Nginx配置(轻量场景)
不需要Kong,用Nginx也能做:
# /etc/nginx/conf.d/api-gateway.conf
upstream user_service {
least_conn; # 最少连接优先
server user-service-1:8001 weight=3;
server user-service-2:8001 weight=2;
keepalive 32;
}
upstream order_service {
server order-service-1:8002;
server order-service-2:8002 backup; # 热备
}
# 限流配置
limit_req_zone $binary_remote_addr zone=user_limit:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=addr:10m;
server {
listen 80;
server_name api.example.com;
# ─── 日志 ───
log_format main '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time uct="$upstream_connect_time" '
'uht="$upstream_header_time" urt="$upstream_response_time"';
access_log /var/log/nginx/api-gateway.log main;
# ─── 全局限流 ───
limit_req zone=user_limit burst=20 nodelay;
limit_conn addr 10;
# ─── 路由 ───
location /api/users/ {
proxy_pass http://user_service/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Connection "";
# 超时配置
proxy_connect_timeout 5s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
# 重试配置
proxy_next_upstream error timeout http_502 http_503;
}
location /api/orders/ {
proxy_pass http://order_service/;
# 其他配置同上...
}
# ─── 健康检查 ───
location /health {
access_log off;
return 200 "OK\n";
add_header Content-Type text/plain;
}
}
实战三:自研Go网关(最高灵活度)
// gateway/main.go
package main
import (
"net/http"
"net/http/httputil"
"net/url"
"time"
"github.com/gin-gonic/gin"
"golang.org/x/time/rate"
)
// 服务路由配置
var routes = map[string]*ServiceConfig{
"/api/users": {Target: "http://user-service:8001", RateLimit: 100},
"/api/orders": {Target: "http://order-service:8002", RateLimit: 50},
"/api/payments": {Target: "http://payment-service:8003", RateLimit: 20},
}
type ServiceConfig struct {
Target string
RateLimit int
limiter *rate.Limiter
}
func main() {
gin.SetMode(gin.ReleaseMode)
r := gin.New()
r.Use(gin.Recovery(), middlewareLogger(), AuthMiddleware(), RateLimitMiddleware())
// 动态路由:匹配前缀后转发
for prefix, cfg := range routes {
target, _ := url.Parse(cfg.Target)
proxy := httputil.NewSingleHostReverseProxy(target)
proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) {
// 错误处理:降级或熔断
w.WriteHeader(http.StatusBadGateway)
w.Write([]byte(`{"error":"service unavailable"}`))
}
cfg.limiter = rate.NewLimiter(rate.Limit(cfg.RateLimit), cfg.RateLimit*2)
r.Any(prefix+"/*path", createProxyHandler(proxy))
}
r.GET("/health", func(c *gin.Context) { c.JSON(200, gin.H{"status": "ok"}) })
r.Run(":8000")
}
func createProxyHandler(proxy *httputil.ReverseProxy) gin.HandlerFunc {
return func(c *gin.Context) {
// 修改请求路径
c.Request.URL.Path = "/" + c.Param("path")[1:]
c.Request.URL.Host = proxy.Director(c.Request).URL.Host
// 添加追踪头
traceID := generateTraceID()
c.Request.Header.Set("X-Trace-ID", traceID)
c.Header("X-Trace-ID", traceID)
// 执行代理
proxy.ServeHTTP(c.Writer, c.Request)
}
}
func RateLimitMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
for prefix, cfg := range routes {
if c.Request.URL.Path HasPrefix(prefix) {
if !cfg.limiter.Allow() {
c.AbortWithStatusJSON(http.StatusTooManyRequests,
gin.H{"error": "rate limit exceeded", "retry_after": "1s"})
return
}
break
}
}
c.Next()
}
}
认证鉴权(网关统一处理)
# Nginx + Lua 实现JWT验证
# 放在 /etc/nginx/nginx.conf 或通过OpenResty加载
access_by_lua_block {
local jwt = require("resty.jwt")
local cjson = require("cjson")
local token = ngx.var.arg_jwt -- URL参数
if not token then
-- 从Header取
local auth_header = ngx.var.http_authorization
if auth_header and string.match(auth_header, "Bearer%s+(.+)") then
token = string.match(auth_header, "Bearer%s+(.+)")
end
end
if not token then
ngx.exit(ngx.HTTP_UNAUTHORIZED)
return
end
local jwt_obj = jwt:verify("your-secret-key", token)
if not jwt_obj.valid then
ngx.exit(ngx.HTTP_UNAUTHORIZED)
return
end
-- 把用户信息注入到请求头,透传给后端
ngx.req.set_header("X-User-ID", jwt_obj.payload.sub)
ngx.req.set_header("X-User-Role", jwt_obj.payload.role)
}
灰度发布(按Header/IP/权重)
# Nginx灰度:Header-based
geo $backend {
default user-service-stable;
~* "^v2" user-service-v2;
}
# Nginx灰度:Weight-based
upstream user_service_stable {
server 10.0.0.11:8001;
}
upstream user_service_v2 {
server 10.0.0.21:8001;
}
upstream user_service_all {
server 10.0.0.11:8001 weight=7;
server 10.0.0.21:8001 weight=3; # 30%流量到v2
}
// 自研网关灰度策略
type GrayStrategy struct {
headerRules map[string]string // Header值 → 版本
ipRules map[string]string // IP段 → 版本
weightV2 float64 // v2权重(0.0~1.0)
}
func (g *GrayStrategy) Route(r *http.Request) *url.URL {
// 1. Header优先
if v := r.Header.Get("X-Canary"); v != "" {
if target := g.headerRules[v]; target != "" {
return parseURL(target)
}
}
// 2. IP白名单
clientIP := getClientIP(r)
if target := g.ipRules[clientIP]; target != "" {
return parseURL(target)
}
// 3. 权重
if rand.Float64() < g.weightV2 {
return parseURL("http://user-service-v2")
}
return parseURL("http://user-service-stable")
}
监控指标(接入Prometheus)
# Nginx开启status + Prometheus采集
location /metrics {
stub_status on;
access_log off;
}
# Prometheus配置
- job_name: 'nginx'
static_configs:
- targets: ['nginx:80']
metrics_path: '/metrics'
MonkeyCode Prompt模板
帮我的微服务架构设计API网关,需求:
1. 服务列表:[列出所有后端服务]
2. 认证方案:[JWT/Cookie/OAuth2]
3. 限流要求:[QPS/用户/IP维度]
4. 灰度策略:[按Header/权重/IP]
5. 方案选型:Nginx/Kong/APISIX/自研,给出推荐
6. 生成完整的配置文件或代码
7. 配置健康检查和降级策略
总结
API网关三大职责:路由、过滤、可观测。
- Nginx:轻量、成熟,适合中小型
- Kong:功能全、UI友好,适合中大型
- 自研:最灵活,适合有特殊需求的场景
MonkeyCode能帮你选型、配置、写代码,一站式搞定API网关。

浙公网安备 33010602011771号