在后端服务和redis的方式实现概率抽奖逻辑
需求前言
笔者曾经在保险电商公司任职过,其中负责营销活动服务,一开始开发的抽奖活动,抽奖概率都是一样的,这种呢,很简单,直接用redis的set实现即可:
127.0.0.1:6379> sadd luckydraw 111 222 333 444 555 666 777 888 999 000
(integer) 10
127.0.0.1:6379> srandmember luckydraw
"999"
类似于这样的逻辑。
但随着需求变更,代码版本也需要迭代,每次保险代理人开单满多少金额就拥有一次抽奖机会,而且是开单金额越高,抽到一等奖的概率就越高,这种情况就要配置概率,例如有888、666、233、10元红包,所有概率加起来为1,开单10w以下,概率分别为“0.1”,“0.2”,“0.2”,“0.4”,10w以上,概率就配置为“0.4”,“0.3”,“0.2”,“0.1”,类似于这样的配置,这种配置的好处是扩展性啊,后续有关于新的活动上线,配置其他中奖概率即可。
在后端服务方式实现
public static void main(String[] args) {
//该probabilities数组就是配置的概率顺序,0.1就是一等奖的概率,0.2是二等奖的概率,依次类推……
double [] probabilities = {0.1,0.2,0.3,0.4};
//该awards list相当于一等奖概率对应的奖品,也是依次类似……
List<String> awards = new ArrayList<>();
awards.add("一等奖_888元");
awards.add("二等奖_666元");
awards.add("三等奖_233元");
awards.add("四等奖_10元");
for (int i = 0;i<10000;i++) {
System.out.println("抽奖结果为" + drawLottery(probabilities, awards));
}
}
public static String drawLottery(double[] probabilities, List<String> awards ){
Random random = new Random();
//生成一个0-1的数字,相当于抽奖,例如生成0.1的概率是比0.2要小的,其实在该区间内生成的数字是越大,概率越大
double v = random.nextDouble();
System.out.println("查看抽奖的区间为"+v);
double tempProbabilitie = 0;
for (int i = 0; i < probabilities.length; i++) {
tempProbabilitie+=probabilities[i];
if (v<tempProbabilitie){
//中奖区间
return awards.get(i);
}
}
//表示抽奖生成的概率不在中奖区间
return "没有中奖!";
}
演示效果
上面配置一等奖、二等奖、三等奖、四等奖的概率分别为0.1,0.2,0.3,0.4,模拟抽奖1w次,笔者分别查看四个奖品抽中的次数:
都近似于概率。
使用Redis实现
如果抽奖并发过大,后端服务可能无法应对过大请求,于是引入Redis,通过Nginx接收请求后使用lua脚本与Redis连接进行抽奖即可,无需经过后端服务,lua脚本实现抽奖代码如下:
-- Lua 脚本:抽奖逻辑
-- KEYS[1]: 存储概率的 Redis 列表键名
-- KEYS[2]: 存储奖项的 Redis 列表键名
-- 获取概率和奖项列表
local probabilities = redis.call('LRANGE', KEYS[1], 0, -1)
local awards = redis.call('LRANGE', KEYS[2], 0, -1)
-- 生成一个 0-1 的随机数
local randomValue = math.random()
-- 初始化累加概率
local tempProbabilitie = 0
-- 遍历概率列表
for i = 1, #probabilities do
tempProbabilitie = tempProbabilitie + tonumber(probabilities[i])
if randomValue < tempProbabilitie then
-- 返回对应的奖项
return awards[i]
end
end
-- 如果没有中奖
return "没有中奖!"
ps:配置的概率也是存在Redis上;