年会现场抽奖代码到底该怎么写?过来人告诉你答案
托尼老师 人气:2前沿
说件严肃到事情,2019真到快要结束了。各家公司一定在紧锣密鼓到准备年会当中了吧。年会肯定离不开抽奖吧?现场几百上千人抽奖可千万别出bug。如果真出bug老板得要杀你祭天了。现场好多人看着呢。
抽奖代码
/**
* 抽奖
*
* @author 托尼老师
* @create 2019-12-27 11:11
**/
public class LotteryTest {
/**
* 抽奖
*
* @param originalRates 原始的概率列表
* @return 物品的索引
*/
public static int lottery(List<Double> originalRates) {
if (originalRates == null || originalRates.isEmpty()) {
return -1;
}
int size = originalRates.size();
double sumRate = 0d;
for (double rate : originalRates) {
sumRate += rate;
}
List<Double> sortOrignalRates = new ArrayList<>(size);
Double tempSumRate = 0d;
for (double rate : originalRates) {
tempSumRate += rate;
sortOrignalRates.add(tempSumRate / sumRate);
}
// 根据区块值来获取抽取到的物品索引
double nextDouble = Math.random();
sortOrignalRates.add(nextDouble);
Collections.sort(sortOrignalRates);
return sortOrignalRates.indexOf(nextDouble);
}
@Test
public void testLottery() {
List<Reward> lotteryList = new ArrayList<>();
Reward r = new Reward();
r.setRate(0.03d);
r.setRewardName("双色球彩票一张");
lotteryList.add(r);
r = new Reward();
r.setRate(0.001d);
r.setRewardName("mac book");
lotteryList.add(r);
r = new Reward();
r.setRate(0.06d);
r.setRewardName("没抽中");
lotteryList.add(r);
r = new Reward();
r.setRate(0.001d);
r.setRewardName("迪拜七日双人豪华游");
lotteryList.add(r);
List<Double> originalRates = new ArrayList<>();
for (Reward e : lotteryList) {
originalRates.add(e.getRate());
}
for (int i = 0; i < 20; i++) {
System.out.println("恭喜抽中========>"
+ lotteryList.get(lottery(originalRates)).getRewardName());
}
}
class Reward {
//奖品名称
private String rewardName;
//概率
private Double rate;
public String getRewardName() {
return rewardName;
}
public void setRewardName(String rewardName) {
this.rewardName = rewardName;
}
public Double getRate() {
return rate;
}
public void setRate(Double rate) {
this.rate = rate;
}
}
}
运行结果如下
分析结果
老板让做个抽奖的功能,抽奖到底该怎么做?
前端分析
大家都知道前端显示的数据,都是可以修改的,数据都不正确。有些抽奖逻辑代码写在前端文件中,这种只是针对不懂技术的人员,稍微懂些技术的肯定忽悠不住。- 概率
- 公平性,公平性怎么做?上面的代码就是根据概率来实现,大奖概率低小奖概率低。这个就是随机的,全凭运气这个词。
- 不公平,这种的话就靠逻辑来实现来。比如,某个时间段提高中奖概率。还有一种情况直接代码里面判断某个用户直接中xx奖。这种就是所谓的内在用户,白名单用户。
库存
奖品千万要设置库存,千万要设置,千万要设置。好了,重要的事情已经说了三遍了 。
讲讲并发的场景
以前Reids没出场的时候,做起来真的麻烦。现在好了,很大并发我们可以交给Redis来扛了。Redis 官方数据官方表示Redis读的速度是110000次/s,写的速度是81000次/s 。
Redis单机支持万级别的别分可以是很轻松,一般小公司足够用了。 不会出现你们所说的超卖现象。顺便说一句如果要10万+的需求可以使用
Reids replication模式。
每个人只能抽奖一次
这种场景可以看看Redis Incr 命令,Redis key再加上用户的ID代表用户的唯一键,根据自增和原子性能保证唯一还能抗大并发。
Redis Incr 命令将 key 中储存的数字值增一。
如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。
如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。
本操作的值限制在 64 位(bit)有符号数字表示之内。
我们线上很多场景都用到过Incr命令。
1个小时只能抽奖一次
这种时候用Reis 的Expire 命令,Redis Expire 命令用于设置 key 的过期时间,key 过期后将不再可用。单位以秒计。顺便说个题外话,实现原理和Redis 的Set 命令差不多,
只有当然取这个key的时候,它才会判断当前key有没有失效。也就是Expire命令过期策略是你Get用到这个可key的时候才判断当前的key有没有失效。
Nginx
其实Nginx也有对应的模块抗并发做拦截,根据Ip来做黑名单的拦截。当有人来刷接口,大量的黑产IP过来抽奖,这个时候就要根据你们的场景来增加抽奖门槛。比如你在我们公司注册过,或者你10天前预约过抽奖活动,这些都是例子,可以根据场景来调整。
这个就是道高一尺魔高一丈,怎么说呢?和黄牛斗其乐无穷。我们以前就遇到过很多IP来刷接口,黄牛来搞事情的场景。Nginx做一层防护,Redis再做一层防护,这样经过层层的筛选
打到数据库的流量就很少了许多。
总结
其实这个只是抽奖环节中比较简单的场景,可能会遇到的问题。线上乱七八糟什么情况都会出现了, 但是遇到bug了一定不要慌张。要坦然去面对,线上数据库我们曾经遇到过一条sql引发惨案。
具体什么情况呢?有个员工执行sql 语句的时候 where 参数没生效。最终每个人的账户都增加了相同都一笔钱。最终系统账面总资金增加了一个多亿。对的,你们没看错一个多亿。幸亏夜里发布,然后那天夜里我们就忙了一宿,具体那晚的情况下次我再发个博文好好说说。
加载全部内容
- 猜你喜欢
- 用户评论