1. 主页 > 大智慧

从电商系统崩溃到高并发稳定:Java异常处理的5个实战守则

场景一:用户支付异常雪崩(不要捕获通用Exception)

在618大促中,某电商支付模块因捕获Exception导致:

java复制
// 错误示例:吞没具体异常类型
try {
    paymentService.process(order);
} catch (Exception e) { // 同时捕获了NullPointer和支付超时
    logger.error("支付失败");
}

解决方案:细化捕获PaymentTimeoutExceptionPaymentFailedException,针对网络超时自动触发重试机制

场景二:库存锁定时资源泄漏(使用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); // 打印百万行相同堆栈

}

处理方案:



  1. 使用e.toString()替代完整堆栈

  2. 对高频异常增加发生次数阈值统计

  3. 通过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}

本文由嘻道妙招独家原创,未经允许,严禁转载