程序员人生 网站导航

秒杀

栏目:互联网时间:2014-12-19 08:55:44

“秒杀”成了现在互联网的新宠,动不动就来个秒杀或抢购之类的活动,特别是近期的双101或双102。甚么“秒杀”“抢购”这类型活动对系统来讲还真是1个考验,不过BOSS们就是喜欢,随意1句“他们能做,为何我们不能做”。我还嘴贱地问了1下"想怎样秒杀?",得到的答复是“像抢小米1样就好了”。

 

......

 

我怕我再多问两句就连淘宝都要我做出来。为了不影响正常服务的,只能从现有机器上单独抽1台刀片机出来“玩玩”了。

 

“秒杀”考验的就是系统确保事务安全情况下的并发处理能力,小米的抢购应当异步的,先给你进来排队,稍后再给你答复那种,相对用户体验没那末好。面对这样的领导,出来后肯定是1大堆问题“为何要让我等那末久才给我答复”,“为何不立刻就给我知道有无成功”等等。为了封住他的嘴巴,只能做同步的了。

 

问题:安全性、稳定性、性能消耗、速度、防御。

 

安全性:主要指的是线程安全和事务安全

 

稳定性:能确保在秒杀进程中不要宕机就好了

 

性能消耗:越简单越好,保证最大并发数下资源足够支持,包括JVM大小、系统相干参数、web容器线程数、数据库连接数等。

 

速度:说白了,就是甚么都不要做,就告知用户秒杀成功与否就能够,剩下的事情(例以下单之类的)秒杀后再做。越简单越好,不过常常越简单的事情就越复杂。

 

防御:就是防范那些“黄牛党”用那些抢购工具发起无穷攻击的那种。

 

 

防御:

 

安全性

Java支持多线程,这不用多说,反正不要用到全局变量就行。在事物方面,我用到oracle数据库的“行锁”:select for update nowait。nowait相当重要,当获得不到锁的时候就是立刻抛异常直接返回“秒杀失败”响应。不要在web容器或数据库里面多待1好秒。如果不用nowait关键字就跟Java里面的synchronized或lock性能1样:等待锁。1等的话,就准备“收尸”了。当用户线程获得到行锁了就能够留下“脚印”成功解锁走人。为了确保数据的准确性,还可以通过用户ID为中奖名单表的主键,就算“不谨慎”让它溜进来了,还可以以主键束缚限制他重复秒杀成功。

 

稳定性

多大的独自就得有多大的嘴巴,通过评估,我们这个“秒杀”系统约支持每秒100个并发吧,也足够应付用户了,那我们就根据这个并发数做好入口的限制,例如web服务器入口我就开个500吧(由于还有静态文件)。多了的会直接返回500的,为了确保系统稳定,还真得需要那末做。接下来就是web容器线程数了,基于漏斗形原则,设置肯定要比前面的少,例如测试计算1个线程1秒能处理3个线程,那我们就给他设置个50左右就能够了。接下来的就是数据库连接数了,基于我们数据库服务器能力还是挺强的,处理能力不比JVM处理得慢,为了不让web容器线程等待,设置了与并发1样的线程数量100。

 

性能消耗

由于只支持秒杀功能,所以JVM的消耗还是可以控制的,对4C、16G服务器来讲应当没甚么问题。IO开消不算太大。不过1切还得活动过后再说。

 

速度

秒杀1条路下来其实就是获得活动信息和秒杀两个步奏,活动信息已初始化在JVM内存里面了,速度没甚么问题。秒杀通过上面所说的数据库行锁确保了线程不会等待,直接返回结果。还有秒杀成功名单也用户“留脚印”的时候同步到JVM内存的全局变量里面。所以,如果成功秒杀过的用户会在JVM判断里面直接返回结果了。就算1个不谨慎,在同步成功名单与获得行锁之间的时间差里面逃过1劫,上面提到的成功抢购名单的主键还是可以避免他屡次成功抢购的。

其实数据库里面的行锁决定了秒杀还只是1个单线程,由于大家就是为了争取1行的锁,如果秒杀商品数量众多,我们可以分多行锁,就是我们把商品平分了,例如我有1000个商品可以秒杀,我分出100行,每行负责10个商品,在系统初始化的时候把100行的行ID初始化到JVM,用户进来的时候随机获得行ID进行锁行和秒杀。当某行秒杀完了以后就在JVM剔除,直到所有行都秒杀完,不过半分钟的事情。

 

防御

这里的防御主要是防御那些通过秒杀工具抢购的用户,通过工具他们可以大批量的并发进攻。通过IP限制是不可能的了,由于IP会误杀很多无辜用户,现在只有通过用户ID限制了。限制有很多层面的限制,越是在系统的外围就越能为系统减轻负担。例如在路由、防火墙等做拦截最少比web容器层面拦截好。想象是美好的,由于企业的复杂关系与权利的限制,自己只能控制在自己的利用里面了,所以对我来讲最外层也就是web服务器了。只能尽可能在这里拦截了,每一个用户ID只能同时进入1次。为了能应付大并发的负荷,在这选用了redis的swatch命令机制,详情可参考(http://book.2cto.com/201306/25048.html)。用了redis 也有1段时间了,通过网上的资料也能够看得到它的出色的地方,也经过了我们系统的考验,还是不错的。

 

结果

活动前期,秒杀的前5分钟,通过对端口的监控,线程急剧飙升,看来“洪水”开始来临。

活动开始,约经过半分钟,web容器线程爆满,服务器直接返回500。等了约1分钟,线程还是不降,商品已秒杀终了了,通过对javacore文件分析,大部份线程在wait,1部份block了,数据库服务器性能消耗不高,导出AWR文件,渐渐分析。经过对web服务重启,1切恢复正常。

 

分析

事后通过对各种日志的分析,发现活动开始的最高并发有500访问量/秒,这个500还只是web容器的并发,在端口处的实际并发肯定不止。还有对javacore文件的分析,发现很多线程在等待数据的资源,难道100个数据库连接数都还不够,再跟踪数据库日志,发现有1条SQL语句写得还不是很好,以后再接再砺。

 

总结

秒杀真不好服侍,领导更不好服侍,不过经过了,学习了,就是自己的了。

 

------分隔线----------------------------
------分隔线----------------------------

最新技术推荐