feat: 切换定时任务至分布式调度底座
- 以执行账本和有界 Worker 承载重负载任务与故障接管 - 接入统一调度 Starter 并增加 MySQL 迁移、指标和回归测试
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package tech.easyflow.common.cache;
|
||||
|
||||
import jakarta.annotation.PreDestroy;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -10,6 +11,11 @@ import org.springframework.stereotype.Component;
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
@@ -27,6 +33,13 @@ public class RedisLockExecutor {
|
||||
private static final DefaultRedisScript<Long> NEXT_FENCING_TOKEN_SCRIPT;
|
||||
private static final DefaultRedisScript<Long> ACQUIRE_FENCED_LOCK_SCRIPT;
|
||||
|
||||
private final ScheduledExecutorService lockRenewalExecutor =
|
||||
Executors.newSingleThreadScheduledExecutor(runnable -> {
|
||||
Thread thread = new Thread(runnable, "easyflow-redis-lock-renewal");
|
||||
thread.setDaemon(true);
|
||||
return thread;
|
||||
});
|
||||
|
||||
static {
|
||||
RELEASE_LOCK_SCRIPT = new DefaultRedisScript<>();
|
||||
RELEASE_LOCK_SCRIPT.setScriptText(
|
||||
@@ -94,6 +107,66 @@ public class RedisLockExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在自动续租的分布式锁保护下执行任务。
|
||||
*
|
||||
* <p>适用于包含数据库锁等待或外部持久化操作、无法由固定租约严格覆盖的管理命令。
|
||||
* 若执行期间确认锁已丢失,则不向调用方返回成功。</p>
|
||||
*/
|
||||
public void executeWithRenewingLock(
|
||||
String lockKey,
|
||||
Duration waitTimeout,
|
||||
Duration leaseTimeout,
|
||||
Runnable task) {
|
||||
executeWithRenewingLock(lockKey, waitTimeout, leaseTimeout, () -> {
|
||||
task.run();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 在自动续租的分布式锁保护下执行有返回值任务。
|
||||
*/
|
||||
public <T> T executeWithRenewingLock(
|
||||
String lockKey,
|
||||
Duration waitTimeout,
|
||||
Duration leaseTimeout,
|
||||
Supplier<T> task) {
|
||||
LockHandle handle = acquire(lockKey, waitTimeout, leaseTimeout);
|
||||
AtomicBoolean lost = new AtomicBoolean();
|
||||
long renewalIntervalMillis = Math.max(1L, leaseTimeout.toMillis() / 3L);
|
||||
ScheduledFuture<?> renewal = lockRenewalExecutor.scheduleWithFixedDelay(
|
||||
() -> {
|
||||
try {
|
||||
if (!handle.renew()) {
|
||||
lost.set(true);
|
||||
}
|
||||
} catch (RuntimeException exception) {
|
||||
lost.set(true);
|
||||
log.warn("分布式锁续租失败,当前命令不得返回成功: lockKey={}",
|
||||
lockKey, exception);
|
||||
}
|
||||
},
|
||||
renewalIntervalMillis,
|
||||
renewalIntervalMillis,
|
||||
TimeUnit.MILLISECONDS);
|
||||
try {
|
||||
T result = task.get();
|
||||
if (lost.get()) {
|
||||
throw new IllegalStateException("执行期间分布式锁已丢失,lockKey=" + lockKey);
|
||||
}
|
||||
return result;
|
||||
} finally {
|
||||
renewal.cancel(false);
|
||||
handle.release();
|
||||
}
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void shutdownLockRenewalExecutor() {
|
||||
lockRenewalExecutor.shutdownNow();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取显式释放的分布式锁句柄。
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user