高并发代表着高流量,高并发系统的魅力就在于我们可用凭借自己的聪明才智设计巧妙的方案,从而抵抗住流量的冲击,带给用户更好的使用体验。让流量可以平稳的被系统中服务和组件进行处理。
而我们在应对高并发大流量时也会采用类似“抵御洪水”的方案,归纳起来共有三种方法。
- Scale-out(横向扩展): 分而治之是一种常见的高并发系统设计方法,采用分布式部署的方式把流量分流开,让每个服务器都承担一部分并发和流量
- 缓存:使用缓存来提高系统性能,就好比用“拓宽河道”的方式抵抗高并发大流量的冲击。
- 异步: 在某些场景下,未处理完成之前我们可以让请求先返回,在数据准备好之后,再通知请求方,这样可以在单位时间处理更多的请求。
异步调用在大规模高并发系统中被大量使用,比如我们熟知的 12306 网站。 当我们订票时,页面会显示系统正在排队,这个提示就代表着系统在异步处理我们的订票请求。 在 12306 系统中查询余票、下单和更改余票状态都是比较耗时的操作,可能涉及多个内部系统的互相调用,如果是同步调用就会像 12306 刚刚上线时那样,高峰期永远不可能下单成功。
而采用异步的方式,后端处理时会把请求丢到消息队列中,同时快速响应用户,告诉用户我们正在排队处理,然后释放出资源来处理更多的请求。订票请求处理完之后,再通知用户订票成功或者失败。
处理逻辑后移到异步处理程序中,Web 服务的压力小了,资源占用的少了,自然就能接收更多的用户订票请求,系统承受高并发的能力也就提升了。
既然我们了解了这三种方法,那么是不是意味着在高并发系统设计中,开发一个系统时要把这些方法都用上呢?当然不是,系统的设计是不断演进的。
罗马不是一天建成的,系统的设计也是如此。不同量级的系统有不同的痛点,也就有不同的架构设计的侧重点。如果都按照百万、千万并发来设计系统,电商一律向淘宝看齐,IM 全都学习微信和 QQ,那么这些系统的命运一定是灭亡。
我之前也踩过一些坑,参与的一个创业项目,在初始阶段就采用了微服务化架构,但由于当时人力有限,团队技术积累不足,因此在实际项目开发中,发现无法驾驭如此复杂的架构,出现问题,难以定位,系统整体性能下降等多方面的问题,设置系统宕机了都无法查到哪方面的原因,最后不得不回归到简体的架构中。
所以我的建议:
- 满足业务需求和流量现状,选择最简单的系统设计和技术体系
- 随着业务的增加和流量的变化,逐步修正系统中存在的问题,比如单点问题、横向扩展问题,遇到瓶颈的组件等,尽量选择有开源社区支持的组件,社区实在没有解决方案的前提下,才会自己造轮子。
- 当对架构的小修小补无法满足需求时,考虑重构、重写等调整方式以解决现有问题。
以淘宝为例,当时在业务从 0 到 1 的阶段是通过购买的方式快速搭建了系统。而后,随着流量的增长,淘宝做了一系列的技术改造来提升高并发处理能力,比如数据库存储引擎从 MyISAM 迁移到 InnoDB,数据库做分库分表,增加缓存,启动中间件研发等。
当这些都无法满足时,就考虑整体架构的大规模重构,比如著名的五彩石”项目让淘宝的架构从单体演进为服务化架构。 正是通过逐步的技术演进,淘宝才进化出如今承担过亿 QPS 的技术架构。
总结
归根揭底一句话:高并发系统的演进应该时循序渐进的,以解决实际业务中问题为目的和驱动力的。
本节主要介绍三种高并发系统设计的思路:scale-out、缓存、异步。
至于具体采取哪一种方式,还是全部都采用还要跟进你自己系统来考量。
高并发系统是一个渐进的过程,并非一蹴而就:
martin fowler 说过,能用单体解决的问题,就不用分布式。不能为了技术而技术,采用分布式固然可以分流用户请求,提高系统的响应能力,但同样也带来了复杂性。如果单体能解决的问题,就不要想着诗和远方,因为系统内部进程间的调用,肯定比不同物理机的进程之间调用的更快。
高并发除了横向扩容,缓存和异步化,系统还要做好保护,比如限流降级,过载保护。