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.

117 lines
12 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.

# 11 | 架构设计流程:设计备选方案
上一期我讲了架构设计流程第1步识别复杂度确定了系统面临的主要复杂度问题后方案设计就有了明确的目标我们就可以开始真正进行架构方案设计了。今天我来讲讲架构设计流程第2步设计备选方案同样还会结合上期“前浪微博”的场景谈谈消息队列设计备选方案的实战。
## 架构设计第2步设计备选方案
架构师的工作并不神秘,成熟的架构师需要对已经存在的技术非常熟悉,对已经经过验证的架构模式烂熟于心,然后根据自己对业务的理解,挑选合适的架构模式进行组合,再对组合后的方案进行修改和调整。
虽然软件技术经过几十年的发展,新技术层出不穷,但是经过时间考验,已经被各种场景验证过的成熟技术其实更多。例如,高可用的主备方案、集群方案,高性能的负载均衡、多路复用,可扩展的分层、插件化等技术,绝大部分时候我们有了明确的目标后,按图索骥就能够找到可选的解决方案。
只有当这种方式完全无法满足需求的时候,才会考虑进行方案的创新,而事实上方案的创新绝大部分情况下也都是基于已有的成熟技术。
* NoSQLKey-Value的存储和数据库的索引其实是类似的Memcache只是把数据库的索引独立出来做成了一个缓存系统。
* Hadoop大文件存储方案基础其实是集群方案+ 数据复制方案。
* Docker虚拟化基础是LXCLinux Containers
* LevelDB的文件存储结构是Skip List。
在《技术的本质》一书中,对技术的组合有清晰的阐述:
> 新技术都是在现有技术的基础上发展起来的,现有技术又来源于先前的技术。将技术进行功能性分组,可以大大简化设计过程,这是技术“模块化”的首要原因。技术的“组合”和“递归”特征,将彻底改变我们对技术本质的认识。
虽说基于已有的技术或者架构模式进行组合,然后调整,大部分情况下就能够得到我们需要的方案,但并不意味着架构设计是一件很简单的事情。因为可选的模式有很多,组合的方案更多,往往一个问题的解决方案有很多个;如果再在组合的方案上进行一些创新,解决方案会更多。因此,如何设计最终的方案,并不是一件容易的事情,这个阶段也是很多架构师容易犯错的地方。
第一种常见的错误:设计最优秀的方案。
很多架构师在设计架构方案时心里会默认有一种技术情结我要设计一个优秀的架构才能体现我的技术能力例如高可用的方案中集群方案明显比主备方案要优秀和强大高性能的方案中淘宝的XX方案是业界领先的方案……
根据架构设计原则中“合适原则”和“简单原则“的要求挑选合适自己业务、团队、技术能力的方案才是好方案否则要么浪费大量资源开发了无用的系统例如之前提过的“亿级用户平台”的案例设计了TPS 50000的系统实际TPS只有500要么根本无法实现例如10个人的团队要开发现在的整个淘宝系统
第二种常见的错误:只做一个方案。
很多架构师在做方案设计时,可能心里会简单地对几个方案进行初步的设想,再简单地判断哪个最好,然后就基于这个判断开始进行详细的架构设计了。
这样做有很多弊端:
* 心里评估过于简单,可能没有想得全面,只是因为某一个缺点就把某个方案给否决了,而实际上没有哪个方案是完美的,某个地方有缺点的方案可能是综合来看最好的方案。
* 架构师再怎么牛,经验知识和技能也有局限,有可能某个评估的标准或者经验是不正确的,或者是老的经验不适合新的情况,甚至有的评估标准是架构师自己原来就理解错了。
* 单一方案设计会出现过度辩护的情况,即架构评审时,针对方案存在的问题和疑问,架构师会竭尽全力去为自己的设计进行辩护,经验不足的设计人员可能会强词夺理。
因此,架构师需要设计多个备选方案,但方案的数量可以说是无穷无尽的,架构师也不可能穷举所有方案,那合理的做法应该是什么样的呢?
* **备选方案的数量以3 ~ 5个为最佳**。少于3个方案可能是因为思维狭隘考虑不周全多于5个则需要耗费大量的精力和时间并且方案之间的差别可能不明显。
* **备选方案的差异要比较明显**。例如主备方案和集群方案差异就很明显或者同样是主备方案用ZooKeeper做主备决策和用Keepalived做主备决策的差异也很明显。但是都用ZooKeeper做主备决策一个检测周期是1分钟一个检测周期是5分钟这就不是架构上的差异而是细节上的差异了不适合做成两个方案。
* **备选方案的技术不要只局限于已经熟悉的技术**。设计架构时架构师需要将视野放宽考虑更多可能性。很多架构师或者设计师积累了一些成功的经验出于快速完成任务和降低风险的目的可能自觉或者不自觉地倾向于使用自己已经熟悉的技术对于新的技术有一种不放心的感觉。就像那句俗语说的“如果你手里有一把锤子所有的问题在你看来都是钉子”。例如架构师对MySQL很熟悉因此不管什么存储都基于MySQL去设计方案系统性能不够了首先考虑的就是MySQL分库分表而事实上也许引入一个Memcache缓存就能够解决问题。
第三种常见的错误:备选方案过于详细。
有的架构师或者设计师在写备选方案时,错误地将备选方案等同于最终的方案,每个备选方案都写得很细。这样做的弊端显而易见:
* 耗费了大量的时间和精力。
* 将注意力集中到细节中,忽略了整体的技术设计,导致备选方案数量不够或者差异不大。
* 评审的时候其他人会被很多细节给绕进去评审效果很差。例如评审的时候针对某个定时器应该是1分钟还是30秒争论得不可开交。
正确的做法是备选阶段关注的是技术选型而不是技术细节技术选型的差异要比较明显。例如采用ZooKeeper和Keepalived两种不同的技术来实现主备差异就很大而同样都采用ZooKeeper一个方案的节点设计是/service/node/master另一个方案的节点设计是/company/service/master这两个方案并无明显差异无须在备选方案设计阶段作为两个不同的备选方案至于节点路径究竟如何设计只要在最终的方案中挑选一个进行细化即可。
## 设计备选方案实战
还是回到“前浪微博”的场景上期我们通过“排查法”识别了消息队列的复杂性主要体现在高性能消息读取、高可用消息写入、高可用消息存储、高可用消息读取。接下来进行第2步设计备选方案。
1.备选方案1采用开源的Kafka
Kafka是成熟的开源消息队列方案功能强大性能非常高而且已经比较成熟很多大公司都在使用。
2.备选方案2集群 + MySQL存储
首先考虑单服务器高性能。高性能消息读取属于“计算高可用”的范畴单服务器高性能备选方案有很多种。考虑到团队的开发语言是Java虽然有人觉得C/C++语言更加适合写高性能的中间件系统但架构师综合来看认为无须为了语言的性能优势而让整个团队切换语言消息队列系统继续用Java开发。由于Netty是Java领域成熟的高性能网络库因此架构师选择基于Netty开发消息队列系统。
由于系统设计的QPS是13800即使单机采用Netty来构建高性能系统单台服务器支撑这么高的QPS还是有很大风险的因此架构师选择采取集群方式来满足高性能消息读取集群的负载均衡算法采用简单的轮询即可。
同理,“高可用写入”和“高性能读取”一样,可以采取集群的方式来满足。因为消息只要写入集群中一台服务器就算成功写入,因此“高可用写入”的集群分配算法和“高性能读取”也一样采用轮询,即正常情况下,客户端将消息依次写入不同的服务器;某台服务器异常的情况下,客户端直接将消息写入下一台正常的服务器即可。
整个系统中最复杂的是“高可用存储”和“高可用读取”“高可用存储”要求已经写入的消息在单台服务器宕机的情况下不丢失“高可用读取”要求已经写入的消息在单台服务器宕机的情况下可以继续读取。架构师第一时间想到的就是可以利用MySQL的主备复制功能来达到“高可用存储“的目的通过服务器的主备方案来达到“高可用读取”的目的。
具体方案:
![](https://static001.geekbang.org/resource/image/7b/8a/7b224715dc8efe67faa2af94922f948a.png)
简单描述一下方案:
* 采用数据分散集群的架构,集群中的服务器进行分组,每个分组存储一部分消息数据。
* 每个分组包含一台主MySQL和一台备MySQL分组内主备数据复制分组间数据不同步。
* 正常情况下,分组内的主服务器对外提供消息写入和消息读取服务,备服务器不对外提供服务;主服务器宕机的情况下,备服务器对外提供消息读取的服务。
* 客户端采取轮询的策略写入和读取消息。
3.备选方案3集群 + 自研存储方案
在备选方案2的基础上将MySQL存储替换为自研实现存储方案因为MySQL的关系型数据库的特点并不是很契合消息队列的数据特点参考Kafka的做法可以自己实现一套文件存储和复制方案此处省略具体的方案描述实际设计时需要给出方案
可以看出高性能消息读取单机系统设计这部分时并没有多个备选方案可选备选方案2和备选方案3都采取基于Netty的网络库用Java语言开发原因就在于团队的Java背景约束了备选的范围。通常情况下成熟的团队不会轻易改变技术栈反而是新成立的技术团队更加倾向于采用新技术。
上面简单地给出了3个备选方案用来示范如何操作实践中要比上述方案复杂一些。架构师的技术储备越丰富、经验越多备选方案也会更多从而才能更好地设计备选方案。例如开源方案选择可能就包括Kafka、ActiveMQ、RabbitMQ集群方案的存储既可以考虑用MySQL也可以考虑用HBase还可以考虑用Redis与MySQL结合等自研文件系统也可以有多个可以参考Kafka也可以参考LevelDB还可以参考HBase等。限于篇幅这里就不一一展开了。
## 小结
今天我为你讲了架构设计流程的第二个步骤:设计备选方案,基于我们模拟的“前浪微博”消息系统,给出了备选方案的设计样例,希望对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧,除了这三个备选方案,如果让你来设计第四个备选方案,你的方案是什么?
欢迎你把答案写到留言区,和我一起讨论。相信经过深度思考的回答,也会让你对知识的理解更加深刻。(编辑乱入:精彩的留言有机会获得丰厚福利哦!)