依赖

    1. <!--spring-redis分布式锁-->
    2. <dependency>
    3. <groupId>org.springframework.boot</groupId>
    4. <artifactId>spring-boot-starter-integration</artifactId>
    5. </dependency>
    6. <dependency>
    7. <groupId>org.springframework.integration</groupId>
    8. <artifactId>spring-integration-redis</artifactId>
    9. <version>5.0.4.RELEASE</version>
    10. </dependency>
    11. <dependency>
    12. <groupId>org.springframework.boot</groupId>
    13. <artifactId>spring-boot-starter-data-redis</artifactId>
    14. </dependency>

    配置

    1. spring:
    2. redis:
    3. host: 192.168.0.202
    4. port: 6379

    工具类
    DistributionLockConfiguration

    1. /**
    2. * 分布式锁配置
    3. *
    4. * @author gavin
    5. * @date 2020-06-18
    6. */
    7. @Configuration
    8. public class DistributionLockConfiguration {
    9. private static final String DEFALULT_LOCK_KEY = "DEFALULT_DISTRIBUTION_LOCK";
    10. /**
    11. * 初始化redis分布式锁配置.
    12. *
    13. * 注意,这里的分布式锁的解锁时间默认为60秒,这可能会导致以下安全性问题(出现概率依次递减):
    14. * 1. 分布式锁用在网络IO的场景,必须设置超时时间,否则可能会因为对方超时导致锁自动释放(奇门的超时时间不能高于分布式锁的加锁时间)
    15. * 2. 服务器时钟跳跃,可能会出现不可预料的锁到期情况,可能出现的场景:业务因其他原因执行时间过长 + 服务器时钟跳跃 可能会导致分布式锁自动释放
    16. * 3. JVM GC的STW过长导致(OMS中几乎不可能出现STW长达60秒的情况)
    17. *
    18. * @param connectionFactory redis连接工厂
    19. * @return redis分布式锁配置
    20. */
    21. @Bean
    22. public RedisLockRegistry defaultRedisLockRegistry(RedisConnectionFactory connectionFactory) {
    23. return new RedisLockRegistry(connectionFactory, DEFALULT_LOCK_KEY);
    24. }
    25. @Bean
    26. public DistributionLock defaultDistributionLock(RedisLockRegistry redisLockRegistry) {
    27. return new SpringRedisLock(redisLockRegistry);
    28. }
    29. }

    DistributionLock

    1. /**
    2. * @author gavin
    3. * @date 2020-06-18
    4. */
    5. public interface DistributionLock {
    6. void lock(String var1);
    7. boolean tryLock(String var1, long var2, TimeUnit var4);
    8. void unlock(String var1);
    9. }

    SpringRedisLock

    1. /**
    2. * @author gavin
    3. * @date 2020-06-18
    4. */
    5. public class SpringRedisLock implements DistributionLock {
    6. private static final Logger LOGGER = LoggerFactory.getLogger(SpringRedisLock.class);
    7. private RedisLockRegistry redisLockRegistry;
    8. public SpringRedisLock(RedisLockRegistry redisLockRegistry) {
    9. this.redisLockRegistry = redisLockRegistry;
    10. }
    11. @Override
    12. public void lock(String key) {
    13. this.redisLockRegistry.obtain(key).lock();
    14. if (LOGGER.isDebugEnabled()) {
    15. LOGGER.debug(String.format("线程%s获取锁成功", Thread.currentThread().getName()));
    16. }
    17. }
    18. @Override
    19. public boolean tryLock(String key, long waitTimeout, TimeUnit timeUnit) {
    20. Lock lock = this.redisLockRegistry.obtain(key);
    21. try {
    22. boolean isLockSuccess = lock.tryLock(waitTimeout, timeUnit);
    23. if (LOGGER.isDebugEnabled()) {
    24. LOGGER.debug(String.format("线程%s获取锁%s", Thread.currentThread().getName(), isLockSuccess ? "成功" : "失败"));
    25. }
    26. return isLockSuccess;
    27. } catch (InterruptedException var7) {
    28. return false;
    29. }
    30. }
    31. @Override
    32. public void unlock(String key) {
    33. this.redisLockRegistry.obtain(key).unlock();
    34. if (LOGGER.isDebugEnabled()) {
    35. LOGGER.debug(String.format("线程%s释放锁", Thread.currentThread().getName()));
    36. }
    37. }
    38. }

    DistributionLockUtil

    1. /**
    2. * 构建分布式锁的工具类
    3. *
    4. * @author gavin
    5. * @date 2020-06-18
    6. */
    7. public class DistributionLockUtil {
    8. private static final String DISTRIBUTION_LOCK = "DISTRIBUTION_LOCK_";
    9. /**
    10. * 构建分布式锁的key
    11. *
    12. * @param bizCode 业务编码
    13. * @return key
    14. */
    15. public static String buildKey(String bizCode) {
    16. return DISTRIBUTION_LOCK + bizCode;
    17. }
    18. }

    使用示例

    1. @Override
    2. @Transactional(rollbackFor = Exception.class)
    3. public void receive(Integer stockOutId) {
    4. StockOutDO stockOutDO = getStockOutById(stockOutId)
    5. .orElseThrow(() -> new ServiceException("出库单不存在, id: " + stockOutId));
    6. validateApproveStatus(stockOutDO.getApproveStatus());
    7. stockOutDO.setApproveStatus(ApproveStatusEnum.COMPLETE.getValue());
    8. stockOutDao.updateById(stockOutDO);
    9. List<StockOutDetailDO> detailDOS = listStockOutDetails(stockOutId);
    10. if (null == detailDOS || detailDOS.size() == 0) {
    11. return;
    12. }
    13. // 扣减来源仓sku库存与占用
    14. for (StockOutDetailDO detailDO : detailDOS) {
    15. warehouseSkuDao
    16. .reduceFreezeStock(stockOutDO.getUserId(), stockOutDO.getUserType(),
    17. stockOutDO.getWarehouseId(), detailDO.getSkuId(), detailDO.getWeight(),
    18. detailDO.getQuantity());
    19. }
    20. // 添加目标仓sku库存
    21. WarehouseDO warehouseDO = warehouseDao.selectById(stockOutDO.getTargetId());
    22. for (StockOutDetailDO detailDO : detailDOS) {
    23. String distributionLockKey = DistributionLockUtil.buildKey(String.valueOf(detailDO.getSkuId()));
    24. try {
    25. distributionLock.lock(distributionLockKey);
    26. int result = warehouseSkuDao
    27. .addAvailableStock(stockOutDO.getTargetId(), UserTypeEnum.FACTORY.getValue(),
    28. warehouseDO.getId(),
    29. detailDO.getSkuId(),
    30. detailDO.getWeight(), detailDO.getQuantity());
    31. if (result == 0) {
    32. // TODO 成本待处理
    33. WarehouseSkuDO warehouseSkuDO = WarehouseSkuDO.builder().userId(warehouseDO.getUserId())
    34. .userType(warehouseDO.getUserType()).warehouseId(warehouseDO.getId())
    35. .productId(detailDO.getProductId()).productSource(detailDO.getProductSource())
    36. .productCategoryId(stockOutDO.getProductCategoryId()).skuId(detailDO.getSkuId())
    37. .availableQuantity(detailDO.getQuantity()).availableWeight(detailDO.getWeight()).build();
    38. warehouseSkuDao.insert(warehouseSkuDO);
    39. }
    40. } finally {
    41. distributionLock.unlock(distributionLockKey);
    42. }
    43. }
    44. }