(更新时间)2021年6月4日 商城高并发秒杀系统(.NET Core版) 30-lua文件封装加载和执行
【摘要】
一:lua文件的作用
1、批量执行redis命令 2、保证redis命令能够原子执行
二:lua文件定义和使用
1、lua文件
--[[
1、函数定义
]]--
--1、单品限流
local f...
一:lua文件的作用
1、批量执行redis命令
2、保证redis命令能够原子执行
二:lua文件定义和使用
1、lua文件
--[[
1、函数定义
]]--
--1、单品限流
local function seckillLimit()
local seckillLimitKey = ARGV[2];
-- 1、获取单品已经请求数量
local limitCount = tonumber(redis.call('get',seckillLimitKey) or "0");
local requestCountLimits = tonumber(ARGV[4]); --限制的请求数量
local seckillLimitKeyExpire = tonumber(ARGV[5]); --2秒过期
if limitCount + 1 > requestCountLimits then --超出限流大小
return 0,seckillLimitKeyExpire.."内只能请求"..requestCountLimits.."次"; --失败
else --请求数+1,并设置过期时间
redis.call('INCRBY',seckillLimitKey,"1")
redis.call('expire',seckillLimitKey,seckillLimitKeyExpire)
return 1; --成功
end
end
--2、记录订单号:目的:创建订单方法幂等性,调用方网络超时可以重复调用,存在订单号直接返回抢购成功,不至于超卖
local function recordOrderSn()
local requestIdKey = ARGV[6]; -- 订单号key
local orderSn = ARGV[7]; -- 订单号
local hasOrderSn = tostring(redis.call('get',requestIdKey) or "");
if string.len(hasOrderSn) == 0 then
-- 存储订单号
redis.call('set',requestIdKey,orderSn);
return 1; -- 设置成功
else
return 0,"不能重复下单"; --失败
end
end
--3、用户购买限制
local function userBuyLimit()
local userBuyLimitKey = ARGV[1]; -- 购买限制key
local productKey =KEYS[1]; --商品key
local productCount = tonumber(ARGV[3]);-- 商品数量
-- 1、用户已经购买数量
local userHasBuyCount = tonumber(redis.call('hget',userBuyLimitKey,"UserBuyLimit") or "0");
-- 2、获取限制的数量
local seckillLimit = tonumber(redis.call('hget',productKey,"SeckillLimit") or "0");
if userHasBuyCount + 1 > seckillLimit then --超出购买数量
return 0,"该商品只能购买"..seckillLimit.."件"; --失败
else --请求数+1,并设置过期时间
redis.call('HINCRBY',userBuyLimitKey,'UserBuyLimit',productCount)
return 1; --成功
end
end
--4、扣减库存
local function subtractSeckillStock()
local productKey =KEYS[1]; --商品key
local productCount = tonumber(ARGV[3]);--商品数量
-- 1.1、扣减库存
local lastNum = redis.call('HINCRBY',productKey,"SeckillStock",-productCount);
-- 1.2、判断库存是否完成
if lastNum < 0 then
return 0,"秒杀已结束"; --失败
else
return 1; --成功
end
end
--[[
2、函数调用
]]--
--1、单品限流
local status,msg = seckillLimit();
if status == 0 then
return msg
end
--2、记录订单号;
local status,msg = recordOrderSn();
if status == 0 then
return msg
end
--3、用户购买限制
status,msg = userBuyLimit();
if status == 0 then
return msg
end
--4、扣减秒杀库存
status,msg = subtractSeckillStock();
if status == 0 then
return msg
end
-- 返回成功标识
return 1;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
--[[
1、函数定义
]]--
--1、删除记录订单号:目的:创建订单方法幂等性,调用方网络超时可以重复调用,存在订单号直接返回抢购成功,不至于超卖
local function delRecordOrderSn()
local requestIdKey = ARGV[3]; -- 订单号key
local orderSn = ARGV[4]; -- 订单号
--删除订单号
redis.call('del',requestIdKey)
end
--2、删除用户购买限制
local function delUserBuyLimit()
local userBuyLimitKey = ARGV[1]; -- 购买限制key
local productKey =KEYS[1]; --商品key
local productCount = tonumber(ARGV[2]);-- 商品数量
redis.call('HINCRBY',userBuyLimitKey,'UserBuyLimit',-productCount)
end
--3、恢复库存
local function recoverSeckillStock()
local productKey =KEYS[1]; --商品key
local productCount = tonumber(ARGV[2]);--商品数量
-- 3.1、恢复库存
redis.call('HINCRBY',productKey,"SeckillStock",productCount);
end
--[[
2、函数调用
]]--
--1、删除记录订单号;
delRecordOrderSn();
--2、撤销用户购买限制
delUserBuyLimit();
--3、恢复秒杀库存
recoverSeckillStock();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
2、IHostedService
/// <summary>
/// 服务启动加载秒杀Lua文件
/// </summary>
public class SeckillLuaHostedService : IHostedService
{
private readonly IMemoryCache memoryCache;
public SeckillLuaHostedService(IMemoryCache memoryCache)
{
this.memoryCache = memoryCache;
}
/// <summary>
/// 加载秒杀库存缓存
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Task StartAsync(CancellationToken cancellationToken)
{
try
{
Console.WriteLine("加载执行lua文件到redis中");
// 1、加载lua到redis
FileStream fileStream = new FileStream(@"Luas/SeckillLua.lua", FileMode.Open);
using (StreamReader reader = new StreamReader(fileStream))
{
string line = reader.ReadToEnd();
string luaSha = RedisHelper.ScriptLoad(@line);
// 2、保存luaSha到缓存中
memoryCache.Set<string>("luaSha", luaSha);
}
Console.WriteLine("加载回滚lua文件到redis中");
// 1、加载lua到redis
FileStream fileStreamCallback = new FileStream(@"Luas/SeckillLuaCallback.lua", FileMode.Open);
using (StreamReader reader = new StreamReader(fileStreamCallback))
{
string line = reader.ReadToEnd();
string luaSha = RedisHelper.ScriptLoad(@line);
// 2、保存luaShaCallback到缓存中
memoryCache.Set<string>("luaShaCallback", luaSha);
}
}
catch (Exception e)
{
Console.WriteLine($"lua文件异常:{e.Message}");
}
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
三:使用
// 9、加载seckillLua文件
services.AddHostedService<SeckillLuaHostedService>();
- 1
- 2
在这里插入代码片
- 1
/// <summary>
/// 4.3、创建订单(redis + 消息队列 + lua)
/// </summary>
/// <param name="orderDto"></param>
[HttpPost]
public PaymentDto CreateOrder(SysUser sysUser, [FromForm]OrderPo orderPo)
{
*//* RedisHelper.Eval("SeckillLua.lua","222","3222","2222");
RedisHelper.EvalSHA("dddddd"); key = "22222222222"; // 内存缓存MemroyCache
RedisHelper.ScriptLoad*//*
// 1、redis秒杀开始
string ProductKey = Convert.ToString(orderPo.ProductId);// 商品key
string SeckillLimitKey = "seckill_stock_:SeckillLimit" + orderPo.ProductCount; // 单品限流key
string UserBuyLimitKey = "seckill_stock_:UserId" + sysUser.UserId + "ProductId" + orderPo.ProductId;// 用户购买限制key
int productCount = orderPo.ProductCount; // 购买商品数量 2
int requestCountLimits = 100; // 单品限流数量
int seckillLimitKeyExpire = 1;// 单品限流时间
var SeckillResult = RedisHelper.EvalSHA(memoryCache.Get<string>("luaSha"), ProductKey, UserBuyLimitKey, SeckillLimitKey, productCount, requestCountLimits, seckillLimitKeyExpire);
if (!SeckillResult.ToString().Equals("1"))
{
throw new BizException(SeckillResult.ToString());
}
// 1、创建订单号
string orderSn = OrderUtil.GetOrderCode();
// 2、扣减库存(redis缓存+redis扣减)
seckillStockCache.SubtractSeckillStock(orderPo.ProductId, orderPo.ProductCount);
// 3、发送订单消息到rabbitmq
SendOrderCreateMessage(sysUser.UserId, orderSn, orderPo);
// 6、创建支付信息
PaymentDto paymentDto = new PaymentDto();
paymentDto.OrderSn = orderSn;
paymentDto.OrderTotalPrice = orderPo.OrderTotalPrice;
paymentDto.UserId = sysUser.UserId;
paymentDto.ProductId = orderPo.ProductId;
paymentDto.ProductName = orderPo.ProductName;
return paymentDto;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
文章来源: codeboy.blog.csdn.net,作者:愚公搬代码,版权归原作者所有,如需转载,请联系作者。
原文链接:codeboy.blog.csdn.net/article/details/117574982
【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)