转载至:https://www.zybuluo.com/yulin718/note/93148

微信红包的架构设计简介

架构


@来源于QCon某高可用架构群整理,整理朱玉华。

背景:有某个朋友在朋友圈咨询微信红包的架构,于是乎有了下面的文字(有误请提出,谢谢)

概况:2014年微信红包使用数据库硬抗整个流量,2015年使用cache抗流量。

    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操作。另外还有一个对账来保障。

 简单代码实现

参考简书上的一篇文章,整合后附上完整的代码:(参考文章:https://www.jianshu.com/p/026ceece4d80

定义红包类RedPackage:

 public class RedPackage {

     //红包数量
int remainSize;
//红包钱
int remainMoney;
}

测试类TestRed

public class TestRed {

    private static RedPackage redPackage = new RedPackage();

    //初始化函数
static void init() {
redPackage.remainSize = 30;
redPackage.remainMoney = 500;
}
//获取每一次抢到的红包金额
public static double getRandomMoney(RedPackage _redPackage) {
//红包只有一个或只剩下一个
if(_redPackage.remainSize == 1) {
_redPackage.remainSize--;
return (double)Math.round(_redPackage.remainMoney * 100)/100; //取整 四舍五入
}
Random r = new Random();
//规定最少抢到的红包有0.01元,最多为剩余的红包钱的平均值*2
double min = 0.01;
double max = _redPackage.remainMoney / _redPackage.remainSize * 2;
double money = r.nextDouble() * max; //nextDouble返回0.0~1.0之间的数
money = money <= min ? 0.01 : money;
money = Math.floor(money * 100) / 100; //取整 不大于这个数的最大整数
_redPackage.remainSize--;
_redPackage.remainMoney -= money;
return money;
} public static void main(String[] args) {
//初始化
init();
int size = redPackage.remainSize;
for(int j=1; j<=2; j++) {
System.out.println("第"+j+"轮");
for(int i=1; i<=size; i++) {
double single = getRandomMoney(redPackage);
// System.out.println("第"+ i + "个红包:"+ single);
System.out.print(single + " ");
}
System.out.println("");
init();
}
}
}

测试结果:

第一轮
8.68   21.49   11.61   12.56   23.91   14.64   25.68   18.36   15.28   22.43   5.39   13.83   26.18   1.68   33.18   26.46   25.61   21.87   12.04   9.53   4.43   17.89   0.09   26.31   15.97   0.26   16.62   31.84   3.49   19.0  
第二轮
15.82   1.67   26.43   0.86   1.93   3.03   23.16   0.15   36.19   5.54   2.73   29.29   19.17   2.12   1.03   36.46   37.51   14.36   26.26   35.93   12.24   2.74   27.88   33.03   3.05   21.09   2.33   2.0   23.12   36.0

java 微信红包算法代码实现及架构设计的更多相关文章

  1. c# Java 微信红包算法

    int total_money_cent = 1000; // 红包总金额 单位:分 int total_people = 8; // 抢红包总人数 int[] array = new int[tot ...

  2. 微信红包算法TEST

    1.基本算法 设定总金额为10元,有N个人随机领取:N=1 则红包金额=X元: N=2 为保证第二个红包可以正常发出,第一个红包金额=0.01至9.99之间的某个随机数 第二个红包=10-第一个红包金 ...

  3. php微信红包算法

    微信红包算法.php /**生成红包的函数*/ function getRandMoney($totalMoney, $totalPeople=2, $miniMoney=1){ $randRemai ...

  4. PHP实现简易微信红包算法

    <?php /** * PHP实现简易的微信红包算法 * @version v1.0 * @author quetiezheng */ function getMoney($total, $pe ...

  5. PHP实现微信红包算法和微信红包的架构设计简介

    微信红包的架构设计简介: 原文:https://www.zybuluo.com/yulin718/note/93148 @来源于QCon某高可用架构群整理,整理朱玉华. 背景:有某个朋友在朋友圈咨询微 ...

  6. Python微信红包算法

    sklearn实战-乳腺癌细胞数据挖掘(博主亲自录制视频) https://study.163.com/course/introduction.htm?courseId=1005269003& ...

  7. Java进阶专题(十八) 系统缓存架构设计 (下)

    前言 上章节介绍了Redis相关知识,了解了Redis的高可用,高性能的原因.很多人认为提到缓存,就局限于Redis,其实缓存的应用不仅仅在于Redis的使用,比如还有Nginx缓存,缓存队列等等.这 ...

  8. 微信小程序全面实战,架构设计 && 躲坑攻略(小程序入门捷径教程)

    最近集中开发了两款微信小程序,分别是好奇心日历(每天一条辞典+一个小投票)和好奇心日报(轻量版),直接上图: Paste_Image.png 本文将结合具体的实战经验,主要介绍微信小程序的基础知识.开 ...

  9. java设计模式--基础思想总结--抽象类与架构设计思想

    抽象类?这个东西我感觉没什么卵用啊,又不能拿来new对象,没有具体的对象的抽象类,有什么实际的意义呢?这是很多刚刚接触java抽象类语法时的第一反应(当然,包括我).确实,很多刚刚接触抽象类这个概念的 ...

随机推荐

  1. 环境变量方式使用 Secret【转】

    通过 Volume 使用 Secret,容器必须从文件读取数据,会稍显麻烦,Kubernetes 还支持通过环境变量使用 Secret. Pod 配置文件示例如下: 创建 Pod 并读取 Secret ...

  2. 第3节 sqoop:3、sqoop的入门测试使用

    3.5. Sqoop的数据导入 “导入工具”导入单个表从RDBMS到HDFS.表中的每一行被视为HDFS的记录.所有记录都存储为文本文件的文本数据(或者Avro.sequence文件等二进制数据) 列 ...

  3. 题目:给定一数组 例如:a = [1,2,3,5,2,1] 现用户提供一个数字 请返回用户所提供的数字的所有下标

    def test(ary): ds = {} for i in range(len(ary)): if ds.get(ary[i]): ds[ary[i]].append(i) else: ds[ar ...

  4. ROS学习笔记7-理解服务(services)和参数(parameter)

    本文内容来自官方wiki:http://wiki.ros.org/ROS/Tutorials/UnderstandingServicesParams ROS服务(service)服务也是ROS中一种通 ...

  5. 剑指offer圆圈中最后剩下的数字 和 迭代器总结

    迭代器只有++ ,--,==,!=四种运算方法,不能将iter = iteration+ 1,因为迭代器是指针类型,1是整数类型,不能直接相加赋值给一个指针. 题目描述 每年六一儿童节,牛客都会准备一 ...

  6. 03.swoole学习笔记--web服务器

    <?php //创建web服务器 $serv=); //获取请求 /* * $request:请求信息 * $response:响应信息 */ $serv->on('request',fu ...

  7. git下载安装、配置及idea初始化

    安装 wget https://mirrors.edge.kernel.org/pub/software/scm/git/git-2.19.0.tar.gz git 安装依赖 yum -y insta ...

  8. ucosiii 移植

    最近想在 f429 上面使用 mdk526 版本的 IDE,配合 HAL 和ucosiii.考虑到的方法是对比 v7 开发板的 ucosiii 和裸机程序,找出需要修改的地方,然后对比 v6 开发板的 ...

  9. Fr3设置图片打印

    见 fr3的文件内容,为xml <?xml version="1.0" encoding="utf-8"?> <TfrxReport Vers ...

  10. VMware Workstation上新建虚拟机

    准备开始,话不多少,直接上图 点击创建新的虚拟机或者在文件上面选择新建虚拟机 点击完成就可以了 后面的步骤,是在公司电脑上完成的,新建了一个CentOs1,步骤同上,后面继续,然后需要更改配置,点击虚 ...