共计 3545 个字符,预计需要花费 9 分钟才能阅读完成。
线上事故引发的思考
去年我们团队遭遇过一次严重的线上故障:用户提交订单时偶发 500 错误,由于未正确处理NullPointerException,导致错误蔓延至网关层,最终引发服务雪崩。事后分析发现两个关键问题:

- 线程池采用 Tomcat 默认配置,突发流量下请求堆积
- 异常信息直接返回前端,暴露内部表结构
这次事故让我们意识到:生产级 API 必须具备完善的防御性编程机制。下面分享 7 个经过实战验证的优化方案。
核心技术方案
1. 线程池优化实战
SpringBoot 默认使用 Tomcat 线程池,配置参数如下:
# 默认配置(不推荐)server.tomcat.threads.max=200
server.tomcat.threads.min-spare=10
通过 Jmeter 压测发现:当并发请求达到 150 时,99 线响应时间从 50ms 飙升到 800ms。优化方案:
@Configuration
public class ThreadPoolConfig {
/**
* 自定义线程池
* 核心线程数 = CPU 核心数 * 2
* 最大队列长度 = 核心线程数 * 5
*/
@Bean
public ExecutorService apiExecutor() {int coreSize = Runtime.getRuntime().availableProcessors() * 2;
return new ThreadPoolExecutor(
coreSize,
coreSize * 2,
60L, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(coreSize * 5),
new ThreadPoolExecutor.CallerRunsPolicy() // 重要!避免任务丢失);
}
}
优化后对比数据:
| 指标 | 默认配置 | 优化配置 |
|---|---|---|
| 500 并发 RT99 | 1200ms | 350ms |
| 错误率 | 8.7% | 0.2% |
2. 全局异常处理
推荐使用 @ControllerAdvice 实现三层异常捕获:
@ControllerAdvice
public class GlobalExceptionHandler {
// 处理业务异常
@ExceptionHandler(BizException.class)
public ResponseEntity<ResultVO<?>> handleBizException(BizException e) {return ResponseEntity.status(e.getCode())
.body(ResultVO.error(e.getCode(), e.getMessage()));
}
// 处理参数校验异常
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ResultVO<?>> handleValidException(MethodArgumentNotValidException e) {String message = e.getBindingResult().getAllErrors()
.stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.collect(Collectors.joining(";"));
return ResponseEntity.badRequest()
.body(ResultVO.error(400, message));
}
// 兜底异常处理
@ExceptionHandler(Exception.class)
public ResponseEntity<ResultVO<?>> handleException(Exception e) {log.error("系统异常", e);
return ResponseEntity.internalServerError()
.body(ResultVO.error(500, "系统繁忙,请稍后重试"));
}
}
3. DTO 验证进阶
基础校验(使用@Valid):
@Data
public class UserDTO {@NotBlank(message = "用户名不能为空")
@Size(max = 20, message = "用户名长度超限")
private String username;
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式错误")
private String mobile;
}
@PostMapping("/users")
public ResultVO<String> createUser(@Valid @RequestBody UserDTO dto) {// 业务逻辑}
自定义校验器示例(检查密码强度):
@Documented
@Constraint(validatedBy = PasswordValidator.class)
@Target({FIELD, PARAMETER})
@Retention(RUNTIME)
public @interface StrongPassword {String message() default "密码必须包含大小写字母和数字";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};}
public class PasswordValidator implements ConstraintValidator<StrongPassword, String> {private static final Pattern PATTERN = Pattern.compile("^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).{8,}$");
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {return value != null && PATTERN.matcher(value).matches();}
}
4. 响应标准化设计
通用响应体模板:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ResultVO<T> implements Serializable {
private Integer code;
private String message;
private T data;
private long timestamp = System.currentTimeMillis();
public static <T> ResultVO<T> success(T data) {return new ResultVO<>(200, "success", data);
}
public static <T> ResultVO<T> error(int code, String message) {return new ResultVO<>(code, message, null);
}
}
生产环境检查清单
必须开启的 Actuator 端点
management:
endpoints:
web:
exposure:
include: health,metrics,threaddump
endpoint:
health:
show-details: always
日志脱敏正则
// 手机号 / 身份证脱敏
logback.xml 配置:<conversionRule conversionWord="msg"
converterClass="com.util.SensitiveDataConverter"/>
// 实现类关键代码:String pattern = "(?<=[^0-9a-zA-Z])(1[3-9]\\d{2})(\\d{4})(\\d{4})(?=[^0-9a-zA-Z])";
return message.replaceAll(pattern, "$1****$3");
Prometheus 关键指标
# 接口成功率
sum(rate(http_server_requests_seconds_count{status=~"2.."}[1m]))
/ sum(rate(http_server_requests_seconds_count[1m]))
# 线程池使用率
thread_pool_active_threads / thread_pool_max_threads
开放性问题
在微服务架构下,我们既需要统一的异常处理机制保证系统健壮性,又要允许业务服务定制特殊错误码。如何设计既能通过 @ControllerAdvice 实现全局拦截,又能支持服务自定义异常类型?欢迎在评论区分享你的方案。
正文完
发表至: 后端开发
近三天内
