从电商系统崩溃到高并发稳定:Java异常处理的5个实战守则
日期:2025-05-27 18:21:04 •原创
场景一:用户支付异常雪崩(不要捕获通用Exception)
在618大促中,某电商支付模块因捕获Exception
导致:
java复制// 错误示例:吞没具体异常类型 try { paymentService.process(order); } catch (Exception e) { // 同时捕获了NullPointer和支付超时 logger.error("支付失败"); }
解决方案:细化捕获PaymentTimeoutException
和PaymentFailedException
,针对网络超时自动触发重试机制
场景二:库存锁定时资源泄漏(使用try-with-resources)
秒杀系统中未关闭的Redis连接:
// 传统写法存在资源泄漏风险
Jedis jedis = pool.getResource();
try {
jedis.setnx("lock:"+skuId, "1");
} finally {
if (!jedis.isClosed()) jedis.close(); // 可能忘记执行
}
最佳实践:改用自动资源管理
java复制try (Jedis jedis = pool.getResource()) {
jedis.setnx("lock:"+skuId, "1", "NX", "EX", 10);
}
场景三:第三方API调用黑洞(防御性异常封装)
物流接口超时引发的服务瘫痪:
// 直接抛出第三方异常
public TrackingResult queryLogistics(String orderNo)
throws ThirdPartyException { ... }
正确做法:创建业务级异常体系
java复制public class LogisticsException extends RuntimeException {
private ErrorCode code; // 包含可重试状态码
public LogisticsException(ThirdPartyException cause) {
super("物流系统异常:" + cause.getMessage(), cause);
}
}
场景四:日志风暴淹没有效信息(异常日志规范)
某P2P平台在资金对账时出现的日志灾难:
catch (DataInconsistentException e) {
logger.error("Error:" + e); // 打印百万行相同堆栈
}
处理方案:
- 使用
e.toString()
替代完整堆栈
- 对高频异常增加发生次数阈值统计
- 通过MDC注入请求ID实现日志追踪
场景五:分布式事务中的异常回滚(异常传播控制)
跨服务扣减库存的资金损失事故:
@Transactional
public void deductStock() {
try {
inventoryService.remoteDeduct(); // 可能超时
orderService.create();
} catch (RpcException e) {
// 事务未回滚导致数据不一致
}
}
解决策略:
- 在
@Transactional
中指定rollbackFor
- 对checked exception显式设置回滚属性
- 使用Seata的分布式事务补偿机制
落地建议:建立异常处理清单
检查项 生产事故案例 检查频率
是否捕获Throwable 导致JVM无法回收内存 每次发版
异常链是否完整 日志无法定位根本原因 CR时检查
资源关闭操作 文件句柄耗尽 压力测试
code {background:#f8f9fa;padding:2px 4px;border-radius:4px}
pre {background:#1e1e1e;color:#dcdcdc;padding:15px;overflow-x:auto}
table {border-collapse:collapse;margin:20px 0}
td,th {border:1px solid #ddd;padding:8px}
section {margin-bottom:40px;border-bottom:1px solid #eee;padding-bottom:30px}
本文由嘻道妙招独家原创,未经允许,严禁转载