我们做了什么业务背景在春节活动中,抖音结合了视频和春节红包,用户可以通过以下方式给粉丝朋友发红包拍摄视频送上祝福。商业玩法整个活动分为B2C和C2C两种玩法。下面简单介绍一下这两种玩法的过程。春节红包雨活动有一定概率获得红包补贴。用户可以在领取补贴后直接跳转到相机页面,也可以在拍完视频后跳转到相机页面。在相机页面,用户拍摄完视频后会看到一个红包挂件,在挂件中可以看到补贴已经发放。用户选择补贴后点击下一步完成提交,即可完成视频红包的发放。图1春节红包下雨事件图2红包补贴图3红包挂件图4b2c红包发送标签页C2C红包在C2C红包游戏中,用户拍摄视频点击挂件,填写金额和红包数量,并选择红包领取范围,点击发送红包会拉出收银台。用户完成支付后,点击下一步发布视频,完成C2C红包发放。图1C2C红包发送Tab页图2支付界面图3红包支付后挂件显示红包领取B2C和C2C的红包领取流程是一样的。当用户在抖音上观看视频时遇到有红包的视频,视频下方会出现一个红包按钮。用户点击红包领取,会弹出红包封面。用户点击红包封面打开红包后,即可领取红包,红包领取成功。之后会弹出红包的弹窗。在理赔结果中,用户可以看到收到的金额,并跳转到理赔详情页面。在领奖详情页面,可以看到其他用户领到红包的幸运值。图1红包视频图2红包封面图3红包领取结果图4红包领取详情,这两类红包有一些相似的服务,也有很多不同的服务。同时,它们都包含了发红包和收红包这两个操作。在不同点上,例如,B2C派发红包需要使用补贴发放,而C2C派发红包则需要用户完成支付。B2C红包用户领取后需要提现,C2C红包用户领取后直接找零。因此,需要设计一个通用的红包系统来支持多种类型的红包。另外,对于红包系统本身来说,除了收发红包之外,还涉及到一些红包信息的查询以及各种状态机的推进。如何划分这些功能模块也是需要考虑的一点。发放大额流量补贴前面提到,B2C红包游戏首先会发放补贴。春节期间,每次红包雨都会有大量用户参与。如果这些流量直接上数据库,需要大量的数据库资源,春节期间数据库资源非常紧缺。如何减少这部分资源呢?消费也是一个需要考虑的问题。红包领取方案的选择在红包业务中,领取是一个高频操作。在设计接收方式时,需要考虑业务场景中是否会出现一个红包被多个用户同时领取的情况。多个用户同时领取同一个红包可能会造成热点账号问题,成为系统性能的瓶颈。解决账号热点问题也有多种方案,需要我们根据视频红包的业务场景特点选择合适的方案。稳定性和容灾在这个春节活动中,有B2C和C2C两个业务流程,其中每个业务流量环节都依赖很多下游服务和基础服务。在这样的大型活动中,如果出现黑天鹅事件,如何快速止损,减少对系统的整体影响,是必须要考虑的问题。资金安全保障春节期间,B2C将发放大量红包补贴。如果补贴超发,或者补贴的核销存在问题,多次核销的补贴将造成大量的资金损失。此外,C2C还涉及用户资金的流入流出。如果用户收到红包后发现钱减少了,也可能造成大量的客户投诉和资金损失。因此,有必要为财务安全做好充分的准备。红包系统的压力测试在传统的压力测试方法中,我们通常会针对某个高流量的接口进行压力测试,从而获得系统的瓶颈。但是在红包系统中,用户的发送和查看是同时进行的,这些接口也是相互依赖的。比如需要先发红包。查看收据详细信息。如果使用传统的单接口压测方式,先mock数据会非常困难,而且由于涉及实名制,需要专门生成相应的支付压测数据,很难获取到真实数据通过单接口、单接口的压力测试,找出系统的瓶颈。如何对系统进行全链路压测,准确获取系统瓶颈,也是我们需要解决的问题。我们如何设计通用的红包系统?对于红包系统,核心操作包括红包的发送、接收和无人认领的退款。此外,我们还需要查看一些红包信息和领取信息。同时,对于发送、接收、退款这三个核心操作,我们需要维护它们的状态。同时,在我们的业务场景中,还有B2C专用补贴的发放,我们也需要维护补贴的状态。经过上面红包系统的初步介绍,大家可以看到红包的几个功能模块,包括发放、收款、退款、补贴发放,以及各种信息查询,还有状态机维护等。红包的功能,我们开始划分红包的模块。划分的原则是功能是内聚的。每个系统只处理一个任务,方便后续的系统开发和迭代,以及问题排查。API网关层只进行简单的代理处理异步任务拆解。读写分离,红包和红包查询的核心操作分为两个服务划分模块红包网关服务HTTPAPI网关,外接client和h5,内部封装各系统rpc接口,限流,权限控制、降级等功能红包核心服务主要承载了红包的核心功能,包括红包的发放、接收、退款,以及发放红包补贴、维护红包状态机、提升红包状态红包。补贴信息红包异步服务主要承载红包异步任务,保证状态机的流转,包括红包转账、红包退款、红包补贴状态提升。红包基础服务主要承载各种红包系统的公共调用,如DB、redis、tcc操作、公共常量和工具等,相当于红包的基础工具包。红包对账服务主要承载红包与财务的对账逻辑。根据天河金融对账整体架构,整个视频红包的系统架构如图所示。展示大流量补贴发放及处理及奖励同步发放在红包补贴发放环节流程中,为了应对春节期间的大流量,整个环节流程也经历了数次方案迭代。在最初的方案设计中,我们按照同步补贴发放流程进行处理。上游链路调用红包系统接口发放优惠券。优惠券发放成功后,用户感知优惠券发放成功,即可使用优惠券发放红包。原方案整体流程如下图所示:上述方案存在的一个问题是,春节期间,整个链路需要能够处理活动期间的总流量,流量最终会打到数据库,春节期间数据库的资源也是供不应求。.异步发放奖励为了解决同步发放奖励的问题,整体流程通过MQ改为调峰,从而减轻下游流量压力,相当于由同步改为异步。用户参与活动后,首先会向客户端发放一个加密的Token,用于在客户端展示和与服务端交互。活动异步凭证发放方案解决了下图大流量的问题,但是相应地引入了其他问题。在初始方案中,用户的红包补贴会先存储在红包系统中,后续用户查询验证补贴,我们可以在红包数据库中找到对应的记录,但是在异步的方式下,整个补贴预计耗时10分钟,用户在APP界面感应到优惠券后,可立即开始使用补贴发放视频。红包,或者去红包挂件查看自己收到的红包补贴,但是此时补贴还没有记录在红包系统中。最终解决方案为了解决以上问题,我们修改了红包补贴的视频红包发放和红包补贴查询的整个逻辑。这笔补贴只有在图书馆成功后才能用于发放红包。另外,对于查询接口,我们无法感知所有的补贴是否全部到账,所以每次查询都需要去奖励发放方查询完整的Token列表,同时还需要查询用户补贴情况数据库。对这两部分数据进行合并操作,才能得到完整的补贴名单。上述过程中,为了解决MQ异步延迟的问题,我们在用户请求时主动进入账户,用户主动操作包括使用补贴发放红包和查询补贴。为什么我们只有在补贴发红包的时候才入账?但是查询补贴时不输入账号怎么办?由于用户的查询行为属于高频行为,涉及到批量操作,在操作DB之前我们无法感知补贴是否已经记录,所以会涉及到DB的批量处理,甚至每次都需要重复用户来查询这个操作会造成DB资源的大量浪费。补贴计入是一种低频、单一的补贴操作。我们只需要在用户注销的时候记入,可以大大减轻数据库的压力,节省数据库资源。领取红包方案的选择在视频红包领取的技术方案中,我们也有一些选择和注意事项,在这里分享给大家。悲观锁解决方案选项1也是最常见的想法。当用户收到红包后,将红包锁定在数据库中,然后扣取金额,然后解除锁定,完成整个红包领取。这种方案的优点是清晰明了,但是这种方案的问题是当多个用户同时过来领取红包时,会导致数据库行锁发生冲突,需要排队等候。整体系统性能。同时,上游长时间没有收到反馈,导致超时,用户端可能会不断重试,导致整体数据库连接耗尽,导致系统崩溃。红包预分方案方案一的问题是多个用户同时认领会造成锁冲突。解锁锁冲突可以拆分到更细的粒度,从而提高单个红包的并发度。具体方案如下:方案二对红包的发放流程进行了改动。在发出红包时,红包会被预先拆分成多个红包,从而完成细粒度锁定当用户收到红包时,从之前的单个红包锁竞争到现在的分配多个红包锁。所以,在收红包的时候,问题就变成了如何把红包发给用户。一个常用的思路是,当用户请求领取红包时,通过redis的自增方式生成序号,序号对应应该领取哪个红包。但是这种方式对redis的依赖很大。当redis网络波动或redis服务异常时,需要降级查询DB未收到的红包获取序列号。整体实现比较复杂。在最终的解决方案中,在视频红包的场景下,整个业务流程是用户拍摄视频发红包,然后在视频推荐流中刷视频时,就会触发领取。与微信、飞书等群聊场景相比,视频红包中同一个红包的并发领取数并不是很高,因为用户刷视频的操作和推流本身就完成了流量的打散,所以对于视频红包,据说收到的并发数不会很高。从业务的角度来说,在需求实现上,我们需要能够获取到无人领取的红包数量,在用户完成领取后发送给用户展示。方案一获取红包库存很方便,方案二获取库存比较麻烦。另外,从系统开发的复杂度和容灾的角度来看,方案一是相对更合适的选择。但我们需要应对选项一中的风险。我们需要其他方法来保护数据库资源并最大限度地减少锁冲突。具体方案如下:红包redis限流为了尽可能少的减少DB锁冲突,先根据红包个数限流,每次允许剩余红包数*1.5个请求通过。限流返回特殊错误码,前端最多轮换10次。在请求过多的情况下,采用这种方式慢慢处理内存队列。除了redis限流,为了减少DB锁,我们加了一个红包内存锁,对于单个红包,只有请求获得内存锁才能继续请求DB,这样就不会产生冲突了DB锁提前迁移到内存处理,内存资源相比DB资源非常便宜。当请求量过大时,我们可以横向扩容。为了实现内存锁,我们做了几处改动。首先要保证同一个红包请求可以发送到同一个tce实例。这里我们调整了网关层的路由。在网关层调用下游服务时,会根据红包编号执行路由策略,保证相同编号的请求可以发送。到同一个实例。另外,我们在红包系统的核心服务中实现了一套基于通道的内存锁,收到红包后会释放对应的内存锁。另外,为了防止锁的内存占用过大或者没有及时释放,我们设置了一个定时任务来定时处理。异步转账从接口耗时来看,转账是一个涉及到与第三方支付机构交互的耗时操作,会存在跨机房请求,响应时延长。异步转账可以减少领取红包的界面另外,从用户感知的角度来说,用户更关心的是点击打开红包后是否领取成功。至于余额是否同步到账户,用户的感知并没有那么强烈。此外,转会本身也存在问题。从转账到成功转账的过程,让转账异步,基本不会影响用户对稳定性和容灾的感知。对于整个红包系统的容灾,我们主要围绕接口限流、业务降级和多重机制来保证状态机的推进。下面分别介绍方法:接口限流接口限流是一种常见的容灾手段,用于保护系统只处理可接受范围内的请求,防止外部请求过多导致系统宕机。坍塌。在限制接口流量之前,我们首先需要和上游、下游、产品进行通信,得到一个预估的红包发放量和接收量,然后根据发放的红包量,按模块对整个链路的整体流量进行梳理并收到。以下是我们当时整理的一个b2c全链接的请求量。之后有每个模块的请求量,汇总后可以得到每个接口的流量请求,红包系统的每个服务,下游依赖的每个服务。这时候限流就比较方便了。业务降级核心依赖降级春节期间,红包系统整个链路所依赖的服务有很多。这些下游链路依赖又可以分为核心依赖和非核心依赖。当下游核心业务异常时,可能导致某条链路不可用。这时候可以直接在API层降级,返回更友好的复制提示,待下游服务恢复后再释放。例如在C2C的发红包过程中,用户需要先完成支付再发红包。如果财务支付流程异常或者支付成功状态长时间未完成,会导致用户支付后红包发送失败,也会导致前端停止查询状态轮训红包过多,导致请求量激增,造成服务压力,甚至影响B2C红包发放和询价。此时C2C红包发放可以通过降级接口降级返还,减轻服务压力,减少对其他业务逻辑的影响。非核心依赖降级除了核心依赖,红包系统还有一些非核心的下游依赖。对于这些依赖,如果服务出现异常,我们可以通过降低用户体验来保证服务的可用性。比如我们在4.2中提到的,用户在发送B2C红包之前,需要获取所有可用的红包补贴。我们会去rewardissuer查询所有的Token列表,然后查询我们自己的DB,然后进行merge返回。如果获取Token列表的接口异常,我们可以降级,只返回自己DB中的补贴数据,可以保证用户在这种情况下依然可以发红包,只影响部分补贴的展示影响整个红包发送链接。多重机制保证状态机的推进。在红包系统中,如果一个订单长时间没有达到最终状态,比如用户收到红包后很久没有收到红包,或者用户没有收到C2C的红包信封很久了,也很久没有给用户退款了。引起用户的客户投诉。因此,需要保证每个订单在系统中的状态能够及时、准确的推送到最终状态。这里我们有几种方式来保证,第一种是回调,在依赖方系统的订单处理完毕后,会及时通知红包系统,也是最及时的方式。但是,仅依赖回调可能会导致依赖方异常或网络抖动导致回调丢失。这时我们会在红包的每个阶段向红包系统发送一个mq,每隔一定的时间消费一次mq,主动查询依赖方的订单状态进行更新。最后,我们将为底线的每个状态机安排一个计划任务。如果定时任务多次执行后仍未达到最终状态,lark会通知,及时进行人工干预,发现问题。资金安全保证交易的幂等性在编程中,幂等性是指一个请求执行任意多次都会产生与一次执行相同的影响。在资金安全上,通过订单号进行相应的幂等逻辑处理,可以防止资金流失的发生。具体来说,在红包系统中,我们使用订单号的唯一key来保证接口在红包的派发、领取和退款上是幂等的。另外,红包系统的补贴发放接口是幂等的。如果同一订单号多次申请补贴,我们需要保证只发放一张代金券。实现幂等的方案有很多,包括通过数据库或者redis。最可靠的方法是实现数据库的唯一键冲突,但是这种方法在数据库中存在碎片化实例时会引入一些额外的问题。下面我们简单介绍一下补贴的分配情况。在业务系统设计中,我们按照uid分片方式构建业务数据库表,导致补贴分片key为uid,虽然我们也设置了红包的补贴订单号作为唯一key。但是存在一个风险,如果上游系统调用补贴分发,同一个外部订单号改变了uid,可能会导致两次请求发送到不同的数据库实例,导致唯一索引失效,造成资金损失.为了解决这个问题,我们额外引入了一个数据库,使用补贴的外部订单号作为分片键来解决这个风险。B2C红包检查除了在开发过程的系统设计中考虑相应的资金安全外,我们还需要通过对账来验证我们的系统是否存在资金安全问题。在B2C环节,整个环节主要是从补贴发放到红包领取。我们对这些链路的上下游数据进行对应的每小时hive对账。C2C红包支票在C2C环节,整个过程主要是从用户发起支付,到用户收到转账,最后退还过期红包。支付、转账、退款三个流程需要做相应的检查。同时还需要保证用户的红包发放金额大于等于红包转账金额+红包返还金额,大于等于因为从发放成功开始的整个周期红包到退款成功需要24小时以上。另外,可能会有这样的订单,正在转账中,导致出现多笔退款订单。如果要求严格相等,则无法控制对账的具体时间。红包系统的压力测试上文提到,红包系统的链路包含发送、检查等多个接口,需要模拟用户的真实行为进行压力测试,以获得真实的性能表现系统。这里我们使用压测平台的脚本压测方式进行压测。首先,需要改造整个压测环节,与上下游沟通是否可以压测,如果不能压测,则需要进行相应的mock。另外,对于存储服务、数据库、redis、mq,确保压测目标下发正确,否则可能会影响上线。改造压测环节后,需要构建相应的压测脚本,分为B2C和C2C两种脚本。B2C红包链路压测以上就是B2C压测的整个链路。首先发放补贴,然后通过查询补贴,通过补贴发放红包。领取红包。C2C红包链路压测C2C红包涉及支付相关操作,整个链路又是一套流程,所以C2C也需要单独的脚本。在压测过程中,由于对外部系统的依赖,如果等到整个链路都OK后再一起压测,可能会出现一些未知的问题。所以,我们需要在压测OK之后,开始对整个链路进行压测。在图中与支付相关的蓝色模块中,我们添加了相应的模拟开关来控制压测的结果。开启mock开关后,会直接构造一个结果返回。模拟开关关闭后,正常会请求财经获取结果。后续规划服务搭建在上面提到的系统容灾中,如果红包核心服务发生变化,或者数据库DB机房挂掉,所有用户都会受到影响。此时只能降级返回,无法快速切换和恢复整个系统。以后考虑把服务改成一套架构。将业务服务器和对应的存储划分成一个单独的集合,每个集合只处理对应划分单元内的流量,实现多个单元之间的流量拆分和故障隔离,以及集合之间的数据备份。这样,当某个单元出现异常时,可以及时将相应单元的流量切换到备份单元。
