架构设计 十五字诀
Posted 网事如烟云
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了架构设计 十五字诀相关的知识,希望对你有一定的参考价值。
架构设计 十五字诀
软件架构设计,不是三言两语能说清楚的。只是笔者一直也抽不出时间静下心来去长篇大论。本文,就暂且以一个“听”来的故事,简单讲述一下笔者对于软件架构的设计心得——架构设计,十五字诀:
范围与目标
假设与约束
风险与代价
一、“听”来的故事
这个听来的故事是这样的:一个 NMS(Network Management System,网络管理系统)的 HA(High Availability,高可用性)是冷备方案,如图1所示。
图1 冷备系统示意
图1中,NMS1 是主用(master),NMS2 是备用(slave),并且主备方案选用的是冷备方式。所谓冷备,简单地理解,就是 NMS2 并没有启动,待到 NMS1 不能工作时,NMS2 才会(由人工或者其他系统)启动,接替 NMS1 的工作。
NMS1 与网络设备之间的网络链接记为 L1,NMS2 与网络设备之间的链接记为 L2。L1 穿越了一个网络 N1,L2 穿越了另一个网络 L2。
现在的问题是:即使 NMS2 自身可以完美地接管 NMS1 的工作,但是,当 NMS2 试图接管工作时,如果 L2 不通怎么办?
假设 NMS2 自身接管工作需要10分钟,这也就意味着从 NMS1 停止工作到 NMS2 开始工作,中间会有10分钟的业务中断——这一点,用户可以接受。但是如果 L2 中断了,其修复时间需要一天,那么业务中断时间将变为24小时——这一点,用户是万万不能接受的。
所以,用户期望 NMS2 能够实时检测其与网络设备之间的链接状态,如果出现中断,能够报一个告警(Alarm)给用户。如此一来,用户就可以及时修复网络,能够有效保证将来系统倒换(NMS2 接管 NMS1)时的业务中断时间。
客观地说,用户的期望非常自然也非常“合理”,然而,NMS2 却是一脸懵逼:我连启动都没有启动,我该如何检测 L2 的连通性?又该如何给你报告警?于是NMS2 一脸委屈、义正言辞地拒绝了客户的期望。
客户觉得 NMS2 说的非常有道理,大声地回了一句:
傻逼!
作为甲方爸爸,客户骂一句“傻逼”,那都是轻的。真把客户惹毛了,他反手一个投诉单寄到公司,NMS2 恐怕就不止是傻逼这么简单——也许十年以后,NMS2 还会在那喃喃自语:曾经有一份期望摆在我面前,我没有珍惜 ...... 如果老天能够再给我一次机会,我一定会去写那一万行代码 ......
这个故事是笔者在听人传闻的基础上,又做了一些演绎,不过总体上来说,还是比较“真实”和典型的。说它典型,是因为站在不同的角度,甲方(NMS 的购买方)和乙方(NMS 的供应方)对于同一件事情(冷备方案),有着截然不同的理解和诉求,而且双方都觉得自己有道理,都觉得对方是傻逼。
笔者以为,要化解这种不可调和的矛盾,就需要说清楚软件架构的十五字诀。在正式阐述十五字诀之前,为了更好地描述和理解,笔者先简述一下 HA 方案的冷备、热备、双活等概念。
二、HA 简述
由于篇幅和主题原因,本文只是简单介绍一下HA(High Availability,高可用性)方案的基本概念和基本分类。
HA,顾名思义,乃是尽量保持一个系统一直可用,就像喝酒一样,去努力追求无终止喝或者一直喝。
喝酒的 HA,拼的是个人酒量。而软件系统的 HA,拼的是“群殴”:一个系统倒下了,另一个系统站起来。所以,在软件系统的 HA 方案中,至少有两个 Server,如图2所示。
图2 软件系统 HA 方案示意
需要特别说明的是图2中的“data”。一般来说,软件系统服务端(Server)响应客户端(Client)的请求,总会改变其内部数据,而一个服务端(比如 Server_2)接管另个一服务端(比如 Server_1)之前,两者之间需要保证数据一致,否则系统的运行会出错。
在保证系统正确运行的基础上,根据服务端的工作方式,HA 分为两大类:主备(active-passive)、双活(active-active)。
主备方案,就是有两个 Server,一个作为主服务(master)在工作,另一个作为备服务(slave),不工作,待到 master 出现故障不能工作时,slave 会接管 master 的工作,同时自己也就变成了 master。
说明:这种情况称为 1:1 备份(1主1备),还有 n:1 备份、n:m 备份等等。本文仅以 1:1 备份为例进行讲述,其他情况同理。
根据 slave 的启动情况以及 master-salve 之间的数据同步情况,主备方案又细分为:冷备、温备、热备等三种方案,如表1所示。
表1 主备方案分类
方案 |
Slave 启动 |
Slave 工作方式 |
数据同步方式 |
冷备 |
Slave 平时不启动;待到需要接管 Master 时,才会(由人工或者其他系统)启动 |
Master 不能工作时,Slave 接管 Master 的工作 |
待 Slave 启动准备接管 Master 时,由人工或其他系统将 Master 数据导入 Slave |
温备 |
Slave 与 Master 同时启动 |
Master 不能工作时,Slave 接管 Master 的工作 |
待 Slave 准备接管 Master 时,由人工或其他系统将 Master 数据导入 Slave |
热备 |
Slave 与 Master 同时启动 |
Master 不能工作时,Slave 接管 Master 的工作 |
在 Master 工作期间,Master-Slave 保持数据实时一致(比如由 Master 实时推送数据给 Slave) |
如果从倒换时间来衡量三种主备方案,通过表1可以看到,热备倒换时间最短(不考虑其他因素的话,热备倒换时间为0),温备倒换时间较长,冷备倒换时间最长。
当然,衡量主备方案的指标不仅仅是倒换时间,还有其他因素,这个我们下文还会涉及,这里暂且不表,而是继续讨论 HA 的另一个方案:双活。
双活(active-active),顾名思义,就是没有主备一说,两个(也可以是多个)Server 不分主次,同时工作,如图3所示。
图3 软件系统双活方案示意
图3与图2相比,多了一个 LB(Load Balance,负载均衡器),这是因为 Server_1 与 Server_2 是同时在工作。既然同时在工作,那么面对众多 Client 的请求,两者总得有所分工,而负载分担则是一种比较好的分配方式。
LB 不是本文的重点,重点是两个 Server 在同时工作,这也就意味着两者的数据需要双向实时同步。
双活需要数据实时同步,热备也需要,两者非常容易弄混。确实,它们有一定的共同点。
(1)双 Server 同时启动
(2)双 Server 数据实时同步
不过仔细分析,双活与热备的不同点也非常显著的:
(1)数据同步的方向不同。热备的方向是 master 到 slave;双活的方向是互相数据同步
(2)工作方式不同。热备,只有一个 Server 在工作;双活,两个 Server 同时在工作。两者的工作方式,如图4所示。
图4 热备与双活关于工作方式的区别
对于图4,再次强调一点:热备与双活不取决于 Client(猫咪)的数量(虽然图4中,热备的 Client 是1个,双活的 Client 是2个),而是取决于有几个 Server 在同时工作(注意,图4中的 Server,不是狗,而是狗的 **)。
为了更加清晰地看出双活、热备以及其他 HA 方案的异同,HA 方案小结如表2所示。
表2 HA 方案小结
同时启动 |
同时工作 |
数据同步 |
||
主备 |
冷备 |
N |
N |
N |
温备 |
Y |
N |
N |
|
热备 |
Y |
N |
实时单向 |
|
双活 |
双活 |
Y |
Y |
实时双向 |
表2对 HA 方案做了一个简单小结,但是它与架构的十五字诀又是什么关系呢?
三、十五字诀
我们将目光再投向那个听来的故事。故事中的甲乙双方互相骂对方是傻逼,一个行于言表,一个默念于心。如果乙方能够将软件架构的十五字诀跟甲方解释清楚,两者都基于十五字诀理解问题,那么双方会不会继续没羞没臊地在一起玩耍呢?
3.1 范围与目标
范围与目标,有两个视角。一个是设计者(乙方)的视角,他需要给出其设计产品的范围与目标。
连宇宙都是有限的,所以一个软件设计,如果没有明确它的范围,或者是因为范围本身很明显,众所周知,不需要明示,但是更多的原因是
没搞明白
仿佛自己无所不能!
与范围形成强烈对比的是“目标”,一个没有明确架构目标的设计,就是
耍流氓
需要澄清的是,虽然我们说,不能为了指标而忘了目标,但是软件架构的目标,绝大多数需要承载于指标。数字出架构!这里的数字,既指设计的输入需要量化,也指设计的输出(指标)也需要量化。
在实际的设计中,没有明确范围与目标的案例,比比皆是,这里就不再赘述了。
相比于设计者的视角,另一个视角则往往容易被忽略,不过从某种意义上讲,它却更加重要,那就是:需求者(甲方)视角。
甲方视角与乙方视角,往往并不相同,以本文的故事为例,如图5所示。
图5 不同视角中的冷备方案
图5中,乙方视角中的冷备,其范围仅仅是 NMS1 与 NMS2 本身的主备倒换(目标是倒换成功率、倒换时间等),而甲方视角中,还多了一个 NMS2 与设备之间的链接 L2 的连通性检测(目标是中断多少时间内能够检测出来,等等)。
双方视角不一致,没有关系,重要的是要把不一致明确说出来。除了有明确的文字说明,从架构图的角度来说,系统的上下文有时候也需要明确标识出双方视角中的范围与目标,如图6所示。
图6 NMS 冷备方案的上下文示意图
图6并不是一个标准的规范的系统上下文视图,它只是想重点表达:关于 L2 的连通性检测,需要另外一个服务或者另外一个系统来完成。
除了表达范围和目标以外,系统上下文视图,有时候也可以表达系统的依赖,或者说系统的假设和约束。
3.2 假设与约束
一个系统只要不是太简单,总免不了与他系统打交道,包括与环境、人打交道,或者一个系统内部,也存在服务/模块之间互相打交道的事情。既然打交道,就存在依赖。
在软件架构的语境中,“依赖”是一个中性的词汇。A 依赖 B,并不是说 A 就是 B 的“妈宝男”,而很可能仅仅是 A 调用了 B 的一个接口而已。
有些依赖是已知的、可控的,有些依赖是未知的、或者不可控的。对于前者,需要做好依赖的设计,比如接口抽象、服务解耦等等,而对于后者,则需要设计好假设和约束。
假设是对未知依赖的一种预判。比如决定明天去爬山,是基于明天不会下雨的假设。
约束是对不可控依赖的一种合约,这种不可控,可能已知,也可能未知。比如对于一个不防水的手机,它也不能自己会不会掉进水里,但是它可以宣称:只要进水了,概不负责。
假设和约束这两个概念,也有一定的混淆性,如图7所示。
图7 告警管理系统示意图
图7是一个告警管理系统(AlarmMgr)示意。网络设备由于多种原因(比如单板重启、端口损坏)会给 AlarmMgr 发送各种各样的告警。一个软件系统的处理能力是有限的,而网络设备发送告警的频率却有高有低,这与网络规模(网络中包含多少设备)有关,也与网络当时所处环境、网络上所跑的业务也有关,比如有的网络会发送100条(告警)/秒,有的网络会发送500条/秒。
对于 AlarmMgr 来说,它不能将自己的能力设计为无限大或者超级大(比如能够处理100万条/秒),毕竟它受限于很多客观条件。但是,AlarmMgr 总得有一个设计指标,比如能够处理200条/秒。那么,这个200条/秒,是“假设”还是“约束”呢?
都可以!只是其后果不一样!
将200条/秒设计为假设,如果实际为300条/秒,显然 AlarmMgr 无法有效处理告警,此时的责任在乙方——AlarmMgr 的提供方。
将200条/秒设计为约束,如果实际为300条/秒,虽然 AlarmMgr 无法有效处理告警,但是此时的责任在甲方——AlarmMgr 的需求方。
因为约束本质上是一个合同条款,是对甲方的约束。只有满足约束条件,乙方才能承诺正确工作,否则,乙方概不负责。说白了,这有点像
把丑话说在前面
不管好坏,就是这么回事,甲方你要愿意就愿意,不愿意拉倒!
而假设却不具备合同条款的性质,它只是乙方自己对未知条件的一种认识。如果实际条件突破了乙方的假设,从而造成系统不能正确工作,那是乙方自己的责任,甲方概不负责。从某种意义上说,假设,是乙方对未来的一种赌博,那就要
愿赌服输
一个人即使貌美“如花”,但只要婚前以真面目示人,甲方就不能婚后职责,这是约束。而如果是洞房之夜才能揭开红盖头,甲方就可以跳起来骂娘——这要怪只能怪乙方对甲方的容忍度假设得太过美好。
再回到本文所说的故事中,如图8所示。
图8 L2 的连通性保证
图8的重点是:L2 的连通性,到底是假设还是约束?如果是约束,那么乙方就不必提供“连通性检测”服务,即使 L2 中断了,那也是甲方的责任。如果是假设,那乙方就有必要提供“连通性检测”服务——既然赌 L2 是连通的,那或者对 L2 的中断负责,或者就是去老老实实地检测 L2 的连通性。
在乙方假设 L2 是连通的前提下,如果乙方不愿意去检测 L2 的连通性,那它就得冒着 L2 中断的风险。所以,从风险的角度看问题,
假设,就是乙方的风险
约束,就是甲方的风险
3.3 风险与代价
对于未知的、不确定的依赖,无论是将其定义为假设还是约束,甲乙总得有一方承担风险。难道没有更好的方案,使得双方都不承担风险吗?没有!
因为,
不承担风险,就得承担代价!
我们还是以 AlarmMgr(告警管理系统)为例,乙方假设告警发送频率是200条/秒,它就得承担着如此风险:如果实际频率大于200,系统就不能正确工作,那就可能被甲方啪啪啪......打脸
如果乙方视图消减这个风险呢?比如将告警发送频率假设为1亿条/秒(这个假设绝对足矣),将来面对任何网络,都不会出现被打脸的风险。
世界上没有免费的午餐,更没有零代价的风险消减。如果要实现处理能力为1亿条/秒的高精处理系统,其实现代价可能高到无法想象。这个时候,恐怕就不是甲方打脸了,而是自己对自己啪啪啪 ......
而且,这个代价不仅仅是乙方的实现代价,还有甲方的商用代价。设想一下,如果乙方将这样的系统做出来了,那么其售价会是多少?甲方爸爸虽然对乙方比较豪横,但是其花起钱来,也不能无限“爽”,毕竟地主家也没有余粮啊!
我们继续将目光投到本文所说的故事。如果甲方要求乙方必须做连通性检测(也就是甲方不愿意接受自己保证 L2 连通性的约束),那么乙方很可能会将原来的冷备方案,替换为双活方案,如图9所示。
图9 NMS 双活系统示意
为什么是双活系统呢?因为 NMS 天然有检测连通性的功能,如果采用双活方案,NMS2 也就天然可以检测 L2 的连通性。看起来很美好,好像原来的问题根本就不是问题,双方的撕逼也根本就不应该发生。
但是,一切的岁月静好,总是有人默默地负重前行。笔者只强调一点:图9中标红加粗的 L3,也即 NMS1 与 NMS2 之间的链接。为了支撑双活,L3 的带宽、时延、丢包、抖动等一系列的网络指标,都需要甲方保证。
再加上其他代价,甲方未必能够承受。而且,双活系统不是仅有好处,没有坏处,甲方就算能够承受初始代价,它仍然要承担后续各种各样的风险 ......
或者不采用双活系统,而是采用其他方案,这背后的原理是一样的:任何一种方案都不是绝对的优秀,都有其代价和风险。
数字出架构
对于每一种方案,都需要严格给出目标(收益)、风险、代价以后,才能做出最终的决策。这是另外一个话题了,本文就不再延展。
3.4 小结
其实,关于软件架构设计,笔者的心得是二十个字,而不是十五个字。
范围与目标
假设与约束
风险与代价
原则与规范
只是,前十五个字,更多的是面向黑盒、面向客户、面向合约(“假设”,面向的是内部),后五个字,主要是面向白盒、面向内部、面向架构管理。所以,本文就只讲述了前十五个字。
大道至简,相由心生。当年聚贤庄一役,萧锋一套太祖长拳,令天下英雄概莫叹服!软件技术发展日新月异,各种名词、概念,甚至包括各种三方件、平台、框架,你方唱罢我方登场,乱花渐欲迷人眼。越是如此,我们越是要拨云见日,去追求软件架构设计的至简之大道!
由于时间关系,本文匆匆而就,有些话题也无法深入,敬请谅解。希望能早日抽出身来,写一本软件架构的秘笈,与君宫享!
---------------------
(5月底上市)
以上是关于架构设计 十五字诀的主要内容,如果未能解决你的问题,请参考以下文章
淘宝十年资深架构师吐血总结淘宝的数据库架构设计和采用的技术手段。