尼恩说在前面
在40岁老架构师 尼恩的读者交流群(50+)中,最近有小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格,遇到很多很重要的架构类/设计类的场景题:
1.如何设计高并发红包系统 ,请说出你的方案?
2.听说你会架构设计,请问一下如果让你来设计红包系统,说说你的架构设计方案。
最近有小伙伴在面试字节,又遇到了红包架构问题。小伙伴支支吾吾的说了几句,面试挂了。
所以,尼恩给大家做一下系统化、体系化的梳理,使得大家内力猛增,可以充分展示一下大家雄厚的 "技术肌肉",让面试官爱到 "不能自已、口水直流",然后实现"offer直提"。
当然,这道面试题,以及参考答案,也会收入咱们的 《尼恩Java面试宝典PDF》V171版本,供后面的小伙伴参考,提升大家的 3高 架构、设计、开发水平。
最新《尼恩 架构笔记》《尼恩高并发三部曲》《尼恩Java面试宝典》的PDF,请关注本公众号【技术自由圈】获取,回复:领电子书
本文的2个重量级作者:
第一重量级作者 Moen(资深架构师,负责写初稿 )
第二重量级作者 尼恩 (40岁老架构师, 负责提升此文的 技术高度,让大家有一种 俯视 技术的感觉)
《尼恩Java面试宝典》 是大家 面试的杀手锏, 此文当最新PDF版本,可以找43岁老架构师尼恩获取。
红包架构背景
红包是一种 瞬时流量很大的应用, 会在很短的时间内,产生巨大的瞬时流量,
所以,作为架构师来说,这种场景有很大的架构挑战
以2017年除夕为例,微信红包峰值QPS在76w左右,除夕当天收发微信红包的数量为142亿个。
这种百亿级别的数据量、100Wqps级别的超高并发,而且整个系统核心功能和支付相关,需要做好高并发、高可用、高可靠。
特别说明:红包架构也是高端面试的核心场景题, 后面也会以视频的形式,对这些架构的一系列的架构场景题目,做系统化的介绍。
尼恩架构团队会录制成为架构视频, 帮助大家做架构拿高薪。
红包系统和秒杀系统的对比
我们先了解下微信红包支付的流程。
在上面的红包流程中,核心业务包含包红包、发红包、抢红包、拆红包
其中最关键的步骤是
-
发红包
-
抢红包。
在整个体系中,红包系统 是 支付系统的商户,红包这个商户出售的是钱。
所以,用户发红包在红包系统 使用微信支付购买一份 红包商品,红包系统 将钱发放到相对应的微信群,供参与者领取。
微信群里的用户抢红包,得到的是商品里边的 零钱。
在整个体系中,红包系统 和支付系统 之间的关系是商家和第三方支付平台的关系。
红包的流程,很类似 商品"秒杀"活动。
- 包红包类似秒杀商品管理
- 发红包类似"秒杀"活动的商品上架;
- 抢红包等同于"秒杀"活动中的查询库存;
- 拆红包对应"秒杀"活动中用户的"秒杀"动作。
不过除了上面的相同点之外,红包业务 与 "秒杀"活动相比,还具备自身的特点:
首先,抢红包 比 "秒杀"有更海量的并发要求。假设同一时间有 10 万个群里的用户同时在发红包,那就相当于同一时间有 10 万个"秒杀"活动发布出去。10 万个微信群里的用户同时抢红包,将产生海量的并发请求。
其次,微信红包业务要求更严格的安全级别。红包业务本质上是资金交易。资金交易业务比普通商品"秒杀"活动有更高的安全级别要求。
-
"秒杀"时可以允许存在"超卖"(即实际被抢的商品数量比计划的库存多)、"少卖"(即实际被抢的商户数量比计划的库存少)的情况。
-
但是对于 红包,不允许存在"超卖"、"少卖"。
特别说明:红包架构也是高端面试的核心场景题, 后面也会以视频的形式,对这些架构的一系列的架构场景题目,做系统化的介绍。
尼恩架构团队会录制成为架构视频, 帮助大家做架构拿高薪。
红包系统功能分析
接下来,40岁老架构带大家,从架构师视角开始红包系统的功能分析。
微信红包和微信支付之间的交互,与普通商家与微信支付的交互一样,需要经过多个步骤。
-
用户发红包时,进入微信红包下一笔订单,系统记录发红包用户、发红包金额、红包数量和要发送到的用微信群。
-
然后微信红包系统请求微信支付服务器进行下单,用户使用微信支付进行支付。
-
支付成功后,微信支付后台系统通知微信红包后台系统支付成功结果,微信红包后台系统收到通知后推送微信红包消息到微信群。
-
微信群里用户便可抢红包。
-
用户发现红包还有剩余,就可以拆剩下的红包
这就是微信红包和微信支付的关系以及交互过程。
功能1:包红包
系统给每个红包分配一个唯一ID,也就是发红包的订单号,然后将红包发送给用户,红包的个数,红包金额写入到存储。
功能2:发红包
用户使用微信支付完成付款,微信红包后台收到微信支付成功的通知。红包系统将红包发送订单状态更新,更新为用户已支付,并写入用户发红包记录表,这样用户可以在钱包中找到用户的发红包流水和收发红包的记录,之后微信红包系统调用微信通知,将微信红包信息发送到微信群。
功能3:抢红包
微信群中的用户收到红包消息之后,点开红包,开始抢红包,这个过程微信红包系统会检查红包是否已经被抢完,是否已经过期,是否已经抢过等验证逻辑。
功能4:拆红包
拆红包是整个发红包流程最复杂的一个操作,需要查询这个红包的红包订单,判断用户是否可以拆包,计算本次可拆到的红包金额,并记录抢红包流水。
拆红包包括如下步骤:
-
1)查询这个红包发送订单,判断用户是否可拆,然后计算本次可拆到的红包金额;
-
2)然后写入一条抢红包记录。如果把拆红包过程,类比为一个秒杀活动的过程,相当于扣库存与写入秒杀记录的过程;
-
3)更新库存对应于更新红包发送订单,写入秒杀记录对应于写入这个红包的领取红包记录;
-
4)另外,还要写入用户整体的红包领取记录;
-
5)最后请求微信支付系统给拆到红包用户转入零钱,成功后更新抢红包的订单状态为已转账成功。
拆红包过程类似于一个秒杀活动的过程,需要做好库存扣减和秒杀记录的操作。
更新库存就是更新红包发送的订单,写入秒杀记录就是写入红包领取的信息流水。
还需要以用户为中心记录用户整体的红包领取记录。
最后调用支付系统将拆红包后的金额转入用户零钱中,成功之后更新抢红包的订单状态为转账成功。
所以,在看此文之前,最好看看尼恩的高并发三部曲的Java高并发核心编程卷3*(注意是最新的清华大学出版社出版的加强版),里边有秒杀架构,可以作为知识铺垫。
微信红包的业务特点
微信红包,特别是群红包,业务形态上类似于普通商品的"秒杀"活动。
-
包红包类似秒杀商品管理
-
发红包类似"秒杀"活动的商品上架;
-
抢红包等同于"秒杀"活动中的查询库存;
-
拆红包对应"秒杀"活动中用户的"秒杀"动作。
微信红包在业务形态上和普通商品"秒杀"活动相比,还有自身特点:
-
海量并发请求:微信红包用户在微信群发一个红包,等同于在网上发布一次商品"秒杀"活动,假设同时有10万个群的用户同时发红包,那就相当于同一时间有10万个"秒杀"活动发布。10万个微信群的用户同时抢红包,将产生海量并发请求。
-
更严格的安全级别:微信红包业务本质上是资金交易,微信红包是微信支付的一个商户,提供资金流转服务,用户发红包相当于在微信红包这个商户上使用微信支付购买了一笔"钱",且收货地址是微信群。当用户支付成功后,红包"发货"到微信群里,群里的用户拆开红包后,微信红包提供了将"钱"转入拆红包用户微信零钱的服务。
-
订单层南北独立体系:微信红包系统采用南北独立体系的订单层设计,即数据在南北两个系统中不同步。用户就近接入系统,请求发红包时,系统会根据用户所在地分配订单到南或北的系统,并在订单号上打上南北标识。这种设计有助于分摊流量,降低系统风险。
-
流量闭环:在抢红包、拆红包、查红包详情列表时,接入层会根据红包单号上的南北标识,将流量分别引导到对应的南北系统闭环。这意味着,无论是发红包还是抢红包,用户都能够在就近的系统中完成操作,无需跨城,提高了系统的响应速度和稳定性。
-
用户数据处理:微信红包系统的用户数据采取写多读少、全量保存的策略。用户数据的查询入口在微信钱包中,相对隐藏,访问量不会太大,且被视为可旁路的非关键信息,实时性要求不高。这种设计方式可以减少数据存储的压力,提高系统性能。
-
实时计算红包金额:
微信红包的金额是在拆红包时实时计算的,而不是预先分配的。系统会在拆红包时取0.01到剩余平均值*2之间的数值作为红包金额。
这种实时计算的方式基于内存进行,不需要额外的存储空间,且计算效率很高。
同时,为了保证操作的原子性,拆包过程中使用了CAS(Compare-and-Swap)算法,确保每次只有一个并发用户拆包成功。如果拆包CAS失败,系统会自动进行重试,但也可能在重试过程中被其他用户抢得先机而空手而归。
-
架构演进:随着微信红包功能的不断发展和用户量的增长,其系统架构也经历了不断的演进和优化。从最初的数据库硬抗整个流量,到后来的使用缓存(cache)抗流量,再到现在的南北独立体系等设计,都是为了更好地应对高并发、提升系统性能和稳定性。
资金交易业务比普通商品"秒杀"有更高的安全级别要求,普通的商品"秒杀"由商户提供,库存是商户预设,"秒杀"允许存在超卖和少卖的情况,但对于微信红包,用户发100元的红包绝对不可以拆出101元,以及只被领取99元时,剩下的1元在24小时过期后要精准退还给发红包用户。
总的来说,微信红包系统架构的设计充分考虑了用户量、并发量、性能要求等因素,通过南北独立体系、流量闭环、用户数据处理、实时计算红包金额以及架构演进等多种手段,保证了系统的稳定运行和良好用户体验。
微信红包的技术难点
微信红包系统架构的技术难点主要体现在以下几个方面:
-
高并发难点:
微信红包在特定时间(如春节、节假日等)会面临极高的并发量,如何有效地处理这些并发请求,保证系统的稳定性和响应速度,是红包系统架构设计的关键挑战之一。
-
资金安全难点:
红包系统涉及到资金的转移和存储,因此资金安全是系统设计的重中之重。如何确保资金安全,防止被攻击或篡改,是系统架构设计中需要重点考虑的问题。
红包业务涉及资金交易,所以一定不能出现超卖、少卖的情况。
-
超卖:发了 10 块钱,结果抢到了 11 块钱,多的钱只能系统补上,如此为爱发电应用估计早就下架了;
-
少卖:发了 10 块钱,只抢了 9 块,多的钱得原封不动地退还用户,否则第二天就接到法院传单了。
-
-
用户体验难点
红包系统需要保证良好的用户体验,包括响应速度、公平性(先抢先得)、成功率等。
如何在高并发场景下保证用户体验,是系统架构设计的重要目标。
了解下微信红包的用户体验 的4大核心:摇/发/抢/拆。
-
摇:摇的流畅
-
快:抢的要快
-
爽:拆的爽
-
稳:能分享出去
-
-
数据一致性难点:
红包系统需要保证数据的一致性,包括红包库存数据、用户账户数据等。如何在高并发场景下保证数据的一致性,是系统架构设计的重要挑战。
-
参与用户越多,并发 DB 请求越大,数据越容易出现事务问题,所以系统得做好事务一致性;
-
抢红包系统涉及金钱交易,所以事务级别要求更高,不能出现脏数据。
-
-
系统扩展性难点:
随着用户量和业务量的增长,红包系统需要具备良好的扩展性,能够方便地增加新的功能或提升性能。
如何设计可扩展的系统架构,是系统架构设计的重要考虑因素。
为了解决这些技术难点,我们可以采用多种技术手段,如分而治之、负载均衡、读写分离、水平切分、垂直切分等,来提升系统的性能和稳定性。同时,系统还采用了柔性服务、系统降级等策略,来保证在有限资源下满足用户的核心需求。
红包系统概要设计
系统功能说明
抢红包功能允许用户在群聊中发送任意个数和金额的红包,群成员可以抢到随机金额的红包,但要保证每个用户的红包金额不小于 0.01 元。
抢红包的详细交互流程如下:
-
用户接收到抢红包通知,点击通知打开群聊页面;
-
用户点击抢红包,后台服务验证用户资格,确保用户尚未领取过此红包;
-
若用户资格验证通过,后台服务分配红包金额并存储领取记录;
-
用户在微信群中看到领取金额,红包状态更新为"已领取";
-
异步调用支付接口,将红包金额更新到钱包里。
数据库设计
红包表:redpack
红包表用来记录用户发了多少红包,以及需要维护的剩余金额,
红包表:redpack的字段如下:
字段 | 描述 |
---|---|
id | 主键,红包ID。 |
user_id | 发送红包的用户id。 |
total_amount | 红包总金额。 |
surplus_amount | 红包剩余金额。 |
total | 红包总数。 |
surplus_total | 剩余红包总数。 |
红包记录表:redpack_record
字段 | 描述 |
---|---|
id | 主键,记录id。 |
redpack_id | 红包id。 |
user_id | 用户id。 |
amount | 抢到的金额。 |
发红包
设置完红包参数后,微信支付,完成付款,然后收到付款成功通知,红包系统更新红包订单状态,更新为已支付,并写入红包发送记录表。
这样用户可以将用户的红包信息和红包的收发记录发出,红包系统调用微信通知,将红包信息发送到微信群。
发红包的交互步骤如下:
-
用户设置红包的总金额和个数后,在红包表中增加一条数据,开始发红包;
-
为了保证实时性和抢红包的效率,在 Redis 中增加一条记录,存储红包 ID 和总人数 n;
-
抢红包消息推送给所有群成员。
抢红包
微信群用户收到红包后,点开,红包系统会校验红包是否被抢完,是否过期。
微信红包的抢红包和拆红包是两个分离的服务,用户点击抢红包后需要进行两次操作。
这也是为什么明明有时候抢到了红包,点开后却发现该红包已经被领取完了。
抢红包的交互步骤如下:
-
抢红包:抢操作在 Redis 缓存层完成,通过原子递减的操作来更新红包个数,个数递减为 0 后就说明抢光了。
-
拆红包:拆红包时,首先会实时计算金额,一般是通过二倍均值法实现(即 0.01 到剩余平均值的 2 倍之间)。
-
红包记录:用户获取红包金额后,通过数据库的事务操作累加已经领取的个数和金额,并更新红包表和记录表。
-
转账:为了提升效率,最终的转账为异步操作,这也是为什么在春节期间,红包领取后不能立即在余额中看到的原因。
红包系统详细设计
红包整体架构
如下图所示,是微信红包的系统架构
总体是三层架构:
-
首先是微信统一接入层,下面是微信红包系统 API,包括发、抢、拆、查红包详情、查红包用户列表。
-
接入层下面,是封装微信红包关键业务的逻辑服务;
-
业务下面数据存储层,微信红包最主要的数据是订单数据,包括发红包订单和拆红包订单两部分。
数据存储层的冷热分离设计:微信红包数据的访问热度,随着时间流逝会急剧降低,也就是数据的访问时间段非常集中,一般红包发出三天后,99% 的用户不会再去点开这个红包了。因此微信红包系统采取按时间做冷热数据分离,降低数据的存储成本,同时提升了热数据的访问性能。
除了在线计算的三层架构,还有离线处理的数据分析。
数据分析平台用于对红包数据的分析计算,比如朋友圈里的文章,统计从 某年 1 月 1 日到 2017 年 1 月一个用户总共抢红包的金额,在全国的排名情况,发红包数最多的城市等。
数据分析平台另外一个作用就是对账,红包的订单和微信支付的订单需要对账,以保证最终资金的一致性;
-
订单的数据和订单的 cache 需要做对账,以保证数据的完整性;
-
订单数据和用户的收发记录需要对账,以保证用户列表完整性。
高并发常用解决方案
普通商品秒杀系统,解决高并发问题的方案,大致有以下2种:
1- 使用内存替代实时的DB
将实时扣库存的行为上移到内存Cache中,内存Cache操作成功直接给Server返回成功,然后异步落DB持久化。
这个方案的优缺点如下:
优点:用内存操作替代磁盘操作,提高了并发性能。
缺点:在内存操作成功DB持久化失败,或者内存Cache故障的情况下,DB持久化会丢数据,不适合微信红包这种资金交易系统。
2- 使用乐观锁替代悲观锁
什么是悲观锁呢?
所谓悲观锁,是关系数据库管理系统里的一种并发控制的方法。它可以阻止一个事务以影响其他用户的方式来修改数据。如果一个事务执行的操作对某行数据应用了锁,那只有当这个事务把锁释放,其他事务才能够执行与该锁冲突的操作。对应于上文分析中的"并发请求抢锁"行为。
什么是乐观锁呢?
所谓乐观锁,它假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自影响的那部分数据。在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务又修改了该数据。如果其他事务有更新的话,正在提交的事务会进行回滚。
乐观锁分为DB乐观锁和 JVM CAS 乐观锁。
DB乐观锁的具体应用方法,是在 DB 的"库存"记录中维护一个版本号。
商品"秒杀"系统中,在更新"库存"的操作进行前,先去 DB 获取当前版本号。
在更新库存的事务提交时,检查该版本号是否已被其他事务修改。如果版本没被修改,则提交事务,且版本号加 1;如果版本号已经被其他事务修改,则回滚事务,并给上层报错。
DB乐观锁可以提高DB的并发处理能力,但是如果应用于微信红包系统,则会存在下面三个问题:
-
如果拆红包采用乐观锁,那么在并发抢到相同版本号的拆红包请求中,只有一个能拆红包成功,其他的请求将事务回滚并返回失败,给用户报错,用户体验完全不可接受。
-
如果采用乐观锁,将会导致第一时间同时拆红包的用户有一部分直接返回失败,反而那些"手慢"的用户,有可能因为并发减小后拆红包成功,这会带来用户体验上的负面影响。
-
如果采用乐观锁的方式,会带来大数量的无效更新请求、事务回滚,给 DB 造成不必要的额外压力。
JVM CAS 乐观锁方案
出于性能原因,微信红包系统使用 JVM CAS 乐观锁,而不是DB乐观锁的方式解决并发抢锁问题。
微信红包的金额是在拆红包时实时计算的,而不是预先分配的。系统会在拆红包时取0.01到剩余平均值*2之间的数值作为红包金额。
这种实时计算的方式基于JVM 内存进行,不需要额外的存储空间,且计算效率很高。
同时,为了保证操作的原子性,拆包过程中使用了CAS(Compare-and-Swap)算法,确保每次只有一个并发用户拆包成功。如果拆包CAS失败,系统会自动进行重试,但也可能在重试过程中被其他用户抢得先机而空手而归。
如何保证同一个红包由同一个节点去拆包呢?将同一个红包 ID 的所有请求 stick 到同一台 Server,这个后面介绍。
以上内容比较复杂,尼恩架构团队会录制成为架构视频, 帮助大家做架构拿高薪。
微信红包系统的高并发解决方案
综合上面的分析,我们的红包系统针对相应的技术难点,采用如下3个方案,解决高并发问题。
1- 分而治之:系统架构设计垂直Set化。
什么是Set(单元)化架构呢?
单元化架构是一种将系统划分为多个独立的、自包含的单元的部署架构,每个单元都能够完成所有业务操作,包含所有业务所需的服务以及分配给该单元的数据。这种架构将单元作为部署的基本单位,在全站所有机房中部署多个单元,每个机房内的单元数目不固定,但任一单元均部署系统所需的全部应用。数据则是全量数据按照某种维度划分后的一部分。与传统意义上的SOA(服务化)架构不同,单元化架构下,服务仍然是分层的,但每一层中的任意一个节点都属于且仅属于某一个单元,上层调用下层时,仅会选择本单元内的节点。
通俗的理解为:
单元化架构,简单来说,就是把系统拆分成若干个独立的单元,每个单元都包含了完成业务操作所需的所有服务和数据。这些单元可以独立部署、管理和监控,就像一个个小房子,每个房子都有自己的客厅、卧室和厨房(服务),也有自己的食物和水(数据)。
如下图,是一种单元化架构设计。
微信红包用户发一个红包时,微信红包系统生成一个 ID 作为这个红包的唯一标识。
接下来这个红包的所有发红包、抢红包、拆红包、查询红包详情等操作,都根据这个 ID 关联。
红包系统根据这个红包 ID,按一定的规则(如按 ID 尾号取模等),垂直上下切分。
切分后,一个垂直链条上的逻辑 Server 服务器、DB 统称为一个 SET。
各个 SET 之间相互独立,互相解耦。并且同一个红包 ID 的所有请求,包括发红包、抢红包、拆红包、查详情详情等,垂直 stick 到同一个 SET 内处理,高度内聚。
通过这样的方式,系统将所有红包请求这个巨大的洪流分散为多股小流,互不影响,分而治之,如下图所示。
这个方案解决了同时存在海量事务级操作的问题,将海量化为小量。
2- 解决DB并发:逻辑Server层将请求排队。
红包系统是资金交易系统,DB 操作的事务性无法避免,所以会存在"并发抢锁"问题。但是如果到达 DB 的事务操作(也即拆红包行为)不是并发的,而是串行的,就不会存在"并发抢锁"的问题了。
按这个思路,为了使拆红包的事务操作串行地进入 DB,只需要将请求在 Server 层以 FIFO(先进先出)的方式排队,就可以达到这个效果。从而问题就集中到 Server 的 FIFO 队列设计上。
红包系统设计了分布式的、轻巧的、灵活的 FIFO 队列方案。其具体实现如下:
1)将同一个红包 ID 的所有请求 stick 到同一台 Server。
上面 SET 化方案已经介绍,同个红包 ID 的所有请求,按红包 ID stick 到同个 SET 中。
不过在同个 SET 中,会存在多台 Server 服务器同时连接同一台 DB(基于容灾、性能考虑,需要多台 Server 互备、均衡压力)。
为了使同一个红包 ID 的所有请求,stick 到同一台 Server 服务器上,在 SET 化的设计之外,微信红包系统添加了一层基于红包 ID hash 值的分流,如下图所示。
以上内容比较复杂,尼恩架构团队会录制成为架构视频, 帮助大家做架构拿高薪。
2)设计单机请求排队方案。
将 stick 到同一台 Server 上的所有请求在被接收进程接收后,按红包 ID 进行排队。
然后串行地进入 worker 进程(执行业务逻辑)进行处理,从而达到排队的效果,如下图所示。
3)增加 Redis缓存 控制并发。
为了防止 Server 中的请求队列过载导致队列被降级,从而所有请求拥进 DB,系统增加了与 Server 服务器同机部署的 Redis ,用于控制拆同一个红包的请求并发数。
具体来说,利用 Redis 的 CAS 原子累增操作,控制同时进入 DB 执行拆红包事务的请求数,超过预先设定数值则直接拒绝服务。用于 DB 负载升高时的降级体验。
通过以上三个措施,系统有效地控制了 DB 的"并发抢锁"情况。
3- 系统性能稳定性保障:双维度分库分表设计。
红包系统的分库表规则,初期是根据红包 ID 的 hash 值分为多库多表。
随着红包数据量逐渐增大,单表数据量也逐渐增加。而 DB 的性能与单表数据量有一定相关性。当单表数据量达到一定程度时,DB 性能会有大幅度下降,影响系统性能稳定性。
采用冷热分离,将历史冷数据与当前热数据分开存储,可以解决这个问题。
处理微信红包数据的冷热分离时,系统在以红包 ID 维度分库表的基础上,增加了以循环天分表的维度,形成了双维度分库表的特色。
具体来说,就是分库表规则像 db_xx.t_y_dd 设计,其中,xx/y 是红包 ID 的 hash 值后三位,dd 的取值范围在 01~31,代表一个月天数最多 31 天。
通过这种双维度分库表方式,解决了 DB 单表数据量膨胀导致性能下降的问题,保障了系统性能的稳定性。同时,在热冷分离的问题上,又使得数据搬迁变得简单而优雅。
综上所述,微信红包系统在解决高并发问题上的设计,主要采用了 SET 化分治 、请求排队 、双维度分库表等方案,使得单组 DB 的并发性能大幅度提升,取得了很好的效果。
红包分配算法
抢红包后,我们需要进行拆红包,接下来我们讨论一下红包系统的红包分配算法。
红包金额分配时,由于是随机分配,所以有两种实现方案:实时拆分和预先生成
1- 实时拆分
实时拆分,指的是在抢红包时实时计算每个红包的金额,以实现红包的拆分过程。
这个对系统性能和拆分算法要求较高,例如拆分过程要一直保证后续待拆分红包的金额不能为空,不容易做到拆分的红包金额服从正态分布规律。
2- 预先生成
预先生成,指的是在红包开抢之前 已经完成了红包的金额拆分,抢红包时只是依次取出拆分好的红包金额。
这种方式对拆分算法要求较低,可以拆分出随机性很好的红包金额,但通常需要结合队列使用。
3- 二倍均值法
综合上述优缺点考虑,以及微信群聊中的人数不多(目前最高 500 人),所以我们采用实时拆分的方式,用二倍均值法来生成随机红包,只满足随机即可,不需要正态分布。
使用二倍均值法生成的随机数,每次随机金额会在 0.01 ~ 剩余平均值*2 之间。
假设当前红包剩余金额为 10 元,剩余个数为 5,10/5 = 2,则当前用户可以抢到的红包金额为:**0.01 ~ 4 **元之间。
以下是使用Java实现的二倍均值算法,在红包分配场景中。
java
public class RedPacketDistribution {
public static List<BigDecimal> distribute(BigDecimal totalAmount, int totalCount) {
// 校验总金额是否为正
if (totalAmount.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("Invalid parameter, totalAmount must be positive.");
}
// 校验红包总数是否至少为1
if (totalCount < 1) {
throw new IllegalArgumentException("Invalid parameter, totalCount must be at least 1.");
}
List<BigDecimal> redPacketList = new ArrayList<>(totalCount);
BigDecimal remainingAmount = totalAmount.multiply(BigDecimal.valueOf(2)); // 初始化为总金额的两倍
ThreadLocalRandom random = ThreadLocalRandom.current(); // 使用线程安全的随机数生成器
for (int i = 0; i < totalCount - 1; i++) {
// 随机获取一个幸运值,范围在[0, 当前剩余金额)
BigDecimal luckValue = BigDecimal.valueOf(random.nextDouble()).multiply(remainingAmount);
// 计算并添加实际分配的红包金额(幸运值的一半)
BigDecimal amount = luckValue.divide(BigDecimal.valueOf(2), BigDecimal.ROUND_HALF_DOWN);
redPacketList.add(amount);
// 更新剩余金额
remainingAmount = remainingAmount.subtract(luckValue);
}
// 最后一个红包直接拿走剩余的全部金额,确保总和正确
redPacketList.add(remainingAmount.divide(BigDecimal.valueOf(2), BigDecimal.ROUND_HALF_DOWN));
return redPacketList;
}
public static void main(String[] args) {
BigDecimal totalAmount = new BigDecimal("100.00"); // 红包总金额
int totalCount = 10; // 红包个数
List<BigDecimal> redPackets = distribute(totalAmount, totalCount);
System.out.println("Red packet distribution:");
for (BigDecimal amount : redPackets) {
System.out.printf("%.2f, ", amount);
}
}
}
以上内容比较复杂,尼恩架构团队会录制成为架构视频, 帮助大家做架构拿高薪。
红包系统高可用架构设计
红包业务量级的高速发展,对后台系统架构的可用性要求越来越高。在保障微信红包业务体验的前提下,红包后台系统进行了一系列高可用方面的优化设计。
要保障红包系统架构的高可用性,可以从多个方面进行优化和设计。以下是一些详细说明:
-
系统架构设计:
- 信息流、业务流、资金流分离:红包系统由信息流、业务流、资金流三部分组成,这三部分在组织架构上应由不同的后台团队完成,以提高系统的模块化和可维护性。
- 微服务架构:采用微服务架构,将红包系统拆分成多个独立的服务,每个服务负责特定的功能,这样可以提高系统的可扩展性和容错性。
- 无状态设计:确保红包系统的服务是无状态的,即服务不需要保存用户的会话信息,这样可以提高系统的并发处理能力和可靠性。
-
高可用性策略:
- 冗余部署:将系统的关键组件部署在多台服务器上,通过搭建主备或者集群的架构来实现冗余。当主服务器出现故障时,备用服务器能够自动接管,保证系统的可用性。
- 负载均衡:通过将流量分发到多台服务器上,均衡系统的请求负载,提高系统的可用性和扩展性。负载均衡可以通过硬件(如负载均衡器)或者软件(如Nginx、HAProxy)实现。
- 服务容器化:使用容器技术(如Docker、Kubernetes)将应用程序与其依赖项打包为容器,实现快速部署、弹性扩展和自动化管理。容器化可以提高系统的可移植性、弹性和可伸缩性,从而增加系统的高可用性。
-
数据保障:
- 数据备份与恢复:定期对关键数据进行备份,并确保备份的数据可用性。这样,在发生数据丢失或损坏时,可以快速恢复数据,减少系统停机时间。
- 分布式缓存:使用多级缓存技术(如Redis、Memcached等),将数据分别存储在内存缓存、本地缓存和分布式缓存中,以提高访问速度和降低数据库压力。
- 数据一致性:使用分布式锁技术来保护红包的领取操作,确保每个用户只能领取一次红包。同时,为了保证数据一致性,可以采用消息队列等技术实现请求的异步处理和结果的返回。
-
监控与告警:
- 系统监控:对红包系统的关键指标(如请求量、响应时间、错误率等)进行实时监控,以便及时发现和解决潜在问题。
- 告警机制:设置合理的告警阈值,当系统出现异常情况时,及时发送告警通知给相关人员,以便快速响应和处理。
-
安全性保障:
- 访问控制:实施严格的访问控制策略,确保只有授权的用户才能访问红包系统。
- 数据加密:对敏感数据进行加密存储和传输,以防止数据泄露和篡改。
- 安全审计:定期对红包系统进行安全审计和漏洞扫描,及时修复发现的安全漏洞和隐患。
通过以上措施的综合应用,可以大大提高红包系统架构的高可用性,确保系统在高峰时段能够稳定、高效地运行。
1 - 系统可用性影响因素
系统的可用性影响因素可分成两类:
-
一类计划外;
计划外包含很多因素,系统用到的所有东西都可能产生故障,都可能成功影响可用性的因素。从这个角度上来讲,可以说故障是无法避免的,系统的运作一定会产生故障,尤其是服务器有成千上万个的时候。
-
一类计划内。
计划内的影响因素,主要有与升级相关、运维相关的操作,以及日常的备份等。这一类影响因素,通过精细地设计方案,是可以避免对可用性造成影响的。
2 - 红包系统可用性设计方向
基于上面两个分析结论,可以总结出红包系统的可用性的设计方向。
1.在不能避免意外故障的情况下,尽可能降低出现意外故障时对可用性的影响。
2.绝大多数计划内的日常维护可以通过方案的设计避免影响可用性,其中平行扩容特指关于存储层的平行扩容。
下面从降低故障影响和微信红包系统的平行扩容两方面进行分析。
首先是降低意外故障的影响,重点讲解订单存储层在订单 DB 故障的情况下如何降低对红包系统可用性的影响。
3 - 业务逻辑层 - 部署方案设计
首先是业务逻辑层的部署方案。业务逻辑层是无状态的,微信红包系统的业务逻辑层,部署在两个城市,即两地部署,每一个城市部署至少三个园区,即三个 IDC。并且每个服务需要保证三个 IDC 的部署均衡。另外,三个 IDC 总服务能力需要冗余三分之一,当一个 IDC 出现故障时,服务能力仍然足够。从而达到 IDC 故障不会对可用性产生影响。
4 - 业务逻辑层 - 异步化设计
如下图所示,微信红包的某些步骤不实时完成也不会影响用户对红包业务可用性的体验。
比如拆红包,正常的业务流程很长,但关键步骤只有订单相关的几步。
至于转零钱、写红包记录等操作不需要实时。
用户抢到红包时,一般不会实时去钱包查看微信零钱,而是在微信群中点开消息查看本次抢到金额和他人抢红包金额。
所以拆红包时只需要从 cache 查询用户是否拆过红包,然后写入拆红包的订单记录,更新发红包订单,其他的操作都可以异步化。
当然,不是每个业务都可以进行异步化设计,需要进行业务分析,判断是否存在非关键步骤之外的事情可以将其异步化,并通过异步对账保证最终一致。
经过上述分析之后,可以采用如下思路与方案:
实现思路:
1.最简关键路径:简化发红包、拆红包核心流程路径,重点关注与订单相关流程。
2.快慢分离:将核心流程与其他非关键步骤分离。
方案:
1.写用户记录、零钱入账使用MQ异步执行
2.增加对帐机制保障最终一致。
如上图所示,微信红包的某些步骤不实时完成也不会影响用户对红包业务可用性的体验。
比如拆红包,正常的业务流程很长,但关键步骤只有订单相关的几步。
至于转零钱、写红包记录等操作不需要实时。
用户抢到红包时,一般不会实时去钱包查看微信零钱,而是在微信群中点开消息查看本次抢到金额和他人抢红包金额。
所以拆红包时只需要从 cache 查询用户是否拆过红包,然后写入拆红包的订单记录,更新发红包订单,其他的操作都可以异步化。
当然,不是每个业务都可以进行异步化设计,需要进行业务分析,判断是否存在非关键步骤之外的事情可以将其异步化,并通过异步对账保证最终一致。
以上内容比较复杂,尼恩架构团队会录制成为架构视频, 帮助大家做架构拿高薪。
5 - 订单存储层 - 故障自愈
为解决 DB 间的相互影响,需要将 DB 间相互隔离,订单存储层 SET 化。
SET 化指订单 DB 和订单接入 SERVER 垂直 stick 一起。业务逻辑层访问订单时,根据订单倒数第二、三位数字找到所属订单 SET,一个 SET 的请求不能路由到其他 SET。
通过 SET 化得到的好处是,控制 DB 连接数、隔离故障影响和分流并发。
如上图所示,所设尾号 90-99 的 SET 故障时,如果业务逻辑服务后续不再生成属于这个 SET 的订单,那后续的业务就可以逐渐恢复。
也就是在发生故障时,业务逻辑层发布一个版本,屏蔽故障号段的单号生成,就可以恢复业务。
进一步想,除了人为发版本,有没有方法可以让 DB 故障时自动恢复?
在 DB 故障导致业务失败时,业务逻辑层可获取到故障 DB 的号段,在发红包时,将这些故障的号段,换一个可用的号段就可恢复业务。
订单号除了最后三位,前面的部分已能保证该红包唯一性,后面的数字只代表着分库表信息,故障时只需要将最后三位换另外一个 SET 便可自动恢复。
完成这个设计后,即使 DB 出现故障,业务的可用性也不会有影响。
这里还有一点,新的发红包请求可避免 DB 故障的影响,但那些故障之前已发出未被领取的红包,红包消息已发送到微信群,单号已确定,拆红包时还是失败。
对这种情况,由于不会有增量,采用正常的主备切换解决即可。
6 - 订单存储层 - 平行扩容设计
红包系统的高可用架构设计,主要包括了部署设计、SET 化设计、异步化设计、DB 故障自愈能力建设、平行扩容设计。
以上内容比较复杂,尼恩架构团队会录制成为架构视频, 帮助大家做架构拿高薪。
总结
红包系统是一个高并发的资金交易系统,最大的技术挑战是保障并发性能与资金安全。
这种全新的技术挑战,传统的"秒杀"系统设计方案已不能完全解决。
在分析了业界"秒杀"系统解决方案的基础上,红包系统采用了 SET 化、请求排队串行化、双维度分库表等设计,形成了独特的高并发、资金安全系统解决方案。
说在最后:有问题找老架构取经
超高并发红包架构,一定是一个超级牛掰的简历亮点项目,黄金项目,稍微晚点把全量的架构方案和视频进行发布。
这个项目写入简历,面试的时候如果大家能对答如流,如数家珍,基本上 面试官会被你 震惊到、吸引到。
最终,让面试官爱到 "不能自已、口水直流"。offer, 也就来了。
在面试之前,建议大家系统化的刷一波 5000页《尼恩Java面试宝典》V174,在刷题过程中,如果有啥问题,大家可以来 找 40岁老架构师尼恩交流。
另外,如果没有面试机会,可以找尼恩来帮扶、领路。
- 大龄男的最佳出路是 架构+ 管理
- 大龄女的最佳出路是 DPM,
女程序员如何成为DPM,请参见:
DPM (双栖)陪跑,助力小白一步登天,升格 产品经理+研发经理
领跑模式,尼恩已经指导了大量的就业困难的小伙伴上岸。
前段时间,领跑一个40岁+就业困难小伙伴拿到了一个年薪100W 的offer,小伙伴实现了 逆天改命。
另外,尼恩也给一线企业提供 《DDD 的架构落地》企业内部培训,目前给不少企业做过内部的咨询和培训,效果非常好。
尼恩技术圣经系列PDF
- 《NIO圣经:一次穿透NIO、Selector、Epoll底层原理》
- 《Docker圣经:大白话说Docker底层原理,6W字实现Docker自由》
- 《K8S学习圣经:大白话说K8S底层原理,14W字实现K8S自由》
- 《SpringCloud Alibaba 学习圣经,10万字实现SpringCloud 自由》
- 《大数据HBase学习圣经:一本书实现HBase学习自由》
- 《大数据Flink学习圣经:一本书实现大数据Flink自由》
- 《响应式圣经:10W字,实现Spring响应式编程自由》
- 《Go学习圣经:Go语言实现高并发CRUD业务开发》
......完整版尼恩技术圣经PDF集群,请找尼恩领取
《尼恩 架构笔记》《尼恩高并发三部曲》《尼恩Java面试宝典》PDF,请到下面公号【技术自由圈】取↓↓↓