You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

174 lines
14 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 21 | 高性能设计,一切都围绕着契约精神
你好,我是乔新亮。这一讲,我们来聊聊如何实现架构的高性能设计。
前面我们讲过,产品思维有两个核心关键词:“契约精神”和“洞察人性”。其实高性能设计,也和契约精神是密切相关的。我将其总结为:高性能设计,一切围绕着契约精神。
你可能会想,高性能设计不就是可以支撑大流量、高并发的架构设计吗?和契约精神又有什么关系?
其实关系很大。所谓的高性能,实际上,是与业务强相关的。比如,对于一台网络游戏服务器来说,可以支撑 400 名玩家同时在线,就算高性能;对于一台流媒体服务器来说,可以支撑 10,000 用户同时在线观看,就算高性能。
当然,数据可能不准确,但逻辑大体如此。在实际的业务场景中,还会有许多其他技术指标被引入。比如游戏需要关注连接稳定性、视频需要关注延时,等等。
明确了性能和业务的关系,下面我们就来聊聊,如何进行高性能设计,也借此加深对契约精神的认知和理解。
## 契约精神是高性能架构设计的“拦路虎”
首先,我们要清楚,高性能设计可以大体拆解为两大步骤:
1. 清晰描述、定义性能目标;
2. 设计并实现这个目标。
而高性能架构的设计目标,是通过三类指标来具体定义的,分别是:
1. 系统响应时间,一般指服务/交易处理的时间,也包括网络响应时间;
2. 系统吞吐量,一般指系统的每秒交易量,通常需要结合并发量共同描述;
3. 系统容量,通常代表系统的可用资源数量,包括硬盘容量、网络带宽等。
但要注意:虽然我们有三类高性能指标,但彼此不能孤立地看待,否则就会出现问题。比如,在定义系统响应时间目标时,如果不与系统吞吐量关联起来,就会变得缺乏实践意义 —— 一旦流量的压力高峰到来,系统往往就不遂人愿了。
这意味着,我们要学会识别架构设计中的组件(或服务),首先弄清哪些组件需要明确高性能指标,再针对性地用以上三个指标做清晰定义。
除此之外,对于系统响应时间,我们还有一个更直观的监控指标,叫做 Top 百分数 Top Percentile简称 TP。比如 TP 90 = 2s意思是90% 的请求的响应时间都在 2s 内;那么 TP 99 = 2s 的意思就是 99% 的请求的响应时间都在 2s 内;我们一般说的 RT = 2s指的是平均响应时间可以作为参考但实际意义可能并不大。我个人一般会重点看 TP 90 和 TP 99 的数据。
当我们明确了 TP 90 或者 TP 99 这一有关系统响应时间的指标时,一个交付给用户的“契约”就出现了。守好诸多类似的契约,也就是保证了高性能设计。但学习需要适当地进行延展思考,比如此时,我们应该多问自己一句:这个契约意味着什么?
对于没有经验的程序员或架构师来说,这里其实存在一个隐含的假设,某种意义上,也算是一种侥幸心理:如果该系统在业务流量低峰场景下,满足了响应时间等性能指标,也就意味着,该系统在任何场景下,都能满足这些指标。
你可能会觉得有点绕, 那我换个说法来描述刚刚签订的高性能“契约”:本系统、组件、服务承诺在任何情况、任何资源、任何压力高峰下,都保证 TP 90 = 2s 。
是不是立刻就有了不一样的感觉?好像契约发生了变化,光是读出来,就让人心里一抖。
其实到此时,还谈不上契约是否发生了变化,因为打从一开始,这个契约就是不明确、不清晰、不具体的。
注意,这里还没有谈架构设计和研发能力,问题就已经出现了。很多时候,我们会被工作中出现的一些实际问题所困扰,其根源往往不在于相关责任人缺乏设计能力,而在于签订的“契约”并不明确。团队不可能实现和保卫一个“空中花园”。
所以,我总说,高性能设计,一切围绕着契约精神。所以接下来,我们的内容都将围绕着“如何将契约描述清楚”这件事来展开。
## 有上限的“契约”才能交付
我曾经培养过许多架构师。一开始在做高性能设计时,他们常常会这样描述给用户的“契约”:
“该架构最高支持 200 万并发流量TP 90 = 2s 。”
在不考虑业务需求的情况下,这个契约看似是没问题的。这意味着:对于第 1 名连接服务器的用户来说TP 90 = 2s ;但对于第 300 万名连接服务器的用户来说,契约又是什么呢?答案是,没有,或者说比较模糊。
**如果目标不清晰,隐患就埋下了。高性能设计非常看重系统响应时间的一致性,尤其是在不同的服务阶段,并发量和吞吐量发生变化的时候。**
对于管理者或架构师而言,理想的契约应该是“保证设计流量内的用户 TP 90 = 2s超出并发限制的用户则暂时不在契约承诺的范围内。”。
这就要求我们通过四大步骤去思考所谓高性能架构的设计问题:
第一步,当服务器不超过 200 万并发用户时TP 90 = 2s 。
第二步,当连接服务器的并发用户数量超过 200 万,达到 250 万时,保证有 200 万用户的 TP 90 = 2s ,拒绝其他用户的连接请求。
第三步,为设计容量外的用户提供服务器排队机制,并附带具体、明确的系统提示。
第四步3 分钟内完成扩容,保证并发用户数量小于等于 250 万时,任何在线用户的 TP 90 = 2s。
其实,这样才构成了一个可以履行的契约:无论流量波动如何,该架构始终支持 200 万用户的 TP 90 = 2s高峰时期其他用户会被拒绝访问或进行排队但系统能在三分钟内完成扩容支持 250 万并发在线用户,并且保证其中任何一个用户的 TP 90 = 2s 。
在某种程度上,这也是对业务诉求的精确表达,对于业务发展来说至关重要。很多同学设计的架构和契约,压根没有控制上限。他们想当然地认为,流量不可能出现太大的变化或波动。对于用户来说,这无异于一张“空头支票”。
所以说,要做高性能设计,一定要明确目标,并交付给用户;没有清晰的目标,也就没有针对性的设计,怎么能实现高性能的架构呢?
目标清晰后,我们再来看看,高性能架构如何设计并落地。
## 实现高性能架构的关键技术点
落地也不难,有三项工作最为关键,分别是:
1. 为架构做好“保护系统”;
2. 使架构具备扩容能力;
3. 提升系统各组件处理能力。
**保护系统,是为应对容量规划外的过载而设计的,通过流量控制来具体实现。**
所谓流量控制,就是当实际并发压力,超过设计性能的时候,人为阻断服务器连接,告知用户需要排队或“稍后再试”。
经常玩游戏的同学可能知道,对于网络游戏来说,流量控制是个基础设计,基本上所有的服务器都有“排队机制”。
流量控制有两种具体的实践,一种是面向连接数做控制,一种是面向用户数做控制。前者让用户在不断尝试连接时,有一定成功的可能;后者则保证用户对系统的期望是一致的 —— 要么可以登录、要么不能登录。具体应该选择哪种方式,取决于业务的实际诉求。
**而对于扩容能力来说,一般要包括储备额外的计算资源和具备快速弹性扩容能力。**
你可能会说,既然已经有计算资源了,为什么不提前进行扩容呢?
工作经验比较丰富的同学可能会知道其中的“隐情”:一般来说,该做的系统扩容,通常很早就完成了,架构设计真正缺乏的是应急手段。毕竟,你不能“不分青红皂白”,将每个系统的应急扩容都提前做好。一个经营稳健的企业,是不会允许这样的开销出现的。
另一个关键要素在于扩容速度。扩容工作是需要一小时、一分钟,还是一秒钟完成,其差别非常之大。“天下武功,唯快不破”,就算架构设计做得不好,如果响应够快,很多情况下,也是能解决问题的。
无论是利用公有云扩容,还是私有云扩容,目标都应该是一致的。想象一下这样的场景:
双十一来了,你通过监控平台,看到在线用户数量快速增长,很快突破了 200 万,流量控制机制开始生效,新用户进入排队序列。你考虑了 3 秒钟然后做了一个决定系统需要扩容。于是你轻轻地输入一个数字按下回车。3 秒钟后,排队序列消失了。你对旁边的同学说,继续监控吧!然后悠闲地端起茶杯,离开了座位。
这将是一种多么美妙的工作状态?更棒的是,以上场景完全有可能实现。
至此,我们聊过了契约,也聊过了应急处理,也就意味着,我们把握住了高性能设计的“头”和“尾”。最后,我们再简单谈一下“中间”部分,也就是对系统各组件处理能力的提升。
在高性能设计中,对于每个组件/服务都要确定目标,进行设计,然后进行评审和测试,确保满足性能需求。对于架构负责人来说,性能设计一定要尽早开始,具体工作内容包括:
* 需求早期收集
* 容量分析
* 估算与建模
* 技术研究
* 设计、开发、跟踪
* 测试计划执行
* 风险与绩效管理
* 实时监控与容量管理
工作内容和流程可能有些多,但这更多属于标准化的研发管理流程,这里列出来仅供参考,实际执行的时候,需要参考具体的业务和企业情况做调整。
**在这中间,有一点需要额外注意,我们称之为“对系统资源的争抢问题”。**
对于一个组件或服务,并发压力增大,响应时间却没有变化,意味着在请求的处理过程中,没有发生对资源的争抢和排队。同理,当响应时间随着并发压力的增大而变长时,一般都意味着这些请求引起了对系统资源的争抢。
**对于无状态的服务,理论上可以通过集群扩容,无限增加系统的并发处理能力,简单、直接地解决问题;**
**但对于有状态的数据服务而言,比如缓存或数据访问,就要考虑资源争抢问题,并针对性地设计、处理**。
所以,高性能架构在设计落地时,一个重要的任务,就是去发现那些可能出现资源争抢的模块,并一一进行隔离。
谈到“隔离”,在架构维度有两种方案,一种是在应用层进行隔离,也就是说,在业务功能层面就开始隔离;一种是基础软件层面进行隔离,比如与数据库相关的“读写分离”概念,就是在基础软件层面进行隔离。
对应到具体的实施方法上,有三种主要的“隔离”技巧,你可以多做了解:
1. 缓存机制,适用于部分场景,可以解决数据库资源的争抢问题;
2. EDA 架构,适用于处理时间较长的代码逻辑,需要提前区分哪些模块可以做同步处理,哪些模块可以做异步处理;
3. 提前进行预处理,以空间换时间,这也是一种卓有成效的处理手段。
当然了,方法还有很多,听完这节课后,你可以单独再做了解,并将了解到的方法,在评论区分享给大家。
做好隔离后,我们也要注意提升架构的可扩展性,具体方法可以参考[《架构设计,专业分工和协作精神的体现》](https://time.geekbang.org/column/article/317135),这里不再重复。
至此,关于高性能设计的三个最重要的步骤就讲完了,我们总结一下,分别是:
1. 为架构做好“保护系统”:做好系统的流量控制;
2. 使架构具备扩容能力:储备额外的计算资源,提升弹性扩容的速度;
3. 提升系统各组件处理能力:识别高并发情境中的资源争抢情况,同时注意保留架构的可扩展性。
最后,我们也要对测试工作保持关注。可能你在 PRE 环境做过压测,但后来发现,实际的业务复杂度是远远超过想象的。
针对这种问题,行业内的解决方案更偏向“全链路生产压测”,说白了,就是在生产环境准备数据、进行压测。如果一个系统通过了全链路压测,那就可以基本确认没有性能问题了。
## 结语
从我的角度来看,**技术行业发展到今天,基本不存在太多的技术挑战了。**
如果你能将业务问题抽象为一个技术问题,那么不管是寻求同事的帮助,还是 Google、看书、到知识平台付费学习都能解决你的疑惑。
所以,在理清高性能架构设计的整体思路后,困惑、焦虑,应该从你的认知中消失。具体到数据库设计、缓存设计、队列使用等技术细节,我认为都不是问题。关键之处,是要守住契约,按照我们今天所讲的三大步骤尝试落地。
过去的几年间,我曾在多个企业内建设高性能的 IT 系统架构,很好地服务了用户,使用的就是我们今天所讲的各个方法和思维框架。我个人感觉,过程也是相对轻松的。
需要注意的是,如果时间充足且落实到位,系统确实不会出现高性能问题。可是实际情况是,团队没有时间做完整的高性能设计,没有预算做全链路压测,所以埋下了隐患。
这时,我建议相关负责人要去识别最关键的服务,针对性地进行设计、测试,确保关键服务没有问题,并且,为非关键服务提供降级和熔断处理的开关。
另外一个比较常见的情况,是企业决策层将高性能的设计问题和技术问题混为一谈:看似是技术能力太过欠缺,其实是契约和设计没有做好。**任何复杂问题都可以被拆解为简单问题,只要拆解得足够细致。一定要牢记这一点,这种思维能力是技术人的安身立命之本。**
当然,如果你有问题或想交流的内容,也不要犹豫,立刻在评论区写出来,我会认真回复。
我们下一讲再见。