依赖
<!--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.202port: 6379
工具类
DistributionLockConfiguration
/*** 分布式锁配置** @author gavin* @date 2020-06-18*/@Configurationpublic 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分布式锁配置*/@Beanpublic RedisLockRegistry defaultRedisLockRegistry(RedisConnectionFactory connectionFactory) {return new RedisLockRegistry(connectionFactory, DEFALULT_LOCK_KEY);}@Beanpublic 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;}@Overridepublic void lock(String key) {this.redisLockRegistry.obtain(key).lock();if (LOGGER.isDebugEnabled()) {LOGGER.debug(String.format("线程%s获取锁成功", Thread.currentThread().getName()));}}@Overridepublic 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;}}@Overridepublic 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);}}}
