优化
This commit is contained in:
+10
-1
@@ -27,8 +27,11 @@ public class OverseasFeeOutboxRepository {
|
||||
public boolean claim(long id, String worker, LocalDateTime now, LocalDateTime stale) {
|
||||
return mapper.update(null, new LambdaUpdateWrapper<LastlegEventOutbox>()
|
||||
.eq(LastlegEventOutbox::getId, id).eq(LastlegEventOutbox::getEventType, EventTypes.BUSINESS_FEE_SOURCE_CHANGED)
|
||||
.and(status -> status.eq(LastlegEventOutbox::getStatus, "PENDING").or(processing ->
|
||||
.and(status -> status.eq(LastlegEventOutbox::getStatus, "PENDING")
|
||||
.and(retry -> retry.isNull(LastlegEventOutbox::getNextRetryAt).or().le(LastlegEventOutbox::getNextRetryAt, now))
|
||||
.or(processing ->
|
||||
processing.eq(LastlegEventOutbox::getStatus, "PROCESSING").le(LastlegEventOutbox::getLockedAt, stale)))
|
||||
.setSql("retry_count = retry_count + CASE WHEN status='PROCESSING' THEN 1 ELSE 0 END")
|
||||
.set(LastlegEventOutbox::getStatus, "PROCESSING").set(LastlegEventOutbox::getLockedBy, worker)
|
||||
.set(LastlegEventOutbox::getLockedAt, now)) == 1;
|
||||
}
|
||||
@@ -40,6 +43,12 @@ public class OverseasFeeOutboxRepository {
|
||||
mapper.update(null, claimed(id, worker, "PUBLISHED").set(LastlegEventOutbox::getPublishedAt, now)
|
||||
.set(LastlegEventOutbox::getNextRetryAt, null).set(LastlegEventOutbox::getLastError, null));
|
||||
}
|
||||
public void dead(long id, String worker, int failures, String error) {
|
||||
mapper.update(null, claimed(id, worker, "DEAD")
|
||||
.set(LastlegEventOutbox::getRetryCount, failures)
|
||||
.set(LastlegEventOutbox::getNextRetryAt, null)
|
||||
.set(LastlegEventOutbox::getLastError, truncate(error)));
|
||||
}
|
||||
public void retry(long id, String worker, int retryCount, LocalDateTime next, String error) {
|
||||
mapper.update(null, claimed(id, worker, "PENDING").set(LastlegEventOutbox::getRetryCount, retryCount)
|
||||
.set(LastlegEventOutbox::getNextRetryAt, next).set(LastlegEventOutbox::getLastError, truncate(error)));
|
||||
|
||||
+28
-11
@@ -1,7 +1,8 @@
|
||||
package com.baiyue.lastleg.financefee.service;
|
||||
|
||||
import com.baiyue.common.event.EventEnvelope;
|
||||
import com.baiyue.lastleg.client.EventBridgeClient;
|
||||
import com.baiyue.common.core.retry.BoundedRetryPolicy;
|
||||
import com.baiyue.lastleg.client.DirectEventClient;
|
||||
import com.baiyue.lastleg.entity.LastlegEventOutbox;
|
||||
import com.baiyue.lastleg.financefee.repository.OverseasFeeOutboxRepository;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
@@ -10,7 +11,6 @@ import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.UUID;
|
||||
@@ -18,28 +18,45 @@ import java.util.UUID;
|
||||
@Component
|
||||
public class OverseasFeeOutboxRelay {
|
||||
@Resource private OverseasFeeOutboxRepository repository;
|
||||
@Resource private EventBridgeClient client;
|
||||
@Resource private DirectEventClient client;
|
||||
@Resource private ObjectMapper objectMapper;
|
||||
@Value("${baiyue.security.internal-service-token:}") private String token;
|
||||
private final String worker = ManagementFactory.getRuntimeMXBean().getName() + ':' + UUID.randomUUID();
|
||||
|
||||
@Value("${baiyue.event-delivery.max-attempts:12}") private int maxAttempts = 12;
|
||||
@Value("${baiyue.event-delivery.base-delay-seconds:5}") private long baseDelaySeconds = 5;
|
||||
@Value("${baiyue.event-delivery.max-delay-seconds:1800}") private long maxDelaySeconds = 1800;
|
||||
@Value("${baiyue.event-delivery.jitter:0.1}") private double retryJitter = 0.1;
|
||||
|
||||
@Value("${baiyue.event-delivery.lease-seconds:60}") private long leaseSeconds = 60;
|
||||
|
||||
@Scheduled(fixedDelayString = "${baiyue.finance-fee-relay.fixed-delay:3000}")
|
||||
public void relay() {
|
||||
if (leaseSeconds < 1) throw new IllegalArgumentException("Lease duration must be positive");
|
||||
var policy = new BoundedRetryPolicy(maxAttempts, baseDelaySeconds, maxDelaySeconds, retryJitter);
|
||||
LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);
|
||||
for (LastlegEventOutbox candidate : repository.pending(50, now, now.minusSeconds(30))) {
|
||||
if (!repository.claim(candidate.getId(), worker, now, now.minusSeconds(30))) continue;
|
||||
for (LastlegEventOutbox candidate : repository.pending(50, now, now.minusSeconds(leaseSeconds))) {
|
||||
String worker = UUID.randomUUID().toString();
|
||||
now = LocalDateTime.now(ZoneOffset.UTC);
|
||||
|
||||
if (!repository.claim(candidate.getId(), worker, now, now.minusSeconds(leaseSeconds))) continue;
|
||||
LastlegEventOutbox row = repository.claimed(candidate.getId(), worker);
|
||||
if (row == null) continue;
|
||||
int failures = row.getRetryCount() == null ? 0 : row.getRetryCount();
|
||||
if (policy.exhausted(failures)) {
|
||||
repository.dead(row.getId(), worker, failures, "自动投递次数已耗尽(含租约超时)");
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
EventEnvelope envelope = objectMapper.readValue(row.getPayloadJson(), EventEnvelope.class).requireValid();
|
||||
var response = client.receive(token, envelope);
|
||||
if (response == null || !"200".equals(response.code()) || response.data() == null
|
||||
|| !envelope.eventId().equals(response.data().eventId()))
|
||||
throw new IllegalStateException("Event Bridge未确认海外费用事件");
|
||||
if (response == null || !response.successful() || response.data() == null)
|
||||
throw new IllegalStateException("Finance未确认费用事件");
|
||||
response.data().requireMatches(envelope);
|
||||
repository.published(row.getId(), worker, LocalDateTime.now(ZoneOffset.UTC));
|
||||
} catch (Exception exception) {
|
||||
int retries = row.getRetryCount() == null ? 1 : row.getRetryCount() + 1;
|
||||
repository.retry(row.getId(), worker, retries, now.plusSeconds(Math.min(900, 5L << Math.min(retries, 7))), exception.getMessage());
|
||||
int retries = failures + 1;
|
||||
if (policy.exhausted(retries)) repository.dead(row.getId(), worker, retries, exception.getMessage());
|
||||
else repository.retry(row.getId(), worker, retries, LocalDateTime.now(ZoneOffset.UTC).plus(policy.delayAfterFailure(retries)), exception.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user