背景
说起架构,大多人想到的是技术语言、技术框架、SOA、微服务、中间件等,这些都是纯粹的系统架构或基础架构,它们基本不受业务影响,大多可以独立于具体业务进行开发和发展,形成自己独立的体系甚至标准化的技术产品。
但实际上大多情况下 技术是为业务服务 的,我们开发的更多的是应用系统或者称之为业务系统,业务的不同特点决定了应用(业务)架构也必然有不同的特点。
而这些不同的特点单纯靠技术肯定解决不了,应用架构设计的一条重要原则是 技术中立,所以更多时候我们要从应用的角度而不是技术的角度去考虑问题。
我做过电商核心交易相关系统,提起电商大家想到的自然是 PV、UV、高性能、高并发、高稳定、抢购秒杀、订单、库存、分布式事务 等。
这里的每一个点初听起来都充满着高深与神秘,以关心较多的秒杀为例(1000 万人秒杀 100 块100g 的金条)我们来分析看看。
常规秒杀架构
常规架构如下
常规流量分布模型
展示层流量 > 应用层流量 > 服务层流量 > DB 层流量
超 NB 的系统流量分布模型如下
展示层流量 = 应用层流量 = 服务层流量 = DB 层流量
我们知道 DB 是系统最底层也是流量的最大瓶颈,从上面几个图可以看到,超 NB 的公司解决了 DB 瓶颈所有流量可以一路直到 DB 层,每一层都可以任意扩展,那么系统的压力就可以轻松化解。
当然一些没有经验的系统也是这么做的,但 DB 层甚至其他层扩展做不好,所以系统经常挂。而实际上再 NB 的公司也不会这么去做,即使技术上能做到也没有必要,因为 代价实在太大。
所以我们要从 DB 层之前想办法梯形 逐层进行流量过滤,也就成了上边看到的常规流量分布模型,最好的结果就是到 DB 层流量只有实际的订单数 100(100 块金条)。
秒杀流量过滤—常规思路
回到常规流量分布模型,以下是一个常用的秒杀系统流量过滤过程:
(点击放大图像)
如果纯考虑技术极致,那简单,把 DB 层的问题解决就好了,让 DB 可以象应用层和服务层一样随时分布式扩展,可实际上 DB 做不到, DB 是最大的瓶颈,所以才有了 排队系统 和 预约系统。
缓存是一定要用到的,但秒杀往往是瞬间的事,缓存的时效性导致缓存系统在这样的大流量对 DB 的瞬间冲击时几乎没有帮助。
如对秒杀商品的库存更新使得大量的 udpate sql 直接到了 DB,DB 压力非常大。我们尝试了在 mysql 下先 select,有库存时再 update,效果比较明显,特别是秒杀单 sku 或少量 sku 的场景,因为 select 和 update 对 DB 的压力大家懂的,有兴趣的可以自行测试下看。
排队系统是目前大多秒杀场景最常用的,本质是 异步处理放缓流量、削平瞬间峰值,降低对后续服务层和 DB 层的流量冲击。
预约系统的作用在于 提前预知流量,虽然预约量本身不可控,但秒杀前可以针对已知流量提前做好预案,让 系统处于可控状态。
预约 + 排队 的方式已经可以满足大多抢购秒杀场景的需要。
秒杀流量过滤—来点新思路
预约和排队毕竟是 外挂系统,各层之间是否有必要做好自己的预案和防范呢,我们再上个图逐层来看看想点办法:
(点击放大图像)
DB 层
DB 层不能任意或随时扩展,是最大的瓶颈和最后的底线,是绝对不能破的,一是 DB 并发连接不能超过最大连接数,二是 DB 压力不能太大,所以流量必须在前端控制。
服务层
服务层虽然可以分布式扩展,但受限于 DB 连接,并不意味着可以无限扩展。如果是公共服务层不区分秒杀业务和普通业务,是不好做流量过滤的,因为会影响到普通业务的正常流量,这种情况下只能从应用层想办法。
如果是单独为秒杀流程服务,或者说流量来源能区分出秒杀业务或其他业务的,那还是有思路的,办法总比困难多,如:
随机数过滤
将一定百分比的用户请求直接过滤返回给应用层包装,以友好的方式返回提示给用户。
预设阀值限流
设定单机在单位时间的处理最大阀值,如单机的实际处理能力 TPS 最大是 10000/ 秒,设定阀值为 20,当单机单位时间内(秒)的并发请求达到阀值时,后续请求直接返回给应用层,以友好的方式返回提示给用户。
此时系统并不处理业务逻辑和进行 DB 操作,只是简单地判断和响应返回,所以单机的处理能力 20+X 是远大于 10000/ 秒的。
注:上述两种方法 并不是 串行或有依赖的,两者都是一种可选的方法,它们的本质都是 流量过滤 和 提升单机处理能力 保护系统以免被冲垮。
在保证一定用户体验(单机处理能力)的情况下将流量过滤最大化,如第 2 种方案中单机仅 20 的流量能到 DB 层,保证 DB 层绝对没问题,如果服务层压力依然大,可以继续加分布式服务器和降低预设阀值。第 2 种方案实际上是第 1 种的升级版,更可控。
应用层
1、随机数过滤,同上。
2、预设阀值限流,设定单机在单位时间的处理最大阀值,同上。
3、通过 Tomcat 最大连接数控制,超过最大连接数的请求 直接拒绝服务,但用户体验很不好,系统假死崩溃的感觉,尽量通过加分布式服务器的方式解决。(服务层一定要通过应用层控制不能超过最大连接数,展示层和应用层直接取决于用户量,很难控制,可以使用预约系统让流量可控)
展示层
随机数过滤,将一定百分比的流量请求直接以友好的方式提示给用户。
正常讲展示层是 不应该过滤 的,请求都没有到服务器 [摊手],但从业务角度看,抢购秒杀本身就是一个概率事件,并不是完全取决于先后顺序 (有时后来的反而能抢到,这取决于分布式服务器处理、网络、排队系统的异步处理等)。
虽然对技术人来说欺骗用户的感觉很不耻,但关键时刻偶尔抱一下佛脚也是一种办法,总比系统被冲垮了好。
以上说的对几个层的处理,并不是很好的方案或架构,只是想说明,一个小的思路或方案就可能解决不少问题,完全靠技术我们不一定能解决的了,而这些架构或方案与高深的技术本身并无太多直接关系。
对分布式事务的思考
再说一个例子。很多人非常关心怎么保证分布式事务,特别是异构系统和异构 DB,我也知道至少目前还没有很好的技术手段来 彻底解决 这个问题,目前通用的方式就是回滚和补偿。
当然很多人还少做了一招,即 数据对账,但凡没有事后进行回滚补偿或数据对账的架构方案,在分布式事务的最终数据一致性上都会有问题,毕竟这是异构的系统或 DB。如图:
如果说秒杀还有些技术要求,那这里的分布式事务处理方案技术含量更少,只要 把细节做好(回滚、补偿、对账),数据的准确性依旧 100%。
然而我们很多人把重点放在了分布式事务架构或技术方案,过于追求极致忽略或不削于这些容易的细节,系统反而问题不断,无安宁之日。
某品牌手机为追求极致在一款高端手机上采用了特殊工艺的后盖技术,结果因为技术原因导致该后盖良品率低产量低,反而影响了核心产品手机的正常销售量,得不偿失。两者有异曲同工之妙吧。
总结
应用架构很容易也很难,容易的在于 不需过多关注技术实现,难的在于 必须根据实际业务场景和业务需要及时间、成本、资源等给出当下最合适、一定不是最完美的架构方案。
之前看过关于架构是设计出来的还是演进来的的讨论,其实这个也简单,在某个特定的阶段或时间点,如系统初期,它一定是设计出来的。
但纵观一个系统的架构历程,或者说一定要在两者间选择一个的话,它一定是 演进而来 的。
所有的大型互联网系统在初期一定不是设计成现在这个样子的,都是伴随着业务从小到大、从少到多、从简单到复杂的过程演进而来,架构的演进过程也见证了业务的发展历程。
技术无止境,但应用架构(业务系统)对技术的追求要有所止境,当 DB 瓶颈解决不了时 换个思路,来个排队系统和预约系统,技术难度就降低了很多;分布式事务解决不了,那就做好回滚、补偿、对账这些基础工作。
马步扎牢了,系统照样可以健壮稳定高性能。多从应用架构的角度去思考解决方案,前面更有一片天!
作者介绍
张立刚,上海鲜备科技首席架构师,原 1 号店技术部架构总监,负责拆单、运费、库存、订单等电商核心交易系统,对电商核心业务、高并发、高性能等有深刻的理解和实践。
感谢雨多田光对本文的审校。
评论