内容简介:目标是实现高并发的优惠券秒杀系统,项目地址:主要基于redis内嵌lua来实现库存的幂等性扣减,这块代码如下:
目标是实现高并发的优惠券秒杀系统,项目地址: https://github.com/owenliang/redis-lua
redis+lua
主要基于 redis 内嵌 lua 来实现库存的幂等性扣减,这块代码如下:
local quan_id = tostring(KEYS[1])
local uid = tostring(ARGV[1])
-- 应答函数
local function response(errno, msg, data)
errno = errno or 0
msg = msg or ""
data = data or {}
return cjson.encode({errno = errno, msg = msg, data = data})
end
-- 判断用户没有抢过该优惠券
local log_key = "LOG_{" .. quan_id .. "}"
-- return log_key
local has_fetched = redis.call("sIsMember", log_key, uid)
if (has_fetched ~= 0) then
return response(-1, "已经领取过")
end
-- 遍历优惠券所有批次
local quan_key = "QUAN_{" .. quan_id .. "}"
local batch_list = redis.call("hGetAll", quan_key)
local result = false
for batch_idx = 1, #batch_list, 2 do
repeat
-- 校验批次状态(是否online)
local batch_info = cjson.decode(batch_list[batch_idx + 1])
if (batch_info["online"] ~= true) then
break
end
-- 尝试从券池取出1个券码
local batch_key = batch_list[batch_idx]
local coupon = redis.call("zRange", batch_key, 0, 0)
if (#coupon == 0) then
break
end
coupon = coupon[1]
redis.call("zRem", batch_key, coupon)
-- 弹出一个券码, 标记用户已抢
redis.call("sAdd", log_key, uid)
-- 将券码放入异步队列
result = {uid = uid, quanId = quan_id, batchKey = batch_key, coupon = coupon}
redis.call("rPush", "DB_QUEUE", cjson.encode(result))
until true
if result ~= false then
break
end
end
if (result == false) then
return response(-1, "优惠券已抢完")
else
return response(0, "秒杀成功", result)
end
脚本懒惰加载
当执行evalsha发现报错的情况下,则执行一次script load命令上传lua脚本,可以简化脚本上传的工作。
public static function fetchFromRedis($uid, $quanId)
{
// 出于性能考虑, sha1请提配置到程序中
$scriptSha1 = '8c2bdb856cefb05f39dc675c0fde28ef4c7bb0bd';
if (0) {
// 加载lua脚本
$script = file_get_contents(__DIR__ . '/QuanFetch.lua');
// 计算lua的sha1哈希
$scriptSha1 = sha1($script);
}
$redisMaster = Redis::master('default');
// evalsha执行脚本, 完成秒杀
$result = $redisMaster->evalSha($scriptSha1, [$quanId, $uid], 1); // 按quanid做路由
if ($redisMaster->getLastError()) { // 如果evalsha报错, 则进行一次script load
// 懒惰加载lua脚本
$script = file_get_contents(__DIR__ . '/QuanFetch.lua');
if (!$redisMaster->script('load', $script)) {
return false;
}
// 然后重试脚本
$result = $redisMaster->evalSha($scriptSha1, [$quanId, $uid], 1);
}
$result = json_decode($result, true);
return $result;
}
开发与调试脚本
直接基于 PHP 调用lua很难调试脚本的问题,官方的redis-cli支持单步调试lua脚本,你可以自己学习一下: 《Redis Lua scripts debugger》。
我的开发流程是这样的:
编写lua脚本,然后命令行执行脚本:
redis-cli EVAL “$(cat /path/to/your/script.lua)” 1 “mykey” “myargv”
如果发现报错,则进行单步调试:
redis-cli –ldb –eval /path/to/your/script.lua mykey1 mykey2 , myargv1 myargv2
注意逗号之前的是key列表,之后的是argv列表,逗号两边需要留白。
使用起来就像gdb一样,n是下一步,p 变量名查看变量值,restart重新执行脚本,大家自己去挖掘更多吧。
压测脚本
先把脚本上传到redis,得到对应的SHA1值。
redis-cli SCRIPT LOAD “$(cat /path/to/your/script.lua)”
现在,执行压测:
redis-benchmark -r 100000000 -n 1000000 EVALSHA “8c2bdb856cefb05f39dc675c0fde28ef4c7bb0bd” 1 “mykey1” __rand_int__
-r指定了随机数的区间,__rand_int__用于随机数占位。
在这里,我让argv[1]随机生成,请求的次数是-n指定的,并发连接数使用-c(默认50)。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 你想知道的优惠券业务,SkrShop告诉你
- JPress v3.0 rc.3 发布,完善对优惠券的支持
- ruby-on-rails – 产生独特的,难以猜测的“优惠券”代码
- 你真的懂优惠券吗?聊聊京东因为它,被薅羊毛 7000 万背后的玄机
- 重学 Java 设计模式:实战策略模式「模拟多种营销类型优惠券,折扣金额计算策略场景」
- 搬瓦工 2018 最新优惠码及使用方法(最高可优惠 6.25%)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Zen of CSS Design
Dave Shea、Molly E. Holzschlag / Peachpit Press / 2005-2-27 / USD 44.99
Proving once and for all that standards-compliant design does not equal dull design, this inspiring tome uses examples from the landmark CSS Zen Garden site as the foundation for discussions on how to......一起来看看 《The Zen of CSS Design》 这本书的介绍吧!