API 网关配置
概览
设计和配置 API 网关以处理微服务架构中的路由、认证、速率限制和请求/响应转换。
何时使用
- 为微服务设置反向代理
- 集中 API 认证
- 实施请求/响应转换
- 管理后端服务之间的流量
- 速率限制和配额执行
- API 版本控制和路由
指南
1. Kong 配置
# kong.yml - Kong 网关配置
_format_version: "2.1"
_transform: true
services:
- name: user-service
url: http://user-service:3000
routes:
- name: user-routes
paths:
- /api/users
- /api/profile
plugins:
- name: rate-limiting
config:
minute: 100
policy: local
- name: jwt
config:
secret: your-secret-key
key_claim_name: "sub"
- name: cors
config:
origins:
- "http://localhost:3000"
- "https://example.com"
methods:
- GET
- POST
- PUT
- DELETE
allow_headers:
- Content-Type
- Authorization
- name: product-service
url: http://product-service:3001
routes:
- name: product-routes
paths:
- /api/products
plugins:
- name: rate-limiting
config:
minute: 500
- name: request-transformer
config:
add:
headers:
- "X-Service-Name:product-service"
- name: order-service
url: http://order-service:3002
routes:
- name: order-routes
paths:
- /api/orders
plugins:
- name: jwt
- name: request-size-limiting
config:
allowed_payload_size: 5
consumers:
- username: mobile-app
custom_id: mobile-app-001
acls:
- group: api-users
plugins:
- name: prometheus
config:
latency_metrics: true
upstream_addr_header: X-Upstream-Addr
2. Nginx 配置
# nginx.conf - API 网关配置
upstream user_service {
server user-service:3000;
keepalive 32;
}
upstream product_service {
server product-service:3001;
keepalive 32;
}
upstream order_service {
server order-service:3002;
keepalive 32;
}
# 速率限制
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
limit_req_zone $http_x_api_key zone=user_limit:10m rate=100r/s;
server {
listen 80;
server_name api.example.com;
# 启用 gzip 压缩
gzip on;
gzip_types application/json;
gzip_min_length 1000;
# 用户服务路由
location /api/users {
limit_req zone=api_limit burst=20 nodelay;
# 认证检查
access_by_lua_block {
local token = ngx.var.http_authorization
if not token then
return ngx.HTTP_UNAUTHORIZED
end
}
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_connect_timeout 10s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
}
# 产品服务路由
location /api/products {
limit_req zone=api_limit burst=50 nodelay;
proxy_pass http://product_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_cache api_cache;
proxy_cache_valid 200 1m;
proxy_cache_key "$scheme$request_method$host$request_uri";
}
# 订单服务路由(需要认证)
location /api/orders {
limit_req zone=user_limit burst=10 nodelay;
auth_request /auth;
auth_request_set $auth_user $upstream_http_x_user_id;
proxy_pass http://order_service;
proxy_http_version 1.1;
proxy_set_header X-User-ID $auth_user;
}
# 健康检查端点
location /health {
access_log off;
return 200 "healthy
";
add_header Content-Type text/plain;
}
# 指标端点
location /metrics {
stub_status on;
access_log off;
}
}
# 缓存定义
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:10m max_size=1g inactive=60m;
3. AWS API 网关配置
# AWS SAM 模板用于 API 网关
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
ApiGateway:
Type: AWS::Serverless::Api
Properties:
StageName: prod
Auth:
DefaultAuthorizer: JwtAuthorizer
Authorizers:
JwtAuthorizer:
FunctionArn: !GetAtt AuthorizerFunction.Arn
Identity:
Headers:
- Authorization
TracingEnabled: true
MethodSettings:
- ResourcePath: '/*'
HttpMethod: '*'
LoggingLevel: INFO
DataTraceEnabled: true
MetricsEnabled: true
ThrottleSettings:
BurstLimit: 1000
RateLimit: 100
UserServiceFunction:
Type: AWS::Serverless::Function
Properties:
Handler: index.handler
Runtime: nodejs18.x
Environment:
Variables:
USER_SERVICE_URL: !Sub 'https://${UserServiceAlb}.elb.amazonaws.com'
Events:
GetUsers:
Type: Api
Properties:
RestApiId: !Ref ApiGateway
Path: /api/users
Method: GET
Auth:
Authorizer: JwtAuthorizer
CreateUser:
Type: Api
Properties:
RestApiId: !Ref ApiGateway
Path: /api/users
Method: POST
Auth:
Authorizer: JwtAuthorizer
ApiUsagePlan:
Type: AWS::ApiGateway::UsagePlan
Properties:
UsagePlanName: StandardPlan
ApiStages:
- ApiId: !Ref ApiGateway
Stage: prod
Quota:
Limit: 10000
Period: DAY
Throttle:
RateLimit: 100
BurstLimit: 1000
ApiKey:
Type: AWS::ApiGateway::ApiKey
Properties:
Name: StandardKey
Enabled: true
UsagePlanId: !Ref ApiUsagePlan
4. Traefik 配置
# traefik.yml - Traefik API 网关
global:
checkNewVersion: false
sendAnonymousUsage: false
entryPoints:
web:
address: ":80"
websecure:
address: ":443"
api:
insecure: true
dashboard: true
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
file:
filename: dynamic.yml
middleware:
rateLimit:
rateLimit:
average: 100
burst: 50
authMiddleware:
basicAuth:
users:
- "user:$apr1$r31.....$HqJZimcKQFAMYayBlzkrA/"
routers:
api-users:
entrypoints:
- websecure
rule: "Path(`/api/users`)"
service: user-service
tls:
certResolver: letsencrypt
middlewares:
- rateLimit
api-products:
entrypoints:
- web
rule: "Path(`/api/products`)"
service: product-service
services:
user-service:
loadBalancer:
servers:
- url: "http://user-service:3000"
healthCheck:
scheme: http
path: /health
interval: 10s
timeout: 5s
product-service:
loadBalancer:
servers:
- url: "http://product-service:3001"
5. Node.js 网关实现
const express = require('express');
const httpProxy = require('express-http-proxy');
const rateLimit = require('express-rate-limit');
const jwt = require('jsonwebtoken');
const app = express();
// 速率限制
const limiter = rateLimit({
windowMs: 60 * 1000,
max: 100
});
// JWT 验证
const verifyJwt = (req, res, next) => {
const token = req.headers['authorization']?.split(' ')[1];
if (!token) return res.status(401).json({ error: 'No token' });
try {
jwt.verify(token, process.env.JWT_SECRET);
next();
} catch (err) {
res.status(403).json({ error: 'Invalid token' });
}
};
// 请求日志
app.use((req, res, next) => {
console.log(`${new Date().toISOString()} ${req.method} ${req.path}`);
next();
});
app.use(limiter);
// 用户服务代理
app.use('/api/users', verifyJwt, httpProxy('http://user-service:3000', {
proxyReqPathResolver: (req) => `/api/users${req.url}`,
userResDecorator: (proxyRes, proxyResData, userReq, userRes) => {
proxyRes.headers['X-Gateway'] = 'true';
return proxyResData;
}
}));
// 产品服务代理
app.use('/api/products', httpProxy('http://product-service:3001', {
proxyReqPathResolver: (req) => `/api/products${req.url}`
}));
// 健康检查
app.get('/health', (req, res) => res.json({ status: 'ok' }));
app.listen(8080, () => console.log('Gateway on port 8080'));
最佳实践
✅ DO
- 在网关级别集中认证
- 全局实施速率限制
- 添加全面的日志记录
- 使用健康检查后端
- 适当时缓存响应
- 实施断路器
- 监控网关指标
- 生产中使用 HTTPS
❌ DON’T
- 暴露后端服务细节
- 跳过请求验证
- 忘记记录 API 使用情况
- 使用弱认证
- 过度缓存动态数据
- 忽略后端超时
- 跳过安全头
- 暴露内部 IP
监控与可观测性
// Prometheus 指标
const promClient = require('prom-client');
const httpRequestDuration = new promClient.Histogram({
name: 'gateway_http_request_duration_seconds',
help: 'Duration of HTTP requests in seconds',
labelNames: ['method', 'route', 'status_code']
});
const httpRequests = new promClient.Counter({
name: 'gateway_http_requests_total',
help: 'Total number of HTTP requests',
labelNames: ['method', 'route', 'status_code']
});
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = (Date.now() - start) / 1000;
httpRequestDuration.labels(req.method, req.path, res.statusCode).observe(duration);
httpRequests.labels(req.method, req.path, res.statusCode).inc();
});
next();
});