跳到主要内容

微服务架构网关组件

微服务架构网关组件

📚 目录


为什么需要网关

单体架构的痛点

在传统的单体架构中,所有功能都集中在一个应用里:

客户端 → 单体应用 → 数据库

问题:

  • 部署和升级困难,整个应用需要重启
  • 扩展性差,只能整体扩容
  • 技术栈统一,不同功能模块无法选择最适合的技术
  • 代码耦合严重,一个模块出问题影响整个应用

微服务架构的挑战

微服务架构将应用拆分成多个独立的服务:

客户端 → 用户服务
客户端 → 订单服务
客户端 → 商品服务
客户端 → 支付服务

新问题:

  • 客户端需要管理多个服务地址
  • 统一认证和权限控制变得复杂
  • 跨域问题频发
  • 监控和日志分散
  • 服务间调用关系复杂

网关应运而生

微服务网关作为统一的流量入口,解决了上述问题:

客户端 → API网关 → 用户服务
→ 订单服务
→ 商品服务
→ 支付服务

网关核心作用

1. 路由转发 (Routing)

作用: 根据请求规则将流量分发到不同的后端服务

场景:

GET /api/users/**    → 用户服务 (端口8081)
POST /api/orders/** → 订单服务 (端口8082)
PUT /api/products/** → 商品服务 (端口8083)

2. 负载均衡 (Load Balancing)

作用: 将请求分发到同一服务的多个实例

示例:

订单服务有3个实例:
- 192.168.1.101:8082
- 192.168.1.102:8082
- 192.168.1.103:8082

网关通过轮询算法将请求依次分发到这3个实例

3. 统一认证 (Authentication)

作用: 验证用户身份,无需每个服务单独实现

流程:

1. 客户端发送请求(带token)
2. 网关验证token有效性
3. 验证通过 → 转发到后端服务
4. 验证失败 → 返回401未授权

4. 权限控制 (Authorization)

作用: 控制用户对不同接口的访问权限

示例:

普通用户:只能查询自己的订单
管理员用户:可以查询所有订单
VIP用户:享受特殊折扣接口

5. 限流保护 (Rate Limiting)

作用: 防止恶意请求和系统过载

策略:

  • 基于IP限流: 每个IP每分钟最多100次请求
  • 基于用户限流: 每个用户每分钟最多50次请求
  • 基于接口限流: 特定接口每秒最多10次请求

6. 熔断降级 (Circuit Breaker)

作用: 当后端服务不可用时,提供备用方案

熔断机制:

正常状态 → 请求失败率超过阈值 → 熔断状态 → 半开状态 → 恢复正常
↓ ↓ ↓
直接转发请求 返回降级响应 尝试少量请求

7. 请求转换 (Request Transformation)

作用: 修改请求头、参数或响应体

应用场景:

  • 添加统一的请求头(如用户信息)
  • 修改请求路径
  • 统一响应格式
  • 协议转换(HTTP→HTTPS)

8. 日志监控 (Logging & Monitoring)

作用: 统一记录请求日志和性能指标

监控内容:

  • 请求响应时间
  • 错误率统计
  • 服务调用链路
  • 流量统计分析

Spring Cloud Gateway详解

为什么选择Spring Cloud Gateway

相比传统网关的优势:

特性Spring Cloud GatewayZuul 1.xNginx
性能基于WebFlux,非阻塞阻塞IO高性能
扩展性响应式编程有限需要Lua脚本
集成性Spring生态完美集成Spring生态需要额外配置
动态配置支持运行时修改需要重启支持热更新
功能丰富丰富的过滤器基础功能基础功能

核心技术架构

Spring Cloud Gateway基于以下技术栈:

Spring WebFlux (响应式框架)

Spring Boot (应用框架)

Netty (网络通信层)

Reactor (响应式编程库)

响应式编程优势:

  • 非阻塞IO: 一个线程可以处理多个请求
  • 背压处理: 防止下游服务压力过大
  • 高并发: 支持更高的并发连接数

与Zuul的对比

// Zuul 1.x - 阻塞IO
@Component
public class ZuulFilter extends ZuulFilter {
public Object run() {
// 阻塞处理,每个请求占用一个线程
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
// 处理逻辑...
return null;
}
}

// Spring Cloud Gateway - 响应式
@Component
public class GatewayFilter implements GlobalFilter {
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 非阻塞处理,基于事件驱动
ServerHttpRequest request = exchange.getRequest();
// 处理逻辑...
return chain.filter(exchange);
}
}

核心组件与工作流程

1. 路由 (Route)

路由是网关最基本的组成单元,包含:

public class Route {
private String id; // 路由唯一标识
private String uri; // 目标服务地址
private int order; // 路由优先级
private Predicate<ServerWebExchange> predicate; // 匹配条件
private List<GatewayFilter> filters; // 过滤器链
}

2. 断言 (Predicate)

断言用于判断请求是否匹配当前路由:

// 路径匹配
.predicate(PathPredicate("/api/user/**"))

// 方法匹配
.predicate(MethodPredicate(HttpMethod.GET))

// 头部匹配
.predicate(HeaderPredicate("X-Requested-With", "XMLHttpRequest"))

// 组合匹配
.predicate(AndPredicate(
PathPredicate("/api/user/**"),
MethodPredicate(HttpMethod.GET),
HeaderPredicate("Content-Type", "application/json")
))

3. 过滤器 (Filter)

过滤器在请求处理前后执行逻辑:

public interface GatewayFilter {
// 过滤器执行逻辑
Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}

// 过滤器执行顺序
前置过滤器 → 目标服务 → 后置过滤器

工作流程详解

详细步骤说明:

  1. 请求接收: 客户端发送请求到网关
  2. 路由匹配: 根据断言条件找到匹配的路由
  3. 前置过滤: 执行GlobalFilter和GatewayFilter的pre逻辑
  4. 负载均衡: 选择具体的后端服务实例
  5. 请求转发: 将请求发送到目标服务
  6. 响应处理: 接收后端服务的响应
  7. 后置过滤: 执行GlobalFilter和GatewayFilter的post逻辑
  8. 响应返回: 将最终响应返回给客户端

常用路由配置

基础路由配置

spring:
cloud:
gateway:
routes:
# 用户服务路由
- id: user-service
uri: http://localhost:8081
predicates:
- Path=/api/users/**
filters:
- StripPrefix=2 # 移除前2级路径

# 订单服务路由
- id: order-service
uri: http://localhost:8082
predicates:
- Path=/api/orders/**
- Method=POST,PUT # 只允许POST和PUT方法

服务发现路由

spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service # lb表示使用负载均衡
predicates:
- Path=/api/users/**

- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
discovery:
locator:
enabled: true # 开启服务发现
lower-case-service-id: true # 服务ID转为小写

动态路由配置

@Configuration
public class DynamicRouteConfig {

@Bean
public RouteDefinitionRepository routeDefinitionRepository() {
// 从数据库加载路由配置
return new DatabaseRouteDefinitionRepository();
}

@Component
public class DatabaseRouteDefinitionRepository implements RouteDefinitionRepository {

@Override
public Flux<RouteDefinition> getRouteDefinitions() {
// 从数据库查询路由配置
return Flux.fromIterable(loadRoutesFromDatabase());
}

@Override
public Mono<Void> save(Mono<RouteDefinition> route) {
// 保存路由到数据库
return route.flatMap(this::saveRouteToDatabase);
}

@Override
public Mono<Void> delete(Mono<String> routeId) {
// 从数据库删除路由
return routeId.flatMap(this::deleteRouteFromDatabase);
}
}
}

权重路由

spring:
cloud:
gateway:
routes:
- id: user-service-v1
uri: http://localhost:8081
predicates:
- Path=/api/users/**
- Weight=service-group, 80 # 80%的流量
metadata:
version: v1

- id: user-service-v2
uri: http://localhost:8082
predicates:
- Path=/api/users/**
- Weight=service-group, 20 # 20%的流量
metadata:
version: v2

重试路由

spring:
cloud:
gateway:
routes:
- id: retry-service
uri: lb://user-service
predicates:
- Path=/api/users/**
filters:
- name: Retry
args:
retries: 3 # 重试次数
statuses: BAD_GATEWAY # 重试状态码
methods: GET,POST # 重试方法
backoff:
firstBackoff: 50ms
maxBackoff: 500ms
factor: 2

过滤器机制

过滤器类型

1. 全局过滤器 (GlobalFilter)

对所有路由生效的过滤器:

@Component
@Order(-1) // 数值越小,优先级越高
public class AuthGlobalFilter implements GlobalFilter {

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();

// 检查token
String token = request.getHeaders().getFirst("Authorization");
if (StringUtils.isEmpty(token)) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}

// 验证token有效性
if (!validateToken(token)) {
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
return exchange.getResponse().setComplete();
}

return chain.filter(exchange);
}
}

2. 网关过滤器 (GatewayFilter)

针对特定路由的过滤器:

@Component
public class CustomGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomGatewayFilterFactory.Config> {

public CustomGatewayFilterFactory() {
super(Config.class);
}

@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
// 前置处理
System.out.println("前置处理: " + config.getMessage());

return chain.filter(exchange).then(Mono.fromRunnable(() -> {
// 后置处理
System.out.println("后置处理");
}));
};
}

public static class Config {
private String message;
// getter and setter
}
}

内置过滤器详解

路径过滤器

filters:
- StripPrefix=2 # 移除路径前2级
- PrefixPath=/api # 添加路径前缀
- SetPath=/new/{path} # 重设路径

示例:

原始路径: /api/v1/users/123
StripPrefix=1 → /v1/users/123
StripPrefix=2 → /users/123
PrefixPath=/service → /service/users/123

参数过滤器

filters:
- AddRequestParameter=userId,123 # 添加请求参数
- AddResponseHeader=X-Custom,Value # 添加响应头
- RemoveRequestHeader=Cookie # 移除请求头
- SetRequestHeader=Host,newhost.com # 设置请求头

Hystrix过滤器

filters:
- name: Hystrix
args:
name: user-service
fallbackUri: forward:/fallback # 降级处理地址

限流过滤器

filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10 # 每秒补充速率
redis-rate-limiter.burstCapacity: 20 # 桶容量
key-resolver: "#{@userKeyResolver}" # 限流key解析器
@Bean
public KeyResolver userKeyResolver() {
exchange -> {
// 基于用户ID限流
String userId = exchange.getRequest().getQueryParams().getFirst("userId");
return Mono.just(userId != null ? userId : "anonymous");
};
}

自定义过滤器实战

认证过滤器

@Component
public class JwtAuthFilter implements GlobalFilter, Ordered {

@Autowired
private JwtTokenUtil jwtTokenUtil;

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();

// 排除不需要认证的路径
if (isSkipAuth(request.getPath().value())) {
return chain.filter(exchange);
}

// 获取token
String token = getToken(request);
if (StringUtils.isEmpty(token)) {
return handleAuthError(exchange, "Missing token");
}

try {
// 验证token
Claims claims = jwtTokenUtil.parseToken(token);
String userId = claims.getSubject();

// 将用户信息放入请求头
ServerHttpRequest modifiedRequest = request.mutate()
.header("X-User-Id", userId)
.header("X-User-Name", claims.get("name", String.class))
.build();

return chain.filter(exchange.mutate().request(modifiedRequest).build());

} catch (Exception e) {
return handleAuthError(exchange, "Invalid token");
}
}

private boolean isSkipAuth(String path) {
List<String> skipPaths = Arrays.asList("/api/login", "/api/register", "/health");
return skipPaths.stream().anyMatch(path::startsWith);
}

private String getToken(ServerHttpRequest request) {
String authHeader = request.getHeaders().getFirst("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
return authHeader.substring(7);
}
return null;
}

private Mono<Void> handleAuthError(ServerWebExchange exchange, String message) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
response.getHeaders().add("Content-Type", "application/json");

String body = String.format("{\"code\":401,\"message\":\"%s\"}", message);
DataBuffer buffer = response.bufferFactory().wrap(body.getBytes());
return response.writeWith(Mono.just(buffer));
}

@Override
public int getOrder() {
return -100; // 高优先级
}
}

日志过滤器

@Component
public class LoggingFilter implements GlobalFilter, Ordered {

private static final Logger logger = LoggerFactory.getLogger(LoggingFilter.class);

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
long startTime = System.currentTimeMillis();
ServerHttpRequest request = exchange.getRequest();

// 记录请求信息
String requestId = UUID.randomUUID().toString();
logger.info("Request [{}] {} {} from {}",
requestId, request.getMethod(), request.getURI(),
request.getRemoteAddress());

return chain.filter(exchange).then(Mono.fromRunnable(() -> {
long endTime = System.currentTimeMillis();
ServerHttpResponse response = exchange.getResponse();

// 记录响应信息
logger.info("Response [{}] {} {} - {}ms",
requestId, response.getStatusCode(),
request.getURI(), endTime - startTime);
}));
}

@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}

负载均衡与服务发现

负载均衡策略

Spring Cloud Gateway集成Spring Cloud LoadBalancer,支持多种负载均衡策略:

1. 轮询策略 (Round Robin)

# 默认策略,请求依次分发
spring:
cloud:
loadbalancer:
configurations: round-robin

2. 随机策略 (Random)

spring:
cloud:
loadbalancer:
configurations: random

3. 最少连接数策略 (Least Connections)

@Configuration
public class LoadBalancerConfig {

@Bean
public ReactorLoadBalancer<ServiceInstance> leastConnectionsLoadBalancer(
Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {

String serviceId = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);

return new LeastConnectionsLoadBalancer(
loadBalancerClientFactory.getLazyProvider(serviceId, ServiceInstanceListSupplier.class),
serviceId);
}
}

服务注册中心集成

Eureka集成

# application.yml
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
register-with-eureka: true
fetch-registry: true

spring:
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true

Nacos集成

# application.yml
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
namespace: dev
group: DEFAULT_GROUP

gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true

Consul集成

# application.yml
spring:
cloud:
consul:
host: localhost
port: 8500
discovery:
service-name: gateway-service
register: true

gateway:
discovery:
locator:
enabled: true

健康检查机制

@Component
public class HealthCheckFilter implements GlobalFilter {

@Autowired
private HealthIndicatorService healthIndicatorService;

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String serviceId = getServiceIdFromRequest(exchange.getRequest());

return healthIndicatorService.checkHealth(serviceId)
.flatMap(isHealthy -> {
if (isHealthy) {
return chain.filter(exchange);
} else {
return handleUnhealthyService(exchange, serviceId);
}
})
.onErrorResume(throwable -> {
// 健康检查失败,直接转发
return chain.filter(exchange);
});
}

private Mono<Void> handleUnhealthyService(ServerWebExchange exchange, String serviceId) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.SERVICE_UNAVAILABLE);

String body = String.format(
"{\"code\":503,\"message\":\"Service %s is unavailable\"}", serviceId);

DataBuffer buffer = response.bufferFactory().wrap(body.getBytes());
return response.writeWith(Mono.just(buffer));
}
}

服务实例选择器

@Component
public class CustomServiceInstanceSelector {

@Autowired
private DiscoveryClient discoveryClient;

public ServiceInstance selectInstance(String serviceId, ServerHttpRequest request) {
List<ServiceInstance> instances = discoveryClient.getInstances(serviceId);

if (instances.isEmpty()) {
throw new ServiceException("No available instances for service: " + serviceId);
}

// 基于地理位置选择最近的实例
String clientRegion = getClientRegion(request);
return instances.stream()
.filter(instance -> isInSameRegion(instance, clientRegion))
.findFirst()
.orElse(instances.get(0)); // 默认选择第一个
}

private String getClientRegion(ServerHttpRequest request) {
// 根据IP地址获取客户端地理位置
String clientIp = request.getRemoteAddress().getAddress().getHostAddress();
return GeoLocationUtil.getRegionByIp(clientIp);
}
}

限流与熔断

限流策略详解

1. 令牌桶算法 (Token Bucket)

@Component
public class TokenBucketRateLimiter {

private final Map<String, TokenBucket> buckets = new ConcurrentHashMap<>();

public boolean tryConsume(String key, int tokens) {
TokenBucket bucket = buckets.computeIfAbsent(key, k -> new TokenBucket());
return bucket.tryConsume(tokens);
}

private static class TokenBucket {
private final AtomicInteger tokens;
private final int capacity;
private final long refillRate; // 每秒补充的令牌数
private volatile long lastRefillTime;

public TokenBucket() {
this.capacity = 100; // 桶容量
this.refillRate = 10; // 每秒10个令牌
this.tokens = new AtomicInteger(capacity);
this.lastRefillTime = System.currentTimeMillis();
}

public boolean tryConsume(int requestedTokens) {
refill();
return tokens.getAndAdd(-requestedTokens) >= 0;
}

private void refill() {
long now = System.currentTimeMillis();
long timePassed = now - lastRefillTime;

if (timePassed >= 1000) { // 至少过去1秒
int tokensToAdd = (int) (timePassed / 1000 * refillRate);
tokens.updateAndGet(current -> Math.min(capacity, current + tokensToAdd));
lastRefillTime = now;
}
}
}
}

2. 滑动窗口算法 (Sliding Window)

@Component
public class SlidingWindowRateLimiter {

private final Map<String, ConcurrentLinkedQueue<Long>> windows = new ConcurrentHashMap<>();

public boolean isAllowed(String key, int limit, long windowSizeMs) {
long now = System.currentTimeMillis();
ConcurrentLinkedQueue<Long> window = windows.computeIfAbsent(key, k -> new ConcurrentLinkedQueue<>());

// 清理过期的请求记录
while (!window.isEmpty() && now - window.peek() > windowSizeMs) {
window.poll();
}

// 检查是否超过限制
if (window.size() >= limit) {
return false;
}

// 记录当前请求
window.offer(now);
return true;
}
}

Redis分布式限流

Lua脚本实现令牌桶

-- redis_token_bucket.lua
local key = KEYS[1]
local capacity = tonumber(ARGV[1])
local tokens = tonumber(ARGV[2])
local interval = tonumber(ARGV[3])
local requested = tonumber(ARGV[4])

local current = redis.call('GET', key)
if not current then
current = capacity
redis.call('SET', key, current)
end

current = tonumber(current)

local refill = math.floor((ARGV[5] - redis.call('GET', key .. ':last_refill') or 0) / interval * tokens)
if refill > 0 then
current = math.min(capacity, current + refill)
redis.call('SET', key, current)
redis.call('SET', key .. ':last_refill', ARGV[5])
end

if current >= requested then
redis.call('DECRBY', key, requested)
return 1
else
return 0
end
@Service
public class RedisRateLimiter {

@Autowired
private RedisTemplate<String, String> redisTemplate;

@Autowired
private RedisScript<Long> tokenBucketScript;

public boolean tryAcquire(String key, int requestedTokens) {
Long result = redisTemplate.execute(
tokenBucketScript,
Collections.singletonList(key),
"100", // capacity
"10", // tokens per second
"1000", // interval in ms
String.valueOf(requestedTokens),
String.valueOf(System.currentTimeMillis())
);

return result != null && result == 1;
}
}

熔断器模式

Hystrix熔断器

spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/users/**
filters:
- name: Hystrix
args:
name: userCircuitBreaker
fallbackUri: forward:/fallback/user

hystrix:
command:
userCircuitBreaker:
execution:
isolation:
thread:
timeoutInMilliseconds: 5000
circuitBreaker:
requestVolumeThreshold: 20 # 请求阈值
sleepWindowInMilliseconds: 5000 # 熔断持续时间
errorThresholdPercentage: 50 # 错误率阈值

Resilience4j熔断器

spring:
cloud:
gateway:
routes:
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
filters:
- name: CircuitBreaker
args:
name: orderCircuitBreaker
fallbackUri: forward:/fallback/order

resilience4j:
circuitbreaker:
instances:
orderCircuitBreaker:
registerHealthIndicator: true
slidingWindowType: COUNT_BASED
slidingWindowSize: 10
minimumNumberOfCalls: 5
failureRateThreshold: 50
waitDurationInOpenState: 30s
permittedNumberOfCallsInHalfOpenState: 3

retry:
instances:
orderRetry:
maxAttempts: 3
waitDuration: 1s
retryExceptions:
- java.net.SocketTimeoutException
- java.io.IOException

自定义熔断器

@Component
public class CustomCircuitBreakerFilter implements GlobalFilter, Ordered {

private final Map<String, CircuitBreakerState> circuitBreakers = new ConcurrentHashMap<>();

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String serviceId = getServiceIdFromRequest(exchange.getRequest());
CircuitBreakerState state = circuitBreakers.computeIfAbsent(serviceId, CircuitBreakerState::new);

if (state.isOpen()) {
if (state.shouldAttemptReset()) {
state.halfOpen();
} else {
return handleCircuitBreakerOpen(exchange, serviceId);
}
}

long startTime = System.currentTimeMillis();
return chain.filter(exchange)
.doOnSuccess(response -> state.recordSuccess())
.doOnError(error -> state.recordFailure())
.doFinally(signal -> state.recordResponseTime(System.currentTimeMillis() - startTime));
}

private static class CircuitBreakerState {
private static final int FAILURE_THRESHOLD = 5;
private static final int SUCCESS_THRESHOLD = 3;
private static final long TIMEOUT_MS = 30000; // 30秒

private volatile State state = State.CLOSED;
private int failureCount = 0;
private int successCount = 0;
private long lastFailureTime = 0;

public synchronized boolean isOpen() {
if (state == State.OPEN &&
System.currentTimeMillis() - lastFailureTime > TIMEOUT_MS) {
state = State.HALF_OPEN;
successCount = 0;
}
return state == State.OPEN;
}

public synchronized void recordSuccess() {
if (state == State.HALF_OPEN) {
successCount++;
if (successCount >= SUCCESS_THRESHOLD) {
state = State.CLOSED;
failureCount = 0;
}
} else if (state == State.CLOSED) {
failureCount = 0;
}
}

public synchronized void recordFailure() {
failureCount++;
lastFailureTime = System.currentTimeMillis();

if (failureCount >= FAILURE_THRESHOLD) {
state = State.OPEN;
}
}

private enum State {
CLOSED, OPEN, HALF_OPEN
}
}
}

安全认证

JWT认证实现

JWT工具类

@Component
public class JwtTokenUtil {

@Value("${jwt.secret}")
private String secret;

@Value("${jwt.expiration}")
private long expiration;

public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
claims.put("userId", userDetails.getUserId());
claims.put("username", userDetails.getUsername());
claims.put("roles", userDetails.getAuthorities());

return Jwts.builder()
.setClaims(claims)
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expiration))
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}

public Claims parseToken(String token) {
try {
return Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
} catch (ExpiredJwtException e) {
throw new TokenExpiredException("Token has expired");
} catch (UnsupportedJwtException e) {
throw new UnsupportedTokenException("Token is unsupported");
} catch (MalformedJwtException e) {
throw new MalformedTokenException("Token is malformed");
}
}

public boolean validateToken(String token) {
try {
Claims claims = parseToken(token);
return !claims.getExpiration().before(new Date());
} catch (Exception e) {
return false;
}
}
}

认证过滤器

@Component
public class JwtAuthenticationFilter implements GlobalFilter, Ordered {

@Autowired
private JwtTokenUtil jwtTokenUtil;

@Autowired
private UserDetailsService userDetailsService;

private static final List<String> EXCLUDE_PATHS = Arrays.asList(
"/api/auth/login",
"/api/auth/register",
"/api/auth/refresh",
"/health",
"/actuator/**"
);

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String path = request.getPath().value();

// 排除不需要认证的路径
if (isExcludePath(path)) {
return chain.filter(exchange);
}

// 获取Authorization头
String authHeader = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);

if (StringUtils.isEmpty(authHeader) || !authHeader.startsWith("Bearer ")) {
return handleUnauthorized(exchange, "Missing or invalid authorization header");
}

String token = authHeader.substring(7);

try {
// 验证token
if (!jwtTokenUtil.validateToken(token)) {
return handleUnauthorized(exchange, "Invalid token");
}

// 解析用户信息
Claims claims = jwtTokenUtil.parseToken(token);
String username = claims.getSubject();

// 查询用户详情
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (userDetails == null) {
return handleUnauthorized(exchange, "User not found");
}

// 将用户信息放入请求属性中
ServerHttpRequest modifiedRequest = request.mutate()
.header("X-User-Id", claims.get("userId", String.class))
.header("X-Username", username)
.header("X-Roles", String.join(",",
userDetails.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList())))
.build();

return chain.filter(exchange.mutate().request(modifiedRequest).build());

} catch (Exception e) {
return handleUnauthorized(exchange, "Token validation failed: " + e.getMessage());
}
}

private boolean isExcludePath(String path) {
return EXCLUDE_PATHS.stream().anyMatch(excludePath ->
PathMatcher.match(excludePath, path));
}

private Mono<Void> handleUnauthorized(ServerWebExchange exchange, String message) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);

Map<String, Object> errorResponse = new HashMap<>();
errorResponse.put("code", 401);
errorResponse.put("message", message);
errorResponse.put("timestamp", System.currentTimeMillis());

try {
String body = new ObjectMapper().writeValueAsString(errorResponse);
DataBuffer buffer = response.bufferFactory().wrap(body.getBytes());
return response.writeWith(Mono.just(buffer));
} catch (Exception e) {
return response.setComplete();
}
}

@Override
public int getOrder() {
return -100;
}
}

OAuth2集成

OAuth2配置

spring:
security:
oauth2:
client:
registration:
google:
client-id: ${GOOGLE_CLIENT_ID}
client-secret: ${GOOGLE_CLIENT_SECRET}
authorization-grant-type: authorization_code
redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
scope: openid,profile,email
github:
client-id: ${GITHUB_CLIENT_ID}
client-secret: ${GITHUB_CLIENT_SECRET}
authorization-grant-type: authorization_code
redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
scope: user:email
resource:
userInfoUri: https://www.googleapis.com/oauth2/v2/userinfo
cloud:
gateway:
routes:
- id: oauth2-protected
uri: lb://protected-service
predicates:
- Path=/api/protected/**
filters:
- TokenRelay=

OAuth2安全配置

@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {

@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http
.authorizeExchange(exchanges -> exchanges
.pathMatchers("/api/public/**", "/actuator/health").permitAll()
.pathMatchers(HttpMethod.GET, "/api/users/**").hasRole("USER")
.pathMatchers(HttpMethod.POST, "/api/users/**").hasRole("ADMIN")
.anyExchange().authenticated()
)
.oauth2Login(withDefaults())
.logout(logout -> logout
.logoutSuccessHandler(new RedirectServerLogoutSuccessHandler())
)
.csrf(csrf -> csrf.disable())
.build();
}

@Bean
public ReactiveClientRegistrationRepository clientRegistrationRepository() {
ClientRegistration github = ClientRegistration.withRegistrationId("github")
.clientId("github-client-id")
.clientSecret("github-client-secret")
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.redirectUri("{baseUrl}/login/oauth2/code/{registrationId}")
.scope("user:email")
.authorizationUri("https://github.com/login/oauth/authorize")
.tokenUri("https://github.com/login/oauth/access_token")
.userInfoUri("https://api.github.com/user")
.userNameAttributeName("id")
.clientName("GitHub")
.build();

return new InMemoryReactiveClientRegistrationRepository(github);
}
}

API Key认证

@Component
public class ApiKeyAuthFilter implements GlobalFilter, Ordered {

@Value("${api.gateway.api-key-header}")
private String apiKeyHeader;

@Autowired
private ApiKeyService apiKeyService;

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();

// 检查是否需要API Key认证
if (!requiresApiKey(request)) {
return chain.filter(exchange);
}

String apiKey = request.getHeaders().getFirst(apiKeyHeader);

if (StringUtils.isEmpty(apiKey)) {
return handleUnauthorized(exchange, "API Key is required");
}

return apiKeyService.validateApiKey(apiKey)
.flatMap(isValid -> {
if (isValid) {
// 将API Key信息放入请求属性
return apiKeyService.getApiKeyInfo(apiKey)
.flatMap(apiKeyInfo -> {
ServerHttpRequest modifiedRequest = request.mutate()
.header("X-API-Client-Id", apiKeyInfo.getClientId())
.header("X-API-Tier", apiKeyInfo.getTier())
.build();
return chain.filter(exchange.mutate().request(modifiedRequest).build());
});
} else {
return handleUnauthorized(exchange, "Invalid API Key");
}
});
}

private boolean requiresApiKey(ServerHttpRequest request) {
String path = request.getPath().value();
return path.startsWith("/api/v2/") || path.startsWith("/api/external/");
}

@Override
public int getOrder() {
return -50;
}
}

性能优化

连接池优化

# Netty连接池配置
spring:
cloud:
gateway:
httpclient:
connect-timeout: 5000 # 连接超时5秒
response-timeout: 10s # 响应超时10秒
pool:
type: elastic # 弹性连接池
max-connections: 500 # 最大连接数
max-idle-time: 30s # 最大空闲时间
acquire-timeout: 30000 # 获取连接超时
websocket:
max-frame-payload-length: 1048576 # WebSocket最大帧大小

缓存策略

响应缓存

@Component
public class ResponseCacheFilter implements GlobalFilter, Ordered {

private final Cache<String, CachedResponse> responseCache =
Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(Duration.ofMinutes(5))
.build();

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String cacheKey = generateCacheKey(request);

// 检查缓存
CachedResponse cachedResponse = responseCache.getIfPresent(cacheKey);
if (cachedResponse != null && !isExpired(cachedResponse)) {
return writeCachedResponse(exchange, cachedResponse);
}

// 缓存未命中,继续请求
return chain.filter(exchange)
.then(Mono.defer(() -> {
ServerHttpResponse response = exchange.getResponse();

// 缓存响应
if (shouldCacheResponse(request, response)) {
DataBufferFactory bufferFactory = response.bufferFactory();
return DataBufferUtils.join(response.getBody())
.defaultIfEmpty(bufferFactory.allocateBuffer())
.doOnNext(dataBuffer -> {
byte[] content = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(content);
DataBufferUtils.release(dataBuffer);

CachedResponse newCachedResponse = new CachedResponse(
response.getStatusCode(),
response.getHeaders(),
content,
System.currentTimeMillis()
);
responseCache.put(cacheKey, newCachedResponse);
})
.then();
}
return Mono.empty();
}));
}

private boolean shouldCacheResponse(ServerHttpRequest request, ServerHttpResponse response) {
// 只缓存GET请求
if (!request.getMethod().equals(HttpMethod.GET)) {
return false;
}

// 只缓存2xx响应
if (!response.getStatusCode().is2xxSuccessful()) {
return false;
}

// 检查Cache-Control头
String cacheControl = response.getHeaders().getFirst(HttpHeaders.CACHE_CONTROL);
if (cacheControl != null && cacheControl.contains("no-cache")) {
return false;
}

return true;
}
}

本地缓存配置

@Configuration
public class CacheConfig {

@Bean
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(Duration.ofMinutes(10))
.recordStats());
return cacheManager;
}

@Bean("userCache")
public Cache<String, UserInfo> userCache() {
return Caffeine.newBuilder()
.maximumSize(5000)
.expireAfterWrite(Duration.ofHours(1))
.refreshAfterWrite(Duration.ofMinutes(30))
.recordStats()
.build();
}

@Bean("rateLimitCache")
public Cache<String, RateLimitInfo> rateLimitCache() {
return Caffeine.newBuilder()
.maximumSize(10000)
.expireAfterWrite(Duration.ofMinutes(1))
.recordStats()
.build();
}
}

压缩优化

spring:
cloud:
gateway:
compression:
enabled: true
mime-types: text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json
min-response-size: 1024
@Component
public class CompressionFilter implements GlobalFilter, Ordered {

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();

// 检查客户端是否支持压缩
String acceptEncoding = request.getHeaders().getFirst(HttpHeaders.ACCEPT_ENCODING);
if (acceptEncoding != null && acceptEncoding.contains("gzip")) {
response.getHeaders().add(HttpHeaders.CONTENT_ENCODING, "gzip");

// 使用Gzip压缩
return chain.filter(exchange)
.then(Mono.defer(() -> {
DataBufferFactory factory = response.bufferFactory();
return DataBufferUtils.join(response.getBody())
.defaultIfEmpty(factory.allocateBuffer())
.flatMap(dataBuffer -> {
try {
byte[] content = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(content);
DataBufferUtils.release(dataBuffer);

// 压缩内容
byte[] compressed = compress(content);

response.getHeaders().setContentLength(compressed.length);
DataBuffer compressedBuffer = factory.wrap(compressed);
return response.writeWith(Mono.just(compressedBuffer));
} catch (Exception e) {
return Mono.error(e);
}
});
}));
}

return chain.filter(exchange);
}

private byte[] compress(byte[] data) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (GZIPOutputStream gzipOut = new GZIPOutputStream(baos)) {
gzipOut.write(data);
}
return baos.toByteArray();
}
}

监控和指标

Micrometer监控

@Configuration
public class MetricsConfig {

@Bean
public TimedAspect timedAspect(MeterRegistry registry) {
return new TimedAspect(registry);
}

@Bean
public CountedAspect countedAspect(MeterRegistry registry) {
return new CountedAspect(registry);
}
}

@Component
public class GatewayMetricsFilter implements GlobalFilter, Ordered {

private final MeterRegistry meterRegistry;
private final Counter requestCounter;
private final Timer responseTimer;

public GatewayMetricsFilter(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
this.requestCounter = Counter.builder("gateway.requests.total")
.description("Total number of gateway requests")
.register(meterRegistry);
this.responseTimer = Timer.builder("gateway.response.time")
.description("Gateway response time")
.register(meterRegistry);
}

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
long startTime = System.currentTimeMillis();
String path = exchange.getRequest().getPath().value();

return chain.filter(exchange)
.doOnSuccess(response -> {
// 记录请求指标
requestCounter.increment(
Tags.of("path", path, "status", response.getStatusCode().toString())
);

// 记录响应时间
long duration = System.currentTimeMillis() - startTime;
responseTimer.record(duration, TimeUnit.MILLISECONDS,
Tags.of("path", path, "status", response.getStatusCode().toString())
);
})
.doOnError(error -> {
// 记录错误指标
requestCounter.increment(
Tags.of("path", path, "status", "error", "error_type", error.getClass().getSimpleName())
);
});
}
}

实战配置案例

完整的网关配置

# application.yml
server:
port: 8080

spring:
application:
name: gateway-service

cloud:
nacos:
discovery:
server-addr: localhost:8848
namespace: dev

gateway:
# 跨域配置
globalcors:
corsConfigurations:
'[/**]':
allowedOriginPatterns: "*"
allowedMethods: "*"
allowedHeaders: "*"
allowCredentials: true
maxAge: 3600

# 默认过滤器
default-filters:
- name: Retry
args:
retries: 3
statuses: BAD_GATEWAY,GATEWAY_TIMEOUT
methods: GET,POST
backoff:
firstBackoff: 50ms
maxBackoff: 500ms
factor: 2
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin

# 路由配置
routes:
# 用户服务
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/users/**
- Header=Authorization, Bearer .*
filters:
- StripPrefix=1
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
key-resolver: "#{@userKeyResolver}"
- name: CircuitBreaker
args:
name: userCircuitBreaker
fallbackUri: forward:/fallback/user

# 订单服务
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
- Method=GET,POST,PUT
filters:
- StripPrefix=1
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 20
redis-rate-limiter.burstCapacity: 40
key-resolver: "#{@ipKeyResolver}"
- name: CircuitBreaker
args:
name: orderCircuitBreaker
fallbackUri: forward:/fallback/order

# 支付服务(高安全性)
- id: payment-service
uri: lb://payment-service
predicates:
- Path=/api/payments/**
- Header=X-API-Key, .*
filters:
- StripPrefix=1
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 5
redis-rate-limiter.burstCapacity: 10
key-resolver: "#{@apiKeyResolver}"
- AddRequestHeader=X-Request-Source, gateway
- AddResponseHeader=X-Response-Time, "#{T(java.lang.System).currentTimeMillis()}"

# 服务发现配置
discovery:
locator:
enabled: true
lower-case-service-id: true
include: user-service,order-service,payment-service

# HTTP客户端配置
httpclient:
connect-timeout: 5000
response-timeout: 30s
pool:
type: elastic
max-connections: 1000
max-idle-time: 60s
acquire-timeout: 30000

# Redis配置
spring:
redis:
host: localhost
port: 6379
database: 0
timeout: 3000ms
lettuce:
pool:
max-active: 20
max-idle: 10
min-idle: 5

# 熔断器配置
resilience4j:
circuitbreaker:
instances:
userCircuitBreaker:
registerHealthIndicator: true
slidingWindowSize: 10
minimumNumberOfCalls: 5
failureRateThreshold: 50
waitDurationInOpenState: 30s
permittedNumberOfCallsInHalfOpenState: 3

orderCircuitBreaker:
registerHealthIndicator: true
slidingWindowSize: 20
minimumNumberOfCalls: 10
failureRateThreshold: 40
waitDurationInOpenState: 60s
permittedNumberOfCallsInHalfOpenState: 5

# 日志配置
logging:
level:
org.springframework.cloud.gateway: DEBUG
org.springframework.web.reactive: DEBUG
reactor.netty: DEBUG
pattern:
console: "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"

# 监控配置
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus,gateway
endpoint:
health:
show-details: always
metrics:
export:
prometheus:
enabled: true

降级处理

@RestController
@RequestMapping("/fallback")
public class FallbackController {

private static final Logger logger = LoggerFactory.getLogger(FallbackController.class);

@GetMapping("/user")
public ResponseEntity<Map<String, Object>> userFallback(ServerHttpRequest request) {
logger.warn("User service fallback triggered for request: {}", request.getURI());

Map<String, Object> response = new HashMap<>();
response.put("code", 503);
response.put("message", "User service is temporarily unavailable");
response.put("data", null);
response.put("timestamp", System.currentTimeMillis());

return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(response);
}

@GetMapping("/order")
public ResponseEntity<Map<String, Object>> orderFallback(ServerHttpRequest request) {
logger.warn("Order service fallback triggered for request: {}", request.getURI());

Map<String, Object> response = new HashMap<>();
response.put("code", 503);
response.put("message", "Order service is temporarily unavailable");
response.put("data", null);
response.put("timestamp", System.currentTimeMillis());

return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(response);
}

@GetMapping("/payment")
public ResponseEntity<Map<String, Object>> paymentFallback(ServerHttpRequest request) {
logger.error("Payment service fallback triggered for request: {}", request.getURI());

Map<String, Object> response = new HashMap<>();
response.put("code", 503);
response.put("message", "Payment service is temporarily unavailable. Please try again later.");
response.put("data", null);
response.put("timestamp", System.currentTimeMillis());

return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(response);
}
}

限流Key解析器

@Configuration
public class RateLimiterConfig {

@Bean
public KeyResolver userKeyResolver() {
return exchange -> {
// 基于用户ID限流
String userId = exchange.getRequest().getHeaders().getFirst("X-User-Id");
if (StringUtils.isEmpty(userId)) {
userId = exchange.getRequest().getQueryParams().getFirst("userId");
}
return Mono.just(StringUtils.isEmpty(userId) ? "anonymous" : userId);
};
}

@Bean
public KeyResolver ipKeyResolver() {
return exchange -> {
// 基于IP限流
String ip = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
return Mono.just(ip);
};
}

@Bean
public KeyResolver apiKeyResolver() {
return exchange -> {
// 基于API Key限流
String apiKey = exchange.getRequest().getHeaders().getFirst("X-API-Key");
return Mono.just(StringUtils.isEmpty(apiKey) ? "no-api-key" : apiKey);
};
}
}

动态路由管理

@RestController
@RequestMapping("/gateway/routes")
public class RouteController {

@Autowired
private RouteDefinitionWriter routeDefinitionWriter;

@Autowired
private RouteDefinitionLocator routeDefinitionLocator;

@GetMapping
public Flux<RouteDefinition> listRoutes() {
return routeDefinitionLocator.getRouteDefinitions();
}

@PostMapping
public ResponseEntity<String> addRoute(@RequestBody RouteDefinition routeDefinition) {
try {
routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
return ResponseEntity.ok("Route added successfully");
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Failed to add route: " + e.getMessage());
}
}

@PutMapping("/{id}")
public ResponseEntity<String> updateRoute(
@PathVariable String id,
@RequestBody RouteDefinition routeDefinition) {

try {
// 先删除旧路由
routeDefinitionWriter.delete(Mono.just(id)).subscribe();
// 添加新路由
routeDefinition.setId(id);
routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
return ResponseEntity.ok("Route updated successfully");
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Failed to update route: " + e.getMessage());
}
}

@DeleteMapping("/{id}")
public ResponseEntity<String> deleteRoute(@PathVariable String id) {
try {
routeDefinitionWriter.delete(Mono.just(id)).subscribe();
return ResponseEntity.ok("Route deleted successfully");
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Failed to delete route: " + e.getMessage());
}
}
}

常见面试问题

基础概念类

Q1: 什么是API网关?为什么要使用API网关?

参考答案: API网关是微服务架构中的统一入口,它处理所有客户端请求并将其路由到适当的后端服务。

使用原因:

  1. 统一入口管理: 避免客户端直接调用多个微服务
  2. 安全认证: 集中处理身份验证和授权
  3. 负载均衡: 在多个服务实例间分发请求
  4. 协议转换: 处理不同协议之间的转换
  5. 监控日志: 统一收集请求日志和性能指标
  6. 限流熔断: 保护后端服务免受过载影响

Q2: Spring Cloud Gateway和Zuul的区别是什么?

参考答案:

特性Spring Cloud GatewayZuul 1.xZuul 2.x
架构模式响应式编程阻塞IO响应式编程
底层框架WebFlux + NettyServlet + Tomcat响应式Netty
性能高性能性能一般高性能
线程模型事件驱动每请求一线程事件驱动
扩展性易于扩展有限扩展易于扩展
集成性Spring生态完美Spring生态Spring生态

代码示例对比:

// Zuul过滤器 - 阻塞模式
public class PreFilter extends ZuulFilter {
public Object run() {
// 阻塞处理
return null;
}
}

// Gateway过滤器 - 响应式模式
public class GlobalFilter implements GlobalFilter {
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 响应式处理
return chain.filter(exchange);
}
}

技术原理类

Q3: Spring Cloud Gateway的工作原理是什么?

参考答案: Spring Cloud Gateway基于响应式编程模式,工作流程如下:

  1. 客户端请求到达网关
  2. 路由匹配: 通过断言(Predicate)匹配对应的路由
  3. 过滤器链执行:
    • GlobalFilter前置处理
    • GatewayFilter前置处理
  4. 负载均衡: 选择具体的后端服务实例
  5. 请求转发: 发送请求到目标服务
  6. 响应处理:
    • GatewayFilter后置处理
    • GlobalFilter后置处理
  7. 响应返回: 返回最终响应给客户端

关键组件:

  • Route(路由): 包含ID、URI、断言、过滤器
  • Predicate(断言): 判断请求是否匹配路由
  • Filter(过滤器): 前置和后置处理逻辑

Q4: 什么是断言(Predicate)?常用的断言有哪些?

参考答案: 断言是用于匹配HTTP请求的函数,当请求满足所有断言条件时,该路由被选中。

常用断言:

predicates:
- Path=/api/users/** # 路径匹配
- Method=GET,POST # 方法匹配
- Host=**.somehost.org # 主机匹配
- Header=X-Request-Id, \d+ # 请求头匹配
- Query=name, Embrace # 查询参数匹配
- After=2023-12-07T12:00:00+08:00[Asia/Shanghai] # 时间匹配
- Cookie=name, Embrace # Cookie匹配
- RemoteAddr=192.168.1.1/24 # IP地址匹配
- Weight=group1, 80 # 权重匹配

实际应用类

Q5: 如何实现动态路由?

参考答案: 动态路由允许在运行时修改路由配置,不需要重启应用。

实现方式:

  1. 基于数据库:
@Component
public class DatabaseRouteDefinitionRepository implements RouteDefinitionRepository {

@Autowired
private RouteMapper routeMapper;

@Override
public Flux<RouteDefinition> getRouteDefinitions() {
return Flux.fromIterable(routeMapper.selectAll());
}

@Override
public Mono<Void> save(Mono<RouteDefinition> route) {
return route.flatMap(r -> {
routeMapper.insert(r);
return Mono.empty();
});
}
}
  1. 基于配置中心(Nacos):
@Component
public class NacosRouteRefresher implements ApplicationListener<ApplicationReadyEvent> {

@Autowired
private RouteDefinitionWriter routeDefinitionWriter;

@NacosConfigListener(dataId = "gateway-routes", groupId = "gateway-group")
public void onConfigChange(String newContent) {
// 解析配置并更新路由
List<RouteDefinition> routes = parseRoutes(newContent);
routes.forEach(route -> routeDefinitionWriter.save(Mono.just(route)).subscribe());
}
}

Q6: 如何实现分布式限流?

参考答案: 分布式限流需要考虑多个网关实例间的状态同步。

Redis + Lua实现:

@Service
public class DistributedRateLimiter {

@Autowired
private RedisTemplate<String, String> redisTemplate;

@Autowired
private RedisScript<Long> rateLimitScript;

public boolean tryAcquire(String key, int limit, int window) {
List<String> keys = Collections.singletonList(key);
return redisTemplate.execute(rateLimitScript, keys,
String.valueOf(limit), String.valueOf(window)) == 1;
}
}

Lua脚本:

local key = KEYS[1]
local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local current = redis.call('GET', key) or 0

if current + 1 > limit then
return 0
else
redis.call('INCR', key)
redis.call('EXPIRE', key, window)
return 1
end

性能优化类

Q7: 如何优化网关性能?

参考答案:

1. 连接池优化:

spring:
cloud:
gateway:
httpclient:
pool:
max-connections: 1000 # 增加连接数
max-idle-time: 60s # 优化空闲时间

2. 缓存策略:

@Bean
public Cache<String, String> routeCache() {
return Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(Duration.ofMinutes(10))
.build();
}

3. 异步处理:

  • 使用WebFlux响应式编程
  • 避免阻塞操作
  • 合理使用线程池

4. 压缩优化:

spring:
cloud:
gateway:
compression:
enabled: true
min-response-size: 1024

Q8: 如何保证网关的高可用?

参考答案:

1. 集群部署:

  • 部署多个网关实例
  • 使用负载均衡器分发流量

2. 健康检查:

@Component
public class GatewayHealthIndicator implements HealthIndicator {

@Override
public Health health() {
// 检查关键组件状态
boolean isHealthy = checkComponents();
return isHealthy ? Health.up().build() : Health.down().build();
}
}

3. 优雅关闭:

server:
shutdown: graceful

spring:
lifecycle:
timeout-per-shutdown-phase: 30s

4. 监控告警:

  • 集成Prometheus监控
  • 设置关键指标告警
  • 使用ELK收集日志

故障排查类

Q9: 网关出现504超时如何排查?

参考答案:

排查步骤:

  1. 检查网关超时配置:
spring:
cloud:
gateway:
httpclient:
response-timeout: 30s
  1. 检查后端服务状态:
  • 服务是否正常启动
  • 接口响应时间是否过长
  • 数据库连接是否正常
  1. 检查网络连接:
  • 网络延迟
  • 防火墙设置
  • DNS解析
  1. 查看日志:
# 查看网关日志
tail -f logs/gateway.log | grep "504\|timeout"

# 查看后端服务日志
tail -f logs/user-service.log | grep "error\|timeout"
  1. 监控指标分析:
  • 响应时间分布
  • 错误率统计
  • 连接池使用情况

Q10: 如何解决网关内存溢出问题?

参考答案:

原因分析:

  1. 响应数据过大
  2. 连接池配置不当
  3. 缓存策略不合理
  4. 内存泄漏

解决方案:

1. 调整JVM参数:

-Xms2g -Xmx4g -XX:+UseG1GC -XX:MaxGCPauseMillis=200

2. 优化连接池:

spring:
cloud:
gateway:
httpclient:
pool:
max-connections: 500
max-idle-time: 30s

3. 控制响应大小:

@Component
public class ResponseSizeFilter implements GlobalFilter {

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return chain.filter(exchange)
.then(Mono.defer(() -> {
DataBufferFactory factory = exchange.getResponse().bufferFactory();
return DataBufferUtils.join(exchange.getResponse().getBody())
.map(dataBuffer -> {
if (dataBuffer.readableByteCount() > MAX_RESPONSE_SIZE) {
throw new ResponseTooLargeException();
}
return dataBuffer;
});
}));
}
}

4. 内存监控:

@Bean
public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
return registry -> registry.config().commonTags("application", "gateway");
}

📖 总结

微服务网关是现代分布式系统的核心组件,它提供了统一的服务入口、强大的流量管理能力和完善的保护机制。通过本文的详细介绍,相信你已经掌握了:

核心要点

  • ✅ 网关的重要作用和应用场景
  • ✅ Spring Cloud Gateway的技术架构和工作原理
  • ✅ 路由配置、过滤器机制的实际应用
  • ✅ 负载均衡、限流熔断的实现方法
  • ✅ 安全认证和性能优化的最佳实践
  • ✅ 实战配置案例和常见面试问题

面试准备建议

  1. 理解核心概念: 掌握网关的基本作用和技术原理
  2. 熟悉配置方式: 能够编写和调试网关配置
  3. 了解性能优化: 掌握常见的性能调优方法
  4. 实践经验: 结合实际项目经验说明网关应用
  5. 故障排查: 了解常见问题和解决方案

学习路径建议

  1. 基础学习: 先理解微服务架构和网关概念
  2. 技术深入: 学习Spring Cloud Gateway的详细功能
  3. 实践应用: 搭建实际的网关项目进行练习
  4. 源码研究: 深入了解网关的底层实现原理
  5. 持续学习: 关注最新的技术发展和最佳实践

希望这份详细的网关文档能帮助你深入理解微服务网关技术,在面试和实际工作中都能游刃有余!