模块化与微服务比较

本文比较了微服务和模块化整体架构(modularized monolith )的区别。现在大家一股脑从整体单片monolith迁移到微服务,但是这种转变真的适合你公司吗?整体单片monolith确实有很多问题,但是模块化(modularized monolith)作为微服务竞争对手是否被忽视了?

模块化能够带来以下特点:
1. 强大的封装:隐藏实现细节在组件内部,实现不同部分之间的低耦合。
2.良好的接口:组件之间依赖的是稳定API,一个组件可以被任何实施符合接口规范的其他组件更换。
3.显式依赖:模块化系统需要将不同组件组合一起工作,因此你需要有一个明确表达它们(验证)关系的好途径。

这些原则也都可以使用微服务实现。一个微服务可以以任何方式实现,只要它暴露了一个定义明确的接口(通常一个REST API)给其他服务。它的实现细节是服务内部自身事情,你可以改变这些细节而不影响整个系统。微服务之间的依赖关系通常在开发时间如果不十分明确,可能在运行时导致服务的业务流程失败,因此,最后一条模块原则胜过微服务一点。

微服务也包含了模块化的重要原则,有以下实实在在的好处:
1.团队可以独立工作和扩展规模。
2.微服务小而聚焦,能降低复杂性。
3.服务可以在不影响全局情况下内部进行改变或整体替换。

但是微服务缺点是,你已经从一个单一的方式(尽管有些轻微肥胖)过度到微服务的分布式系统,这带来了表操作的巨大复杂性。突然,你需要不断地部署许多不同的(可能是使用容器包装)的服务。新的问题会出现:服务发现、分布式日志记录、跟踪等。版本控制接口和配置管理也会成为一个大问题。

微服务之间连接的复杂性是因为所有微服务个体需要联合起来实现业务逻辑。

模块化的选择
我们是否要么使用混乱的monolith整体单片架构,要么就会被微服务复杂性淹没呢?模块化其实是另外一个选择。重要的是通过模块化我们可以在开发过程中有效地绘制和执行边界,这当然需要我们积极拥抱编程语言和开发工具以支持模块化。

在java中有几种模块系统,OSGi是最著名的一个,但随着java 9本地模块系统发布并添加到java平台本身中。模块现在是语言和平台的一部分,作为一等公民而构建。java模块可以表达对其他模块的依赖,并公开导出接口而同时实现类可进行强大的内部封装。即使是java平台本身(一个巨大的代码库)也已经被模块化。

其他语言提供了类似的机制。例如,JavaScript在ES2015有模块系统。在这之前,Node.js已经提供了一个标准的JavaScript后端模块系统。然而,作为一个动态语言,JavaScript在模块之间支持较弱(类型)接口和封装。微软的.NET框架有类似Java较强的类型,但是它没有等同于java即将推出的模块系统。然而,一个好的模块架构可以利用.net标准的反转控制模式实现。即使C++正在考虑增加模块化。

当你有意识地使用开发平台的模块化功能时,可以使用模块化获得微服务一样的好处。从本质上讲,更好的模块系统更能在开发过程中帮助你。不同的团队可以工作在不同部位,这些部位之间只通过定义良好的接口调用,在部署时这些模块能一起部署在单一单元。这样可以防止迁移到精卫带来开发和管理相关的巨大复杂性和成本。

模块化设计
创建好的模块同样需要设计严谨的良好的微服务。一个模块应该基于有界上下文(bounded context)建模。选择微服务边界是架构重大决策,一旦选择错误会带来昂贵的代价。在一个模块化的应用中模块的界限更容易改变。跨模块重构通常由类型系统和编译器支持。重新划分微服务边界包含很多内部个人交流以确保不会失败,诚实点,你能第一次就正确划分你的服务边界,或者第二次就可以?

在许多方面,静态类型语言模块通过定义良好的接口提供更好的构建。直接调用另一模块暴露的接口方法比调用另外一个微服务REST端点会更健壮,但是REST+JSON是无处不在的,但它由于缺乏(编译器检查)等结构在交互性上会差一点,再加上穿越网络包括序列化数据也不是免费。

模块是代码所有权的自然单位,团队可以负责一个或多个模块的系统。与其他团队共享的唯一事情是他们的公共API模块。在运行时,相比微服务模块之间有较少的隔离,而一切都在同一个进程中运行。

模块之间也可以通过定义良好接口和消息共享数据,没有必要共享一个数据库,模块化和微服务最大区别是模块的一切发生在同一个进程内。最终一致性问题可不容小觑。使用模块化架构最终一致性可以是主动的策略选择。对于微服务,最终一致性是没有选择:是注定的,你只有适应。

微服务适合你的公司吗?
当你的公司达到谷歌或Netflix的规模,拥抱微服务也许有意义。你要有建立自己的平台和工具的能力。但大多数公司达不到这个规模。即使你认为你的公司会有一天成为十亿美元的独角兽,在一开始实现模块化整体架构(modularized monolith )不会有多大伤害。

使用微服务的一个好处是,不同的服务可采取不同的技术栈。然后,你必须吸引这些不同栈的人才并保持这些平台的建立和运行。

微服务能独立部署,不同的微服务可以部署到相匹配的硬件上。模块化(modularized monolith)可以水平缩放,但你需要将模块捆在一起拓展,不能独立扩展。