一、問題描述
在高并發場景下,大量設備實時上報狀態數據,需要異步保存到MySQL,同時需要解決冪等性校驗和線程池耗盡問題。
二、解決方案
1. 冪等性控制
作用:確保同一請求無論執行多少次,結果都一致,避免重復處理。
實現方式:
- 唯一標識:設備ID + 時間戳組合
- Redis原子操作:SET NX EX實現原子校驗
- 多級保障:Redis快速判斷 + 數據庫唯一索引 + 業務層查詢確認
- 超時機制:設置合理過期時間,避免永久占用資源
2. 防止線程池耗盡
策略:
- 合理配置線程池:
- 核心線程數:CPU核心數×2
- 最大線程數:100
- 隊列容量:500
- 拒絕策略:CallerRunsPolicy(壓力返回調用方)
- 快速失敗:冪等校驗快速過濾重復請求
- 監控告警:建議添加線程池監控指標
3. 異步處理模式
CompletableFuture等待結果模式:
- 控制器等待異步操作完成后返回結果
- 設置超時時間(5秒)避免無限等待
- 超時返回HTTP 202狀態碼,提供狀態查詢接口
三、示例
1.Controller
@PostMapping("/{deviceId}/status")
public ResponseEntity<?> reportStatus(@PathVariable String deviceId,@RequestParam("timestamp") long timestamp,@RequestBody DeviceStatus status) {String requestId = "device:" + deviceId + ":status:" + timestamp;try {// 等待異步操作完成(最多5秒)deviceStatusService.processDeviceStatus(requestId, deviceId, timestamp, status).get(5, TimeUnit.SECONDS);return ResponseEntity.ok().build();} catch (TimeoutException e) {return ResponseEntity.accepted().header("X-Request-ID", requestId).build();} catch (Exception e) {return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();}
}
2.Service
@Async("deviceStatusExecutor")
public CompletableFuture<Void> processDeviceStatus(