返回 筑基・网络云路秘径
API网关设计与实现
博主
大约 6 分钟
API网关设计与实现
一、问题引入:微服务入口的治理难题
1.1 真实案例:网关重构之路
场景:微服务从10个扩展到100个,客户端调用混乱
问题:每个客户端需要维护大量服务地址
解决方案演进:
┌─────────────────────────────────────────────────────────────┐
│ 阶段1:直连模式(问题) │
│ 客户端 → 服务A/服务B/服务C... │
│ - 客户端需要知道所有服务地址 │
│ - 认证逻辑分散在各服务 │
│ - 限流熔断难以统一管理 │
├─────────────────────────────────────────────────────────────┤
│ 阶段2:引入API网关 │
│ 客户端 → API网关 → 服务A/服务B/服务C... │
│ - 统一入口,客户端只需知道网关地址 │
│ - 统一认证鉴权 │
│ - 统一流量控制 │
│ - 统一监控日志 │
└─────────────────────────────────────────────────────────────┘
二、API网关核心功能
API网关功能架构:
┌──────────────────────────────────────────────────────────────┐
│ 客户端请求 │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 接入层 │ │
│ │ - 协议转换(HTTP/HTTPS/WebSocket/gRPC) │ │
│ │ - 负载均衡 │ │
│ │ - SSL终止 │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 安全层 │ │
│ │ - 身份认证(JWT/OAuth2/API Key) │ │
│ │ - 权限鉴权(RBAC/ABAC) │ │
│ │ - 防刷限流 │ │
│ │ - WAF防护 │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 流量控制层 │ │
│ │ - 路由转发 │ │
│ │ - 限流熔断 │ │
│ │ - 灰度发布 │ │
│ │ - 服务降级 │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 协议层 │ │
│ │ - 请求/响应转换 │ │
│ │ - 协议适配(REST/gRPC/Dubbo) │ │
│ │ - 数据格式转换(JSON/XML) │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 后端服务 │
└──────────────────────────────────────────────────────────────┘
三、Spring Cloud Gateway实现
3.1 基础配置
# application.yml
spring:
cloud:
gateway:
routes:
# 订单服务路由
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
filters:
- StripPrefix=1
- name: Retry
args:
retries: 3
statuses: BAD_GATEWAY
- name: CircuitBreaker
args:
name: orderCircuitBreaker
fallbackUri: forward:/fallback/order
# 用户服务路由
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/users/**
- Method=GET,POST
filters:
- StripPrefix=1
- AddRequestHeader=X-Request-From,Gateway
- RequestRateLimiter=
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
# 全局默认过滤器
default-filters:
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
- AddResponseHeader=X-Gateway-Version,1.0
# 全局跨域配置
globalcors:
cors-configurations:
'[/**]':
allowedOrigins: "https://example.com"
allowedMethods: "*"
allowedHeaders: "*"
allowCredentials: true
3.2 自定义过滤器
/**
* JWT认证过滤器
*/
@Component
public class JwtAuthenticationFilter implements GlobalFilter, Ordered {
@Autowired
private JwtTokenProvider tokenProvider;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// 跳过白名单路径
if (isWhiteList(request.getPath().value())) {
return chain.filter(exchange);
}
// 获取Token
String token = extractToken(request);
if (token == null) {
return unauthorized(exchange, "Missing token");
}
// 验证Token
try {
Claims claims = tokenProvider.validateToken(token);
// 将用户信息传递到下游服务
ServerHttpRequest mutatedRequest = request.mutate()
.header("X-User-Id", claims.getSubject())
.header("X-User-Roles", claims.get("roles").toString())
.build();
return chain.filter(exchange.mutate().request(mutatedRequest).build());
} catch (Exception e) {
return unauthorized(exchange, "Invalid token");
}
}
private String extractToken(ServerHttpRequest request) {
String bearerToken = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
private Mono<Void> unauthorized(ServerWebExchange exchange, String message) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
String body = String.format("{\"error\":\"%s\"}", message);
DataBuffer buffer = response.bufferFactory().wrap(body.getBytes());
return response.writeWith(Mono.just(buffer));
}
@Override
public int getOrder() {
return -100; // 高优先级
}
}
3.3 限流实现
/**
* 基于Redis的分布式限流
*/
@Component
public class RateLimiterConfig {
@Bean
public RedisRateLimiter redisRateLimiter(ReactiveRedisTemplate<String, String> redisTemplate) {
return new RedisRateLimiter(10, 20); // replenishRate, burstCapacity
}
@Bean
KeyResolver userKeyResolver() {
// 按用户限流
return exchange -> Mono.just(
exchange.getRequest().getHeaders().getFirst("X-User-Id")
);
}
@Bean
KeyResolver ipKeyResolver() {
// 按IP限流
return exchange -> Mono.just(
exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()
);
}
}
/**
* 自定义限流过滤器
*/
@Component
public class CustomRateLimiterFilter extends AbstractGatewayFilterFactory<CustomRateLimiterFilter.Config> {
@Autowired
private StringRedisTemplate redisTemplate;
public CustomRateLimiterFilter() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
String key = "rate_limit:" + getClientId(exchange);
Long current = redisTemplate.opsForValue().increment(key);
if (current == 1) {
redisTemplate.expire(key, config.getWindowSize(), TimeUnit.SECONDS);
}
if (current > config.getMaxRequests()) {
exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
};
}
@Data
public static class Config {
private int maxRequests = 100;
private int windowSize = 60; // 秒
}
}
四、Kong网关配置
# kong.yml 声明式配置
_format_version: "3.0"
services:
- name: order-service
url: http://order-service:8080
routes:
- name: order-routes
paths:
- /api/orders
strip_path: true
plugins:
- name: rate-limiting
config:
minute: 100
policy: redis
redis_host: redis
- name: jwt
config:
uri_param_names: []
cookie_names: []
key_claim_name: iss
secret_is_base64: false
claims_to_verify:
- exp
- name: cors
config:
origins:
- "https://example.com"
methods:
- GET
- POST
- PUT
- DELETE
headers:
- Authorization
- Content-Type
max_age: 3600
- name: user-service
url: http://user-service:8080
routes:
- name: user-routes
paths:
- /api/users
plugins:
- name: key-auth
- name: prometheus
consumers:
- username: mobile-app
jwt_secrets:
- algorithm: HS256
secret: "${JWT_SECRET}"
五、网关高可用设计
高可用架构:
┌──────────────────────────────────────────────────────────────┐
│ 负载均衡器 │
│ (Nginx/ALB/CLB) │
│ │ │
│ ┌──────────────┼──────────────┐ │
│ ▼ ▼ ▼ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Gateway │◄──►│ Gateway │◄──►│ Gateway │ │
│ │ #1 │ │ #2 │ │ #3 │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │
│ └──────────────┼──────────────┘ │
│ │ │
│ ┌──────┴──────┐ │
│ │ Consul │ │
│ │ /Eureka │ │
│ └─────────────┘ │
│ │ │
│ ┌─────────────┼─────────────┐ │
│ ▼ ▼ ▼ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │Service A│ │Service B│ │Service C│ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │
└──────────────────────────────────────────────────────────────┘
六、网关监控指标
关键监控指标:
┌──────────────────────────────────────────────────────────────┐
│ │
│ 性能指标: │
│ - 请求延迟(P50/P95/P99) │
│ - 吞吐量(QPS) │
│ - 错误率(4xx/5xx) │
│ │
│ 资源指标: │
│ - CPU使用率 │
│ - 内存使用率 │
│ - 连接数 │
│ │
│ 业务指标: │
│ - 各路由请求量 │
│ - 限流触发次数 │
│ - 熔断触发次数 │
│ │
└──────────────────────────────────────────────────────────────┘
系列上一篇:微服务网络通信模式
系列下一篇:网络安全防护体系
知识点测试
读完文章了?来测试一下你对知识点的掌握程度吧!
评论区
使用 GitHub 账号登录后即可发表评论,支持 Markdown 格式。
如果评论系统无法加载,请确保:
- 您的网络可以访问 GitHub
- giscus GitHub App 已安装到仓库
- 仓库已启用 Discussions 功能