先定义lock对象
package com.huazhu.erp.commonbc.domain.lock;
import com.huazhu.base.utils.DateUtil;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import org.joda.time.DateTime;
import java.time.Duration;
@ToString
@EqualsAndHashCode
@Getter
public final class GlobalLock {
private final String lockKey;
private final String lockValue;
private final Duration duration;
private GlobalLock(String lockKey, Duration duration) {
this.lockKey = lockKey;
this.duration = duration;
this.lockValue = DateTime.now().toString(DateUtil.COMMON_PATTERN_SS);
}
public static GlobalLock of(String lockKey, Duration duration) {
return new GlobalLock(lockKey, duration);
}
}
添加锁的实现方式。可用于避免方法被重复执行等
package com.huazhu.erp.commonbc.application;
import com.huazhu.base.utils.DateUtil;
import com.huazhu.erp.commonbc.domain.lock.GlobalLock;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.Random;
@Slf4j
@Service
public class GlobalLockAppService {
private RedisTemplate<String, String> redisTemplate;
private final Random rand = new Random();
/**
* 获取锁
*/
public boolean getLock(GlobalLock lock) {
if (StringUtils.isEmpty(lock.getLockKey())
|| StringUtils.isEmpty(lock.getLockValue())) {
return false;
}
return Boolean.TRUE.equals(
redisTemplate.execute((RedisCallback<Boolean>) connection -> {
RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
byte[] key = serializer.serialize(lock.getLockKey());
byte[] value = serializer.serialize(lock.getLockValue());
Boolean locked;
if (key == null || value == null) {
locked = false;
} else {
locked = connection.set(key,
value,
Expiration.from(lock.getDuration()), RedisStringCommands.SetOption.SET_IF_ABSENT);
}
log.info("getLock:{}:{}:{}:{}", locked, lock.getLockKey(), lock.getLockValue(), lock.getDuration());
return locked;
})
);
}
/**
* 获取锁
*/
public boolean tryLockMultipleTimes(GlobalLock lock) {
for (int i = 0; i < 3; i++) {
if (getLock(lock)) {
return true;
}
try {
int millis = rand.nextInt(200) + 1;
Thread.sleep(millis);
} catch (InterruptedException ex) {
log.warn(ex.getMessage(), ex);
Thread.currentThread().interrupt();
}
}
log.info("加锁:{}-失败",lock.getLockKey());
return false;
}
//lua脚本删除redis锁
public void unlock(GlobalLock lock) {
String luaScript = "local value = redis.call('get', KEYS[1]) if(value) then if(value == ARGV[1]) then return " +
"redis.call('del', KEYS[1]) else return -1 end else return 0 end";
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(luaScript,Long.class);
Long execute = redisTemplate.execute(redisScript, Collections.singletonList(lock.getLockKey()),
lock.getLockValue());
String releaseTime = DateTime.now().toString(DateUtil.COMMON_PATTERN_SS);
log.info("lock-close:{}:{}:releaseTime:{}:delFlag:{}",
lock.getLockKey(), lock.getLockValue(), releaseTime, execute);
}
@Autowired
public void setRedisTemplate(RedisTemplate<String, String> redisTemplate) {
this.redisTemplate = redisTemplate;
}
}
使用实例:
//lockKey是此次方法需要加锁的对象,通常是主键或者单号之类的,能够区分请求的关键信息
GlobalLock globalLock = GlobalLock.of(lockKey, Duration.ofSeconds(30L));
//如果没有获取锁则不做处理,只有获取了锁之后才能做业务操作
if (!globalLockAppService.getLock(globalLock)) {
log.warn("RepeatSupplierSync:{}",supplierVO.getSupplierName());
} else {
//执行业务代码
} finally {
//释放锁
globalLockAppService.unlock(globalLock);
}