WebClient发送Webhook时记录重试次数并保存请求详情至数据库的实现咨询
WebClient发送Webhook时记录重试次数并保存请求详情至数据库的实现咨询
嘿,看起来你已经在着手搭建Webhook的重试与日志记录功能了,这可是保障Webhook可靠性的核心环节!我来帮你梳理现有代码的优化方向,把每次请求的响应状态、响应体和重试次数完整存入数据库的需求落地。
核心需求复盘
你需要实现的是:
- 用WebClient向指定URL发送Webhook请求
- 请求失败时自动重试(最多5次,间隔10秒)
- **每次请求(无论成功/失败)**都要记录:响应状态码、响应体、尝试次数到数据库
现有代码的优化点
你已经用到了Retry.fixedDelay和onStatus处理错误响应,但还需要完善两个关键部分:重试次数的准确追踪和全请求链路的日志记录(包括成功请求和每一次重试的失败请求)。
完整实现代码示例
1. 完善后的sendWebHook方法
import reactor.util.retry.Retry; import java.time.Duration; import java.time.LocalDateTime; @SneakyThrows public void sendWebHook(Transaction transaction) { // 定义重试策略:最多重试5次,每次间隔10秒,同时追踪重试次数 RetryBackoffSpec retryStrategy = Retry.fixedDelay(5, Duration.ofSeconds(10)) .doOnRetry(retrySignal -> { // totalRetries()是已重试的次数,尝试次数 = 已重试次数 + 1(第一次尝试是1,重试后依次递增) int attemptNum = retrySignal.totalRetries() + 1; log.info("Webhook请求重试中,第{}次尝试,交易ID:{}", attemptNum, transaction.getId()); }); GetTransactionResponse requestBody = transactionMapper.transactionToGetResponse(transaction); String targetUrl = transaction.getNotificationUrl(); webClient.post() .uri(targetUrl) .body(Mono.just(requestBody), GetTransactionResponse.class) .retrieve() // 处理错误状态码:记录本次失败请求,然后抛出异常触发重试 .onStatus(HttpStatusCode::isError, response -> { return response.toEntity(WebhookResponse.class) .flatMap(errorResponse -> { // 第一次失败是第1次尝试 saveWebhookLog(transaction, errorResponse, 1); return Mono.error(new RuntimeException("Webhook请求失败,状态码:" + errorResponse.getStatusCode())); }); }) // 处理成功请求:记录成功的请求详情 .toEntity(WebhookResponse.class) .doOnSuccess(successResponse -> { saveWebhookLog(transaction, successResponse, 1); }) // 绑定重试策略,捕获异常后自动重试 .retryWhen(retryStrategy) // 处理最终重试失败的情况:记录最后一次尝试的失败详情 .doOnError(finalError -> { int finalAttemptNum = 5 + 1; // 最多重试5次,加上第一次尝试,共6次 // 构造失败响应实体用于记录 WebhookResponse errorBody = new WebhookResponse(); errorBody.setErrorMsg(finalError.getMessage()); ResponseEntity<WebhookResponse> finalFailedResponse = new ResponseEntity<>(errorBody, HttpStatus.INTERNAL_SERVER_ERROR); saveWebhookLog(transaction, finalFailedResponse, finalAttemptNum); }) .block(); // 同步场景用block,异步场景改用subscribe }
2. 数据库保存方法saveWebhookLog
这个方法负责把请求详情持久化到数据库,需要接收尝试次数参数:
private void saveWebhookLog(Transaction transaction, ResponseEntity<WebhookResponse> responseEntity, int attemptNumber) { // 构造Webhook日志实体(根据你的数据库表结构调整) WebhookLog logEntity = new WebhookLog(); logEntity.setTransactionId(transaction.getId()); logEntity.setNotificationUrl(transaction.getNotificationUrl()); logEntity.setAttemptNumber(attemptNumber); logEntity.setResponseStatusCode(responseEntity.getStatusCode().value()); logEntity.setResponseBody(objectMapper.writeValueAsString(responseEntity.getBody())); // 可选:记录请求体,让日志更完整 logEntity.setRequestBody(objectMapper.writeValueAsString(transactionMapper.transactionToGetResponse(transaction))); logEntity.setRequestTime(LocalDateTime.now()); // 存入数据库 webhookLogRepository.save(logEntity); }
关键细节说明
- 重试次数的准确追踪:通过
retrySignal.totalRetries()获取已重试的次数,尝试次数等于已重试次数+1,比如第一次尝试失败后重试,totalRetries()为1,尝试次数就是2。 - 全链路记录:不管是首次请求成功、首次请求失败触发重试,还是最终所有重试都失败,都会调用
saveWebhookLog,确保每一次请求都有记录。 - 灵活调整重试策略:如果需要指数退避(间隔时间递增),可以把
Retry.fixedDelay换成Retry.backoff(5, Duration.ofSeconds(10))。 - 异步场景适配:如果你的服务是异步架构,把
block()换成subscribe(),并在subscribe的回调里处理成功/失败逻辑,避免阻塞线程。
额外注意事项
- 确保
saveWebhookLog方法是线程安全的,尤其是在高并发场景下。 - 可以给WebClient添加超时设置,比如
.timeout(Duration.ofSeconds(30)),避免请求长时间挂起。 - 数据库表可以添加索引(比如
transaction_id),方便后续查询某笔交易的所有Webhook请求记录。
备注:内容来源于stack exchange,提问作者Александр Кошкин
相关产品推荐
相关产品推荐

