feat(REQ-3714): 更新org_user加上锁
This commit is contained in:
parent
ad22076462
commit
8f1cc335be
@ -50,6 +50,10 @@
|
|||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<artifactId>spring-cloud-starter-bootstrap</artifactId>
|
<artifactId>spring-cloud-starter-bootstrap</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.axzo.pokonyan</groupId>
|
||||||
|
<artifactId>pokonyan</artifactId>
|
||||||
|
</dependency>
|
||||||
<!-- <dependency>-->
|
<!-- <dependency>-->
|
||||||
<!-- <groupId>com.baomidou</groupId>-->
|
<!-- <groupId>com.baomidou</groupId>-->
|
||||||
<!-- <artifactId>lock4j-redisson-spring-boot-starter</artifactId>-->
|
<!-- <artifactId>lock4j-redisson-spring-boot-starter</artifactId>-->
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import cn.axzo.orgmanax.infra.dao.orguser.repository.OrgUserUpsertRepository;
|
|||||||
import cn.axzo.orgmanax.server.mq.producer.OrgUserChangedEventProducer;
|
import cn.axzo.orgmanax.server.mq.producer.OrgUserChangedEventProducer;
|
||||||
import cn.axzo.orgmanax.server.orguser.foundation.OrgUserFoundationService;
|
import cn.axzo.orgmanax.server.orguser.foundation.OrgUserFoundationService;
|
||||||
import cn.axzo.orgmanax.server.orguser.foundation.req.OrgUserWithdrawOrQuitReq;
|
import cn.axzo.orgmanax.server.orguser.foundation.req.OrgUserWithdrawOrQuitReq;
|
||||||
|
import cn.axzo.orgmanax.server.util.RedisLockUtil;
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.lang.Pair;
|
import cn.hutool.core.lang.Pair;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@ -35,6 +36,13 @@ import java.util.stream.Collectors;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class OrgUserFoundationServiceImpl implements OrgUserFoundationService {
|
public class OrgUserFoundationServiceImpl implements OrgUserFoundationService {
|
||||||
|
|
||||||
|
private static final String LOG_PREFIX = "[ORG_USER_OP] ";
|
||||||
|
|
||||||
|
private static final long OP_LOCK_TIMEOUT = 1000 * 5;
|
||||||
|
private static final String OP_LOCK_PREFIX = "ORG_USER";
|
||||||
|
private static final String OP_SINGLETON_UPSERT = "SINGLETON_UPSERT";
|
||||||
|
private static final String OP_BATCH_UPSERT = "BATCH_UPSERT";
|
||||||
|
|
||||||
private final WorkspaceGateway workspaceGateway;
|
private final WorkspaceGateway workspaceGateway;
|
||||||
private final OrgUserQueryRepository orgUserQueryRepository;
|
private final OrgUserQueryRepository orgUserQueryRepository;
|
||||||
private final OrgUserUpsertRepository orgUserUpsertRepository;
|
private final OrgUserUpsertRepository orgUserUpsertRepository;
|
||||||
@ -42,10 +50,16 @@ public class OrgUserFoundationServiceImpl implements OrgUserFoundationService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void batchWithdrawOrQuit(OrgUserWithdrawOrQuitReq req) {
|
public void batchWithdrawOrQuit(OrgUserWithdrawOrQuitReq req) {
|
||||||
OrgUserStatusEnum status;
|
|
||||||
if (NumberUtil.isNotPositiveNumber(req.getWorkspaceId())) {
|
if (NumberUtil.isNotPositiveNumber(req.getWorkspaceId())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// 优先获取批量写操作的锁,成功获取后再执行单个的写操作
|
||||||
|
String redisKey = generateBatchOperationKey(req.getOuId(), req.getWorkspaceId());
|
||||||
|
RedisLockUtil.tryLock(redisKey, OP_LOCK_TIMEOUT, () -> dealUpdate(req));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dealUpdate(OrgUserWithdrawOrQuitReq req) {
|
||||||
|
OrgUserStatusEnum status;
|
||||||
if (req.isUnitUpdate()) {
|
if (req.isUnitUpdate()) {
|
||||||
WorkspaceDetailReq workspaceDetailReq = new WorkspaceDetailReq();
|
WorkspaceDetailReq workspaceDetailReq = new WorkspaceDetailReq();
|
||||||
workspaceDetailReq.setId(req.getWorkspaceId());
|
workspaceDetailReq.setId(req.getWorkspaceId());
|
||||||
@ -123,4 +137,8 @@ public class OrgUserFoundationServiceImpl implements OrgUserFoundationService {
|
|||||||
return dto;
|
return dto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String generateBatchOperationKey(Long ouId, Long workspaceId) {
|
||||||
|
return RedisLockUtil.formatKey(OP_LOCK_PREFIX, OP_BATCH_UPSERT, ouId, workspaceId);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
package cn.axzo.orgmanax.server.util;
|
||||||
|
|
||||||
|
import cn.axzo.basics.common.util.AssertUtil;
|
||||||
|
import cn.axzo.maokai.common.enums.ErrorCodeEnum;
|
||||||
|
import cn.axzo.pokonyan.config.redis.RedisClient;
|
||||||
|
import cn.hutool.core.lang.UUID;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author luofu
|
||||||
|
* @version 1.0
|
||||||
|
* @date 2024/6/3
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
|
public final class RedisLockUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* redis key format
|
||||||
|
*
|
||||||
|
* @param prefix 前缀
|
||||||
|
* @param params 参数
|
||||||
|
* @return 格式化后的字符串
|
||||||
|
*/
|
||||||
|
public static String formatKey(String prefix, Object... params) {
|
||||||
|
AssertUtil.notEmpty(prefix, "prefix can not be empty");
|
||||||
|
if (Objects.isNull(params) || 0 == params.length) {
|
||||||
|
return prefix;
|
||||||
|
}
|
||||||
|
String strParams = Arrays.stream(params).map(Object::toString)
|
||||||
|
.collect(Collectors.joining(REDIS_LOCK_SPLITER));
|
||||||
|
return APP_NAME + ":" + prefix + REDIS_LOCK_SPLITER + strParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* do some thing while successfully fetch lock
|
||||||
|
*
|
||||||
|
* @param key 锁
|
||||||
|
* @param tryTimeoutMills 超时时间,单位毫秒
|
||||||
|
* @param executor 执行器
|
||||||
|
*/
|
||||||
|
public static void tryLock(String key, long tryTimeoutMills, Runnable executor) {
|
||||||
|
AssertUtil.notEmpty(key, "key can not be empty");
|
||||||
|
AssertUtil.notNull(executor, "executor can not be null");
|
||||||
|
String requestId = UUID.randomUUID().toString();
|
||||||
|
try {
|
||||||
|
long keyTimeoutMills = tryTimeoutMills * 2;
|
||||||
|
Boolean lockResult = RedisClient.LockOps.getLockUntilTimeout(key, requestId, keyTimeoutMills, TimeUnit.MILLISECONDS, tryTimeoutMills);
|
||||||
|
AssertUtil.isTrue(Boolean.TRUE.equals(lockResult), ErrorCodeEnum.SYSTEM_BUSY.getMessage());
|
||||||
|
executor.run();
|
||||||
|
} finally {
|
||||||
|
RedisClient.LockOps.releaseLock(key, requestId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* do some thing while successfully fetch lock
|
||||||
|
*
|
||||||
|
* @param key 锁
|
||||||
|
* @param tryTimeoutMills 超时时间,单位毫秒
|
||||||
|
* @param executor 执行器
|
||||||
|
*/
|
||||||
|
public static <T> T tryLock(String key, long tryTimeoutMills, Supplier<T> executor) {
|
||||||
|
AssertUtil.notEmpty(key, "key can not be empty");
|
||||||
|
AssertUtil.notNull(executor, "executor can not be null");
|
||||||
|
String requestId = UUID.randomUUID().toString();
|
||||||
|
try {
|
||||||
|
long keyTimeoutMills = tryTimeoutMills * 2;
|
||||||
|
Boolean lockResult = RedisClient.LockOps.getLockUntilTimeout(key, requestId, keyTimeoutMills, TimeUnit.MILLISECONDS, tryTimeoutMills);
|
||||||
|
AssertUtil.isTrue(Boolean.TRUE.equals(lockResult), ErrorCodeEnum.SYSTEM_BUSY.getMessage());
|
||||||
|
return executor.get();
|
||||||
|
} finally {
|
||||||
|
RedisClient.LockOps.releaseLock(key, requestId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String REDIS_LOCK_SPLITER = ":";
|
||||||
|
private static final String APP_NAME = "[maokai]";
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user