01 设计秒杀系统时应该注意的5个架构原则

01 设计秒杀系统时应该注意的5个架构原则

秒杀场景,最近很常见,双十一,12306抢票,到处可见秒杀系统的场景。简单来说,秒杀就是在同一时刻有大量的请求抢购同一个商品并完成交易的过程,用技术的来说就是大量的并发读写。

不管那一面开发语言,并发都是最为头疼的部分,同样,对于一个软件而言也是这样,可以很快增删改查做出一个秒杀系统,但是要让它支持高并发访问就比较难,

比如,如何让系统面对百万级的请求流量而不出故障?如何保证高并发情况下数据的一致性?

秒杀系统本质上就是一个满足高并发、高性能和高可用的分布式系统。

架构原则:41不要

如果你是一个架构师,你首先要勾勒出一个轮廓,想一想如何构建一个超大流量并发读写、高性能,以及高可用的系统,这其中有哪些要素要考虑,总结如下

--1 数据要尽量少

所谓”数据要尽量少”,首先是指用户请求的数据能少就少,请求的数据包括上传给系统的数据和系统返回给用户的数据(通常就是网页)

首先这些数据在网络上传输需要时间,其次不管是请求数据还是返回数据都需要服务器做处理,而服务器在写网络时通常都要做压缩和字符编码,这些都比较耗cpu

所以减少传输的数据量可以显著减少cpu的使用,例如,我们可以简化秒杀页面的大小,去掉不必要的页面装饰效果等

其次,要求系统依赖的数据能少就少,包括系统完成某些业务逻辑需要读取和保存的数据,这些数据一般是和后台服务以及数据库打交道的,调用其他服务会涉及数据的序列化和反序列化,

这也是cpu的一大杀手,同样也会增加延时。而且,数据库本身也容易成为一个瓶颈,所以和数据库打交道越少越好,数据越简单越好,越小越好。

--2 请求数量尽量少

用户请求的页面返回后,浏览器渲染这个页面还要包含其他的额外请求,比如说,这个页面依赖的CSS/javascript、图片以及Ajax请求等等都定义为”额外请求”,这些额外请求应该尽量少,

因为浏览器每发出一个请求都多少会有一些消耗,例如建立连接要做三次握手,有的时候有页面依赖或者连接数限制,一些请求(JavaScript)还需要串行加载等。另外,如果不同

请求的域名不一样的话, 还涉及这些域名的DNS解析,可能会耗时更久,所以要记住的是,减少请求数可以显著减少以上这些因素导致的资源消耗。

例如,减少请求数最常用的一个实践就是合并CSSJavaScript文件,把多个JavaScript文件合并成一个文件,在URL中用都好隔开

https://g.xxx.com/tm/xxx-b/4.6.36/mods/??module-preview/index.xtpl.js,module-jhs/index.xtpl.js,module-focus/index.xtpl.js.这种方式在服务端仍然是单个文件各自存放,只是服务端会有一个组件解析这个URL,然后动态把这些文件合并起来一起返回。

--3 路径要尽量短

所谓”路径”,就是用户发出请求到返回数据这个过程中,需求经过的中间的节点数。

通常,这些节点可以表示为一个系统或者一个新的socket连接(比如代理服务器只是创建一个新的socket连接来转发请求)。每经过一个节点,一般都会产生一个新的socket连接,

然后,每增加一个连接都会增加新的不确定性,所以缩短请求路径不仅可以增加不确定性,同样可以有效提升性能(减少中间节点可以减少数据的序列化和反序列化),并减少延时(可以减少网络传输耗时)

要缩短访问路径由一种版本,就是多个相互强依赖的应用合并部署在一起,把远程过程调用(RPC)变成JVM内部之间的方法调用,

--4 依赖要尽量少

所谓依赖直要完成一次用户请求必须依赖的系统或者服务,这里的依赖指强依赖。

,比如说要展示的秒杀页面,而这个页面必须强依赖商品信息、用户信息,还有其他如优惠券、成交列表等对这些秒杀不是必须的信息(弱依赖),这些弱依赖在紧急情况下就可以去掉

要减少依赖,可以给系统进行分级,比如0级系统、1级系统、2级系统、3级系统。0级系统如果是最重要的系统,那么0级系统强依赖的系统同样也是最重要的系统

注意,0级系统要尽量减少对1级系统的强依赖,防止重要的系统被不重要的系统拖垮,例如支付系统是0级系统,而优惠券是1级系统的话,在极端情况下可以把优惠券给降级,防止支付系统被优惠券系统给拖垮。

--5 不要有单点

单点系统的架构设计很不好,因为单点没有备份,风险不可控,设计分布式系统最重要的原则是”消除单点”

如何避免单点呢?关键点是避免将服务的状态和机器绑定,即把服务无状态好,这样服务就可以在机器中随意移动

如何把服务的状态和机器解耦呢?例如把和机器相关的配置动态化,这些参数可以通过配置中心来动态推送,在服务启动时动态拉去下来,在这些配置中心设置一些规则来方便地更改这些映射关系

应用无状态化是有效避免单点的一种方式,但是像存储服务本身很难无状态化,因为数据要存储在磁盘上,本身就要和机器绑定,那么这种场景一般需要通过冗余多个设备备份的方式来解决单点问题

前面介绍了设计上的一些原则,一直强调的是”尽量”而不是”绝对”

架构是一种平衡的艺术,而最好的架构一旦脱离了它所适应的场景,一切都将是空谈。这里说的几点只是一个方向,应该尽量往这些方向上去努力,但也要考虑平衡其他因素

不同场景下的不同架构案例

针对秒杀场景,怎样才是一个好的架构呢?下面以淘宝早期秒杀系统架构的演进为主线,梳理不同的请求量下,最佳秒杀系统架构

如果想快速搭建一个简单的秒杀系统,只需要把商品购买页增加一个”定时上架”功能,仅在秒杀开始时才让用户看到购买按钮,当商品的库存卖完了也就结束了,就当时第一个版本的秒杀系统实现方式。

随着请求量的加大(比如从1w/s10w/s的量级),这个简单的架构很快就遇到了瓶颈,因此需要做架构改造来提升系统性能,这些改造包括:

--1 把秒杀系统独立出来单独打造一个系统,这样可以有针对性的做优化,例如这个独立出来的系统就减少了店铺装饰的功能,减少了页面的复杂度。

--2 在系统部署上也独立做了个机器集群,这样秒杀的大流量就不会影响到正常的商品购买集群的机器负载

--3 将热点数据(库存修改)单独放到一个缓存系统中,以提高”读性能”

--4 增加秒杀答题,防止有秒杀器抢单

然后这个架构仍然支持不超过100w/s的请求量,所以为了进一步提升秒杀系统的性能,需要进一步升级

--1 对页面进行彻底的动静分离,使得用户秒杀时不需要刷新整个页面,而只需要点击抢购按钮,借此把页面刷新的数据降到最少

--2 在服务端对秒杀商品进行本地缓存,不需要再调用依赖系统的后台服务获取数据,甚至不需要去公共的缓存集群中查询数据,这样不仅可以减少系统调用,而且能够避免压垮公共缓存集群

--3 增加系统限流保护,防止最坏情况发生

经过这些优化,系统架构变成了下图,在这里,我们对页面进行了进一步的静态化,秒杀过程中不需要刷新整个页面,而只需要向服务端请求很少的动态数据,

而且,最关键的是详情和交易系统都增加了本地缓存,来提前缓存秒杀商品的信息,热点数据库也做了独立部署,等等。

 

从前面的几次升级来看,其实越到后面需要定制的地方越多,也就是越”不通用”,例如,把秒杀商品缓存在每天机器的内存中,这种方式显然不适合太多的商品同时进行秒杀的情况,

因为单机的内存始终有限,所以要取得极致的性能,就要在其他地方(通用性、易用性、成本等方面)有所牺牲。

--库存放在独立的缓存系统如redis,采用主动失效的方式来失效缓存。--对比定时失效

--ps 今天618,jd,tianmao等商场都有大量的抢购活动,借此机会学习一下秒杀系统

--此系列来自极客订阅~~

原文地址:https://www.cnblogs.com/yhq1314/p/11046215.html