修改总结
新增文件
SyncLockType.java - 同步锁类型枚举,定义了 JENKINS_SYNC、GITLAB_SYNC、K8S_SYNC 三种锁类型
修改文件
文件	修改内容
SyncLockManager.java	新增 tryLock(SyncLockType, Object...) 和 unlock(SyncLockType, Object...) 方法,支持显式指定锁类型
JenkinsViewServiceImpl.java	syncViews 改用 SyncLockType.JENKINS_SYNC
JenkinsJobServiceImpl.java	syncJobs 两个重载方法都改用 SyncLockType.JENKINS_SYNC,锁粒度统一为 externalSystemId
JenkinsBuildServiceImpl.java	syncBuilds 改用 SyncLockType.JENKINS_SYNC
This commit is contained in:
dengqichen 2025-12-31 10:42:10 +08:00
parent c82bb937d1
commit 9f3353e54a
4 changed files with 98 additions and 18 deletions

View File

@ -7,13 +7,16 @@ import java.util.concurrent.ConcurrentHashMap;
/**
* 智能同步任务本地锁管理器
* <p>自动识别调用者类名+方法名和参数生成唯一锁key无需手动指定锁类型</p>
* <p>支持两种使用方式</p>
* <ol>
* <li>自动识别调用者类名+方法名生成锁key - 适用于单方法独立锁</li>
* <li>显式指定锁类型 - 适用于跨方法/跨服务协调锁</li>
* </ol>
*
* <p>使用示例
* <p>使用示例1自动识别
* <pre>
* // 加锁自动识别调用方法和参数
* if (!syncLockManager.tryLock(externalSystemId)) {
* return; // 已有同步任务在执行
* return;
* }
* try {
* // 执行同步逻辑
@ -22,17 +25,91 @@ import java.util.concurrent.ConcurrentHashMap;
* }
* </pre>
*
* <p>锁key格式ClassName.methodName:param1:param2:...</p>
* <p>使用示例2显式锁类型 - 推荐用于跨服务协调
* <pre>
* if (!syncLockManager.tryLock(SyncLockType.JENKINS_SYNC, externalSystemId)) {
* return;
* }
* try {
* // 执行同步逻辑
* } finally {
* syncLockManager.unlock(SyncLockType.JENKINS_SYNC, externalSystemId);
* }
* </pre>
*/
@Slf4j
@Component
public class SyncLockManager {
// 统一的锁存储key = ClassName.methodName:param1:param2:...
// 统一的锁存储
private final ConcurrentHashMap<String, Boolean> locks = new ConcurrentHashMap<>();
// ==================== 显式锁类型方法推荐用于跨服务协调 ====================
/**
* 尝试获取锁显式指定锁类型
* <p>推荐用于需要跨方法/跨服务协调的场景</p>
*
* @param lockType 锁类型
* @param params 锁参数 externalSystemId
* @return true-获取成功false-已被锁定
*/
public boolean tryLock(SyncLockType lockType, Object... params) {
String lockKey = buildLockKey(lockType, params);
boolean acquired = locks.putIfAbsent(lockKey, true) == null;
if (!acquired) {
log.warn("同步任务正在执行中,跳过本次同步: lockType={}, params={}",
lockType.getKey(), formatParams(params));
} else {
log.debug("获取同步锁成功: lockType={}, params={}",
lockType.getKey(), formatParams(params));
}
return acquired;
}
/**
* 释放锁显式指定锁类型
*
* @param lockType 锁类型
* @param params 锁参数必须与tryLock时完全一致
*/
public void unlock(SyncLockType lockType, Object... params) {
String lockKey = buildLockKey(lockType, params);
locks.remove(lockKey);
log.debug("同步任务完成,释放锁: lockType={}, params={}",
lockType.getKey(), formatParams(params));
}
/**
* 检查锁是否被持有显式指定锁类型
*
* @param lockType 锁类型
* @param params 锁参数
* @return true-已被锁定false-未锁定
*/
public boolean isLocked(SyncLockType lockType, Object... params) {
String lockKey = buildLockKey(lockType, params);
return locks.containsKey(lockKey);
}
/**
* 构建锁key显式锁类型
*/
private String buildLockKey(SyncLockType lockType, Object... params) {
StringBuilder key = new StringBuilder(lockType.getKey());
for (Object param : params) {
key.append(":").append(param != null ? param : "null");
}
return key.toString();
}
// ==================== 自动识别调用者方法向后兼容 ====================
/**
* 尝试获取锁自动识别调用者
* <p>适用于单方法独立锁场景不需要跨服务协调</p>
*
* @param params 锁参数 externalSystemId, viewId等
* @return true-获取成功false-已被锁定

View File

@ -25,6 +25,7 @@ import com.qqchen.deploy.backend.notification.service.INotificationSendService;
import com.qqchen.deploy.backend.notification.dto.SendNotificationRequest;
import com.qqchen.deploy.backend.deploy.dto.sync.JenkinsSyncContext;
import com.qqchen.deploy.backend.deploy.lock.SyncLockManager;
import com.qqchen.deploy.backend.deploy.lock.SyncLockType;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.framework.exception.BusinessException;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
@ -117,8 +118,8 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl<JenkinsBuild, Jenki
@Async
@Transactional
public void syncBuilds(Long externalSystemId) {
// 尝试获取锁自动识别调用者JenkinsBuildServiceImpl.syncBuilds
if (!syncLockManager.tryLock(externalSystemId)) {
// 尝试获取锁使用统一的 JENKINS_SYNC 锁类型 syncViewssyncJobs 共享锁
if (!syncLockManager.tryLock(SyncLockType.JENKINS_SYNC, externalSystemId)) {
return;
}
@ -129,7 +130,7 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl<JenkinsBuild, Jenki
checkBuildNotifications(externalSystemId);
} finally {
// 释放锁
syncLockManager.unlock(externalSystemId);
syncLockManager.unlock(SyncLockType.JENKINS_SYNC, externalSystemId);
}
}

View File

@ -20,6 +20,7 @@ import com.qqchen.deploy.backend.deploy.service.IJenkinsSyncHistoryService;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.framework.exception.BusinessException;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import com.qqchen.deploy.backend.deploy.lock.SyncLockType;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
@ -74,15 +75,15 @@ public class JenkinsJobServiceImpl extends BaseServiceImpl<JenkinsJob, JenkinsJo
@Async
@Transactional(rollbackFor = Exception.class)
public void syncJobs(Long externalSystemId) {
// 尝试获取锁只使用 externalSystemId 作为锁参数
if (!syncLockManager.tryLock(externalSystemId)) {
// 尝试获取锁使用统一的 JENKINS_SYNC 锁类型 syncViewssyncBuilds 共享锁
if (!syncLockManager.tryLock(SyncLockType.JENKINS_SYNC, externalSystemId)) {
return;
}
try {
doSyncJobs(externalSystemId, null);
} finally {
syncLockManager.unlock(externalSystemId);
syncLockManager.unlock(SyncLockType.JENKINS_SYNC, externalSystemId);
}
}
@ -96,15 +97,15 @@ public class JenkinsJobServiceImpl extends BaseServiceImpl<JenkinsJob, JenkinsJo
@Async
@Transactional(rollbackFor = Exception.class)
public void syncJobs(Long externalSystemId, Long viewId) {
// 尝试获取锁使用 externalSystemId viewId 作为锁参数
if (!syncLockManager.tryLock(externalSystemId, viewId)) {
// 尝试获取锁使用统一的 JENKINS_SYNC 锁类型锁粒度为 externalSystemId忽略 viewId
if (!syncLockManager.tryLock(SyncLockType.JENKINS_SYNC, externalSystemId)) {
return;
}
try {
doSyncJobs(externalSystemId, viewId);
} finally {
syncLockManager.unlock(externalSystemId, viewId);
syncLockManager.unlock(SyncLockType.JENKINS_SYNC, externalSystemId);
}
}

View File

@ -18,6 +18,7 @@ import com.qqchen.deploy.backend.deploy.service.IJenkinsViewService;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.framework.exception.BusinessException;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import com.qqchen.deploy.backend.deploy.lock.SyncLockType;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
@ -72,15 +73,15 @@ public class JenkinsViewServiceImpl extends BaseServiceImpl<JenkinsView, Jenkins
@Async
@Transactional(rollbackFor = Exception.class)
public void syncViews(Long externalSystemId) {
// 尝试获取锁
if (!syncLockManager.tryLock(externalSystemId)) {
// 尝试获取锁使用统一的 JENKINS_SYNC 锁类型 syncJobssyncBuilds 共享锁
if (!syncLockManager.tryLock(SyncLockType.JENKINS_SYNC, externalSystemId)) {
return;
}
try {
doSyncViews(externalSystemId);
} finally {
syncLockManager.unlock(externalSystemId);
syncLockManager.unlock(SyncLockType.JENKINS_SYNC, externalSystemId);
}
}