【愚公系列】2022年05月 .NET架构班 053-分布式中间件 .Net Core下使用Redis秒杀场景落地情况分析

举报
愚公搬代码 发表于 2022/05/03 23:21:17 2022/05/03
【摘要】 前言所谓秒杀,就是网络卖家发布一些超低价格的商品,所有买家在同一时间网上抢购的一种销售方式。由于商品价格低廉,往往一上架就被抢购一空,有时只用一秒钟。虽说秒杀只是一个促销活动,但对技术要求不低。 一、Redis秒杀场景落地情况分析 1.扣减商品库存业务场景落地基本的业务逻辑有四步:获取商品库存判断商品库存是否为空秒杀消息扣减商品库存/// <summary>/// 商品控制器/// </s...

前言

所谓秒杀,就是网络卖家发布一些超低价格的商品,所有买家在同一时间网上抢购的一种销售方式。由于商品价格低廉,往往一上架就被抢购一空,有时只用一秒钟。虽说秒杀只是一个促销活动,但对技术要求不低。

一、Redis秒杀场景落地情况分析

1.扣减商品库存业务场景落地

基本的业务逻辑有四步:

  1. 获取商品库存
  2. 判断商品库存是否为空
  3. 秒杀消息
  4. 扣减商品库存
/// <summary>
/// 商品控制器
/// </summary>
[ApiController]
[Route("[controller]")]
public class ProductController : ControllerBase
{
     /// <summary>
    /// 扣减商品库存
    /// 做4件事情
    /// </summary>
    /// <returns></returns>
    [HttpGet("SubStock")]
    public IActionResult SubStock()
    {
        #region 1、扣减库存流程
        {
              // 1、获取商品库存
              var stocks = getPorductStocks();

                // 2、判断商品库存是否为空
                if (stocks.Count == 0)
                {
                    // 2.1 秒杀失败消息
                    Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId}:不好意思,秒杀已结束,商品编号:{stocks.Count}");
                return new JsonResult("秒杀失败");
                }

                // 3、秒杀成功消息
                Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId}:恭喜你,秒杀成功,商品编号:{stocks.Count}");

                // 4、扣减商品库存
                subtracProductStocks(stocks);
            return new JsonResult("秒杀成功");
        }
        #endregion

        return new JsonResult("秒杀成功");
    }
    
    
    /// <summary>
    /// 获取商品库存
    /// </summary>
    /// <returns></returns>
    private Stocks getPorductStocks()
    {
        // 1、查询数据库获取库存,获取第一个商品的库存数(1)
        Stocks stocks = _productDbContext.Stocks.FirstOrDefault(s => s.Id == 1);

        // 2、返回库存
        return stocks;
    }

    /// <summary>
    /// 扣减商品库存
    /// </summary>
    private void subtracProductStocks(Stocks stocks)
    {
        // 1、扣减商品库存
        Stocks updateStocks = _productDbContext.Stocks.FirstOrDefault(s => s.Id == stocks.Id);
        updateStocks.Count = stocks.Count - 1;

        // 2、更新数据库
        _productDbContext.SaveChanges();
    }
}

2.分布式锁实现秒杀

分析:当客户端从电商网站购买商品的时候,需要扣减商品库存,直接从数据库进行扣减,当电商系统是单实例的时候,如果客户端扣减库存并发量比较大,会出现超卖问题。如何解决超卖(10件商品卖出12件等等)问题?这个使用lock锁的方式可以进行解决,当电商系统是集群实例的时候,如果客户端扣减库存并发量比较大,这个时候,lock锁就会出现缺陷,依然存在超卖问题。如何解决集群电商系统中的超卖问题?

方案:分布式锁

RedisLock代码封装

/// <summary>
/// redis分布式锁
/// 1、封装redis分布锁
///    1、加锁
///    2、解锁
/// 2、应用分布式锁
/// </summary>
public class RedisLock
{
    // 1、redis连接管理类
    private ConnectionMultiplexer connectionMultiplexer = null;

    // 2、redis数据操作类
    private IDatabase database = null;
    public RedisLock()
    {
        connectionMultiplexer = ConnectionMultiplexer.Connect("192.168.44.4:6379");

        database = connectionMultiplexer.GetDatabase(0);
    }

    /// <summary>
    /// 加锁
    /// 1、key:锁名称
    /// 2、value:谁加的这把锁。线程1
    /// 3、exprie:过期时间:目的是为了防止死锁
    /// 
    /// </summary>

    public void Lock()
    {
        while (true)
        {
            bool flag = database.LockTake("redis-lock", Thread.CurrentThread.ManagedThreadId, TimeSpan.FromSeconds(60));
            // 1、true 加锁成功 2、false 加锁失败
            if (flag)
            {
                break;
            }
            // 防止死循环。通过等待时间,释放资源
            Thread.Sleep(10);
        }
    }

    /// <summary>
    /// 解锁
    /// </summary>

    public void UnLock()
    {
        bool flag = database.LockRelease("redis-lock", Thread.CurrentThread.ManagedThreadId);

        // true:释放成功  false 释放失败
        // 方案:释放资源
        connectionMultiplexer.Close();
    }
}

业务逻辑

/// <summary>
/// 商品控制器
/// </summary>
[ApiController]
[Route("[controller]")]
public class ProductController : ControllerBase
{
     /// <summary>
    /// 扣减商品库存
    /// 做4件事情
    /// </summary>
    /// <returns></returns>
    [HttpGet("SubStock")]
    public IActionResult SubStock()
    {
        #region 1、扣减库存流程
        {
          
            RedisLock redisLock = new RedisLock();
            redisLock.Lock();
              // 1、获取商品库存
              var stocks = getPorductStocks();

                // 2、判断商品库存是否为空
                if (stocks.Count == 0)
                {
                    // 2.1 秒杀失败消息
                    Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId}:不好意思,秒杀已结束,商品编号:{stocks.Count}");
                redisLock.UnLock();
                return new JsonResult("秒杀失败");
                }

                // 3、秒杀成功消息
                Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId}:恭喜你,秒杀成功,商品编号:{stocks.Count}");

                // 4、扣减商品库存
                subtracProductStocks(stocks);
            redisLock.UnLock();
            return new JsonResult("秒杀成功");
        }
        #endregion

        return new JsonResult("秒杀成功");
    }
    
    
    /// <summary>
    /// 获取商品库存
    /// </summary>
    /// <returns></returns>
    private Stocks getPorductStocks()
    {
        // 1、查询数据库获取库存,获取第一个商品的库存数(1)
        Stocks stocks = _productDbContext.Stocks.FirstOrDefault(s => s.Id == 1);

        // 2、返回库存
        return stocks;
    }

    /// <summary>
    /// 扣减商品库存
    /// </summary>
    private void subtracProductStocks(Stocks stocks)
    {
        // 1、扣减商品库存
        Stocks updateStocks = _productDbContext.Stocks.FirstOrDefault(s => s.Id == stocks.Id);
        updateStocks.Count = stocks.Count - 1;

        // 2、更新数据库
        _productDbContext.SaveChanges();
    }
}
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。