摘要
本文深入探讨Spring Cloud Gateway的异常处理机制,包括全局异常处理器、自定义错误响应、异常监控等内容。通过详细的代码示例和架构分析,帮助开发者构建健壮的网关异常处理体系。
1. 异常处理概述
1.1 网关异常类型
在Spring Cloud Gateway中,常见的异常类型包括:
- 路由异常:路由匹配失败、目标服务不可用
- 认证异常:认证失败、令牌过期
- 限流异常:请求被限流
- 系统异常:网络超时、服务降级
1.2 异常处理的重要性
完善的异常处理机制能够:
- 提供友好的错误响应
- 避免系统级错误暴露
- 收集异常信息用于监控
- 提升用户体验
2. Spring WebFlux 异常处理机制
2.1 WebExceptionHandler 接口
public interface WebExceptionHandler {
/**
* 处理Web交换异常
*/
Mono<Void> handle(ServerWebExchange exchange, Throwable ex);
}
2.2 默认异常处理器
Spring Cloud Gateway提供了默认的异常处理器,但通常需要自定义以满足业务需求。
3. 自定义全局异常处理器
3.1 自定义异常处理器实现
/**
* 自定义全局异常处理器
* 处理网关层的各种异常情况
*/
@Slf4j
@Component
@Order(-2)
public class CustomErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {
public CustomErrorWebExceptionHandler(ErrorAttributes errorAttributes,
ApplicationContext applicationContext,
ServerCodecConfigurer serverCodecConfigurer) {
super(errorAttributes, new WebProperties.Resources(), applicationContext);
super.setMessageWriters(serverCodecConfigurer.getWriters());
super.setMessageReaders(serverCodecConfigurer.getReaders());
}
@Override
protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
}
/**
* 渲染错误响应
*/
private Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
Map<String, Object> errorPropertiesMap = getErrorAttributes(request, ErrorAttributeOptions.defaults());
Throwable error = getError(request);
// 根据不同异常类型返回相应的错误响应
ErrorResponseInfo errorResponseInfo = buildErrorResponse(error, errorPropertiesMap);
log.error("网关异常: {}", errorResponseInfo.getMessage(), error);
return ServerResponse.status(errorResponseInfo.getStatus())
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(errorResponseInfo));
}
/**
* 构建错误响应信息
*/
private ErrorResponseInfo buildErrorResponse(Throwable error, Map<String, Object> errorPropertiesMap) {
ErrorResponseInfo responseInfo = new ErrorResponseInfo();
if (error instanceofResponseStatusException) {
ResponseStatusException statusException = (ResponseStatusException) error;
responseInfo.setStatus(statusException.getStatus().value());
responseInfo.setMessage(statusException.getReason());
} else if (error instanceof SocketException) {
responseInfo.setStatus(HttpStatus.SERVICE_UNAVAILABLE.value());
responseInfo.setMessage("目标服务不可用");
} else if (error instanceof ConnectTimeoutException) {
responseInfo.setStatus(HttpStatus.GATEWAY_TIMEOUT.value());
responseInfo.setMessage("连接超时");
} else if (error instanceof ReadTimeoutException) {
responseInfo.setStatus(HttpStatus.GATEWAY_TIMEOUT.value());
responseInfo.setMessage("读取超时");
} else if (error instanceof TooManyRequestsException) {
responseInfo.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
responseInfo.setMessage("请求过于频繁,请稍后再试");
} else {
responseInfo.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
responseInfo.setMessage("系统内部错误");
}
// 设置错误代码和时间戳
responseInfo.setCode(responseInfo.getStatus());
responseInfo.setTimestamp(System.currentTimeMillis());
// 添加请求ID用于追踪
responseInfo.setRequestId(UUID.randomUUID().toString());
return responseInfo;
}
/**
* 错误响应信息类
*/
@Data
public static class ErrorResponseInfo {
private int code;
private String message;
private int status;
private long timestamp;
private String requestId;
private Map<String, Object> details;
}
}
3.2 异常处理器自动配置
/**
* 异常处理器自动配置
* 自动注册自定义异常处理器
*/
@Configuration
@EnableConfigurationProperties(WebProperties.class)
public class ExceptionAutoConfiguration {
private final WebProperties webProperties;
public ExceptionAutoConfiguration(WebProperties webProperties) {
this.webProperties = webProperties;
}
@Bean
@ConditionalOnMissingBean(value = ErrorWebExceptionHandler.class,
search = SearchStrategy.CURRENT)
public ErrorWebExceptionHandler errorWebExceptionHandler(
ErrorAttributes errorAttributes,
WebProperties webProperties,
ServerCodecConfigurer serverCodecConfigurer,
ApplicationContext applicationContext) {
CustomErrorWebExceptionHandler exceptionHandler = new CustomErrorWebExceptionHandler(
errorAttributes,
applicationContext,
serverCodecConfigurer);
exceptionHandler.setMessageWriters(serverCodecConfigurer.getWriters());
exceptionHandler.setMessageReaders(serverCodecConfigurer.getReaders());
return exceptionHandler;
}
@Bean
@ConditionalOnMissingBean
public ErrorAttributes errorAttributes() {
return new GatewayErrorAttributes();
}
}
/**
* 自定义错误属性
* 控制错误响应中包含的属性
*/
@Slf4j
public class GatewayErrorAttributes implements ErrorAttributes {
private static final String ERROR_ATTRIBUTE =
GatewayErrorAttributes.class.getName() + ".ERROR";
@Override
public void storeErrorInformation(Throwable error, ServerWebExchange exchange) {
exchange.getAttributes().put(ERROR_ATTRIBUTE, error);
}
@Override
public Map<String, Object> getErrorAttributes(ServerRequest request,
ErrorAttributeOptions options) {
Map<String, Object> errorMap = new HashMap<>();
// 获取原始异常
Throwable error = getError(request);
if (error != null) {
errorMap.put("error", error.getClass().getSimpleName());
errorMap.put("message", error.getMessage());
// 记录异常日志
log.error("请求处理异常", error);
}
// 添加请求信息
errorMap.put("timestamp", System.currentTimeMillis());
errorMap.put("path", request.path());
return errorMap;
}
@Override
public Throwable getError(ServerRequest request) {
return (Throwable) request.attribute(ERROR_ATTRIBUTE)
.orElseThrow(() -> new IllegalStateException(
"Missing exception attribute in ServerWebExchange"));
}
}
4. 网关特定异常处理
4.1 网关异常处理切面
/**
* 网关异常处理切面
* 捕获和处理网关层的特定异常
*/
@Slf4j
@Component
public class GateWayExceptionHandlerAdvice {
/**
* 处理网关路由异常
*/
@ExceptionHandler(NoSuchElementException.class)
public Mono<ServerResponse> handleNoSuchElementException(ServerRequest request,
NoSuchElementException ex) {
log.error("路由未找到异常: {}", ex.getMessage(), ex);
ErrorResponseInfo errorResponse = createErrorResponse(
HttpStatus.NOT_FOUND.value(),
"请求的路由不存在"
);
return ServerResponse.status(HttpStatus.NOT_FOUND)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(errorResponse));
}
/**
* 处理路由定义异常
*/
@ExceptionHandler(RouteDefinitionException.class)
public Mono<ServerResponse> handleRouteDefinitionException(ServerRequest request,
RouteDefinitionException ex) {
log.error("路由定义异常: {}", ex.getMessage(), ex);
ErrorResponseInfo errorResponse = createErrorResponse(
HttpStatus.INTERNAL_SERVER_ERROR.value(),
"路由配置错误"
);
return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(errorResponse));
}
/**
* 处理超时异常
*/
@ExceptionHandler({TimeoutException.class, ReadTimeoutException.class, ConnectTimeoutException.class})
public Mono<ServerResponse> handleTimeoutException(ServerRequest request, Exception ex) {
log.error("请求超时异常: {}", ex.getMessage(), ex);
ErrorResponseInfo errorResponse = createErrorResponse(
HttpStatus.GATEWAY_TIMEOUT.value(),
"请求超时,请稍后重试"
);
return ServerResponse.status(HttpStatus.GATEWAY_TIMEOUT)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(errorResponse));
}
/**
* 处理连接异常
*/
@ExceptionHandler({SocketException.class, ConnectException.class})
public Mono<ServerResponse> handleConnectionException(ServerRequest request, Exception ex) {
log.error("连接异常: {}", ex.getMessage(), ex);
ErrorResponseInfo errorResponse = createErrorResponse(
HttpStatus.SERVICE_UNAVAILABLE.value(),
"目标服务不可用"
);
return ServerResponse.status(HttpStatus.SERVICE_UNAVAILABLE)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(errorResponse));
}
/**
* 处理限流异常
*/
@ExceptionHandler(TooManyRequestsException.class)
public Mono<ServerResponse> handleTooManyRequestsException(ServerRequest request,
TooManyRequestsException ex) {
log.warn("请求过于频繁: {}", ex.getMessage());
ErrorResponseInfo errorResponse = createErrorResponse(
HttpStatus.TOO_MANY_REQUESTS.value(),
"请求过于频繁,请稍后再试"
);
ServerResponse response = ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)
.header("Retry-After", "60") // 建议客户端60秒后重试
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(errorResponse));
return response;
}
/**
* 处理认证异常
*/
@ExceptionHandler(UnauthorizedException.class)
public Mono<ServerResponse> handleUnauthorizedException(ServerRequest request,
UnauthorizedException ex) {
log.warn("认证失败: {}", ex.getMessage());
ErrorResponseInfo errorResponse = createErrorResponse(
HttpStatus.UNAUTHORIZED.value(),
"认证失败,请重新登录"
);
return ServerResponse.status(HttpStatus.UNAUTHORIZED)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(errorResponse));
}
/**
* 处理通用异常
*/
@ExceptionHandler(Exception.class)
public Mono<ServerResponse> handleGenericException(ServerRequest request, Exception ex) {
log.error("通用异常: {}", ex.getMessage(), ex);
ErrorResponseInfo errorResponse = createErrorResponse(
HttpStatus.INTERNAL_SERVER_ERROR.value(),
"系统内部错误"
);
return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(errorResponse));
}
/**
* 创建错误响应
*/
private ErrorResponseInfo createErrorResponse(int code, String message) {
ErrorResponseInfo response = new ErrorResponseInfo();
response.setCode(code);
response.setMessage(message);
response.setStatus(code);
response.setTimestamp(System.currentTimeMillis());
response.setRequestId(UUID.randomUUID().toString());
return response;
}
/**
* 错误响应信息类
*/
@Data
public static class ErrorResponseInfo {
private int code;
private String message;
private int status;
private long timestamp;
private String requestId;
private Map<String, Object> details;
}
}
5. 异常响应格式统一
5.1 统一响应格式
/**
* 统一响应格式
* 定义API响应的标准格式
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result<T> {
private int code;
private String message;
private T data;
private long timestamp;
private String requestId;
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.setCode(200);
result.setMessage("success");
result.setData(data);
result.setTimestamp(System.currentTimeMillis());
result.setRequestId(UUID.randomUUID().toString());
return result;
}
public static <T> Result<T> success(String message, T data) {
Result<T> result = new Result<>();
result.setCode(200);
result.setMessage(message);
result.setData(data);
result.setTimestamp(System.currentTimeMillis());
result.setRequestId(UUID.randomUUID().toString());
return result;
}
public static <T> Result<T> error(int code, String message) {
Result<T> result = new Result<>();
result.setCode(code);
result.setMessage(message);
result.setTimestamp(System.currentTimeMillis());
result.setRequestId(UUID.randomUUID().toString());
return result;
}
public static <T> Result<T> error(String message) {
return error(500, message);
}
public static <T> Result<T> error(HttpStatus status, String message) {
return error(status.value(), message);
}
}
5.2 异常响应格式
/**
* 异常响应格式
* 专门用于异常情况的响应格式
*/
@Data
public class ErrorResponse {
private int code;
private String message;
private String error;
private long timestamp;
private String path;
private String requestId;
public static ErrorResponse of(HttpStatus status, String message, String path) {
ErrorResponse errorResponse = new ErrorResponse();
errorResponse.setCode(status.value());
errorResponse.setMessage(message);
errorResponse.setError(status.getReasonPhrase());
errorResponse.setTimestamp(System.currentTimeMillis());
errorResponse.setPath(path);
errorResponse.setRequestId(UUID.randomUUID().toString());
return errorResponse;
}
}
6. 异常监控与日志
6.1 异常监控过滤器
/**
* 异常监控过滤器
* 记录和监控异常事件
*/
@Component
public class ExceptionMonitorFilter implements GlobalFilter, Ordered {
private static final Logger exceptionLogger = LoggerFactory.getLogger("EXCEPTION_LOGGER");
private final MeterRegistry meterRegistry;
public ExceptionMonitorFilter(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
long startTime = System.currentTimeMillis();
return chain.filter(exchange)
.doOnError(throwable -> {
// 记录异常信息
recordException(exchange, throwable, startTime);
})
.doOnSuccess(signal -> {
// 记录成功请求的响应时间
long duration = System.currentTimeMillis() - startTime;
recordSuccessRequest(exchange, duration);
});
}
private void recordException(ServerWebExchange exchange, Throwable throwable, long startTime) {
long duration = System.currentTimeMillis() - startTime;
String path = exchange.getRequest().getURI().getPath();
String method = exchange.getRequest().getMethodValue();
String clientIp = getClientIp(exchange);
// 记录异常日志
exceptionLogger.error("异常请求 - path: {}, method: {}, clientIp: {}, duration: {}ms, error: {}",
path, method, clientIp, duration, throwable.getMessage(), throwable);
// 更新监控指标
if (meterRegistry != null) {
meterRegistry.counter("gateway.exceptions.total",
Tags.of(
"exception", throwable.getClass().getSimpleName(),
"path", path,
"method", method
)
).increment();
meterRegistry.timer("gateway.request.duration",
Tags.of("status", "error", "path", path, "method", method)
).record(duration, TimeUnit.MILLISECONDS);
}
}
private void recordSuccessRequest(ServerWebExchange exchange, long duration) {
String path = exchange.getRequest().getURI().getPath();
String method = exchange.getRequest().getMethodValue();
if (meterRegistry != null) {
meterRegistry.timer("gateway.request.duration",
Tags.of("status", "success", "path", path, "method", method)
).record(duration, TimeUnit.MILLISECONDS);
}
}
private String getClientIp(ServerWebExchange exchange) {
String xForwardedFor = exchange.getRequest().getHeaders().getFirst("X-Forwarded-For");
if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
return xForwardedFor.split(",")[0].trim();
}
String xRealIp = exchange.getRequest().getHeaders().getFirst("X-Real-IP");
if (xRealIp != null && !xRealIp.isEmpty()) {
return xRealIp;
}
InetSocketAddress remoteAddress = exchange.getRequest().getRemoteAddress();
return remoteAddress != null ? remoteAddress.getAddress().getHostAddress() : "unknown";
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
7. 异常处理架构
8. 特定异常处理
8.1 路由异常处理
/**
* 路由异常处理器
* 专门处理路由相关的异常
*/
@Component
public class RouteExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(RouteExceptionHandler.class);
/**
* 处理路由不可用异常
*/
public Mono<ServerResponse> handleRouteUnavailable(ServerWebExchange exchange,
String routeId, String serviceId) {
logger.error("路由不可用 - routeId: {}, serviceId: {}", routeId, serviceId);
ErrorResponse errorResponse = ErrorResponse.of(
HttpStatus.SERVICE_UNAVAILABLE,
"目标服务暂时不可用,请稍后重试",
exchange.getRequest().getURI().getPath()
);
errorResponse.setError("Route Unavailable");
return ServerResponse.status(HttpStatus.SERVICE_UNAVAILABLE)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(errorResponse));
}
/**
* 处理路由配置错误
*/
public Mono<ServerResponse> handleRouteConfigurationError(ServerWebExchange exchange,
String errorMessage) {
logger.error("路由配置错误: {}", errorMessage);
ErrorResponse errorResponse = ErrorResponse.of(
HttpStatus.INTERNAL_SERVER_ERROR,
"网关路由配置错误",
exchange.getRequest().getURI().getPath()
);
errorResponse.setError("Route Configuration Error");
return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(errorResponse));
}
}
8.2 降级异常处理
/**
* 降级异常处理器
* 处理服务降级情况下的异常
*/
@Component
public class FallbackExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(FallbackExceptionHandler.class);
/**
* 服务降级响应
*/
public Mono<ServerResponse> handleServiceFallback(ServerWebExchange exchange) {
logger.warn("服务降级 - 返回默认响应");
// 返回降级响应
String fallbackResponse = "{\"code\": 200, \"message\": \"服务暂时不可用,已启用降级策略\", \"data\": null}";
return ServerResponse.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(fallbackResponse));
}
/**
* 熔断异常处理
*/
public Mono<ServerResponse> handleCircuitBreakerOpen(ServerWebExchange exchange) {
logger.warn("熔断器开启 - 拒绝请求");
ErrorResponse errorResponse = ErrorResponse.of(
HttpStatus.SERVICE_UNAVAILABLE,
"服务暂时不可用,熔断器已开启",
exchange.getRequest().getURI().getPath()
);
errorResponse.setError("Circuit Breaker Open");
return ServerResponse.status(HttpStatus.SERVICE_UNAVAILABLE)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(errorResponse));
}
}
9. 异常处理测试
9.1 异常处理单元测试
@SpringBootTest
@AutoConfigureTestDatabase
public class ExceptionHandlerTest {
@Autowired
private WebTestClient webTestClient;
@Test
public void testRouteNotFoundException() {
// 测试路由不存在的情况
webTestClient.get()
.uri("/nonexistent/path")
.exchange()
.expectStatus().isNotFound()
.expectBody()
.jsonPath("$.code").isEqualTo(404)
.jsonPath("$.message").isEqualTo("请求的路由不存在");
}
@Test
public void testTimeoutException() {
// 测试超时异常
webTestClient.get()
.uri("/api/timeout")
.exchange()
.expectStatus().isEqualTo(HttpStatus.GATEWAY_TIMEOUT)
.expectBody()
.jsonPath("$.code").isEqualTo(504)
.jsonPath("$.message").isEqualTo("请求超时,请稍后重试");
}
@Test
public void testTooManyRequestsException() {
// 测试限流异常
webTestClient.get()
.uri("/api/rate-limited")
.exchange()
.expectStatus().isEqualTo(HttpStatus.TOO_MANY_REQUESTS)
.expectBody()
.jsonPath("$.code").isEqualTo(429)
.jsonPath("$.message").isEqualTo("请求过于频繁,请稍后再试");
}
}
10. 异常处理最佳实践
10.1 异常分类处理
/**
* 异常分类处理器
* 根据异常类型进行分类处理
*/
@Component
public class ClassifiedExceptionHandler {
/**
* 客户端错误处理 (4xx)
*/
public Mono<ServerResponse> handleClientError(HttpStatus status, String message,
ServerWebExchange exchange) {
ErrorResponse errorResponse = ErrorResponse.of(status, message,
exchange.getRequest().getURI().getPath());
errorResponse.setError("Client Error");
return ServerResponse.status(status)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(errorResponse));
}
/**
* 服务端错误处理 (5xx)
*/
public Mono<ServerResponse> handleServerError(HttpStatus status, String message,
ServerWebExchange exchange) {
ErrorResponse errorResponse = ErrorResponse.of(status, message,
exchange.getRequest().getURI().getPath());
errorResponse.setError("Server Error");
// 记录严重错误日志
log.error("服务端错误 - status: {}, message: {}, path: {}",
status, message, exchange.getRequest().getURI().getPath());
return ServerResponse.status(status)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(errorResponse));
}
/**
* 系统错误处理
*/
public Mono<ServerResponse> handleSystemError(String message, ServerWebExchange exchange) {
ErrorResponse errorResponse = ErrorResponse.of(
HttpStatus.INTERNAL_SERVER_ERROR,
"系统内部错误,请联系管理员",
exchange.getRequest().getURI().getPath()
);
errorResponse.setError("System Error");
// 记录系统错误日志
log.error("系统错误: {}", message);
return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(errorResponse));
}
}
10.2 异常响应模板
/**
* 异常响应模板
* 提供标准化的异常响应模板
*/
@Component
public class ExceptionResponseTemplate {
private final Map<HttpStatus, String> responseTemplates;
public ExceptionResponseTemplate() {
responseTemplates = new HashMap<>();
responseTemplates.put(HttpStatus.NOT_FOUND,
"{\"code\": %d, \"message\": \"请求的资源不存在\", \"error\": \"Not Found\", \"timestamp\": %d, \"path\": \"%s\"}");
responseTemplates.put(HttpStatus.UNAUTHORIZED,
"{\"code\": %d, \"message\": \"认证失败,请重新登录\", \"error\": \"Unauthorized\", \"timestamp\": %d, \"path\": \"%s\"}");
responseTemplates.put(HttpStatus.FORBIDDEN,
"{\"code\": %d, \"message\": \"权限不足,禁止访问\", \"error\": \"Forbidden\", \"timestamp\": %d, \"path\": \"%s\"}");
responseTemplates.put(HttpStatus.TOO_MANY_REQUESTS,
"{\"code\": %d, \"message\": \"请求过于频繁,请稍后再试\", \"error\": \"Too Many Requests\", \"timestamp\": %d, \"path\": \"%s\"}");
responseTemplates.put(HttpStatus.GATEWAY_TIMEOUT,
"{\"code\": %d, \"message\": \"请求超时,请稍后重试\", \"error\": \"Gateway Timeout\", \"timestamp\": %d, \"path\": \"%s\"}");
}
/**
* 获取异常响应模板
*/
public String getTemplate(HttpStatus status) {
return responseTemplates.getOrDefault(status,
"{\"code\": %d, \"message\": \"%s\", \"error\": \"Internal Server Error\", \"timestamp\": %d, \"path\": \"%s\"}");
}
}
11. 异常处理配置
11.1 配置示例
# 异常处理配置
gateway:
exception:
# 是否启用详细错误信息(生产环境建议关闭)
show-details: false
# 错误响应超时时间
response-timeout: 5000
# 是否记录异常堆栈信息
log-stack-trace: true
# 异常响应格式
response-format: json
# 监控配置
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
metrics:
export:
prometheus:
enabled: true
# 日志配置
logging:
level:
com.springboot.cloud.gateway.exception: DEBUG
EXCEPTION_LOGGER: INFO
org.springframework.cloud.gateway: WARN
pattern:
file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{traceId:-},%X{spanId:-}] %logger{50} - %msg%n"
12. 最佳实践与建议
12.1 异常处理原则
- 统一格式:所有异常响应使用统一格式
- 友好信息:向用户返回友好的错误信息
- 安全考虑:不暴露系统内部信息
- 监控告警:建立异常监控和告警机制
12.2 性能优化建议
- 异步处理:异常处理采用异步方式
- 缓存策略:缓存常见异常响应
- 批量处理:批量处理异常日志
13. 总结
Spring Cloud Gateway的异常处理是构建健壮微服务系统的重要组成部分。通过合理的异常处理机制,可以提供友好的错误响应,保障系统稳定性,并建立完善的监控体系。在实际应用中,需要根据业务需求设计合适的异常处理策略。
14. 参考资料
- Spring WebFlux异常处理文档
- Spring Cloud Gateway异常处理指南
- 微服务异常处理最佳实践
- API错误响应设计规范
作者信息:本文详细介绍了Spring Cloud Gateway异常处理与错误响应,适合有一定微服务开发经验的开发者阅读。
注意事项:在生产环境中,应避免返回详细的系统错误信息,以防安全风险。
扩展阅读:如需深入了解响应式编程异常处理,请参考相关技术文档。

被折叠的 条评论
为什么被折叠?



