当系统架构师完成了复杂度的识别,这意味着我们已经拨开了笼罩在系统之上的迷雾,明确了真正的挑战所在。然而,这仅仅是架构长征的起点——真正的考验才刚刚开始。
为何需要设计备选方案?因为架构设计从来不是一道只有一个标准答案的数学题。成熟的架构师需要对现有的技术版图了然于胸,对已经过无数场景验证的架构模式烂熟于心,然后根据对业务的深邃理解,挑选合适的架构模式进行精妙的组合,再对组合后的方案进行恰到好处的调整。这正是架构设计艺术性的体现——它既是科学,也是哲学,更是一种创造性的实践。
软件技术历经数十年的飞速发展,新技术如雨后春笋般层出不穷。但经过时间大浪淘沙般的考验,已经被各种场景验证过的成熟技术其实更多。例如:
绝大部分时候,我们有了明确的目标后,按图索骥就能够找到可选的解决方案。只有当这种方式完全无法满足需求的时候,才会考虑进行方案的创新。而事实上,方案的创新绝大部分情况下也都是基于已有的成熟技术。
《技术的本质》一书对技术的组合有深刻的阐述:新技术都是在现有技术的基础上发展起来的,现有技术又来源于先前的技术。将技术进行功能性分组,可以大大简化设计过程,这是技术"模块化"的首要原因。技术的"组合"和"递归"特征,将彻底改变我们对技术本质的认识。
虽说基于已有的技术或者架构模式进行组合调整,大部分情况下就能够得到我们需要的方案,但这并不意味着架构设计是一件很简单的事情。因为可选的模式有很多,组合的方案更多。因此,如何设计最终的方案,并不是一件容易的事情,这个阶段也是很多架构师容易犯错的地方。
第一种错误:设计最优秀的方案
很多架构师在设计架构方案时,心里会默认有一种技术情结:我要设计一个优秀的架构,才能体现我的技术能力!例如,高可用的方案中,集群方案明显比主备方案要优秀和强大;高性能的方案中,淘宝的 XX 方案是业界领先的方案……
根据架构设计原则中"合适原则"和"简单原则"的要求,挑选合适自己业务、团队、技术能力的方案才是好方案;否则要么浪费大量资源开发了无用的系统,要么根本无法实现。
第二种错误:只做一个方案
很多架构师在做方案设计时,可能心里会简单地对几个方案进行初步的设想,再简单地判断哪个最好,然后就基于这个判断开始进行详细的架构设计了。
这样做有很多弊端:
第三种错误:备选方案过于详细
有的架构师在写备选方案时,错误地将备选方案等同于最终的方案,每个备选方案都写得很细。这样做的弊端显而易见:
正确的做法是备选阶段关注的是技术选型,而不是技术细节。
架构师需要设计多个备选方案,但方案的数量可以说是无穷无尽的,架构师也不可能穷举所有方案。合理的做法应该遵循以下原则:
数量法则:备选方案的数量以 3 ~ 5 个为最佳。少于 3 个方案可能是因为思维狭隘,考虑不周全;多于 5 个则需要耗费大量的精力和时间,并且方案之间的差别可能不明显。
差异法则:备选方案的差异要比较明显。例如,主备方案和集群方案差异就很明显,或者同样是主备方案,用 ZooKeeper 做主备决策和用 Keepalived 做主备决策的差异也很明显。但是都用 ZooKeeper 做主备决策,一个检测周期是 1 分钟,一个检测周期是 5 分钟,这就不是架构上的差异,而是细节上的差异了。
视野法则:备选方案的技术不要只局限于已经熟悉的技术。很多架构师积累了一些成功的经验,出于快速完成任务和降低风险的目的,可能自觉或者不自觉地倾向于使用自己已经熟悉的技术,对于新的技术有一种不放心的感觉。就像那句俗语说的:"如果你手里有一把锤子,所有的问题在你看来都是钉子"。
让我们回到"前浪微博"的场景。通过"排查法"识别了消息队列的复杂性主要体现在:高性能消息读取、高可用消息写入、高可用消息存储、高可用消息读取。接下来进行第 2 步,设计备选方案。
备选方案 1:采用开源的 Kafka
Kafka 是成熟的开源消息队列方案,功能强大,性能非常高,而且已经比较成熟,很多大公司都在使用。
备选方案 2:集群 + MySQL 存储
考虑到团队的开发语言是 Java,架构师选择基于 Netty 开发消息队列系统。由于系统设计的 QPS 是 13800,单台服务器支撑这么高的 QPS 还是有很大风险的,因此架构师选择采取集群方式来满足高性能消息读取。
对于"高可用存储"和"高可用读取",架构师想到利用 MySQL 的主备复制功能来达到目的。具体方案:
备选方案 3:集群 + 自研存储方案
在备选方案 2 的基础上,将 MySQL 存储替换为自研实现存储方案,因为 MySQL 的关系型数据库的特点并不是很契合消息队列的数据特点,参考 Kafka 的做法,可以自己实现一套文件存储和复制方案。
可以看出,高性能消息读取单机系统设计这部分时并没有多个备选方案可选,备选方案 2 和备选方案 3 都采取基于 Netty 的网络库,用 Java 语言开发,原因就在于团队的 Java 背景约束了备选的范围。