谎言 1:跨服务的关注点分离降低了复杂度
“[关注点分离]”指的是在不相关的代码间应存在隔离墙。当不相关的代码需要协同工作时,应该使用抽象良好的接口并尽量减少状态共享。很多入门编程课程都将其视为标准的软件开发公理。你的代码对其他代码的了解,越少越好。同样,一个函数执行的功能越多,你就越需要考虑运动部件之间的复杂关系(即复杂度)。而且,我们作为合格的工程师就应该努力降低复杂度。
我坚信这一公理。从逻辑上讲,分离关注点的最佳方法是否就是让你的无关代码运行在不同的服务(服务之间以API沟通)中呢?
不,并非如此。
经典的单进程关注点分离之所以有效,是因为它可以最小化并简化不相关代码之间的接口。在设计良好的程序中,此接口可以只有带return语句的单个函数调用。不相关代码之间的边界本质上是复杂的,而简单的接口有助于管理这种复杂性。
相比之下,在微服务中,函数调用被替换为网络请求。这种新的服务间障碍严格来说更加复杂且更不可靠。首先,每个网络调用都需要一定数量的样板。其次,工程师现在需要默认任何服务随时会失效。相反,在单体中,当代码失败时整个服务都会失败。尽管这听起来很糟糕,但由于现在只有一种故障情况,因此它更易于管理。
在实践中更糟
左下方的图表是几年前Uber的微服务架构。它很简单,很容易理解。右边是Uber的实际服务地图。
我敢说Uber的任何人都不知道这个架构是如何工作的。曾在使用微服务架构的大型公司工作过的人都知道,Uber的经验并不是特例。
模型与现实之间存在这种差异的原因有两个。首先,这些图表通常过于简化。架构师设计这些图表是为了交流,而非完美反映现实。但因为它们隐藏了复杂性,也就容易误导决策。如果Uber的技术领导者知道自己的架构会变成什么样,他们还会走这条路吗?
其次,一旦你投身于微服务,随后的所有技术决策都将受其影响。因此,开发新功能时总要启动新的服务。架构图很快就会变得非常复杂,膨胀成上图那种密密麻麻的网状结构。