00:00
接下来我们来完善我们的秒杀代码,首先我们来考虑一下我们整个秒杀的流程,那这个秒杀呢,我们可以走两套流程,第一套流程,那就像京东小米那些做的,我们秒杀呢,其实只是一个商品的优惠显示信息,这个价格呢做了优惠,那整个购物流程呢,还是来加入购物车,然后跳到我们的订单确认页,然后再来去结算,去来支付,这是我们的第一套流程。那第二套流程,如果应对超高并发的流量,我们也可以来选择第二套,第二套我们点击立即抢购,以后们的请求呢,发给秒杀系统,秒杀系统开始来抢购,抢购呢先做一系列的判断,比如我们这个登录,如果登录成功了,我们再来校验我们这个秒杀请求的合法性,这个校验呢,包括我们当前要秒杀的商品有没有到秒杀时间,然后他的这些随机码跟我们SQ的ID这对应关系是不是都对不对,包括这个人是不是已经把这个商品秒杀过了,整个密等信我们都判断通过了以后,那想要秒杀。
01:01
我们直接来获取信号量,现在呢,就400个商品,我们直接获取信号量,那获取到了那就成功了,获取不到那就失败了,获取到成功以后,我们接下来呢,不是走以前的流程,我们慢慢创建订单给数据库里边保存详细信息,然后再来锁库存这一堆我们秒杀呢,来作为一个独立的流程,我们这个库存只要我们定时任务能上架它的数据库底层的库存已经锁定了400个,然后我们库存的信号量在red里也放了一些,那接下来我们只要得到信号量以后,我们就在秒杀服务里边快速的生成一个订单号,我们只生成一个订单号,把用户信息订单号当前他秒杀的商品这一套信息我们直接发给MQ,我们发出去,所以我们相当于呢,处理到这,我们只发了一个消息,没有动任何数据库,那消息一发出去以后,我们后台的订单服务慢慢监听这个消息,来创建我们这个订单,那我们此时立即给我们前端的。
02:01
用户返回,我们说秒杀成功,正在为您准备订单,那我们可以来在这个订单的准备页,我们十秒以后给他跳转到我们这个支付页,但支付之前我们可以让他确认一下收货地址,来来再选一个收货地址,选好以后呢,我们来点击支付,最终来到我们的支付确认,所以我们可以来选择这套流程,那上下两套流程呢,都各有优缺点,那我们上套流程的优点就是我们这个秒杀呢,是一个优惠信息,我们整个系统,我们如果点加入购物车走这一套流程,流量呢,那就分散开来了,但是它的缺点就是我们这个流量就会急连的映射到其他系统里边,本来秒杀请求呢,可能只是秒杀系统扛不住,但是大量请求一进来,你又在商品详情页展示,然后呢,还要在我们购物车页面要确认,在我们订单页要确认,如果我们此时在极限情况下,大家用户都来去访问的话,其实每一个页呢,可能都扛不住流量呢,除非非常分散,我们呢可以扛得住非。
03:01
当大的并发,这是我们说的这个缺点,当然它的优点呢也很多,因为我们这个如果是使用整个加入购物车的流程的话,我们整个业务呢,其实是统一的,我们数据模型只需要设计的兼容好我们普通的商品的这些数据信息,跟秒杀商品的数据信息,其实就差个价格,差个库存,这些扣除就行了。那这一块呢,我们就暂时不采用第一种加入购物车的方式来采用第二种,第二种的优点呢也非常明显,大家能看到从请求进来,一来到我们这个controlrler,到我们service,一大堆判断处理,截止我们给前端用户返回页面,我们没有操作任何一次数据库,没有做过任何一次远程调用,那我们这一块呢,是一个非常快的流程,我们只需要校验好所有的合法性就行了,因为所有的数据呢,我们都在缓存里边放着,那一切正常以后呢,我们给他快快速的指定一个单号给前端呢,告诉他你的这个单号来,我给你准备好了。但后台我们的订。
04:01
那服务慢慢的在这消费,那它的缺点呢,就是在这样,如果说我们这个极限情况下,我们的订单服务都炸了,你在这秒杀,诶你告诉他秒杀成功了,MQ发出去了,老是没有人来消费这个订单,那没有人这个消费这个订单,把订单的所有信息,详细信息准备不好,你在这儿呢,一直就支付不成功,所以这一块的后续处理,我们跟前边的整套业务又开始不一样,你就得加入一些自己独立的业务处理能力,所以接下来就要看我们这个秒杀呢,是要做一套整个独立的业务,还是要跟我们以前的整个购物流程来做一个融合,那么现在呢,就来做一个独立的业务,那就来写一下核心流程,后续的收尾工作我们说一下,大家后来再去做,就行了,好,那现在秒杀这个请求进来,我们希望呢,有一个service,我们这个秒杀的service,它呢直接能调用我们的秒杀方法,秒杀方法呢,将我们这三个参数传递过来给我们来进行秒杀。好以。
05:01
二还有我们这个三,然后秒杀成功与否,我们让他返回一个单号就行了,所以我助呢返回一个订单号,只要能秒杀成功,他就会给我返回一个订单号,否则呢,那就是没有这个订单号。我们就来创建出我们这个服务,所以我们这一块呢,主要使用我们第二套流程,就是结合了我们这个消息队列。消息队列呢,我们此时在这一块呢,倒不是来做最终一致性,虽然我们肯定是要保证最终一致,由我们这个订单服务要去真正的去创建进店,在数据库里边保存。但是我们更多做的是流量消峰请求一进来,我们不用立即去调用订单服务放到我们的MQ消费队列里边,然后呢,我们订单服务慢慢去消费,就不至于把我们订单服务打垮,我们要打垮也只能打垮我们一个秒杀服务,这是我们说的这个优点。好,我们接下来把这个流程做完,那么来到这个流程做完里边的第一个流程,首先判断登录一副,我们直接的连拦截器都帮我们判断了,我们这一块呢就不用做了,接下来我们再来做合法性校验,那既然传了这么一堆数据,我们先来校验上几个合法性,那在校验之前先来获取当前我们这个秒杀商品的详细信息,获取当前秒杀商品的咱们这个详细信息,这详细信息呢,既然你都给我带了,而且呢,你带的就是这个几杠几,我们就直接从second k ss里边来获取一下它的这个值转换过来就行了,好,那现在呢,Redt我们拿过来,Red点一个来。
06:36
绑定一个哈希操作,我们这个里边呢,保存的是我们所有的SQ信息,我们这是一个哈希结构,哈希的这个key用的是我们前边我们就叫second k sqs ctrl c来复制过来,来绑定一个哈希操作,然后呢,这个哈希操作们来准备好以后,来这个哈希option,所有的东西都是string,我们就直接把这个泛型呢约定好string string好我们来到这个哈希操作里边,我们拿到它以后,第一步获取我们这个秒杀单的这个信息,那么就来get我们获取一个这个数据,我们要用到key,那就是这个KID,这个ID呢,我们以前传的就是七杠一啦这些信息,那么页面给我们组装好的,好,我们就来获取到这个信息,那获取到这个信息呢,就有两种情况,获取到获取不到,所以我们在这来判断一下if,一个string us is emptyts,如果呢,它是空的,我们就可以直接return,如果不为空的话。
07:36
那呢,我们才需要往下执行好我们成功的逻辑,那成功的逻辑在这,我们现在呢,获取到我们当前的这个信息了,们将它转换过来,杰森,我们使用的pass杰森,点一个pass object,我们把我们获取来的这个杰森数据S,我们就叫杰森吧,好,这一块呢也一样,我们就叫杰son,我们把这个杰son数据呢,来转成我们指定的类型,因为我们在reddi里边保存的是我们之前的这个red to数据,我们继续来转换成SK,那们这个这个second kill red的这个to数据。
08:14
来转成它,点一个class,好,来转成它了以后,这是red里边保存的数据,接下来我们就要校验合法性,校验咱们这个合法性,合法性呢有很多校验,首先我们来挨个校验,第一个我们先来校验这个秒杀时间是不是已经过了,你给我发这个请求,秒杀时间都过了,那我们就算了,所以们呢,来做一个判断,如果我们这个red里边我们拿得到一个,他呢,首先有一个开始时间,还有一个结束时间,我得到了这两个时间,还是一个判断,点一个get我们的end time,那这个呢,如果开始结束时间,我们来判断当前时间,用一个date new,一个date,那这个日期点一个get time,那么当前时间如果呢,If判断,如果说当前时间在我们这个区间范围内,大于等于我们的start time,并且呢,它呢是小于等。
09:14
率我们这个时间,所以我们就是秒杀可以用的时间啊,这个new date time,这个当前时间,那就来创建到这,这是我们的当前时间,如果呢,它大于等于它,然后呢,小于等于我们的这个N的它,那我们就可以来进行秒杀,那么想要呢秒杀,那么这一块呢,就是时间判断else,否则呢,我又给它返回为空,只要为空那就是失败,所以呢,Return on,然后接下来。我们做的第一个校验时间的合法性,校验咱们这个时间的合法性,其实我们之前在我们给red里面保存这个数据的时候,我们的SQS这一块的每一个值都应该有一个过期时间,这样呢,能不能获取到一下就能拿到这个值,当我们为了开发期间测试方便,我把这个数据呢,就一直留在这儿,我们上线呢,可以给一个过期时间,只要我们这个活动结束,这个东西呢就自动过期,那现在呢,拿到我们的这个时间,如果我们时间是合法的,接下来我来校验第二步,那现在这个时间是合法的,现当我们是OK的,我们接下来再再来校验这个随机码,你带来的对不对,我们来到这个随机码,校验咱们这个随机码,随机码和我们这个商品ID,商品ID是否正确,那么就从red里边我们拿到我们这个随机码,点一个get random code,这是随机码和我们当前秒杀的这个商品ID点一个。
10:44
Get SQ ID,我们呢,拿到这两个数据以后,当然我们也不用拿这个SQID,那现在呢,想要校验的就是你给我传的这个ID是这个,你想秒杀这个商品随机码是这个,那就是呢,这两个全部得匹配上那才可以,所以我们来判断一下,如果说我们这个random code,我们red里面存的这个点一个equals,我们等于你传过来的这个code,这个是相等的,并且呢,你传过来的这个随机码跟我们red里边的这个码是一样的,好,我们把这个码呢,CTRLC我来复制过来,它点一个equals,那等于什么呢?等于我们这一块呢,我们自己来拼接一下,因为我们是场次信息,点一个get,还有一个session ID,再来加上我们的这个商品信息,才最终得到的是我们的这个码的信息,String,这是我们这个ID的信息。好,我们把。
11:44
这两个ID来做一个匹配,如果这两个随机码跟ID全匹配上了,那说明他带的数据是合法的,那这个数据是合法的呢,我们接下来又可以继续往下做了,否则我们又给它返回为空else来return一个那。
12:01
好,那接下来我们继续往下做,那么这个随机码和商品ID我们都校验通过以后,接下来我们还要校验,因为现在呢要买东西,我们得判断一下我们购买的这个数量合不合适,因为现在呢每一个都有这个限制数量,而且呢,这个限制数量其实我们在验随机码之前,我们都可以验这个当然无分前后顺序,所以我们来验证咱们这个购物的数量,购物数量是否合理,如果这个合理,好我们来验证一下我们从red里边拿到的这个数据,我们秒杀的商品呢,有一个限制来get second kill limit,好,那就是这个限制,我们想要买的这个商品必须小于等于它,那么每一个人呢,必须只能买这么一点的东西,们的这个number小于等于这个了,那就是一切正常的,这就是我们这个验证通过了,这个验证通过以后呢,接下来第四步,第四步我们还得验证这个人是否已经买过了,是否已经购买过了。
13:01
购买过就是呢,他这个秒杀商品呢,他已经买过了,那买过了以后呢,你就不用再去买了,要不然我们来写一个脚本,我们已经知道了这个地址,我们就无限给这个地址来重发请求,你虽然呢给我们有限制,但是呢,你不在这儿来验证,那我每一次呢,你限制两个,我就买两个,但是我买上多次,你没有判断我是否买过了,那这一块呢,就会出现问题,而且呢,这一块其实就是我们说的这个密等性处理,比如说呢,买一次我们就得有一个逆等性字段,那我们这个系统,我们以后就规定,如果呢,只要咱们这个秒杀成功,秒杀成功我们就去redis里边占一个位,我们就去占位,这个站位的名字呢,我们可以这么来结合,我们要标识每一个用户的这个信息,所以我们按照这个user ID来加上我们当前秒杀的这个商品的IDSQID,以及我们当前的这个场次ID,还有我们的session ID,我们使用这三个字段。
14:01
来作为一个key,我们来标识这个人呢,已经买过了,所以我们就在这来做一个测试,我们使用red tempt点一个option for value,我来调用这个方法叫set if Amazon,这Amazon呢,那就是我们以前说过的这个功能,我们来点进来。就是我们说的赛塔NX,那NX就是不存在的时候呢才占位,所以呢最终会返回占位成功还是占位失败点过来,我们来看一下,如果能占位成功,说明这个人呢没买过,而且这是一个原子性的,如果占位失败说明这个人买过,买过呢我们就不占了,所以这也是我们分不知所以前来做的这个效果,所以我们现在呢就来占位,按照我们指定的这个K,好,我们现在呢,指定的K应该是我们的这个我们就叫red k,这个K呢,我们现在是按照我们的要求是当前用户的ID,这个用户我们直接从拦截器里边获取就行了,Logan intercept,那么这个拦截器里边呢,能得到当前用户的所有信息,好我们拿过来,然后呢,只要用户一登录,我们就会有会员的ID,好,我们接下来呢,这个会员ID我来点一个get一个,这儿呢有一个ID,我们得到当前用户的ID,然后呢,我们再来加上一个。
15:19
嗯。我们指定的这个小下划线,我们拿它呢,作为一个K,再来加上一个。我们的这个session ID,我们当前这个场次,我们这个用户呢,在这个场次,场次是哪个场次呢?我们也来加上这个场次,就是red这一块的数据内容,好加上场次信息,再来加上当前的商品信息,相当于我们这个用户这个场次,这个商品呢,我们已经买过了。但这两个信息呢,前面都已经拼好串了,那么就直接给这来一拼就行了,那当前用户呢,只要我在这来占位,Set if absent,我们来占位,K呢,就是它值呢,我们随便来写,假如我们把它买的这个商品数量,我们来作为一个值,我们存到这儿,包括以前他买了几件,我们想要查呢,也好方便把这个数量呢拿到这点一个调用一个to斯string方法,我们给他来做一个占位,而且这个占位呢,不是永久占的,要所有东西都永久占red的话,RED2项呢就完蛋了,所以我们在这一块呢,应该有一个超时时间,我们这个时间呢,就是当前场次只要一结束,那么这个东西呢就自动清空,你再要来买,我们这个场次信息都已经没有了,所有数据都已经没有了,那就没法买了,所以我们可以让它自动过期,这个自动过期呢,相当于是这样,我们这呢,有一个活动的结束时间,我们这是我们的当前时间,这两个的时间差,那就应该是我们缓存的这个。
16:46
这个时间,所以我们现在就来减一下我们的end time,我们结束时间减去我们当前时间得到的一个时间差,这个就是我们的TTL时间,这个GTL时间,我们来看一下我们得到这个时间单位呢?它是一个什么单位?
17:02
它呢,这是一个毫秒单位,所以我们最终呢,得到的也是一个毫秒的时间差,那么接下来我们这一块的TTL的值好能存活时间,然后呢,加上时间单位,那就应该是毫秒值,那time unit这有一个micro seconds,所以这么长以后呢进行过期。这是我们的自动过期功能,所以我们在这儿呢,会来先做一个站位,好,我们在这儿来做一个站位,我们得判断,如果说这个站位成功,如果占位成功,说明这个人从来没买过,占位成功,占位成功说明从来没有买过,没有买过,如果呢,占位失败,说明这个人已经买了,说明已经。买过了,买过了呢,我们就不能再来购买了,所以买过了我们继续来给他return了,当然所有的else呢,我们都可以不加,只有if成功的逻辑进来,最终所有的默认处理我们都给它return on就行了。好,现在呢,我们这一块else return,如果我们站位成功,那么就来到这一步,那来到这一步以后呢,接下来我们就能直接购买商品了吗?其实不是的,那现在相当于我们前期的所有核验操作全部都通过了,你这个呢,也是密等的,你也没买过对应关系你的这个随机码秒杀时间,所有的东西都是对的,都是对的,接下来我们就可以给你秒杀,但你能秒杀多少件,我们呢,每一个商品秒杀的件数,按照这个随机码,我们在这儿呢,都保存了一个数字量,这个数字量呢,是我们的信号量,只要来进来一个人,我们就给他减一个,所以我们接下来就使用分布式信号量,我们来做一个减操作就行了,能减成功那就说明能秒杀成功,所。
18:49
啊,我们继续拿到我们的red client来获取我们的分布式信号量get simple for,那么SIM for的整个值应该是这样子的,我们这个信号量的前缀我们来看一下,这是商品随机码的前缀,好,我们来复制过来,SIM的前缀是这个,然后呢,加上我们这个后缀,后缀呢必须按照我们这个随机码来,那就是我们的random code,每一个商品带它的这个随机码,我们给red里边存了一个信号量,然后呢,这个信号量我们来调用一个方法叫acquire acquire呢,那就是从信号量中取出一个,但是我们要取几个,那要按照我们买的数量来,我们现在所有数量都是核验通过的,我相当要要取这么多的值,所以呢,我们就在信号量这来获取一个买装的with try,看一下,只要我们这个信号量能获取成功,来看一下我们的效果好这一块。
19:45
走,我们这个AC块呢,就是来在这儿获取,而且这个获取呢,其实它是一个阻塞的,这很不好,也就是说我们当前如果我没有秒杀到我们这个商品呢,我们就会一直在这儿等,等别人给我们信号量一释放以后,我们又可以秒杀到了,所以这个方法我们不去掉,我们来调这个TRY,而快我们就来测试一下我能不能拿到这个信号量,哎,我快速的尝试一下,所以我们该调的呢,是我们的块方法把这一块呢全部去掉,我不能让它在这阻塞等一定要等着释放,一定要秒杀到们调用呢穿块我就来试一下,试一下以后呢,这还有一个等待时间,那么就来约定一下,我呢现在就来试这么多个数量,我想要买这么多的商品,那就我要试这么多数量,我等多久呢?比如我就来写一个一百一百的这个时间单位是什么,我呢就来写一个time units,我呢不叫秒了,我呢就叫毫秒,比如我在这来指定一个这个是微秒单位,那。
20:45
下面这个呢是毫秒单位,那来到以前我们这个过期时间,这个时间呢,我们也应该放成我们的毫秒单位,好我们现在呢,就这么长时间,相当于我就用100毫秒试一下,我们拿到呢,就算拿不到呢,我们就不要了,我们就是这么一个等待时间,我们最终来做一个穿二块,那说明我们会做一个快速的尝试,好我们还是把这个surround with置一下,如果我们这个快速尝试成功了,那说明我们拿到信号量了,拿到信号量呢,我们就一切就正常了,只要没有拿到我们就return呢,那没有拿到说明我们这个信号量没有值了,我们拿不到了,拿不到我们就秒杀失败了,所以这一块呢,那就是秒杀成功,因为我们已经获取到这个库存信号量了,我们这儿呢,就秒杀成功,秒杀成功以后,那接下来就来快速下单,这个快速下单那呢,我们现在目前做的是直接发送一个MQ消息,因为如果是以前。
21:45
我们所有核验通过以后,比如我们以前下订单,我们要验令牌,各种验证通过以后呢,我们接下来直接调用我们订单服务的各种处理,我们要给数据库保存,要下单,要做一串的逻辑,现在呢,我们就快速来发一个消息,发消息呢可能只耗费十毫秒时间,所以我们会发现我们整个这一块的方法会超级快,因为我们这一块呢,所有的操作都是在缓存里边操作,那前面的一系列对比,所有的东西可能都不超过十毫秒,那前面呢,这一块假设呢,用了十毫秒来到我们这儿信号量,信号量呢我就来尝试秒杀,我就等上100毫秒,那现在呢就是110毫秒,然后呢,再加上我们十毫秒的发MQ,那就是120毫秒,那相当于我们这个服务单方法呢,120毫秒我们就执行完了,我们现在呢,就给它取个整,比如我们就叫100毫秒吧,那现在取个整,如果我们现在是100毫秒,我们就执行完了,那么相当于。
22:45
就得到了一个快速释放,其实呢,比这个100毫秒我们要更快一点。往往可能十毫秒,20毫秒,乃至于30毫秒,那么就来当成20毫秒,那我们这一块呢,只要能快速执行完,我们来算一下,在我们这个单机状态下,由如我们这个方法呢,只占用了20毫秒的时间,所以呢,这一个线程相当于它只从请求ctrler一进来给我们这个响应,它呢只花费50毫秒,好我们再给他中途的来回数据,我们算上50毫秒,那我们这个一秒钟呢,单线程就能容纳20的这个并发,然后如果我们现在。
23:22
用的tomcat,我们让它能接收500并发的请求,那相当于每一个线程每秒能容纳20,那500的这个线程数再乘个20,那我们的这一块吞吐量那就能有1万,但如果是我们以前超慢,那一个线程都得执行三秒了,我就算TOMCAT500个线程来接请求,每一个线程都保持三秒,所以呢,一秒这个单位时间内,相当于我们这500个只有1/3能处理,那相当于我们的并发量只有200。所以呢,我们现在呢,就是200~1万的区别,我们所有的东西呢,都很快,然后给MQ来发消息,那整个这一块呢,只要创建成功,我们就来使用这个ID worker,比如我们来创建一个订单号,点一个get time ID,我们把这个呢当成一个订单号,这个订单号呢,给它来进行一个返回,你拿到这个订单号就说明我们这个秒杀成功了,所以你只要调用我们这个秒杀方法,能返回这个订单号,那说明你就秒杀成功啊,点一个。
24:23
OK,点一个set一个这个data呢,我们就给里边放一个order SN,这就是我们的订单号,那么秒杀接口呢,就快速写好了,我们这一块发MQ的请求,其他信息呢,我们下一节课可以完善一下。
我来说两句