微服务的真正价值
微服务架构已被确立为一种准标准,并被部署在许多项目中。它们是如何影响应用程序的生命周期的?
模块化
模块化是软件开发中最重要的概念之一。一个模块形成一个独立的系统组件,由于定义明确的内部和外部接口,它变得可交换。一个模块的内部绑定和模块之间的耦合定义了一个系统的结构。绑定是对模块紧凑性的定性衡量。一个模块的元素之间的关系是很重要的,而且这些关系应该是尽可能的与众不同。
好的架构中的模块都拥有非常强的绑定关系。模块的耦合是对它们之间接口的定性衡量。重要的特征是耦合机制、接口宽度和通信的性质。模块之间的低耦合度使得每个模块都可以独立开发和运行。此外,松散连接的模块更容易交换,简化了维护和进一步开发。
正确的切割
在定义一个架构时,最重要同时也是最困难的任务是将系统分解成模块。David L. Parnas在他1971年发表的文章《论将系统分解成模块的标准》[Par71]中已经阐述了形成模块的标准的重要性。就分解成模块而言,我们现在参考了Eric Evans的领域驱动设计[DDD]中的有界上下文的概念。有界上下文将领域分成子域,在这些子域中定义的模型是有效的。
图1
一个经常被用来说明模块化架构的例子是网上商店。通常,有三个模块,如图1所示:客户、订单和产品目录。不幸的是,在这个小例子中,两个模块之间已经存在着很强的关系。客户和订单相互调用。这就导致了一个循环。循环应该被避免,因为它产生了高度的耦合性,导致两个模块只能在同一时间被修改。此外,我们也不清楚这些模块是否被正确定义,或者将它们合并成一个模块不是更好吗?
当分解成模块时,也应该出现理想模块有多大的问题。微服务架构中的 "微 "表明,模块应该尽可能小。如果我们遵循有界的语境原则,大小似乎并不是决定性的。举个例子。一个B2C网店只需要几个实体来描述它的客户,如客户姓名、送货地址和支付信息。在B2B的情况下,一个客户要由十几个实体来描述。但在这两种情况下,我们都是在看客户模块。
大泥球
模块化软件架构的反面被描述为 "一个大泥球"。一个 "大泥球 "描述了一个随着时间的推移而成长的系统,它没有显示出明显的结构。这样的系统很难维护,往往只能被整体替换,在进一步的开发过程中造成非常高的成本。
那么,如何才能避免 "大泥球 "的出现呢?我们必须定义和交流一个目标架构。为此,要对模块进行定义。开发一种模式语言也有帮助。这将有助于所有参与者更好地沟通。根据我在软件现代化项目中的经验,正确分配责任是非常重要的。特别是基础设施的代码应该与技术代码分开。
验证架构是另一个重要方面。一旦写下第一行代码,就必须根据目标架构来验证代码库。Structure101、Sonargraph、ArchUnit、jqAssistant等工具有助于可视化和检查依赖关系。由于微服务架构的特点是分布式的依赖关系,人们应该重视接口管理,反思这些关系是否正确和必要。
SOA 2.0
微服务架构通常被表示为SOA 2.0。围绕着面向服务的架构[SOA]的炒作已经在九十年代中期开始了。SOA和微服务一样,都是一种架构风格。在SOA中,服务可以被描述如下。" 服务是可重复业务活动的逻辑表示,具有一定的结果(例如,检查客户的信用度,查询天气数据)。一个服务是自成一体的,可以由其他服务组成。服务代表了其用户的一个黑匣子"。
微服务的最大区别是,SOA没有做出关于分布的声明。应用程序的SOA服务通常作为接口提供。由于SOA的定义,服务一词可以与无服务器架构的功能相比较。
微服务与单片机
微服务架构的反面是单片机。基于微服务的架构和单片机的架构在分布方面尤其不同。分布式系统不仅有优势,而且也带来了有关网络通信的问题。例如,服务可能发生故障或表现出较长的响应时间。诸如断路、缓存和冗余等概念开始发挥作用,增加了复杂性。Martin Fowler在他的文章[Fow03]中写道:"分布式对象设计的第一定律。不要分布你的对象!"
为了独立地分布和扩展模块,这些模块必须被分布。在这种情况下,通常选择通过RESTH/TTP的同步通信方式。这可能导致O/R-mapper可怕的n+1选择问题转移到微服务之间的通信层。
图2
图2显示了一个客户端试图查询一组客户的所有订单。一个天真的方法是,在分别查询每个客户的订单之前,先向客户服务部门进行初始查询,以获得客户列表。这种沟通方式不可避免地会导致性能问题。
图3
分布式通信的另一个非常重要的方面是所涉及的通信方式。图3显示,除了同步通信方式,还有异步通信方式,这将导致更好的解耦。同步通信可以使用请求/回复模型进行模拟。然而,问题是,我们应该等待多长时间才能得到答案,如果答案没有到来,我们应该怎么做?一种可能性是重复查询。然而,如果服务没有实现空闲,查询可能被处理两次。
事件驱动的通信模型提供了最大可能的解耦。这个模型支持服务之间的松散耦合。一个很大的优点是,发送者不需要知道接收者处于哪种状态,接收者是谁,或者事件是如何处理的(如果有的话)。查询方面的责任被消除了,因为不需要等待答案。基于事件的系统的另一个优点是,任何数量的服务都可以消费这些事件,从而允许数据复制。
分布式的大泥球
图4
在图4中,X轴表示分布程度,Y轴表示模块化程度。这张图很好地说明了,分离成最好的独立模块是良好架构的最重要标准。然后这些模块是否分布,只受可交换性和独立缩放的要求影响。然而,如果我们放弃了模块化,两种架构都会形成一个 "大泥球"。在亚特兰大DevNexus 2016会议期间举行的关于模块化单体的演讲中,Simon Brown恰当地说道。"如果你不能建立一个模块化的单体,那么你为什么认为微服务是解决方案?"
不幸的是,一个分布式的 "大泥球 "比单片式的更不利于维护和进一步发展。一个单体的代码库可以用分析工具进行分析和转换。然而,对于一个分布式的 "大泥球",需要对运行时间进行分析,以确定哪些模块可以相互通信。因此,验证架构和设计规范是否得到满足在任何架构中都是很重要的。
特征
图5
过去,微服务架构大多存在于后端,这导致了所谓前端单体的形成。这使一个优势化为乌有,即独立性。自足系统(SCS)的想法,即包含所有层的模块,是解决这个问题的一个变种。另一方面,在微服务架构中,每个微服务都有自己的数据管理,这是一个原则。然而,在现代化项目中,Fowler描述为 "集成数据库 "的数据管理方法往往是打破单体的唯一可能性,因为现有的数据模型被严重规范化,而且表格有很多依赖关系(见图5)。
此外,其他系统,如数据仓库,经常使用该数据模型,使分解更加复杂。将单体转换为微服务往往遵循 "绞杀 "模式。Martin Fowler在澳大利亚旅行后,于2004年首次描述了这种设计模式[Fow04]。要转换的系统被新的模块陆续拥抱,就像绞杀者一样,直到旧的系统什么都不剩。与大爆炸式的迁移相比,其风险要小得多。
结论
模块化是软件架构中最重要的方面,由于许多关于微服务的讨论,它又回到了人们的视野中。正确的实施,微服务架构将导致更好的模块化。开发人员可以更好地遵循预定的目标架构,因为查询一个远方的服务要比实例化一个类更难。微服务架构也会对未来的迁移产生积极的影响,因为独立分布的模块更容易被替换。重要的是,要知道为什么要选择微服务架构,以及是否愿意为分布式服务付出代价。人们绝不应该重复过去的错误,产生一个基于微服务的 "大泥球"。