依赖
<!--spring-redis分布式锁-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-redis</artifactId>
<version>5.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
配置
spring:
redis:
host: 192.168.0.202
port: 6379
工具类
DistributionLockConfiguration
/**
* 分布式锁配置
*
* @author gavin
* @date 2020-06-18
*/
@Configuration
public class DistributionLockConfiguration {
private static final String DEFALULT_LOCK_KEY = "DEFALULT_DISTRIBUTION_LOCK";
/**
* 初始化redis分布式锁配置.
*
* 注意,这里的分布式锁的解锁时间默认为60秒,这可能会导致以下安全性问题(出现概率依次递减):
* 1. 分布式锁用在网络IO的场景,必须设置超时时间,否则可能会因为对方超时导致锁自动释放(奇门的超时时间不能高于分布式锁的加锁时间)
* 2. 服务器时钟跳跃,可能会出现不可预料的锁到期情况,可能出现的场景:业务因其他原因执行时间过长 + 服务器时钟跳跃 可能会导致分布式锁自动释放
* 3. JVM GC的STW过长导致(OMS中几乎不可能出现STW长达60秒的情况)
*
* @param connectionFactory redis连接工厂
* @return redis分布式锁配置
*/
@Bean
public RedisLockRegistry defaultRedisLockRegistry(RedisConnectionFactory connectionFactory) {
return new RedisLockRegistry(connectionFactory, DEFALULT_LOCK_KEY);
}
@Bean
public DistributionLock defaultDistributionLock(RedisLockRegistry redisLockRegistry) {
return new SpringRedisLock(redisLockRegistry);
}
}
DistributionLock
/**
* @author gavin
* @date 2020-06-18
*/
public interface DistributionLock {
void lock(String var1);
boolean tryLock(String var1, long var2, TimeUnit var4);
void unlock(String var1);
}
SpringRedisLock
/**
* @author gavin
* @date 2020-06-18
*/
public class SpringRedisLock implements DistributionLock {
private static final Logger LOGGER = LoggerFactory.getLogger(SpringRedisLock.class);
private RedisLockRegistry redisLockRegistry;
public SpringRedisLock(RedisLockRegistry redisLockRegistry) {
this.redisLockRegistry = redisLockRegistry;
}
@Override
public void lock(String key) {
this.redisLockRegistry.obtain(key).lock();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("线程%s获取锁成功", Thread.currentThread().getName()));
}
}
@Override
public boolean tryLock(String key, long waitTimeout, TimeUnit timeUnit) {
Lock lock = this.redisLockRegistry.obtain(key);
try {
boolean isLockSuccess = lock.tryLock(waitTimeout, timeUnit);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("线程%s获取锁%s", Thread.currentThread().getName(), isLockSuccess ? "成功" : "失败"));
}
return isLockSuccess;
} catch (InterruptedException var7) {
return false;
}
}
@Override
public void unlock(String key) {
this.redisLockRegistry.obtain(key).unlock();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("线程%s释放锁", Thread.currentThread().getName()));
}
}
}
DistributionLockUtil
/**
* 构建分布式锁的工具类
*
* @author gavin
* @date 2020-06-18
*/
public class DistributionLockUtil {
private static final String DISTRIBUTION_LOCK = "DISTRIBUTION_LOCK_";
/**
* 构建分布式锁的key
*
* @param bizCode 业务编码
* @return key
*/
public static String buildKey(String bizCode) {
return DISTRIBUTION_LOCK + bizCode;
}
}
使用示例
@Override
@Transactional(rollbackFor = Exception.class)
public void receive(Integer stockOutId) {
StockOutDO stockOutDO = getStockOutById(stockOutId)
.orElseThrow(() -> new ServiceException("出库单不存在, id: " + stockOutId));
validateApproveStatus(stockOutDO.getApproveStatus());
stockOutDO.setApproveStatus(ApproveStatusEnum.COMPLETE.getValue());
stockOutDao.updateById(stockOutDO);
List<StockOutDetailDO> detailDOS = listStockOutDetails(stockOutId);
if (null == detailDOS || detailDOS.size() == 0) {
return;
}
// 扣减来源仓sku库存与占用
for (StockOutDetailDO detailDO : detailDOS) {
warehouseSkuDao
.reduceFreezeStock(stockOutDO.getUserId(), stockOutDO.getUserType(),
stockOutDO.getWarehouseId(), detailDO.getSkuId(), detailDO.getWeight(),
detailDO.getQuantity());
}
// 添加目标仓sku库存
WarehouseDO warehouseDO = warehouseDao.selectById(stockOutDO.getTargetId());
for (StockOutDetailDO detailDO : detailDOS) {
String distributionLockKey = DistributionLockUtil.buildKey(String.valueOf(detailDO.getSkuId()));
try {
distributionLock.lock(distributionLockKey);
int result = warehouseSkuDao
.addAvailableStock(stockOutDO.getTargetId(), UserTypeEnum.FACTORY.getValue(),
warehouseDO.getId(),
detailDO.getSkuId(),
detailDO.getWeight(), detailDO.getQuantity());
if (result == 0) {
// TODO 成本待处理
WarehouseSkuDO warehouseSkuDO = WarehouseSkuDO.builder().userId(warehouseDO.getUserId())
.userType(warehouseDO.getUserType()).warehouseId(warehouseDO.getId())
.productId(detailDO.getProductId()).productSource(detailDO.getProductSource())
.productCategoryId(stockOutDO.getProductCategoryId()).skuId(detailDO.getSkuId())
.availableQuantity(detailDO.getQuantity()).availableWeight(detailDO.getWeight()).build();
warehouseSkuDao.insert(warehouseSkuDO);
}
} finally {
distributionLock.unlock(distributionLockKey);
}
}
}