欢迎关注专栏:Java架构技术进阶。里面有大量batj面试题集锦,还有各种技术分享,如有好文章也欢迎投稿哦。微信公众号:慕容千语的架构笔记。欢迎关注一起进步。
模块化还是微服务?
我们的业务由一个大型应用转向微服务的时候,除了很好展示漂亮的PPT,提升KPI之外,实际操作时将整个业务切成微型服务似乎也不费吹灰之力。但这种方法真的是我们的最佳选择吗?确实,维护凌乱的单片应用程序有许多缺点。但是有一个令人信服的替代方案经常被忽视:模块化应用程序开发。在本文中,我们将探讨这种替代方案的含义,并展示它与构建微服务的关系。
什么是微服务?
微服务现在越来越火爆,但是到底什么是微服务?Sam Newman在《构建微服务》中提供了对微服务的简洁定义是:“微服务是一种小型,自主的服务,且相互之间可以协同工作。”
在《微服务架构》这本书中,作者Mike Amundsen,Irakli Nadareishvili,Ronnie Mitra和Matt McLarty通过概述微服务应用程序共享的特征说明微服务的细节:
-
体积小
-
启用消息
-
以上下文为界
-
自主开发
-
可独立部署
-
分散
-
使用自动化流程构建和发布
一、模块化的微服务
“通过微服务,我们最终可以让团队独立工作”,或者“我们的应用太复杂,这让我们放慢脚步”。这些表达只是导致开发团队走上微服务道路的众多原因中的一小部分。另一个问题是需要可扩展性和弹性。开发人员似乎总是渴望的是系统设计和开发的模块化方法。
软件开发中的模块化可以归结为三个指导原则:
1.1,强大的封装
隐藏组件内部的实现细节,导致不同部件之间的低耦合。团队可以在系统的分离部分上独立工作。
1.2,定义良好的接口
您无法隐藏所有内容(否则您的系统不会做任何有意义的事情),因此组件之间定义良好且稳定的API是必须的。组件可以由符合接口规范的任何实现替换。
1.3,显式依赖
拥有模块化系统意味着不同的组件必须协同工作。你最好有一种表达(和验证)他们关系的好方法。
上述原则可以通过微服务实现。而微服务可以用任何方式实现,只要它为其他服务公开定义良好的接口(通常是REST API)即可。其实施细节是服务的内部细节,可以在没有全系统影响或协调的情况下进行更改。但微服务之间的依赖关系在开发时通常不是很明确,导致运行时可能的服务编排失败。
因此,微服务实现了重要的模块化原则,从而带来了实实在在的好处:
-
团队可以独立工作和扩展。
-
微服务规模小,重点突出,降低了复杂性。
-
服务可在内部更改或替换,而不会产生全局影响。
当我们从一个(虽然有点臃肿)应用程序转变为分布式微服务系统,这其实带来了大量的操作复杂性。突然间,您需要不断部署许多不同的(可能是容器化的)服务。出现了新的问题:服务发现,分布式日志记录,跟踪等。现在更容易出现分布式计算下的错误了。接口和配置管理的版本控制也会成为一个主要问题。而且问题清单会一直增长下去。
事实证明,微服务之间的连接与所有单个微服务的组合业务逻辑一样复杂。虽然整体代码库中的“意大利面条代码”存在问题,但在这两者之间存在网络边界会使这些纠缠问题升级为彻头彻尾的痛苦。
ps:意大利面条代码是非结构化和难以维护的源代码的贬义词。可能由多种因素引起,例如易变的项目要求,缺乏编程风格规则以及能力或经验不足。
二、模块化替代方案
这是否意味着我们要么被降级为凌乱的大型应用,还是必须淹没在微服务疯狂的复杂性中?模块化也可以通过其他方式实现。重要的是我们可以在开发过程中有效地绘制和强制执行边界。但我们也可以通过创建一个结构良好的大型应用来实现这一目标。当然,这意味着我们往往会从编程语言和开发工具中获得帮助,以实施模块化原则。
例如,在Java中,有几个模块系统可以帮助构建应用程序。 OSGi是最着名的一个,但随着Java 9的发布,Java平台本身就添加了一个本机模块系统。模块这种特性作为一流的结构现在是语言和平台的一部分。 Java模块可以表达对其他模块的依赖关系,并在强大地封装实现类的同时公开导出接口。甚至Java平台本身(一个庞大的代码库)也使用新的Java模块系统进行了模块化。
其他语言提供类似的机制。例如,JavaScript从ES2015开始获得了一个模块系统。在此之前,Node.js已经为JavaScript后端提供了一个非标准的模块系统。但是,作为一种动态语言,JavaScript对实施接口(类型)和模块之间的封装的支持较弱。可以考虑在JavaScript之上使用TypeScript再次获得此优势。微软的.Net Framework确实具有类似Java的强类型,但它在强大的封装和程序集之间的显式依赖性方面没有直接等同于Java即将推出的模块系统。尽管如此,通过使用在.Net Core中标准化的Inversion-of-Control模式以及通过创建逻辑相关的程序集,可以实现良好的模块化体系结构。甚至C ++也在考虑在未来的版本中增加一个模块系统。许多语言对模块化越来越感兴趣,模块化本身就是一个引人注目的发展。
当我们有意识地使用开发平台的模块化功能时,您可以实现我们之前归因于微服务的相同模块化优势。从本质上讲,模块系统越好,在开发过程中获得的帮助就越多。不同的团队可以在不同的部分上工作,其中只有明确定义的接口才是团队之间的接触点。但是,在部署时,模块在一个部署单元中聚集在一起。这样,您可以防止与迁移到微服务开发和管理相关的实质复杂性和成本。当然,这意味着无法在不同的技术栈上构建每个模块,不过对大多数公司和开发团队而言,真的能掌控好多个技术栈?
三、设计模块
创建好的模块需要与创建良好的微服务相同的设计要求。模块应该模拟业务域的单个有界上下文。选择微服务边界是一项架构上重要的决策,如果做错了,会产生代价高昂的后果。而模块化应用程序中的模块边界更容易更改。类型系统和编译器通常支持跨模块的重构。重新划分微服务边界需要进行大量的人际交流,以免在运行时爆炸。
在许多方面,静态类型语言中的模块为定义良好的接口提供了更好的结构。通过另一个模块公开的类型化接口调用方法对于更改比在另一个微服务上调用REST端点要强大得多。 REST + JSON无处不在,但在没有(编译器检查的)模式的情况下,它不是良好类型的互操作性的标志。更重要的是,许多模块系统允许表达对其他模块的依赖性,当违反这些依赖项时,模块系统会禁止这样做,至少会有个明确的提示,但微服务之间的依赖关系仅在运行时实现,导致难以调试的系统。
模块也是代码所有权的自然单位。团队可以负责系统中的一个或多个模块。与其他团队共享的唯一事情是其模块的公共API。在运行时,与微服务相比,模块之间的隔离更少。毕竟,一切仍然在同一个进程中运行。
没有理由为什么单块中的模块不能像一个好的微服务那样拥有它的数据。然后,模块化应用程序内的共享可以通过模块之间定义良好的接口或消息进行,而不是通过共享数据存储区。微服务的最大区别在于一切都在进行中。不应低估最终的一致性问题。通过模块,最终的一致性可以是一个深思熟虑的战略选择,而不是无法避免的选择。对于微服务,没有选择:只能选择最终的一致性。
四、微服务什么时候适合您的组织?
那么什么时候应该转向微服务?到目前为止,我们主要专注于通过模块化来解决复杂性问题。为此,微服务和模块化应用程序都可以。但除了迄今为止提出的挑战之外,还有不同的挑战。
-
当公司有Google或Netflix的规模时,完全有理由接受微服务。这意味着,有能力构建自己的平台和工具包,并且工程师的数量也已经足够。但是大多数组织并没有以这种规模运作。哪怕是公司有一天会成为价值10亿美元的独角兽,从模块化的大型应用开始也不会造成什么问题。
-
启动单独的微服务的另一个好理由是,不同的服务本质上更适合于不同的技术栈。也就是锁,必须拥有足够的规模来吸引这些不同的技术栈中的人才,并保持这些平台的正常运行。
-
微服务还可以独立部署系统的不同部分,这在大多数模块化平台中很难(甚至不可能)。隔离部署增加了系统的弹性和容错能力。此外,缩放特性对于每个微服务可以是不同的。可以将不同的微服务部署到匹配的硬件上。模块化的整体也可以水平缩放,但是更细微的就很困难。但是在一般实践中,模块化这种方式可以支持很久的业务发展。
五、结论
如何选择?其实取决于环境,组织和应用程序本身。我们可以从模块化应用程序开始,然后随时选择转移到微服务,还可以使用模块在内部构建微服务。
如果我们追求模块化的好处,请确保我们不要落入只能使用微服务的思维模式。尽可能的在最喜欢或者最擅长的技术栈使用内置的模块化功能或框架。
欢迎关注专栏:Java架构技术进阶。里面有大量batj面试题集锦,还有各种技术分享,如有好文章也欢迎投稿哦。微信公众号:慕容千语的架构笔记。欢迎关注一起进步。
网友评论