【愚公系列】2022年05月 .NET架构班 053-分布式中间件 .Net Core下使用Redis秒杀场景落地情况分析
【摘要】 前言所谓秒杀,就是网络卖家发布一些超低价格的商品,所有买家在同一时间网上抢购的一种销售方式。由于商品价格低廉,往往一上架就被抢购一空,有时只用一秒钟。虽说秒杀只是一个促销活动,但对技术要求不低。 一、Redis秒杀场景落地情况分析 1.扣减商品库存业务场景落地基本的业务逻辑有四步:获取商品库存判断商品库存是否为空秒杀消息扣减商品库存/// <summary>/// 商品控制器/// </s...
前言
所谓秒杀,就是网络卖家发布一些超低价格的商品,所有买家在同一时间网上抢购的一种销售方式。由于商品价格低廉,往往一上架就被抢购一空,有时只用一秒钟。虽说秒杀只是一个促销活动,但对技术要求不低。
一、Redis秒杀场景落地情况分析
1.扣减商品库存业务场景落地
基本的业务逻辑有四步:
- 获取商品库存
- 判断商品库存是否为空
- 秒杀消息
- 扣减商品库存
/// <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)