net core 使用redis 分布式锁

在电商项目中,必定会与订单打交道。订单中必定会涉及到扣库存,但是在高并发项目中,库存余量不能及时刷新,导致库存扣除得不正确,1000个并发请求下来,扣除的库存只有几十个。如何解决此问题,那就得加锁了。还有其他解决办法也能处理此问题,本文只分享一下redis 分布式锁解决此问题。

第一步添加一个redis的获取锁,与释放锁的方法。在获取锁时,如果未获取到锁,那么就会尝试重复获取,直到获取倒锁后,才返回。当然在这步你可以加一个超时时间,例如最多尝试获取30秒,避免释放锁异常在此一直死循环。释放锁时只有锁的拥有者才能释放锁,避免锁被其他线程释放了,这个分布式锁就没有意义了。

 public class RedisDistributedLock
 {
     private readonly ConnectionMultiplexer _redis;
     private readonly IDatabase _database;

     public RedisDistributedLock(ConnectionMultiplexer redis)
     {
         _redis = redis;
         _database = redis.GetDatabase();
     }

     public async Task<bool> AcquireLockAsync(string lockKey, string value, TimeSpan lockTimeout)
     {
         var acquired = await _database.StringSetAsync(lockKey, value, lockTimeout, When.NotExists);
         if (!acquired)
         {
             // 循环重试直到成功或超时
             var startTime = DateTime.UtcNow;
             while (true)
             {
                 Thread.Sleep(10); // 等待一段时间后再重试

                 if (await _database.StringSetAsync(lockKey, value, lockTimeout, When.NotExists))
                 {
                     acquired = true; // 成功获取到锁
                     break;
                 }
             }
         }
         return acquired;
     }

     public async Task ReleaseLockAsync(string lockKey, string value)
     {
         //验证锁的拥有者才能释放锁
         string currentValue = await _database.StringGetAsync(lockKey);
         if (currentValue == value)
         {
             await _database.KeyDeleteAsync(lockKey);
         }
     }
 }

订单扣库存场景

  private readonly ConnectionMultiplexer _connectionMultiplexer;
  //创建一个redis锁
  private readonly RedisDistributedLock _lockHelper;
  private readonly string _lockKey = "inventory_lock:OrderSubmit"; // 锁的唯一标识
  public OrdersService(IFreeSql freeSql, ICacheService cacheService, ConnectionMultiplexer connectionMultiplexer)
  {
      _freeSql = freeSql;
      _cacheService = cacheService;
      _connectionMultiplexer = connectionMultiplexer;
      _lockHelper = new RedisDistributedLock(_connectionMultiplexer);
  }

  public async Task<CreateOrdersResult> OrderSubmitAsync(CreateOrderModel arg)
  {
      var orderStatus = OrderStatus.Fail;
      var message = string.Empty;
      var lockValue = $"lock_{CodeHelper.CreateGuid()}";
      const int maxRetries = 5;
      TimeSpan retryDelay = TimeSpan.FromMilliseconds(100);
      if (await _lockHelper.AcquireLockAsync(_lockKey, lockValue, TimeSpan.FromSeconds(10)))
      {
          try
          {
              var oldInventory = await _freeSql.Select<Commodity>().Where(a => a.Code == arg.CommodityCode).FirstAsync();
              if (oldInventory.Inventory >= arg.OrderQuantity && oldInventory.Inventory > 0)
              {
                  var repo = _freeSql.GetRepository<Commodity>();
                  oldInventory.Inventory = oldInventory.Inventory - arg.OrderQuantity;
                  await repo.UpdateAsync(oldInventory);
                  orderStatus = OrderStatus.Success;
                  message = "下单成功";
              }
              else
              {
                  message = "下单失败,库存不足";
              }
          }
          catch (Exception e)
          {
              message = "下单失败," + e.Message;
          }
          finally
          {
              // 无论成功还是失败,最后都要释放锁
              await _lockHelper.ReleaseLockAsync(_lockKey, lockValue);
          }
      }

      var ordersRepo = _freeSql.GetRepository<Orders>();
      await ordersRepo.InsertAsync(new Orders()
      {
          Code = CodeHelper.CreateOrdersCode(),
          UserCode = arg.UserCode,
          CommodityCode = arg.CommodityCode,
          OrderTime = DateTime.Now,
          OrderAmount = arg.OrderAmount,
          OrderQuantity = arg.OrderQuantity,
          OrderStatus = orderStatus
      });
      return new CreateOrdersResult()
      {
          Status = true,
          Message = message
      };

  }

  • 13
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Spring Boot中,可以通过使用Redis实现分布式锁。以下是一种可能的实现方式: 1. 首先,在项目的`pom.xml`文件中添加对spring-boot-starter-data-redis的依赖。 ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> ``` 2. 在`application.properties`文件中配置Redis的连接信息。 ```properties spring.redis.host=127.0.0.1 spring.redis.port=6379 ``` 3. 创建一个`RedisLock`类,用于获取和释放分布式锁。 ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import java.util.concurrent.TimeUnit; @Component public class RedisLock { @Autowired private RedisTemplate<String, String> redisTemplate; public boolean acquireLock(String key, String value, long timeout) { return redisTemplate.opsForValue().setIfAbsent(key, value, timeout, TimeUnit.MILLISECONDS); } public void releaseLock(String key, String value) { String currentValue = redisTemplate.opsForValue().get(key); if (currentValue != null && currentValue.equals(value)) { redisTemplate.delete(key); } } } ``` 4. 在需要使用分布式锁的地方,注入`RedisLock`并使用它进行加锁和解锁操作。 ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class YourService { @Autowired private RedisLock redisLock; public void doSomething() { String lockKey = "your-lock-key"; String requestId = UUID.randomUUID().toString(); try { boolean locked = redisLock.acquireLock(lockKey, requestId, 5000); if (locked) { // 获取到锁,执行业务逻辑 // ... } else { // 未获取到锁,处理失败逻辑 // ... } } finally { redisLock.releaseLock(lockKey, requestId); } } } ``` 上述代码中,`acquireLock`方法用于尝试获取分布式锁,它使用Redis的`setIfAbsent`方法来设置一个键值对,如果键不存在则设置成功,返回`true`表示获取到了锁。`releaseLock`方法用于释放锁,它首先比较当前锁的值是否与传入的值相等,如果相等则删除该键。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值