最全解密微信红包随机算法(含代码实现)

珠江路在线   2020年11月18日  【 转载 】移动端IM技术分享 

本文内容编写时,参考了网上的 材料,详见“参考 材料” 部分, 感激分享者 。

1、引言

这个系列文章已经 整顿了10篇,但都没有 波及到具体的红包算法实现,重要有以下两方面缘由 。

一方面是各社交/IM产品中的红包 性能同质化严峻,红包算法的“可玩性”就是“核心竞争力所在”,这是同质化 性能的差别化竞争思路,不会 随便公开 。

另一方面,市场上还存在各种抢红包插件这类灰产存在,一旦公开这些算法,很可能又被这帮插件开发者们搞出什么幺蛾子 。

所以,这样的状况下,假如要做社交/IM产品中的红包 性能,红包 随便算法该怎么实现, 根本上不得不自已 推敲,很难找到大厂算法直接套用 。

本着即时通信网一向的im 常识 流传 精力,我收集 整顿并参考了大量的网上 材料,综合了 比较靠谱的信息 起源,便有了本文 。本文依据有限的 材料,分享了微信红包随机算法实现中的一些技术要点,并 整顿了两种 比较靠谱的红包算法实现思路(含可运行的实现代码), 指望能给你的红包算法开发带来启示 。

声明本文 材料 整顿自网络,仅供学习探究之用,如有不妥,请 告诉Jack Jiang 。

学习 交换:

- 移动端IM开发入门文章:《新手入门一篇就够:从零开发移动端IM》

- 开源IM框架源码:https://github.com/JackJiang2011/MobileIMSDK

本文已同步公布于“即时通信技术圈”公众号 。

2、系列文章

《社交软件红包技术解密(一):全面解密QQ红包技术 方案——架构、技术实现等》

《社交软件红包技术解密(二):解密微信摇一摇红包从0到1的技术演进》

《社交软件红包技术解密(三):微信摇一摇红包雨背后的技术细节》

《社交软件红包技术解密(四):微信红包系统是如何 应答高并发的》

《社交软件红包技术解密(五):微信红包系统是如何实现高可用性的》

《社交软件红包技术解密(六):微信红包系统的存储层架构演进 实际》

《社交软件红包技术解密(七): 领取宝红包的海量高并发技术 实际》

《社交软件红包技术解密(八):全面解密微博红包技术 方案》

《社交软件红包技术解密(九):谈谈手Q春节红包的设计、容灾、运维、架构等》

《社交软件红包技术解密(十):手Q客户端针对2020年春节红包的技术 实际》

《社交软件红包技术解密(十一):最全解密微信红包随机算法(含演示代码)》(* 本文)

3、微信红包算法要点汇总

这是当前能找到的仅有的一份,有微信团队人员 参加的微信红包算法技术要点的 探讨 材料 。分享于2015年,差不多是微信红包刚火没多久,大约是微信技术团队的人当时没有现在这些技术之外的顾虑,所以作了有限的分享, 材料难得,本次再一次 整顿了一下, 能够作为参考 材料 使用 。以下是 材料 注释 。

材料 起源:来自InfoQ的某架构群的技术 探讨,由朱玉华 整顿(个人博客是:zhuyuhua.com(当前已 无奈 拜访)) 。

材料背景:起因是有朋友在朋友圈 征询微信红包的架构,于是在微信团队成员 参加 探讨的状况下,我(指“朱玉华”) 整顿了这次 探讨的技术要点,也就是下面的内容(内容为问答 模式) 。

3.1、算法实现的技术要点

【1】问:微信的金额什么时候算?

答:微信金额是拆的时候实时算出来,不是预先 调配的,采纳的是纯内存计算,不需求 估算空间存储 。

为何采取实时计算金额?缘由是:实时效率更高, 估算才效率低下 。 估算还要占额外存储 。由于红包只占一条记录并且有效期就几天,所以不需求多大空间 。就算压力大时,水平 扩充机器是 。

【2】问:关于实时实时性,为何明明抢到红包,点开后发现没有?

答:2014年的红包丝毫开就晓得金额,分两次操作,先抢到金额, 而后再转账 。

2015年的红包的拆和抢是 拆散的,需求点两次, 因而会浮现抢到红包了,但点开后告知红包已经被领完的状况 。进入到第一个页面不代表抢到,只 示意当时红包还有 。

【3】问:关于 调配算法,红包里的金额怎么算?为何浮现各个红包金额相差很大?

答:随机,额度在 0.01 和 残余 均匀值 2 中间 。 例如:发 100 块钱,总共 10 个红包,那么 均匀值是 10 块钱一个,那么发出来的红包的额度在 0.01元~20元中间 稳定 。

当前面 3 个红包总共被领了 40 块钱时,剩下 60 块钱,总共 7 个红包,那么这 7 个红包的额度在:0.01~(60/7 * 2)=17.14中间 。

留神:这里的算法是每被抢一个后,剩下的会再次执行上面的这样的算法(Tim老师也感觉上述算法太复杂,不知基于什么样的考量) 。

这样算下去,会超过最开始的全部金额, 因而到了最后面假如不够这么算,那么会采取如下算法: 保障 残余消费者能拿到最低1分钱即可 。

假如前面的人手气不好,那么后面的余额越多,红包额度也就越多, 因而实际概率一样的 。

【4】问:红包的设计

答:微信从财付通拉取金额数据过来,生成个数/红包类型/金额放到redis集群里,app端将红包ID的 申请放入 申请队列中,假如发现超过红包的个数,直接返回 。依据红包的逻辑 解决 顺利得到令牌 申请,则由财付通进行 统一性调用,通过像比特币一样,两边 保留交易记录,交易后交给第三方服务审计,假如交易过程中浮现不 统一就强制回归 。

【5】问:并发性 解决:红包如何计算被抢完?

答:cache会反抗无效 申请,将无效的 申请过滤掉,实际进入到 后盾的量不大 。cache记录红包个数,原子操作进行个数递减,到 0 示意被抢光 。财付通依照 20万笔每秒入账 预备,但实际还不到 8万每秒 。

【6】问:通如何 维持8w每秒的写入?

答:多主sharding,水平 扩充机器 。

【7】问:数据容量多少?

答:一个红包只占一条记录,有效期惟独几天, 因而不需求太多空间 。

【8】问: 查问红包 调配,压力大不?

答:抢到红包的人数和红包都在一条cache记录上,没有太大的 查问压力 。

【9】问:一个红包一个队列?

答:没有队列,一个红包一条数据,数据上有一个计数器字段 。

【10】问:有没有从数据上 证实每个红包的概率是否均等?

答:不是绝对均等,就是一个 容易的拍脑袋算法 。

【11】问:拍脑袋算法,会不会浮现两个最佳?

答:会浮现金额一样的,但是手气最佳惟 唯一个,先抢到的那个最佳 。

【12】问:每领一个红包就更新数据么?

答:每抢到一个红包,就cas更新 残余金额和红包个数 。

【13】问:红包如何入库入账?

答:数据库会累加已经 领取的个数与金额,插入一条 领取记录 。入账则是 后盾异步操作 。

【14】问:入帐出错怎么办? 比方红包个数没了,但余额还有?

答:最后会有一个take all操作 。另外还有一个对账来保障 。

【15】问:既然在抢的时候有原子减了就不应该浮现抢到了拆开没有的状况?

答:这里的原子减并不是真正 意思上的原子操作,是Cache层提供的CAS,通过 比较版本号不停尝试 。

【16】问:cache和db挂了怎么办?

答:主备 +对账 。

【17】问:为何要 拆散抢和拆?

答:总思路是设置多层过滤网,层层筛选,层层削减流量和压力 。

这个设计最初是由于抢操作是业务层,拆是入账操作,一个操作太重了,并且中断率高 。 从接口层面看,第一个接口纯缓存操作,搞压 威力强,一个 容易 查问Cache挡住了绝大 部分消费者,做了第一道筛选,所以大 部分人会看到已经抢完了的 揭示 。

【18】问:抢到红包后再发红包或者提现,这里有什么策略吗?

答:大额优先入账策略 。

针对上面的技术要点,有人还画了张原理图(这是网上能找到的 绝对清楚的版本):

最全解密微信红包随机算法(含代码实现)

3.2、微信抢红包的过程 模仿

针对上节中 整顿的 材料,当有人在微信群里发了一个 N 人的红包、总金额 M 元, 后盾大约的技术逻辑如下 。

3.2.1)发红包 后盾操作:

1)在数据库中添加一条红包记录,存储到CKV,设置过期 工夫;

2)在Cache(可能是腾讯内部kv数据库,基于内存,有落地,有内核态网络 解决模块,以内核模块 模式提供服务))中添加一条记录,存储抢红包的人数N 。

3.2.2)抢红包 后盾操作:

1)抢红包分为抢和拆:抢操作在Cache层 实现,通过原子减操作进行红包数递减,到0就 注明抢光了,最后实际进入 后盾拆操作的量不大,通过操作的 拆散将无效 申请直接挡在Cache层外面 。

这里的原子减操作并不是真正 意思上的原子减操作,是其Cache层提供的CAS,通过 比较版本号不停尝试,存在 定然程度上的 摩擦, 摩擦的消费者会放行,让其进入下一步拆的操作,这也解释了为啥有消费者抢到了拆开发现领完了的状况 。

2)拆红包在数据库 实现:通过数据库的事务操作累加已经 领取的个数和金额,插入一条 领取流水,入账为异步操作,这也解释了为啥在春节期间红包 领取后在余额中看不到 。

拆的时候会实时计算金额,其金额为1分到 残余 均匀值2倍中间随机数,一个总金额为M元的红包,最大的红包为 M * 2 /N(且不会超过M),当拆了红包后会更新 残余金额和个数 。财付通按20万笔每秒入账 预备,实际只到8万每秒 。

4、微信红包算法 模仿实现1(含代码)

依据上一节的微信红包随机算法技术要点 材料,实现了一个算法,以下供参考 。(注:本节内容 引用自《微信红包随机算法初探》一文)

4.1、算法约定

算法很 容易,跟微信的算法一样,不是提前算好,而是抢红包时计算 。

即:金额随机,额度在0.01和 残余 均匀值*2中间 。(参见上一节的 “关于 调配算法,红包里的金额怎么算?为何浮现各个红包金额相差很大?” 内容)

4.2、代码实现

算法的逻辑重要是:

public static double getRandomMoney(RedPackage _redPackage) {

// remainSize 残余的红包数量

// remainMoney 残余的钱

if(_redPackage.remainSize == 1) {

_redPackage.remainSize--;

return (double) Math.round(_redPackage.remainMoney * 100) / 100;

}

Random r = newRandom();

double min = 0.01; //

double max = _redPackage.remainMoney / _redPackage.remainSize * 2;

double money = r.nextDouble() * max;

money = money <= min ? 0.01: money;

money = Math.floor(money * 100) / 100;

_redPackage.remainSize--;

_redPackage.remainMoney -= money;

return money;

}

LeftMoneyPackage数据 构造如下:

class RedPackage {

int remainSize;

double remainMoney;

}

测试时初始化 有关数据是:

static void init() {

redPackage.remainSize = 30;

redPackage.remainMoney = 500;

}

附件是 能够运行的 完全Java代码文件:

( 无奈上传附件,如有需求请从此链接处下载:http://www.52im.net/thread-3125-1-1.html)

4.3、测试 后果

4.3.1 单次测试

按上述代码中的初始化数据(30人抢500块),执行了两次, 后果如下:

//第一次

15.69 21.18 24.11 30.85 0.74 20.85 2.96 13.43 11.12 24.87 1.86 19.62 5.97 29.33 3.05 26.94 18.69 34.47 9.4 29.83 5.17 24.67 17.09 29.96 6.77 5.79 0.34 23.89 40.44 0.92

//第二次

10.44 18.01 17.01 21.07 11.87 4.78 30.14 32.05 16.68 20.34 12.94 27.98 9.31 17.97 12.93 28.75 12.1 12.77 7.54 10.87 4.16 25.36 26.89 5.73 11.59 23.91 17.77 15.85 23.42 9.77

第一次随机红包数据图表如下:

最全解密微信红包随机算法(含代码实现)

▲ x轴为抢的顺序,y轴为抢到的金额

第二次随机红包数据图表如下:

最全解密微信红包随机算法(含代码实现)

▲ x轴为抢的顺序,y轴为抢到的金额

4.3.2 屡次均值

反复执行200次的均值:

最全解密微信红包随机算法(含代码实现)

▲ x轴为抢的顺序,y轴为该次抢到金额的概率均值

反复执行2000次的均值:

最全解密微信红包随机算法(含代码实现)

▲ x轴为抢的顺序,y轴为该次抢到金额的概率均值

从以上两张图的均值 后果 能够看出,这个算法中每一次能抢到的金额几率 几乎是均等的,从随机性来说 比较 正当 。

5、微信红包算法 模仿实现2(含代码)

我对随机算法很有兴趣, 刚巧近期探究的方向有点偏随机数这块,所以也自己实现了一下微信的红包 散发算法(算法要点参考的是本文第三节内容) 。(注:本节内容 引用自《微信红包算法的 综合》一文)

5.1、代码实现

从第三节中 能够了解到,微信并不是一开始就预 调配全部的红包金额,而是在拆时进行计算的 。这样做的 好处是效率高,实时性 。本次的代码中,红包具体是怎么计算的呢?请参见第4节中的“关于 调配算法,红包里的金额怎么算?为何浮现各个红包金额相差很大?” 。

那基于这个 思维, 能够写出一个红包 调配算法:

/**

* 并不 圆满的红包算法

*/

public static double rand(double money, int people, List l) {

if(people == 1) {

double red = Math.round(money * 100) / 100.0;

l.add(red);

return0;

}

Random random = newRandom();

double min = 0.01;

double max = money / people * 2.0;

double red = random.nextDouble() * max;

red = red <= min ? min : red;

red = Math.floor(red * 100) / 100.0;

l.add(red);

double remain = Math.round((money - red) * 100) / 100.0;

return remain;

}

算法整体思路很 容易,就在在最后一个人的时候要 留神,此时不进行随机数计算,而是直接将 残余金额作为红包 。

5.2、第一次 综合

采纳上述算法, 能够对消费者的抢红包行为做 综合 。这里的摹仿行为是:30 元的红包,10 人抢 。操作 100 次 。

能够得出如下 后果:

最全解密微信红包随机算法(含代码实现)

▲ x轴为抢的顺序,y轴为该次抢到金额

从上图中 能够很轻易的看出来,越后抢的人,风险越大,同时收益也越大,有较大几率 获得“手气最佳” 。

那红包面值的 分布性如何呢?

最全解密微信红包随机算法(含代码实现)

▲ x轴为抢的顺序,y轴为该次抢到金额 反复 100 次后的 均匀值

从上图 能够看出,都是 比较接近 均匀值(3 元)的 。

那 反复 1000 次呢?

最全解密微信红包随机算法(含代码实现)

▲ x轴为抢的顺序,y轴为该次抢到金额 反复 1000 次后的 均匀值

更接近了 。 。 。

能够看出,这个算法 能够让大家抢到红包面额在概率上是 大体均等的 。

5.3、缺乏之处

有人提出了这个问题:

最全解密微信红包随机算法(含代码实现)

他接下来放了好几张他试验的截图 。我这里取了一张,假如有兴趣, 能够去知乎的问题里查看更多图片 。

最全解密微信红包随机算法(含代码实现)

而此时,我哥们在和我的在 探讨中,也告诉我, 确切存在某个 法令,可能让最后一个抢的人占有某些弱小的优势, 比方,多 0.01 的之类 。

例如发 6 个,总额 0.09 的包,最后一个抢的有极大约率是 0.03 。

最全解密微信红包随机算法(含代码实现)

但是我之前的代码却没 步骤体现出这丝毫 。

比方 10 人拆 0.11 元的包,我的 后果是:

最全解密微信红包随机算法(含代码实现)

可见以上代码还存在缺乏之处 。

于是我就有一个推测:

微信可能不是对全金额进行随机的,可能在派发红包之前,已经对金额做了 解决, 比方,事先减去(红包个数*0.01),之后在每个红包的随机值 根底上加 0.01,以此来 保障每个红包最小值都是 0.01 。

这个推测兴许 能够解开那位知友和我哥们这边的 不解 。

5.4、完善算法

在原先的 根底上对代码进行 容易的 批改:

public static double rand(double money, int people, List l) {

if(people == 1) {

double red = Math.round(money * 100) / 100.0;

l.add(red+0.01);

return 0;

}

Random random = newRandom();

double min = 0;

double max = money / people * 2.0;

double red = random.nextDouble() * max;

red = red <= min ? min : red;

red = Math.floor(red * 100) / 100.0;

l.add(red+0.01);

double remain = Math.round((money - red) * 100) / 100.0;

return remain;

}

这个算法,在第一次调用时传入 money 的值是总金额减去红包数*0.01,大约像这样:

_money = _money - people * 0.01;

5.5、第二次 综合

5.5.1 验证上次的缺乏之处

1)10 人抢 0.11 元的包:

最全解密微信红包随机算法(含代码实现)

2)2 人抢 0.03 元的包:

最全解密微信红包随机算法(含代码实现)

3)6 人抢 0.09 的包:

最全解密微信红包随机算法(含代码实现)

5.5.2 批改后的代码会不会对已知 论断造成影响?

30 元的红包,10 人抢,操作 100 次 。

最全解密微信红包随机算法(含代码实现)

▲ x轴为抢的顺序,y轴为该次抢到金额

最全解密微信红包随机算法(含代码实现)

▲ x轴为抢的顺序,y轴为该次抢到金额 反复 100 次后的 均匀值

由上面两图可见, 论断 根本上没有转变 。

5.6、 论断

通过上述代码 实际可知:

1)先抢后抢,金额 期冀都是 雷同的;

2)微信的红包算法很可能是预先 调配给每人 0.01 的“底额”;

3)后抢者风险高,收益大 。

5.7、补充

上几张后面测试的图,补充一下之前的观点,发 n 个红包,总金额是(n+1)*0.01,最后一个领的 定然是手气最佳 。

最全解密微信红包随机算法(含代码实现)

最全解密微信红包随机算法(含代码实现)

大家也 能够试试 。

以上,大约 能够 证实,微信红包是在 调配前先给每个人 0.01 的最低金额的!

6、参考 材料

[1] 微信红包随机算法初探

[2] 微信红包算法的 综合

[3] 微信红包的架构设计简介

[4] 微信红包的随机算法是 怎么实现的?

另外,知乎上关于微信红包算法的 探讨问题众多人 参加,有兴趣 能够上去看看,兴许会有更多启示:《微信红包的随机算法是 怎么实现的?》 。

附录:更多微信 有关资源

《IM开发宝典:史上最全,微信各种 性能参数和逻辑 规定 材料汇总》

《微信当地数据库破解版(含iOS、Android),仅供学习探究 [附件下载]》

(本文同步公布于:http://www.52im.net/thread-3125-1-1.html)

免责声明:凡标注转载/编译字样内容并非本站原创,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。