API 网关
概述
关于使用 Kong、NGINX 和 AWS API Gateway 实现微服务的 API 网关模式的全面指南。
目录
API 网关概念
核心概念
## API 网关核心概念
### 什么是 API 网关?
- 所有客户端请求的单一入口点
- 将请求路由到适当的微服务
- 处理跨领域关注点
### 关键职责
- 请求路由
- 认证与授权
- 速率限制
- 负载均衡
- 请求/响应转换
- 缓存
- 日志与监控
### 优势
- 简化客户端架构
- 集中化跨领域关注点
- 提供统一的 API 接口
- 支持服务版本控制
- 提升安全态势
网关架构
// gateway-architecture.ts
export interface GatewayConfig {
name: string;
routes: Route[];
services: Service[];
plugins: Plugin[];
upstreams: Upstream[];
}
export interface Route {
name: string;
paths: string[];
methods: string[];
service: string;
plugins?: string[];
}
export interface Service {
name: string;
url: string;
retries?: number;
connect_timeout?: number;
write_timeout?: number;
read_timeout?: number;
}
export interface Plugin {
name: string;
config: Record<string, any>;
}
export interface Upstream {
name: string;
targets: Target[];
algorithm: 'round-robin' | 'least-connections' | 'ip-hash';
healthchecks?: HealthCheck;
}
export interface Target {
target: string; // host:port
weight: number;
}
export interface HealthCheck {
active: {
type: 'http' | 'https' | 'tcp';
http_path: string;
healthy: { interval: number; successes: number };
unhealthy: { interval: number; http_failures: number };
};
}
Kong 设置与配置
Docker 设置
# docker-compose.yml
version: '3.8'
services:
kong:
image: kong/kong-gateway:3.4
ports:
- "8000:8000" # HTTP
- "8443:8443" # HTTPS
- "8001:8001" # Admin API
environment:
KONG_DATABASE: "off"
KONG_PROXY_ACCESS_LOG: /dev/stdout
KONG_ADMIN_ACCESS_LOG: /dev/stdout
KONG_PROXY_ERROR_LOG: /dev/stdout
KONG_ADMIN_ERROR_LOG: /dev/stdout
KONG_ADMIN_LISTEN: "0.0.0.0:8001"
networks:
- kong-net
konga:
image: pantsel/konga
ports:
- "1337:1337"
environment:
NODE_ENV: production
networks:
- kong-net
networks:
kong-net:
driver: bridge
服务配置
// kong-service.ts
import axios from 'axios';
export class KongServiceManager {
private adminUrl: string;
constructor(adminUrl: string = 'http://localhost:8001') {
this.adminUrl = adminUrl;
}
async createService(config: any): Promise<any> {
const response = await axios.post(`${this.adminUrl}/services`, config);
return response.data;
}
async listServices(): Promise<any[]> {
const response = await axios.get(`${this.adminUrl}/services`);
return response.data.data;
}
async getService(serviceId: string): Promise<any> {
const response = await axios.get(`${this.adminUrl}/services/${serviceId}`);
return response.data;
}
async updateService(serviceId: string, config: any): Promise<any> {
const response = await axios.patch(`${this.adminUrl}/services/${serviceId}`, config);
return response.data;
}
async deleteService(serviceId: string): Promise<void> {
await axios.delete(`${this.adminUrl}/services/${serviceId}`);
}
}
// 使用
const kong = new KongServiceManager();
// 创建用户服务
await kong.createService({
name: 'user-service',
url: 'http://user-service:3000',
retries: 3,
connect_timeout: 60000,
write_timeout: 60000,
read_timeout: 60000
});
路由配置
// kong-route.ts
export class KongRouteManager {
private adminUrl: string;
constructor(adminUrl: string = 'http://localhost:8001') {
this.adminUrl = adminUrl;
}
async createRoute(serviceId: string, config: any): Promise<any> {
const response = await axios.post(`${this.adminUrl}/services/${serviceId}/routes`, config);
return response.data;
}
async listRoutes(): Promise<any[]> {
const response = await axios.get(`${this.adminUrl}/routes`);
return response.data.data;
}
async updateRoute(routeId: string, config: any): Promise<any> {
const response = await axios.patch(`${this.adminUrl}/routes/${routeId}`, config);
return response.data;
}
async deleteRoute(routeId: string): Promise<void> {
await axios.delete(`${this.adminUrl}/routes/${routeId}`);
}
}
// 使用
const routeManager = new KongRouteManager();
// 创建用户路由
await routeManager.createRoute('user-service', {
name: 'user-routes',
paths: ['/api/users'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
strip_path: false,
preserve_host: false
});
// 创建特定用户路由
await routeManager.createRoute('user-service', {
name: 'user-by-id',
paths: ['/api/users/:id'],
methods: ['GET', 'PUT', 'DELETE']
});
插件配置
// kong-plugin.ts
export class KongPluginManager {
private adminUrl: string;
constructor(adminUrl: string = 'http://localhost:8001') {
this.adminUrl = adminUrl;
}
async enablePluginForService(serviceId: string, pluginName: string, config: any): Promise<any> {
const response = await axios.post(
`${this.adminUrl}/services/${serviceId}/plugins`,
{
name: pluginName,
config
}
);
return response.data;
}
async enablePluginForRoute(routeId: string, pluginName: string, config: any): Promise<any> {
const response = await axios.post(
`${this.adminUrl}/routes/${routeId}/plugins`,
{
name: pluginName,
config
}
);
return response.data;
}
async enableGlobalPlugin(pluginName: string, config: any): Promise<any> {
const response = await axios.post(
`${this.adminUrl}/plugins`,
{
name: pluginName,
config
}
);
return response.data;
}
async listPlugins(): Promise<any[]> {
const response = await axios.get(`${this.adminUrl}/plugins`);
return response.data.data;
}
}
// 使用
const pluginManager = new KongPluginManager();
// 启用速率限制
await pluginManager.enablePluginForService('user-service', 'rate-limiting', {
minute: 100,
hour: 1000,
policy: 'local',
fault_tolerant: true
});
// 启用 JWT 认证
await pluginManager.enablePluginForService('user-service', 'jwt', {
key_claim_name: 'sub',
claims_to_verify: ['exp']
});
// 启用 CORS
await pluginManager.enableGlobalPlugin('cors', {
origins: ['*'],
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
headers: ['Accept', 'Content-Type', 'Authorization'],
exposed_headers: ['X-Total-Count'],
max_age: 3600,
credentials: true
});
NGINX 作为 API 网关
NGINX 配置
# nginx.conf
events {
worker_connections 1024;
}
http {
upstream user_service {
least_conn;
server user-service-1:3000 weight=3;
server user-service-2:3000 weight=2;
server user-service-3:3000 weight=1;
keepalive 32;
}
upstream order_service {
least_conn;
server order-service-1:3000;
server order-service-2:3000;
keepalive 32;
}
# 速率限制区
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
server {
listen 80;
server_name api.example.com;
# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
# CORS
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
# 处理预检请求
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
# 用户服务路由
location /api/users {
limit_req zone=api_limit burst=20 nodelay;
proxy_pass http://user_service;
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 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# 订单服务路由
location /api/orders {
limit_req zone=api_limit burst=20 nodelay;
proxy_pass http://order_service;
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 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# 健康检查端点
location /health {
access_log off;
return 200 "healthy
";
add_header Content-Type text/plain;
}
# 错误页面
error_page 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
internal;
}
}
}
动态配置
// nginx-config-generator.ts
export interface UpstreamConfig {
name: string;
servers: Array<{ host: string; port: number; weight?: number }>;
algorithm: 'round-robin' | 'least_conn' | 'ip_hash';
}
export interface RouteConfig {
path: string;
upstream: string;
methods?: string[];
rateLimit?: { zone: string; rate: string; burst: number };
}
export class NginxConfigGenerator {
generateUpstream(config: UpstreamConfig): string {
const servers = config.servers.map(server => {
const weight = server.weight ? ` weight=${server.weight}` : '';
return ` server ${server.host}:${server.port}${weight};`;
}).join('
');
return `upstream ${config.name} {
${config.algorithm};
${servers}
keepalive 32;
}`;
}
generateLocation(config: RouteConfig): string {
const methods = config.methods ? ` if ($request_method !~ "^(${config.methods.join('|')})$") { return 405; }
` : '';
const rateLimit = config.rateLimit
? ` limit_req zone=${config.rateLimit.zone} rate=${config.rateLimit.rate} burst=${config.rateLimit.burst} nodelay;
`
: '';
return `location ${config.path} {
${methods}${rateLimit} proxy_pass http://${config.upstream};
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;
}`;
}
generateConfig(upstreams: UpstreamConfig[], routes: RouteConfig[]): string {
const upstreamBlock = upstreams.map(u => this.generateUpstream(u)).join('
');
const locationBlock = routes.map(r => this.generateLocation(r)).join('
');
return `events {
worker_connections 1024;
}
http {
${upstreamBlock}
server {
listen 80;
server_name api.example.com;
${locationBlock}
}
}`;
}
}
// 使用
const generator = new NginxConfigGenerator();
const config = generator.generateConfig(
[
{
name: 'user_service',
servers: [
{ host: 'user-service-1', port: 3000, weight: 3 },
{ host: 'user-service-2', port: 3000, weight: 2 }
],
algorithm: 'least_conn'
}
],
[
{
path: '/api/users',
upstream: 'user_service',
methods: ['GET', 'POST'],
rateLimit: { zone: 'api_limit', rate: '10r/s', burst: 20 }
}
]
);
功能
认证
// gateway-auth.ts
export interface AuthConfig {
type: 'jwt' | 'oauth2' | 'basic' | 'api-key';
config: Record<string, any>;
}
export class GatewayAuth {
constructor(private kongManager: KongPluginManager) {}
async setupJWT(serviceId: string, config: any): Promise<void> {
await this.kongManager.enablePluginForService(serviceId, 'jwt', {
key_claim_name: 'sub',
secret_is_base64: false,
claims_to_verify: ['exp'],
cookie_names: ['jwt'],
uri_param_names: ['jwt']
});
}
async setupOAuth2(serviceId: string, config: any): Promise<void> {
await this.kongManager.enablePluginForService(serviceId, 'oauth2', {
scopes: ['read', 'write'],
mandatory_scope: true,
enable_authorization_code: true,
enable_client_credentials: true,
enable_implicit_grant: false,
enable_password_grant: false
});
}
async setupBasicAuth(serviceId: string): Promise<void> {
await this.kongManager.enablePluginForService(serviceId, 'basic-auth', {
hide_credentials: true
});
}
async setupAPIKey(serviceId: string): Promise<void> {
await this.kongManager.enablePluginForService(serviceId, 'key-auth', {
key_names: ['apikey', 'X-API-Key'],
hide_credentials: true
});
}
}
速率限制
// gateway-rate-limit.ts
export interface RateLimitConfig {
minute?: number;
hour?: number;
day?: number;
policy: 'local' | 'redis' | 'cluster';
faultTolerant: boolean;
}
export class GatewayRateLimit {
constructor(private kongManager: KongPluginManager) {}
async setupRateLimit(serviceId: string, config: RateLimitConfig): Promise<void> {
await this.kongManager.enablePluginForService(serviceId, 'rate-limiting', {
minute: config.minute,
hour: config.hour,
day: config.day,
policy: config.policy,
fault_tolerant: config.faultTolerant,
redis_host: process.env.REDIS_HOST || 'localhost',
redis_port: parseInt(process.env.REDIS_PORT || '6379'),
redis_password: process.env.REDIS_PASSWORD,
redis_database: parseInt(process.env.REDIS_DB || '0')
});
}
async setupRateLimitByIP(serviceId: string, config: RateLimitConfig): Promise<void> {
await this.kongManager.enablePluginForService(serviceId, 'rate-limiting', {
...config,
limit_by: 'ip'
});
}
async setupRateLimitByConsumer(serviceId: string, config: RateLimitConfig): Promise<void> {
await this.kongManager.enablePluginForService(serviceId, 'rate-limiting', {
...config,
limit_by: 'consumer'
});
}
}
请求/响应转换
// gateway-transformation.ts
export interface TransformConfig {
add?: { headers?: Record<string, string>; querystring?: Record<string, string> };
remove?: { headers?: string[]; querystring?: string[]; body?: string[] };
replace?: { headers?: Record<string, string>; body?: Record<string, string> };
}
export class GatewayTransformation {
constructor(private kongManager: KongPluginManager) {}
async setupRequestTransform(serviceId: string, config: TransformConfig): Promise<void> {
await this.kongManager.enablePluginForService(serviceId, 'request-transformer', {
add: config.add,
remove: config.remove,
replace: config.replace
});
}
async setupResponseTransform(serviceId: string, config: TransformConfig): Promise<void> {
await this.kongManager.enablePluginForService(serviceId, 'response-transformer', {
add: config.add,
remove: config.remove,
replace: config.replace
});
}
async setupBodyTransform(serviceId: string, config: any): Promise<void> {
await this.kongManager.enablePluginForService(serviceId, 'request-transformer', {
append: {
body: config.append
}
});
}
}
路由策略
基于路径的路由
// path-routing.ts
export interface PathRoute {
path: string;
service: string;
stripPath: boolean;
}
export class PathRouter {
async setupRoutes(routes: PathRoute[]): Promise<void> {
const kong = new KongServiceManager();
const routeManager = new KongRouteManager();
for (const route of routes) {
// 确保服务存在
const services = await kong.listServices();
const service = services.find(s => s.name === route.service);
if (!service) {
throw new Error(`Service ${route.service} not found`);
}
// 创建路由
await routeManager.createRoute(service.id, {
name: `${route.service}-route`,
paths: [route.path],
strip_path: route.stripPath,
preserve_host: false
});
}
}
}
// 使用
const router = new PathRouter();
await router.setupRoutes([
{
path: '/api/v1/users',
service: 'user-service',
stripPath: false
},
{
path: '/api/v1/orders',
service: 'order-service',
stripPath: false
}
]);
基于头部的路由
// header-routing.ts
export interface HeaderRoute {
name: string;
headers: Record<string, string>;
service: string;
}
export class HeaderRouter {
async setupRoutes(routes: HeaderRoute[]): Promise<void> {
const kong = new KongServiceManager();
const routeManager = new KongRouteManager();
for (const route of routes) {
const services = await kong.listServices();
const service = services.find(s => s.name === route.service);
if (!service) {
throw new Error(`Service ${route.service} not found`);
}
// 创建带有头部匹配的路由
await routeManager.createRoute(service.id, {
name: route.name,
headers: Object.entries(route.headers).map(([name, value]) => ({
name,
value
})),
strip_path: false
});
}
}
}
// 使用
const router = new HeaderRouter();
await router.setupRoutes([
{
name: 'mobile-route',
headers: { 'X-Client-Type': 'mobile' },
service: 'mobile-service'
},
{
name: 'web-route',
headers: { 'X-Client-Type': 'web' },
service: 'web-service'
}
]);
安全
安全头
// security-headers.ts
export class SecurityHeaders {
static getHeaders(): Record<string, string> {
return {
'X-Frame-Options': 'SAMEORIGIN',
'X-Content-Type-Options': 'nosniff',
'X-XSS-Protection': '1; mode=block',
'Referrer-Policy': 'strict-origin-when-cross-origin',
'Permissions-Policy': 'geolocation=(), microphone=(), camera=()',
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains'
};
}
static setupCORS(allowedOrigins: string[]): Record<string, string> {
return {
'Access-Control-Allow-Origin': allowedOrigins.join(','),
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Allow-Credentials': 'true',
'Access-Control-Max-Age': '86400'
};
}
}
监控
指标收集
// gateway-metrics.ts
import promClient from 'prom-client';
export class GatewayMetrics {
private requestCounter: promClient.Counter;
private responseTime: promClient.Histogram;
private errorCounter: promClient.Counter;
constructor() {
this.requestCounter = new promClient.Counter({
name: 'gateway_requests_total',
help: '总请求数',
labelNames: ['service', 'method', 'status']
});
this.responseTime = new promClient.Histogram({
name: 'gateway_response_time_seconds',
help: '响应时间(秒)',
labelNames: ['service', 'method'],
buckets: [0.01, 0.05, 0.1, 0.5, 1, 5]
});
this.errorCounter = new promClient.Counter({
name: 'gateway_errors_total',
help: '总错误数',
labelNames: ['service', 'error_type']
});
}
recordRequest(service: string, method: string, status: number): void {
this.requestCounter.inc({ service, method, status: status.toString() });
}
recordResponseTime(service: string, method: string, duration: number): void {
this.responseTime.observe({ service, method }, duration);
}
recordError(service: string, errorType: string): void {
this.errorCounter.inc({ service, error_type: errorType });
}
getMetrics(): string {
return promClient.register.metrics();
}
}
高可用性
负载均衡
// load-balancer.ts
export interface LoadBalancerConfig {
algorithm: 'round-robin' | 'least-connections' | 'ip-hash' | 'consistent-hash';
healthCheck: {
enabled: boolean;
interval: number;
timeout: number;
unhealthyThreshold: number;
healthyThreshold: number;
};
}
export class LoadBalancer {
setupLoadBalancer(upstreamName: string, targets: string[], config: LoadBalancerConfig): string {
const targetsBlock = targets.map(target => ` server ${target};`).join('
');
return `upstream ${upstreamName} {
${config.algorithm};
${targetsBlock}
keepalive 32;
}`;
}
}
// 使用
const lb = new LoadBalancer();
const config = lb.setupLoadBalancer('user_service', [
'user-service-1:3000',
'user-service-2:3000',
'user-service-3:3000'
], {
algorithm: 'least-connections',
healthCheck: {
enabled: true,
interval: 30,
timeout: 5,
unhealthyThreshold: 3,
healthyThreshold: 2
}
});
生产部署
Docker Compose 生产配置
# docker-compose.prod.yml
version: '3.8'
services:
kong:
image: kong/kong-gateway:3.4
ports:
- "80:8000"
- "443:8443"
- "8444:8001"
environment:
KONG_DATABASE: "postgres"
KONG_PG_HOST: kong-db
KONG_PG_DATABASE: kong
KONG_PG_USER: kong
KONG_PG_PASSWORD: ${KONG_PG_PASSWORD}
KONG_PROXY_ACCESS_LOG: /dev/stdout
KONG_ADMIN_ACCESS_LOG: /dev/stdout
KONG_PROXY_ERROR_LOG: /dev/stdout
KONG_ADMIN_ERROR_LOG: /dev/stdout
KONG_ADMIN_LISTEN: "0.0.0.0:8001"
KONG_ADMIN_GUI_URL: "http://localhost:8002"
KONG_SSL: "on"
KONG_SSL_CERT: /etc/kong/tls/kong.crt
KONG_SSL_CERT_KEY: /etc/kong/tls/kong.key
volumes:
- ./tls:/etc/kong/tls
depends_on:
- kong-db
networks:
- gateway-net
restart: unless-stopped
kong-db:
image: postgres:15-alpine
environment:
POSTGRES_DB: kong
POSTGRES_USER: kong
POSTGRES_PASSWORD: ${KONG_PG_PASSWORD}
volumes:
- kong-data:/var/lib/postgresql/data
networks:
- gateway-net
restart: unless-stopped
konga:
image: pantsel/konga:next
ports:
- "8002:1337"
environment:
NODE_ENV: production
DB_ADAPTER: postgres
DB_URI: postgresql://kong:${KONG_PG_PASSWORD}@kong-db:5432/konga
depends_on:
- kong-db
networks:
- gateway-net
restart: unless-stopped
volumes:
kong-data:
networks:
gateway-net:
driver: bridge
最佳实践
API 网关检查清单
## API 网关最佳实践检查清单
### 配置
- [ ] 定义清晰的路由规则
- [ ] 设置服务发现
- [ ] 配置负载均衡
- [ ] 启用健康检查
### 安全
- [ ] 实施认证
- [ ] 启用速率限制
- [ ] 添加安全头
- [ ] 正确配置 CORS
- [ ] 启用 HTTPS/TLS
### 性能
- [ ] 启用缓存
- [ ] 配置超时
- [ ] 设置压缩
- [ ] 优化连接池
### 监控
- [ ] 启用访问日志
- [ ] 设置指标收集
- [ ] 配置告警
- [ ] 监控错误率
### 可靠性
- [ ] 设置断路器
- [ ] 配置重试
- [ ] 启用优雅降级
- [ ] 规划高可用性