xkcoding/spring-boot-demo · warning · RuntimeException
请勿重复提交
Error message
请勿重复提交
What it means
ZooLockAspect.around acquires a Curator InterProcessMutex; if lock.acquire(timeout, unit) returns false (lock not acquired within the timeout), it throws RuntimeException('请勿重复提交') at ZooLockAspect.java:77. Distributed-lock contention is treated as duplicate-submission prevention.
Source
Thrown at demo-zookeeper/src/main/java/com/xkcoding/zookeeper/aspectj/ZooLockAspect.java:77
* @throws Throwable 异常信息
*/
@Around("doLock()")
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
Object[] args = point.getArgs();
ZooLock zooLock = method.getAnnotation(ZooLock.class);
if (StrUtil.isBlank(zooLock.key())) {
throw new RuntimeException("分布式锁键不能为空");
}
String lockKey = buildLockKey(zooLock, method, args);
InterProcessMutex lock = new InterProcessMutex(zkClient, lockKey);
try {
// 假设上锁成功,以后拿到的都是 false
if (lock.acquire(zooLock.timeout(), zooLock.timeUnit())) {
return point.proceed();
} else {
throw new RuntimeException("请勿重复提交");
}
} finally {
lock.release();
}
}
/**
* 构造分布式锁的键
*
* @param lock 注解
* @param method 注解标记的方法
* @param args 方法上的参数
* @return
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
private String buildLockKey(ZooLock lock, Method method, Object[] args) throws NoSuchFieldException, IllegalAccessException {
StringBuilder key = new StringBuilder(KEY_SEPARATOR + KEY_PREFIX + lock.key());View on GitHub (pinned to 87a142f960)
Solutions
- Increase the @ZooLock timeout to outlast the critical section
- Make the client idempotent (disable the button, send an idempotency token)
- Verify ZooKeeper quorum health and session-timeout settings
- Retry with backoff where the operation is safe to retry
Example fix
// before
if (lock.acquire(zooLock.timeout(), zooLock.timeUnit())) {
return point.proceed();
} else {
throw new RuntimeException('请勿重复提交');
}
// after (caller-side)
try {
lockedService.doWork(req);
} catch (RuntimeException e) {
if ('请勿重复提交'.equals(e.getMessage())) {
return ApiResponse.of(429, '请求处理中,请勿重复提交', null);
}
throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
// client-side: guard against double submit before invoking the locked method
if (submitting) { return; }
submitting = true;
try { lockedService.doWork(req); } finally { submitting = false; } Try / catch
try {
lockedService.doWork(req);
} catch (RuntimeException e) {
if ('请勿重复提交'.equals(e.getMessage())) {
return ApiResponse.of(429, '请求处理中,请勿重复提交', null);
}
throw e;
} Prevention
- Make the client idempotent (disable the button, idempotency key)
- Size @ZooLock timeout to exceed the critical section
- Keep the ZooKeeper quorum healthy to avoid spurious acquire timeouts
When it happens
Trigger: Two concurrent invocations of the same @ZooLock method/lock key; the current holder exceeds the acquire timeout; the ZooKeeper quorum is slow so acquire times out.
Common situations: Double-click submit on the client; high concurrency on an idempotent endpoint; an unhealthy/latent ZooKeeper cluster; @ZooLock timeout set too short.
Related errors
AI-assisted analysis of xkcoding/spring-boot-demo@87a142f960 (2026-08-14).
Data as JSON: /api/errors/b997bd6e24acdf3c.
Report an issue: GitHub.