95 lines
4.2 KiB
Java
95 lines
4.2 KiB
Java
package com.baiyue.eventbridge.service;
|
|
|
|
import com.baiyue.eventbridge.config.EventDispatcherProperties;
|
|
import com.baiyue.eventbridge.domain.BridgeDeliveryAttempt;
|
|
import com.baiyue.eventbridge.domain.BridgeInbox;
|
|
import com.baiyue.eventbridge.mapper.BridgeDeliveryAttemptMapper;
|
|
import com.baiyue.eventbridge.mapper.BridgeInboxMapper;
|
|
import jakarta.annotation.Resource;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import org.springframework.scheduling.annotation.Scheduled;
|
|
import org.springframework.stereotype.Component;
|
|
|
|
import java.lang.management.ManagementFactory;
|
|
import java.time.LocalDateTime;
|
|
import java.util.UUID;
|
|
|
|
@Component
|
|
@Slf4j
|
|
public class OverseasEventDispatcher {
|
|
@Resource private BridgeInboxMapper inboxMapper;
|
|
@Resource private BridgeDeliveryAttemptMapper attemptMapper;
|
|
@Resource private LastlegEventClient eventClient;
|
|
@Resource private FirstlegEventClient firstlegEventClient;
|
|
@Resource private EventDispatcherProperties properties;
|
|
private final String leaseOwner = ManagementFactory.getRuntimeMXBean().getName() + ":" + UUID.randomUUID();
|
|
|
|
@Scheduled(fixedDelayString = "${baiyue.event-dispatcher.fixed-delay:3000}")
|
|
public void dispatch() {
|
|
for (String eventId : inboxMapper.dispatchCandidates(properties.batchSize())) {
|
|
dispatchOne(eventId);
|
|
}
|
|
}
|
|
|
|
void dispatchOne(String eventId) {
|
|
if (inboxMapper.claim(eventId, leaseOwner, properties.leaseDuration().toSeconds()) != 1) {
|
|
return;
|
|
}
|
|
|
|
BridgeInbox event = inboxMapper.selectById(eventId);
|
|
int retryAttempt = event.getRetryCount() == null ? 1 : event.getRetryCount() + 1;
|
|
int deliveryAttemptNo = inboxMapper.nextAttemptNo(eventId);
|
|
BridgeDeliveryAttempt attempt = startAttempt(event, deliveryAttemptNo);
|
|
try {
|
|
String responseCode = deliver(event);
|
|
if (inboxMapper.markProcessed(eventId, leaseOwner) != 1) {
|
|
throw new IllegalStateException("事件租约已失效,无法确认投递结果");
|
|
}
|
|
finishAttempt(attempt, "DELIVERED", responseCode, null);
|
|
} catch (Exception exception) {
|
|
String error = abbreviate(exception.getMessage(), 1000);
|
|
inboxMapper.markFailed(eventId, leaseOwner, properties.maxAttempts(), backoffSeconds(retryAttempt), error);
|
|
finishAttempt(attempt, retryAttempt >= properties.maxAttempts() ? "DEAD" : "RETRY", null, error);
|
|
log.warn("跨区事件投递失败 eventId={}, retryAttempt={}, deliveryAttempt={}, reason={}",
|
|
eventId, retryAttempt, deliveryAttemptNo, error);
|
|
}
|
|
}
|
|
|
|
private String deliver(BridgeInbox event) throws Exception {
|
|
return "CN".equals(event.getTargetRegion())
|
|
? firstlegEventClient.deliver(event) : eventClient.deliver(event);
|
|
}
|
|
|
|
private BridgeDeliveryAttempt startAttempt(BridgeInbox event, int attemptNo) {
|
|
BridgeDeliveryAttempt attempt = new BridgeDeliveryAttempt();
|
|
attempt.setEventId(event.getEventId());
|
|
attempt.setSourceRegion(event.getSourceRegion());
|
|
attempt.setTargetRegion(event.getTargetRegion());
|
|
attempt.setAttemptNo(attemptNo);
|
|
attempt.setStatus("PROCESSING");
|
|
attempt.setStartedAt(LocalDateTime.now());
|
|
attemptMapper.insert(attempt);
|
|
return attempt;
|
|
}
|
|
|
|
private void finishAttempt(BridgeDeliveryAttempt attempt, String status, String responseCode, String error) {
|
|
attempt.setStatus(status);
|
|
attempt.setResponseCode(responseCode);
|
|
attempt.setErrorMessage(error);
|
|
attempt.setFinishedAt(LocalDateTime.now());
|
|
attemptMapper.updateById(attempt);
|
|
}
|
|
|
|
private long backoffSeconds(int attemptNo) {
|
|
long initial = properties.initialBackoff().toSeconds();
|
|
int shift = Math.min(Math.max(attemptNo - 1, 0), 20);
|
|
long delay = initial > Long.MAX_VALUE >> shift ? Long.MAX_VALUE : initial << shift;
|
|
return Math.min(delay, properties.maxBackoff().toSeconds());
|
|
}
|
|
|
|
private static String abbreviate(String value, int maxLength) {
|
|
String safe = value == null || value.isBlank() ? "未知投递错误" : value;
|
|
return safe.length() <= maxLength ? safe : safe.substring(0, maxLength);
|
|
}
|
|
}
|