如何设计异常分级策略,对不同级别异常(如业务异常、系统异常)采取不同的告警方式?

188 阅读6分钟

在SpringBoot/SpringCloud项目中,设计一套精细的异常分级与告警策略,是保障微服务架构稳定性的关键。下面我将结合具体案例,为你详细讲解如何实现。

🎯 SpringBoot/SpringCloud异常分级与告警策略设计

1. 异常分级策略设计

一个清晰的异常分级体系是有效处理的基础。下表总结了核心的异常级别及其处理方式。

异常级别触发场景日志级别告警方式HTTP状态码处理目标
业务异常(Business Exception)参数校验失败、业务规则违反WARN通常不告警,记录日志400 (Bad Request)用户体验、流程正确性
认证授权异常(Auth Exception)用户未登录、权限不足WARN通常不告警,记录日志401/403 (Unauthorized/Forbidden)安全性、访问控制
依赖服务异常(Service Exception)下游微服务调用超时或失败ERROR即时告警(如钉钉、短信)503 (Service Unavailable)系统可用性、快速恢复
系统异常(System Exception)数据库连接失败、中间件故障等ERROR紧急告警(电话、PagerDuty)500 (Internal Server Error)系统稳定性、及时修复
未知异常(Unknown Exception)未预期的运行时异常ERROR紧急告警(电话、PagerDuty)500 (Internal Server Error)系统稳定性、漏洞发现

2. 核心组件实现

2.1 定义异常枚举与自定义异常体系

首先,使用枚举统一管理错误码和消息,避免硬编码。业务异常应继承自运行时异常(RuntimeException)。

// 错误码枚举
public enum ErrorCode {
    // 业务异常 (1xxx)
    PARAM_INVALID(1001, "请求参数无效"),
    USER_NOT_FOUND(1002, "用户不存在"),
    
    // 认证授权异常 (2xxx)
    TOKEN_EXPIRED(2001, "令牌已过期"),
    PERMISSION_DENIED(2002, "权限不足"),
    
    // 系统及依赖异常 (5xxx)
    DB_CONNECTION_FAILED(5001, "数据库连接失败"),
    REMOTE_SERVICE_UNAVAILABLE(5002, "远程服务不可用");
    
    private final int code;
    private final String msg;
    // 构造方法、getter...
}
// 基础业务异常类
public class BusinessException extends RuntimeException {
    private final int code;
    
    public BusinessException(ErrorCode errorCode) {
        super(errorCode.getMsg());
        this.code = errorCode.getCode();
    }
    // 包含详细信息的构造器
    public BusinessException(ErrorCode errorCode, String detailMessage) {
        super(errorCode.getMsg() + ": " + detailMessage);
        this.code = errorCode.getCode();
    }
    // getter...
}

// 特定的业务异常类
public class UserNotFoundException extends BusinessException {
    public UserNotFoundException(Long userId) {
        super(ErrorCode.USER_NOT_FOUND, "用户ID: " + userId);
    }
}

2.2 实现全局异常处理器(@ControllerAdvice

使用 @ControllerAdvice创建全局异常处理器,它是实现异常分级的核心,根据不同的异常类型返回统一的JSON格式错误信息。

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    
    /**
     * 处理业务异常 (WARN级别日志,不告警)
     */
    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException ex) {
        // WARN级别日志,记录错误码和消息,通常不触发紧急告警
        log.warn("业务异常 [code:{}] - {}", ex.getCode(), ex.getMessage());
        ErrorResponse response = new ErrorResponse(ex.getCode(), ex.getMessage());
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
    }
    
    /**
     * 处理依赖服务异常 (如熔断器抛出的异常),需触发告警
     */
    @ExceptionHandler(RemoteServiceException.class)
    public ResponseEntity<ErrorResponse> handleRemoteServiceException(RemoteServiceException ex) {
        // ERROR级别日志,并触发告警(如集成Sentinel的熔断事件)
        log.error("依赖服务异常,触发告警 [code:{}] - {}", ex.getCode(), ex.getMessage());
        // 此处可集成告警逻辑,例如调用告警服务发送通知
        // alertService.sendAlert("依赖服务异常", ex.getMessage(), AlertLevel.URGENT);
        ErrorResponse response = new ErrorResponse(ex.getCode(), "服务暂时不可用,请稍后重试");
        return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(response);
    }
    
    /**
     * 处理系统严重异常 (最高级别告警)
     */
    @ExceptionHandler(SystemException.class)
    public ResponseEntity<ErrorResponse> handleSystemException(SystemException ex) {
        // ERROR级别日志,记录完整堆栈,并触发紧急告警
        log.error("系统级异常,需立即处理![code:{}]", ex.getCode(), ex);
        // alertService.sendUrgentAlert("系统级异常", ex.getMessage(), AlertLevel.CRITICAL);
        ErrorResponse response = new ErrorResponse(ex.getCode(), "系统内部错误,请联系管理员");
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
    }
    
    /**
     * 兜底处理所有未明确捕获的异常
     */
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleUnknownException(Exception ex) {
        log.error("未预期的未知异常,需关注并检查!", ex);
        // alertService.sendUrgentAlert("未知异常", ex.getMessage(), AlertLevel.CRITICAL);
        ErrorResponse response = new ErrorResponse(ErrorCode.INTERNAL_ERROR.getCode(), "系统繁忙");
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
    }
}

// 统一的错误响应体
@Data
@AllArgsConstructor
class ErrorResponse {
    private int code;
    private String message;
    private long timestamp = System.currentTimeMillis();
}

3. 告警方式集成实战

3.1 集成监控告警平台(Spring Boot Actuator + Admin + Prometheus)

微服务的健康监控和告警可以借助Spring Boot Actuator、Spring Cloud Admin等工具实现。

  • 依赖配置:在pom.xml中引入Actuator和Admin Client依赖。

  • 配置暴露端点:在application.yml中暴露监控端点,并集成Admin Server。

    management:
      endpoints:
        web:
          exposure:
            include: health,metrics,info,prometheus
      endpoint:
        health:
          show-details: always
    spring:
      boot:
        admin:
          client:
            url: http://your-admin-server:8080
    
  • 自定义健康指示器:对于关键依赖(如数据库、特定微服务),实现HealthIndicator接口。当健康状态由UP变为DOWN时,Admin Server可触发告警(如邮件、Slack)。

    @Component
    public class CriticalServiceHealthIndicator implements HealthIndicator {
        @Override
        public Health health() {
            // 检查关键服务状态
            if (isCriticalServiceOk()) {
                return Health.up().withDetail("version", "1.0.0").build();
            } else {
                // 当状态为DOWN时,Spring Cloud Admin可触发配置的告警
                return Health.down().withDetail("Error", "关键服务无响应").build();
            }
        }
    }
    

3.2 集成Sentinel实现熔断与告警

Sentinel的熔断降级功能可以作为异常分级策略的一部分。

  • 资源定义与熔断规则:使用@SentinelResource注解定义资源,并配置熔断规则(如异常比例、慢调用比例)。

    @Service
    public class OrderService {
        @SentinelResource(value = "createOrder",
                blockHandler = "handleBlock", // 流控/熔断处理
                fallback = "handleFallback")  // 业务异常处理
        public Order createOrder(OrderDTO orderDTO) {
            // 业务逻辑,可能抛出业务异常或依赖服务不可用
            return orderRepository.save(order);
        }
    
        // 熔断处理函数
        public Order handleBlock(OrderDTO orderDTO, BlockException ex) {
            log.error("创建订单请求被阻断,触发熔断降级", ex);
            // 可以触发告警,通知系统有大量请求被熔断
            // alertService.sendAlert("订单服务熔断", ex.getMessage(), AlertLevel.URGENT);
            throw new BusinessException(ErrorCode.SERVICE_BUSY);
        }
    
        // 降级处理函数
        public Order handleFallback(OrderDTO orderDTO, Throwable ex) {
            log.error("创建订单业务执行失败,执行降级逻辑", ex);
            // 根据异常类型判断告警级别
            if (ex instanceof RemoteServiceException) {
                // alertService.sendAlert("订单服务依赖异常", ex.getMessage(), AlertLevel.URGENT);
            }
            throw new BusinessException(ErrorCode.OPERATION_FAILED);
        }
    }
    
  • 在Sentinel Dashboard中配置规则:可以在Sentinel控制台根据慢调用比例异常比例异常数来设置熔断规则。当规则触发时,除了执行降级逻辑,还可以通过Sentinel扩展接口将事件推送到告警系统。

4. 最佳实践总结

  1. 清晰分层:建立清晰的异常继承体系,区分业务异常、系统异常等。

  2. 统一处理:使用@ControllerAdvice确保所有异常都能被捕获并转换为统一的JSON响应格式。

  3. 日志与监控:不同级别的异常记录不同级别的日志(如业务异常WARN,系统异常ERROR)。集成像Spring Boot Actuator和Micrometer这样的监控组件,暴露指标。

  4. 智能告警

    • 业务异常通常记录日志即可,除非有特定业务监控需求。
    • 依赖服务异常系统异常需要根据其发生频率和影响范围设定告警阈值,避免告警风暴。
    • 可结合Prometheus和Grafana等工具设置更智能的告警规则(如5分钟内异常数量持续超过某个阈值)。
  5. 熔断降级:对于依赖的外部服务,使用Sentinel等组件实现熔断,防止故障蔓延,并在此过程中触发相应告警。

通过这套结合了异常分级、全局处理、监控告警和熔断降级的策略,你的SpringBoot/SpringCloud应用就能建立起一道坚固的防线,显著提升系统的稳定性和可维护性。