单体和微服务谁是毒瘤?单体、分布式、微服务、SOA 到底是什么关系?我的系统该用什么架构?最近终于下定决心研究这个问题并且有所收获,欢迎一起讨论。
01
架构的发展历程
我坚定地认为要深刻地理解一项技术光靠网上一两张按照各项维度对比的表格是不够的,而是要了解这些技术出现的历史背景:他们的出现到底是解决了什么问题,又带来了什么新的问题,最后又因何而被淘汰。下面这部分内容参考《凤凰架构》以及 Martin Fowler 等人一些文章进行整理,一起来看下历史的浪潮是如何推动架构的演进。
1.1 原始分布式时代
首先介绍的是竟然是“分布式”而不是“单体”这有些反常识,然而事实上分布式确实出现得比单体早,“单体”这个名称是在微服务开始流行之后“事后追认”所形成的概念,在单体出现之前分布式早已流行,并且成果斐然。
1971年 Intel 公司设计了世界上第一台微型计算机 MCS-4,开创了微型计算机的新时代,计算机逐步从专业的科研设备转变为企业的生产设备。但是微型计算机用于商业生产面临一个非常大的问题:计算机硬件有限的运算处理能力,直接限制在单台计算机上信息系统软件能够达到的最大规模。如果你生在这个时代,相信也能自然而然想到这一问题的解决思路:“人多力量大”、”众人拾柴火焰高“,朴素的真理适用于任何地方,当时高校、研究机构、企等不约而同的开始探索“使用多台计算机共同协作来支撑同一套软件系统”的方案,并取得了一系的成果。谈分布式必然绕不开远程调用,所以下面以远程调用为例谈一下这一时期的探索成果有什么局限性,又产生了哪些深渊的影响。
通过多台计算机分布式协同支撑提升系统规模
大家应该了解“UNIX 系统的版本战争”这一故事,为了避免相同的“战争”重演,负责制定 UNIX 系统技术标准的开放软件基金会与主流计算机厂商共同制订了 DCE/RPC 这一影响深远的远程服务调用规范,它也是公认的现代 RPC 鼻祖之一。因为开放软件基金会本身就负责 UNIX 系统技术标准的制定,在这个背景下,DCE/RPC 带着浓厚的 UNIX“简单优先原则”的设计哲学,预设分布式环境中的服务调用、资源访问等操作尽可能透明,使开发人员不必过于关注他们访问的方法或资源是位于本地还是远程。
然而这个过于理想化的目标在当时面临着太多的技术难题,“远程调用”与“本地调用”相比复杂程度完全不可同日而语:服务发现、负载均衡、熔断、隔离、降级、认证、授权等一系列的问题亟待解决。令人敬佩的是面对重重困难,DCE 从无到有构建了大量的协议来解决这些问题,真的做到了相对“透明”,但是分布式还有一个致命的问题——网络所带来的性能问题。
我们来推演一下:硬件性能不足——>采用分布式服务——>分布式的远程调用导致性能降低(与解决硬件性能不足的初心相悖)——>通过合并多个请求等方式刻意(开发人员需要意识到自己在写分布式程序,与 DCE 透明简单相悖)降低网络性能损耗——>人的能力成为软件规模的约束。这时候分布式从结果来看并不成功,设计向性能妥协让简单透明成为一句空话。
当我们玩游戏打 BOSS 时,喊人解决不了问题还有另外个方法——氪金,20世纪80年代摩尔定律稳定发挥,微型计算机的性能以每两年即增长一倍的惊人速度提升,既然分布式充满了矛盾与妥协,那就加钱换更好的机器,单体时代到来。
通过提升单台计算机性能提升系统规模
1.2 单体架构时代
在微服务出现之前“单体”都不认为是架构,因为他太“简单”、太“自然”了,以至于我想找到一本关于单体最佳实践的书籍都没有找到。现在很多书籍把单体当做“毒瘤”,甚至会出现微服务比单体先进的观点,然而事实上真的如此吗?
首先,我们先要明确单体是什么:“单体”只是表明系统中主要的过程调用都是进程内通信,不会发生进程间通信,仅此而已。那么进程内调用是罪恶吗?是毒瘤吗?那肯定不是的。现实中不会有一个人对你说:你这个"Hello, World!"程序不能用单体,因为单体架构是毒瘤。
有个“单体不便于扩展“的论调非常流行,我们着重讨论下这个观点是否准确。我们从性能扩展和功能扩展两个方便说。先说性能扩展:这太简单了,把一个服务部署多个副本,前面加一个负载均衡服务器来均分流量,这是不是就实现了扩展?再说功能扩展:纵向上我从来没见到哪个大型系统代码是不分层的,用过 Spring 都知道写一个服务基本上默认按照 MVC 模式分层以便于扩展;而横向上我们也可以从功能等维度拆分为不同模块(模块之间不进行通信或者进行少量的通信,注意:拆分模块不代表不是单体服务,单体服务也不表示系统只有一个模块),各模块完全可以独立迭代。从这个角度来看单体服务在“可扩展”这一点上也不输与微服务,甚至在开发、调试等方面更优。当前单体服务也有其局限性,比如有个 C++ 实现的系统要实现部分 AI 功能,明显 Pytho