互联网秒杀设计

分享内容抢先看

本次分享介绍了A对抢购业务的设计实践。主要包括例如以下主题: 
1. 抢购业务介绍 
2. 详细抢购项目中的设计 
2.1. 怎样解耦前后端压力 
2.2. 怎样保证商品库存可靠 
2.3. 怎样在业务中多放对账 
3. 项目总结 
4. Q & A

抢购、闪购,从国外风靡后,国内各类站点都開始做相似的业务,我们耳熟能详的唯品会、淘宝、京东都有这类业务。抢购,很多其它出如今电商站点。那么。今天和大家一起学习下抢购业务形态的业务架构设计。

1.抢购业务介绍


我们常见的抢购业务分两种: 限时抢购、限量抢购,我简单分了下这些case,例如以下图: 
 

想必小米的抢购运营的最火爆了。每发一款新品,都限量发售,每次搞的大家心里痒痒的。记得之前还由于抢购太火爆。网站打不开,崩溃了。那么问题来了:为什么抢购总是引发RD、OP恐慌?我理解是。爆品太火爆。瞬时请求太大。导致业务机器、存储机器都在抢购高峰时扛了太多压力。

那么,我们今天以一个抢购业务场景为例。看看怎样扛住压力。做好抢购业务!如果。这时候我们接到了产品层面的需求,例如以下图: 

PM也挺呵呵的。又要有时段的要求、又要有限量的要求。大而全呐。 
只是。对于咱们RD同学,也不是问题。我们一起来看看,怎样设计业务架构。把需求满足的棒棒哒。 
首先,我们冷静的看看需求。

需求说:商品数据来自资源方。(哦,我们没有商品数据。) 
需求说:每天要有好几场抢购,每场抢购都有商品限制(哦,有点商场促销还限量甩的feel) 
需求说:商品要基于用户位置排序(哦,移动端业务嘛。这样的需求总是有的) 
需求说:balabala……

2.详细抢购项目中的设计


通过我们先行的抢购需求分析,我们画一个粗略的流程图,例如以下: 
 
我们将自身简单划为两部分:业务层、数据层,而且旁路设计一个“运营控制”环节。 
当然,数据源自第三方嘛。我们的数据层基于第三方资源数据构建。 
这时,我们来看看这个草图里几个库和几个数据流,是如何的。

首先,看看库。

数据层的“商品库”,显而易见。用于存储第三方商品数据。通过第三方推、我们拉的方式来构建这个数据库信息。数据库层的“抢购计划”库,主要由旁路的“运营控制”环节产生的数据,由运营同学来维护抢购场次、商品数量。

业务层的“抢购库”,事实上是商品库的子集。由运营同学勾选商品并配好该商品放出多少用于抢购,公布到业务层面的抢购库中。

业务层的Transaction Data,一会我们讲到与第三方对账时候,我们再说它。

2.1 怎样解耦前后端压力


我们此时回想下文件夹。文件夹中我们讲。怎样隔离前后端压力呢?做法是: 
1. 让我们业务的压力,不会传递到资源方,避免造成资源方接口压力同比增长。

所以,我们自己建了商品库,此时。第三方笑了。 
2. 业务层与数据层解耦,我们让抢购库位于业务层。让商品库位于数据层。由于我们能够想象到,抢购高峰来暂时,查询“商品还有没有?”的请求是最多的。若“有没有”这样的高频请求每次都去数据层,那我们事实上就将业务、数据耦合在一起了,那么,就有了抢购库这个子库,在业务层抗压力。(这里能够明白的是。数据层的商品库为关系型存储,业务层的抢购库为nosql的)

有了业务层的nosql(我们就用redis吧)抗高频压力。数据层的商品库笑了。 
这里就能够抛一个思想了:我们的架构设计中,须要分解压力。在互联网项目中,来自于用户的大流量不少见,这些流量终于都会落到一个地方。就看我们的设计怎样分解这个压力了,怎样避免它层层传递。抛个case,我们的水平分布业务机器,也是考虑通过水平扩展实例的方式,来分解大流量压力。 
不扯概念的东西了,我们回归我们的抢购业务。

 
有了简单的分层设计。解决的大家都操心的压力问题,我们就看看抢购业务的时序是如何的。 
我们的时序图分两个视角来说明: 
1. 商品的角度。 
2. 用户的角度; 
商品角度的时序图。从左到右:资源方、数据层、旁路-运营控制层、业务层。

例如以下图: 
 
录入商品 即商品从资源方公布到我们的数据层,形式能够是通过API、能够是通过文件传输、能够是我们去拉去。

通过我们的代码逻辑,记录到我们数据层的“商品库DB”。 
有了自建的商品库的数据,我们的运营同学就能够基于商品库设计每天的抢购场次(此事就有Web界面的事情,这里我们就不展开这块了),运营同学创建好一批抢购场次,记录在数据层的“抢购计划”这一关系型数据库中。 
运营同学创建完抢购场次后。没完事。还得应产品需求。基于商品库,配置每场抢购场次中覆盖的商品。及商品的数量。这些抢购场次内的商品配置,会简单的记录在业务层的“抢购库”中。(抢购库记录的信息较为简单,比如商品库中ID为123123的商品有100件,业务层的抢购库中仅仅存ID 123123商品运营配了在第X场抢购中有5件) 
此时,数据层的 商品库有了资源方数据、数据层的 抢购计划库中有运营配的抢购计划。业务层的抢购库中每场抢购活动中商品的情况。 
那么,业务层此时就能够基于时间。来展示运营配的抢购场次了。

业务层,怎样展示,这块就是拼装数据、前端效果了,这里也不展开了。

 
如果此事某场场次的抢购活动已经開始,我们再看看用户角度的时序图: 

用户点击某个商品的抢购button。业务层代码首先去看看抢购计划库此时是否開始(此步可缓存、也可cache在前端页面或Client。若有cache的话,此步可忽略)。

若抢购在进行中。此时业务代码须要查询商品在本次抢购中的库存还有否(高频请求,即图中“争取名额”阶段)。

 
“争抢名额”这块。一会我们细讲。先把时序图说完。 
若用户抢到了名额。就同意用户跳转到第三方的支付页面产生消费。(此时第三方笑了)。产生消费后,第三方自己的库存-1,而且能够实时、异步、完事对账的方式通知我们。

2.2 怎样保证商品库的库存可靠


此时,我们回想下文件夹。“怎样保证商品库的库存可靠”。 
我们事实上是将商品库的子库前置在业务层抗压力。那么,怎样保证大家的库存情况稳定,不会由于抢购业务。导致库存波动影响用户体验。这里就须要提一个业务RD须要关注的问题。须要做好取舍。

要么,我们保证大家看到的库存规律一致,要么,我们保证单个用户看到的库存规律一致。若保证大家看到的库存降低的规律一致,且同一时刻库存大家看到的库存都一样。

这就对系统有数据强一致性要求。须要非常大成本,还仅仅能逐渐逼近此要求要求的效果。

而我们若选择后者,仅保证单个用户看到的库存降低规律一致,虽放弃了数据强一致,但以更少的时间尽可能实现了最好的效果。所以,我们用到了用户来排队。若抢到名额了。在抢购库中的库存 –(减减)。这样单用户操作期间,能看到规律的降低。不会出现此事看剩10个,一会看还有11个的情况。这时我们说怎样内部排队,怎样来控制“查询商品在本次抢购中的库存还有否(高频请求)”这个高频请求。

 

我们构建商品维度的cache。上图中尽管说是“队列”。我们能够用redis的list来真正实现个队列,也能够通过 /–来实现。

如果商品A,运营配了20件。此事来了N多用户的请求。业务代码都会来查询cache_prefix_a_id这个队列的长度,若队列长度≤0,则有权去–(减减)抢购库的商品库存。若队列长度在20件内。则通过业务代码内的等待来等待队头的位置。然后获得抢购权限。若队列长度太长。则能够直接返回,觉得商品已被抢空。

这时插入一个运营配库的时序。便于大家理解。该时序图有具体的说明和标注。就不展开了,例如以下图: 

此时。我们能够想象,若上游用户的请求压力是N,这个N会压在业务层的抢购库,俗话说“责任止于此”:P

2.3 怎样和第三方多方对账


那么,我们回想文件夹“怎样和第三方多方对账”? 
这里就要提到“Transaction Data”这个库了。 
Transaction ID为用户维度的Session记录,用户从进入抢购业务開始。产生一个Transaction ID,该Transaction ID生命周期截止到用户跳转去第三方支付为止。期间在生活服务中产生的浏览、抢购行为均会挂靠到该Transaction ID之下,并会在跳转去第三方支付页时携带该Transaction ID凭证。最基本的是须要记录下:用户获得商品名额后,跳转去第三方时,这一行为。

 
考虑到Transaction ID为抢购业务中,用户操作行为的keyword段。值须要保证唯一。故此处能够採用发号器之类的能力。 
我们构建的Transaction Data记录,就能够依照DailyRun的方式,与第三方对账,来fix双方数据库库存不一致等问题。 
为什么会产生我们和资源方的库存不一致,可能是由于用户在第三方消费后。第三方callback我们时候失败造成,也可能是由于用户跳去第三方后并没有真正支付。但我们的商品库、抢购库的库存都已经降低造成的。原因可能有非常多,对账机制是必要的。

3.项目总结


最后,我们回想回想设计,压力问题在业务层攻克了,库存不一致问题我们通过对账机制攻克了,产品的需求我们也通过旁路可配攻克了,嗯,能够喝杯茶,发起评审。评审通过后開始写代码了。 :)

感谢大家。分享中的数据强一致那块。以及怎样做取舍,都是非常有意思的点,都能够展开聊非常久,这里没展开,大家能够事后查查资料。

4.Q & A


Q1.1:请问。防刷是怎么做的?一般抢购都有非常大优惠。

假设有人恶意刷,那正常的用户就失去了购买的机会。比方,抢购的商品数为1000。有人恶意刷了900,那仅仅有 100 被正经常使用户抢到。等恶意抢到的 900 经过后面的支付环节验证后,可能已经过了抢购时间了。就算恶意抢到的 900 都支付成功。那对正经常使用户也是不公平的。 
A1.1:在这个业务场景中,我们做的是商品展示、商品的购买权的发放,真正产生消费是在第三方。

那么,用户刷的问题,须要我们和第三方支付页面一起来控制。

在用户通过排队机制。获得了购买名额后,跳转去第三方时候,我们依照和第三方约定的加密方式传递加密信息。第三方依照约定的解密方式解密成功后才同意用户支付,加密解密的过程中能够带具有生命周期的内容。这样。用户在高频请求支付页面获取商品时候,实际仅仅有:1)加密对;2)第一次。才可能获得。只是,第三方都是为了销售出商品,所以这类合作的成功几率不大。恶意刷,的确会在我们的业务层面展示商品没量了。

导致想买的用户没了机会。但上面Q1的回复,能够保证第三方不受损。

对于你提到的这样的刷的情况。若想在我们业务层规避,我想这就是一个通用的防SPAM的问题了。

这块自己真懂得不多。

:P 
Q1.2:要想准确的放刷。推断的维度就多,逻辑就复杂;与之矛盾的,抢购要求的是响应迅速。 
A1.2:对的,@裴宝庆|SinoIOV ,抢购业务由于请求压力大、热门商品抢购并发高。切忌添加过多逻辑,切忌过多后端依赖。越简单效果越好。

我们在设计系统时候,非常多事不是咱们一个系统能cover的。多少须要一些前置模块、能力的构建ready后。我们的系统才干run的不错。还是建议 @裴宝庆|SinoIOV 快构建帐号体系、用户消费记录这两部分。

Q2:对账这里,仅仅是和第三方去对照商品的库存量吗,金额这里是否去对照? 
A2:对账,事实上是对照的消费数据。避免出现我们统计今日产生了X件商品共价值Y的消费,第三方给出的是消费了N件共M价值的消费。避免金额不一致,造成结算、分成等问题的出现。我想你问题中的库存量的diff问题,还得靠第三方定期的通过我们数据层的接口来update他们提供的商品。

事实上在我们的商品库中,商品不一定仅仅同意第三方提供,也能够同意第三方通过接口降低商品嘛。比方和一个卖水果的第三方合作,第三方上周公布说有100件,但这周线下热销,仅仅剩20件了。我们也应该同意第三方来update到一个低值。但这样,我们的系统中就会复杂挺多。

Q3:防刷,避免第三方的推广效果达不到问题。

 
A3:对的,用户ID维度、IP维度。都是有效办法。看详细场景。有帐号体系的业务。用用户ID维度效果最好。借助存储记录下每一个用户的购买记录,来控制就好。

市面上的电商站点,基本是抢购业务都须要登录,而且限制每件商品单人购买数量。事实上就是通过存储记录用户的消费,而且再次产生消费前查询并添加代码逻辑来控制。

Q4:每次抢购活动的时候用一套新的验证码? 
A4:验证码这个东东,属于图灵測试嘛,仅仅要測试方法好。而且尽可能保证每次产生的验证信息从未出现过且无规律,就是好的验证码啦。:)

posted @ 2017-08-08 08:18  gccbuaa  阅读(398)  评论(0编辑  收藏  举报