大佬教程收集整理的这篇文章主要介绍了腾讯赢了!470万!用Redis+lua教你实现抢红包,大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
近日c;北京知识产权法院在官方微信发文称c;掌上远景公司开发并运营了一款名为“微信自动抢红包”软件c;卓易讯畅公司运营的豌豆荚应用开发平台提供下载该软件。腾讯科技公司、腾讯计算机公司以不正当竞争为由c;将掌上远景公司和卓易讯畅公司诉至北京知识产权法院。
最终c;法院认定掌上远景公司构成不正当竞争c;判决掌上远景公司赔偿二原告经济损失450万元及合理支出约25.4万元。双方当事人均未提出上诉c;目前该案已生效。
那我们的抢红包是怎么实现的呢?我来一步一步的教你c;用个实例
需求:用户分享红包到微信群中。
每一个用户只能领取一个红包。
比如饿了么的红包分享:
1、lpush+lpop=Stack(栈)
2、lpush+rpop=Queue(队列)
3、lpsh+ltrim=Capped Collection(有限集合)
4、lpush+brpop=message Queue(消息队列)
我们基于lpush+rpop进行红包的设计。如何设计?
1、当用户点击分享按钮c;首先会给该订单生成若干个红包c;将该红包push到redis中。
value : n个红包的List队列c;用来表示n个红包的池子
2、记录哪些用户已抢过红包c;防止重复抢:从红包池中rpop(弹出)一个红包c;就需要记录下来该用户领取了红包c;这里用了hash结构。
key:hb:rd:{orderID} 例如订单号为12345c;key为hb:rd:12345, 有4个用户id分别为 11111,11112,11113,11114
如下图:
判断用户11111是否领过红包c;可以根据 hexists hb:rd:12345 11111 进行判断c;如果领过返回1c;否则返回0;
3、记录用户抢了多少钱:这里采用list结构进行存储。
value : 用户抢到的红包列表。
抢红包流程:
保证用户抢红包整个流程的原子操作就必然要引入lua脚本c;redis+Lua可以保证多条命令组合的原子性。
lua脚本:
/**
* 脚本调用方式
* Object object = jedisUtils.eval(LuaScript.getHbLua,//lua脚本
4,//参数个数 redisKeys.getHbPoolKey(orderId),//对应脚本里的KEYS[1]
redisKeys.getDetailListKey(orderId),//对应脚本里的KEYS[2]
redisKeys.getHbRdKey(orderId),//对应脚本里的KEYS[3]
String.valueOf(userId));//对应脚本里的KEYS[4]
*
*
*/
public static String getHbLua =
//查询用户是否已抢过红包c;如果用户已抢过红包c;则直接返回
"if redis.call('hexists', KEYS[3], KEYS[4]) ~= 0 thenn" +
//如果抢过红包 返回“1”
"return '1';n" + "elsen" +
//从红包池取出一个小红包
"local hb = redis.call('Rpop', KEYS[1]);n" +
//判断红包池的红包不为空
"if hb thenn" +
"local x = cjson.decode(hb);n" +
//将红包信息与用户ID信息绑定c;表示该用户已抢到红包
"x['userId'] = KEYS[4];n" +
"local re = cjson.encode(X);n" +
//记录用户已抢过 比如 hset hb:rd:{orderID} {userID} 1
"redis.call('hset', KEYS[3], KEYS[4], '1');n" +
//将抢红包的结果详情存入hb:detailList:{orderID}
"redis.call('lpush', KEYS[2], rE);n" +
"return re;n" + "elsen" +
//如果红包已被抢完 返回“0”
"return '0';" +
"endn" +
"endn" +
"return nil";
生成红包:
/**
* 生成红包
* @param orderId
*/ public void genRedpack(long orderId,int redPackCount){
Boolean exists = jedisUtils.exists(redisKeys.getHbPoolKey(orderId));
if (!exists){
//根据业务规则生成红包
int @R_386_10586@lamount = 2000;//总的红包金额20元 也就是2000分
int[] redpacks = dopartitionRedpack(@R_386_10586@lamount,redPackCount);
String[] list = new String[redpacks.length];
//将生成的红包push到redis中
for (int i = 0;i < redpacks.length; i++){
JSONObject object = new JSONObject();
object.put("hbId", i); //红包ID
object.put("amount", redpacks[i]); //红包金额,存的是分
list[i] = object.toJSONString();
}
jedisUtils.lpush(redisKeys.getHbPoolKey(orderId),list);
}
}
/**
* 划分红包 * @param @R_386_10586@lamount 红包总额 单位:分
* @param redPackCount 红包数量
* @return
*/
privatE int[] dopartitionRedpack(int @R_386_10586@lamount,int redPackCount) {
Random random = new Random();
int randomMax= @R_386_10586@lamount - redPackCount;//每个人至少分1分钱c;2000 - 6 = 1994元 也就是要随机分的钱。
//要把1994 随机分成6份c;我们需要向1994 这个数字中插入5个点
// 比如 6 100 500 500 1600 这5个数字把1994分成了6份:6分 94分 400分 0分 1000分 394分
int[] posArray = new int[redPackCount-1];
for (int i = 0;i < posArray.length; i++){
int pos = random.nexTint(randomMaX);
posArraY[i] = pos;
}
Arrays.sort(posArray);//对数组进行排序
//生成红包
int[] redpacks = new int[redPackCount];
for (int i = 0;i <= posArray.length; i++){
if (i == 0){
redpacks[i] = posArraY[i] + 1;//第一份
}else if(i == posArray.length){//如果循环到posArray.lengthc;此时数组已越界1位c;randomMax - 该值 + 1分钱=最后一份
redpacks[i] = randomMax - posArraY[i-1] + 1;
}else {
redpacks[i] = posArraY[i] - posArraY[i-1] + 1;
}
}
return redpacks;
}
上面首先给每个红包分1分钱c;然后把剩下的钱通过插入(redPackCount-1)个板子c;就将剩余的钱分为redPackCount份。每份钱加上1分钱c;就是每个红包的大小。分完红包后c;将红包push到redis中。
抢红包:
/**
* 抢红包
* @param userId
* @param orderId
*/
public String snatchRedpack(long userId,long orderId){
Object object = jedisUtils.eval(LuaScript.getHbLua,4,
redisKeys.getHbPoolKey(orderId),//
redisKeys.getDetailListKey(orderId),//
redisKeys.getHbRdKey(orderId),String.valueOf(userId));
return (String) object;
}
抢红包只需要执行一下上面的lua脚本。
测试:
运行生成红包:这里生成5个红包c;orderId为111111.
@Test
public void genRedpack(){
jedisUtils jedisUtils = new jedisUtils("127.0.0.1", 6379, "123456");
Redpackservice redpackservice = new Redpackservice(jedisUtils);
redpackservice.genRedpack(111111,5);
}
@H_874_126@
运行抢红包:这里模拟了100个人c;也就是100个线程。这里用了CyclicBarrierc;等到所有的线程都准备好c;同时开抢。
@Test
public void snatchRedpack() throws InterruptedException {
jedisUtils jedisUtils = new jedisUtils("118.89.196.99", 6379, "123456");
Redpackservice redpackservice = new Redpackservice(jedisUtils);
IdWorker idWorker = new IdWorker();
int n = 100;
CyclicBarrier barrier = new CyclicBarrier(N);
for (int i = 0;i<N;i++){
new Thread(()->{
long userId = idWorker.nextId();
try {
System.out.println("用户"+userId+"准备抢红包");
barrier.await();
} catch (InterruptedException E) {
e.printStackTrace();
} catch (BrokenBarrierException E) {
e.printStackTrace();
}
String result = redpackservice.snatchRedpack(userId, 111111);
if ("0".equals(result)){
System.out.println("用户" + userId + "未抢到红包c;原因:红包已领完");
}else if ("1".equals(result)){
System.out.println("用户" + userId + "未抢到红包c;原因:红包已领过");
}else{
System.out.println("用户" + userId + "抢到红包:" + result);
}
},"thread"+i).start();
}
Thread.sleep(Integer.max_value);
}
运行结果:
......
用户1078994397959344128准备抢红包
用户1078994397959344129准备抢红包
用户1078994397959344130准备抢红包
用户1078994397959344131准备抢红包
用户1078994397896429573抢到红包:{"userId":"1078994397896429573","hbId":2,"amount":126}
用户1078994397888040960抢到红包:{"userId":"1078994397888040960","hbId":1,"amount":526}
用户1078994397896429572抢到红包:{"userId":"1078994397896429572","hbId":5,"amount":666}
用户1078994397950955528未抢到红包c;原因:红包已领完
用户1078994397925789696抢到红包:{"userId":"1078994397925789696","hbId":4,"amount":490}
用户1078994397929984004未抢到红包c;原因:红包已领完
用户1078994397959344128抢到红包:{"userId":"1078994397959344128","hbId":3,"amount":192}
用户1078994397955149824未抢到红包c;原因:红包已领完
用户1078994397900623875未抢到红包c;原因:红包已领完
用户1078994397929984006未抢到红包c;原因:红包已领完
用户1078994397892235264未抢到红包c;原因:红包已领完
.......
结果看出100个线程同时抢c;只有5个人抢成功了。
到这儿就好了。以上是大佬教程为你收集整理的腾讯赢了!470万!用Redis+lua教你实现抢红包全部内容,希望文章能够帮你解决腾讯赢了!470万!用Redis+lua教你实现抢红包所遇到的程序开发问题。
如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。