任何技术都有其缺点,如果倾向于将微服务作为默认的架构,需要考虑以下问题。
微服务是⼀⼤堆各司其职的⼩服务,在理想情况下,每个服务本⾝就是⼀个完整的应⽤程序。
由于微服务在⼈⼒和计算成本⽅⾯需要最少资源,所以它需要的成本较⾼,哪怕是轻量级微服务。⽽且,代码库在将来会越来越⼤,代码库本⾝可能会添加⼀个新的领域。
但应该永远记住:当接近阈值时,设计良好的代码库始终可以切换到微服务。
使⽤微服务架构的主要优点之⼀是易于扩展单个组件。你可能会找到组件需要单独扩展的⼤量应⽤程序,但应⽤程序果真需要这么做吗?
现在,这可能是最难做出的战略性选择之⼀。跨多个服务的事务是整个架构的负担。
解决跨服务的事务意味着:服务之间的互锁会导致⼀系列难以追踪的僵局,以及会危及服务健康状况、有时甚⾄是⼯程师健康状况的竞态条件。
按照定义,REST 服务是⽆状态的。它们不应该参与边界不仅限于⼀项服务的事务。在⾼性能环境下:
由于微服务坚持采⽤分散式数据管理,它带来了最终⼀致性问题。如果是整体式应⽤程序,可以在单个事务中⼀起更新⼀堆东西。⽽微服务需要多个资源才能更新,且分布式事务不受欢迎。因此,开发⼈员需要意识到⼀致性问题。
想象⼀下,三个不同的服务通过⽹络彼此联系。假设每个服务调⽤要花费 100 微秒(这在负载情况下并不少⻅),那么到头来单单在⽹络上就要花掉300 微秒。⽽⼀些应⽤程序本质上与组件和服务紧密集成。添加的通信层可能会给处理实时数据的应⽤程序造成灾难性的后果。
除了上述四个问题,在使⽤微服务之前还需要考虑它的另⼀些缺点,⽐如:
关注点分离:在不相关的代码间应存在隔离墙。
当不相关的代码需要协同工作时,应该使用抽象良好的接口并尽量减少状态共享。
你的代码对其他代码的了解,越少越好。同样,一个函数执行的功能越多,你就越需要考虑运动部件之间的复杂关系(即复杂度)。而且,我们作为合格的工程师就应该努力降低复杂度。
从逻辑上讲,分离关注点的最佳方法是否就是让你的无关代码运行在不同的服务(服务之间以 API 沟通)中呢?不,并非如此。
经典的单进程关注点分离之所以有效,是因为它可以最小化并简化不相关代码之间的接口。在设计良好的程序中,此接口可以只有带 return
语句的单个函数调用。
不相关代码之间的边界本质上是复杂的,而简单的接口有助于管理这种复杂性。
相比之下,在微服务中,函数调用被替换为网络请求。这种新的服务间障碍严格来说更加复杂且更不可靠。
相反,在单体中,当代码失败时整个服务都会失败。尽管这听起来很糟糕,但由于现在只有一种故障情况,因此它更易于管理。
当采用“关注点分离”并将其应用在开发人员时个团队之间各自独立。
从表面上看这似乎是有益的。如果团队只需要操心自己的服务,那将减轻他们的认知负担,并提高他们的生产力。
现在,工程师无需担心基础架构中其他部分的复杂性了。问题在于,大多数新功能都需要一些跨多个服务的补丁,因为,许多功能需要在两个或多个服务上开发。
从技术角度来看,实现多服务功能可能需要编辑多个存储库。至少,它需要一种方法来测试在多个服务上运行的代码。
如何手动测试多服务功能?在机器上启动多个容器,并仔细设置每个容器的状态。 如何进行单元测试呢?在哪里对多服务功能进行单元测试?是仓库 A 还是仓库 B?很容易想象,数据流中的一个小错误会破坏多个下游服务。我们应该期望工程师理解所有可能依赖其代码的下游服务吗?
如果没有投入大量的工程资源来构建多服务测试流程,那么除了最常见的功能之外,开发新功能的速度会像蜗牛般缓慢。如果没有质量测试框架,看似简单的任务(例如“添加分页”)也可能会变成历时数月、跨多个团队的工作。
“回滚”是现代软件工程需要面对的现实。当部署的代码会破坏某些功能时,必须回滚部署并还原提交。
微服务架构的一个观点是,部署多个独立服务比部署整个应用更安全。当一项服务中断时,其他服务还有回退可用。整个应用程序将继续运行,客户不会有什么感觉。
这种方法存在多个问题。
虽然单体部署也会出错,但是有多种方法可以缓解这种情况(蓝色 / 绿色、金丝雀等等)。虽然这些方法也可用于微服务,但是设置和管理安全部署并非易事,应对一项服务总比应对多个服务要容易些。
在每个应用程序中,都有经常运行的部分和很少运行的部分。很少运行的部件比频繁运行的部件需要的资源要少一些。那么分开扩展这些部件是否有意义?
从根本上讲,扩展软件的原因是因为软件需要更多的核心资源。这些资源可能是 CPU 周期、内存、磁盘空间或网络。
例如,当 CPU 以100%运行时,可以启动另一个服务来减轻压力。
对于大多数应用,水平扩展(克隆单体)就足够了。水平扩展的复杂度较低,许多云服务都可以用很少的配置来做到这一点。
相比之下,选择分开扩展许多微服务有两个常见原因。
例如,如果一个请求在开始时是受内存限制的,而在结束时是 CPU 限制的,那么就可以将请求的开始部分放在高内存服务中,将结束部分放在高 CPU 服务中。即便如此,除非你是独角兽级别的企业,否则分开扩展服务带来的财务优势可能也无法抵消额外的复杂性。
经验法则:
2019 年的内存:12.8GB/s
2019 年的数据中心的网络:5Gbit/s = 0.625GB/s
软件工程师喜欢自欺欺人。使用微服务的决策也会有同样的乐观情绪。
正常运作的复杂系统一定是从一个正常运作的简单系统演变而来的。从头开始设计的复杂系统永远无法正常工作,也无法靠打补丁来正常运作。你必须从一个简单系统起步。——Gall 定律