微服务是软件体系结构领域最流行的流行语之一。关于微服务的基础知识和好处的学习材料很多,但是关于如何在实际企业场景中使用微服务的资源很少。
在本文中,我将介绍微服务架构(MSA)的关键架构概念,以及如何在实践中使用这些架构原理。
整体架构
企业软件应用程序旨在满足众多业务需求;给定的软件应用程序提供数百种功能,所有这些功能都堆积在单个整体应用程序中。例如,ERP,CRM和其他各种软件系统被构建为具有数百种功能的整体。如此庞大的软件应用程序的部署,故障排除,扩展和升级是一场噩梦。
面向服务的体系结构(SOA)旨在通过引入服务的概念,应用程序提供的相似功能的聚集和分组来克服某些上述限制。使用SOA,可以将软件应用程序设计为粗粒度服务的组合。但是,在SOA中,服务范围非常广泛。这导致具有数十种操作(功能)的复杂,庞大的服务,以及复杂的消息格式和标准(例如:所有WS *标准)。
整体架构
在大多数情况下,SOA中的服务彼此独立。但是它们与所有其他服务一起部署在同一运行时中(考虑一下将多个Web应用程序部署到同一Tomcat实例中)。与单片软件应用程序相似,这些服务具有通过累积各种功能随着时间而增长的习惯。从字面上看,这将这些应用程序变成了与通用整体应用程序(如ERP)没有区别的整体结构。该图显示了包含多个服务的零售软件应用程序。所有这些服务都部署到同一应用程序运行时中。因此,这是一个整体架构的很好的例子。这是基于整体架构的应用程序的一些特征。
整体应用程序被设计,开发和部署为一个单元。整体应用极其复杂。这导致维护,升级和添加新功能的噩梦。很难用Monolithic架构实践敏捷开发和交付方法。需要重新部署整个应用程序以更新其中的一部分。该应用程序必须按单个单元进行扩展,从而难以管理相互冲突的资源需求(例如,一项服务需要更多的CPU,而另一项则需要更多的内存)一种不稳定的服务可能会导致整个应用程序崩溃。采用新技术和框架真的很困难,因为所有功能都必须基于同类技术/框架。微服务架构
微服务架构(MSA)的基础是将单个应用程序开发为一组小型独立服务,这些独立服务在其自己的流程中运行,独立开发和部署。
在微服务架构的大多数定义中,将其解释为将整体中可用的服务隔离为一组独立服务的过程。但是,在我看来,微服务不仅仅是将整体中可用的服务拆分为独立的服务。
关键思想是,通过查看整体提供的功能,我们可以确定所需的业务能力。然后,可以将那些业务功能实现为完全独立,细粒度且自包含的(微)服务。它们可能在不同的技术堆栈之上实现,并且每种服务都针对非常特定且有限的业务范围。
因此,我们上面解释的在线零售系统场景可以通过微服务架构实现,如下图所示。通过微服务体系结构,零售软件应用程序被实现为一组微服务。因此,正如您在下面看到的那样,根据业务需求,还有一个整体服务是根据整体中存在的原始服务集创建的。因此,很明显,使用微服务体系结构超出了在整体中拆分服务的范围。
微服务架构
让我们深入研究微服务的关键体系结构原理,更重要的是,让我们集中讨论如何在实践中使用它们。
设计微服务:大小,范围和功能
您可能正在使用Microservices Architecture从头开始构建软件应用程序,或者可能将现有的应用程序/服务转换为微服务。无论哪种方式,正确决定微服务的大小,范围和功能都是非常重要的。这可能是您在实践中实现微服务架构时最初遇到的最困难的事情。
让我们讨论与微服务的大小,范围和功能有关的一些关键的实际问题和误解。
代码行/团队的规模是糟糕的指标:关于基于实现的代码行或团队规模(即,两个比萨饼团队)来确定微服务的规模的讨论很多。但是,这些指标被认为是非常不切实际和糟糕的指标,因为我们仍然可以使用更少的代码/具有两个比萨饼的团队来开发服务,但完全违反了微服务架构的原则。“微型”这个词有点误导性:大多数开发人员倾向于认为他们应该尝试使服务尽可能小。这是一个误解。SOA上下文:在SOA上下文中,服务通常实现为整体,并支持数十种操作/功能。因此,拥有类似于SOA的服务并将其重新命名为微服务并不会给您带来微服务架构的任何好处。那么,我们应该如何在微服务架构中正确设计服务?
设计微服务准则
单一责任原则(SRP):微服务的业务范围有限且重点突出,可以帮助我们满足服务开发和交付中的敏捷性。在微服务的设计阶段,我们应该找到它们的边界,并使它们与业务功能(在Domain-Driven-Design中也称为受限上下文)保持一致。确保微服务设计确保敏捷/独立地开发和部署服务。我们的重点应该放在微服务的范围上,而不是缩小服务范围。服务的(正确)大小应该是促进给定业务能力所需的大小。与SOA中的服务不同,给定的微服务应具有很少的操作/功能和简单的消息格式。通常最好的做法是从相对广泛的服务边界开始,然后随着时间的推移重构为较小的服务边界(基于业务需求)。在我们的零售用例中,您可以发现我们已经将整体的功能分为四个不同的微服务,即“库存”,“会计”,“运输”和“商店”。他们正在解决一个有限但集中的业务范围,以便每个服务彼此完全分离,并确保开发和部署的敏捷性。
微服务中的消息传递
在单片应用程序中,使用功能调用或语言级方法调用来调用不同处理器/组件的业务功能。在SOA中,这已转向更加松散耦合的Web服务级别消息传递,该消息传递主要基于SOAP并基于HTTP,JMS等不同协议。具有数十种操作和复杂消息模式的Web服务是Web服务普及的主要阻力。对于微服务架构,要求具有简单轻量的消息传递机制。
同步消息传递-REST,节流
对于Microservices Architecture中的同步消息传递(客户端希望服务及时响应并等待它得到响应),REST是一致的选择,因为它提供了一种简单的消息传递样式,该消息传递样式基于资源API样式通过HTTP请求-响应来实现。因此,大多数微服务实现都使用HTTP以及基于资源API的样式(每种功能都由一种资源表示,并且在这些资源之上执行操作)。
使用REST接口公开微服务
使用Thrift(可以在其中定义微服务的接口定义),以替代REST / HTTP同步消息传递。
异步消息传递-AMQP,STOMP,MQTT
对于某些微服务场景,要求使用异步消息传递技术(客户端不希望立即响应,或者根本不接受响应)。在这种情况下,异步消息传递协议(例如AMQP,STOMP或MQTT)被广泛使用。
消息格式-JSON,XML,Thrift,ProtoBuf,Avro
为微服务确定最佳消息格式是另一个关键因素。传统的单片应用程序使用复杂的二进制格式,基于SOA / Web服务的应用程序使用基于复杂消息格式(SOAP)和架构(xsd)的文本消息。在大多数基于微服务的应用程序中,它们使用简单的基于文本的消息格式,例如HTTP资源API样式之上的JSON和XML。在需要二进制消息格式的情况下(文本消息在某些用例中可能变得很冗长),微服务可以利用二进制消息格式,例如二进制Thrift,ProtoBuf或Avro。
服务合同-定义服务接口-Swagger,RAML,节俭IDL
当您将业务功能实现为服务时,您需要定义和发布服务合同。在传统的整体应用程序中,我们几乎找不到用于定义应用程序业务功能的功能。在SOA / Web服务世界中,WSDL用于定义服务协定,但是,众所周知,WSDL并不是复杂的且与SOAP紧密耦合,因此不是定义微服务协定的理想解决方案。
由于我们是基于REST架构样式构建微服务的,因此我们可以使用相同的REST API定义技术来定义微服务的契约。因此,微服务使用标准的REST API定义语言(例如Swagger和RAML)来定义服务协定。
对于不基于HTTP / REST的其他微服务实现(例如Thrift),我们可以使用协议级别的接口定义语言(IDL)(例如:Thrift IDL)。
集成微服务(服务间/流程通信)
在微服务体系结构中,软件应用程序被构建为一套独立的服务。因此,为了实现业务用例,需要在不同的微服务/流程之间具有通信结构。这就是微服务之间的服务间/流程通信如此重要的原因。
在SOA实现中,使用企业服务总线(ESB)可以促进服务之间的服务间通信,并且大多数业务逻辑位于中间层(消息路由,转换和编排)。但是,微服务体系结构促进消除中央消息总线/ ESB,并将“智能性”或业务逻辑转移到服务和客户端(称为智能端点)。
由于微服务使用诸如HTTP,JSON等标准协议,因此在微服务之间进行通信时,与不同协议集成的要求极小。微服务通信中的另一种替代方法是使用轻量级消息总线或网关,且路由功能最少,并充当“哑管道”,而网关上未实现任何业务逻辑。基于这些样式,微服务体系结构中出现了几种通信模式。
点对点样式-直接调用服务
在点对点样式中,整个消息路由逻辑都驻留在每个端点上,并且服务可以直接进行通信。每个微服务都公开一个REST API,给定的微服务或外部客户端可以通过其REST API调用另一个微服务。
具有点对点连接的服务间通信请点击输入
显然,该模型适用于相对简单的基于微服务的应用程序,但是随着服务数量的增加,这将变得极为复杂。毕竟,这就是在传统SOA实现中使用ESB的确切原因:摆脱凌乱的点对点集成链接。让我们尝试总结微服务通信的点对点样式的主要缺点。
非功能性需求(例如最终用户身份验证,限制,监视等)必须在每个微服务级别上实现。复制通用功能的结果是,每个微服务实现都可能变得复杂。服务与客户端之间的所有通信都无法控制(甚至用于监视,跟踪或筛选)通常,直接通信风格被视为大规模微服务实现的微服务反模式。因此,对于复杂的微服务用例,我们可以拥有轻量级的中央消息传递总线,该轻量级的中央消息传递总线可以为微服务提供抽象层,并且可以用于实现各种非功能性,而不是具有点对点连接或中央ESB。能力。此样式称为API网关样式。
API网关样式
API网关样式背后的关键思想是使用轻量级消息网关作为所有客户端/消费者的主要入口点,并在网关级别实现常见的非功能性要求。通常,API网关允许您通过REST / HTTP使用托管API。因此,在这里,我们可以将通过API-GW作为托管服务实现为微服务的业务功能公开。实际上,这是微服务架构和API管理的结合,可为您提供两全其美的体验。
所有微服务都通过API-GW公开
在我们的零售业务场景中,如上图所示,所有微服务都通过API-GW公开,这是所有客户端的单一入口点。如果微服务想要使用另一个微服务,则也需要通过API-GW来完成。
API-GW样式具有以下优点:
能够在网关级别为现有微服务提供所需的抽象。例如,API网关可以提供每个客户端不同的API,而不是提供一种千篇一律的样式API。网关级别的轻量级消息路由/转换。应用非功能性功能(例如安全性,监视和限制)的中心位置。通过使用API-GW模式,由于所有非功能性需求都在网关级别实现,因此微服务变得更加轻量级。API-GW样式很可能是大多数微服务实现中使用最广泛的模式。
消息代理样式
微服务可以与异步消息传递场景集成,例如单向请求和使用队列或主题的发布-订阅消息传递。给定的微服务可以是消息生产者,并且可以异步将消息发送到队列或主题。然后,消费微服务可以消费来自队列或主题的消息。这种样式使消息生产者与消息使用者分离,中间消息代理将缓冲消息,直到使用者能够处理它们为止。生产者微服务完全不了解消费者微服务。
使用pub-sub的基于异步消息传递的集成
消费者/生产者之间的通信通过基于异步消息传递标准(例如AMQP,MQTT等)的消息代理来促进。
分散数据管理
在整体架构中,应用程序将数据存储在单个集中式数据库中,以实现应用程序的各种功能。
整体式应用程序使用集中式数据库来实现
在微服务架构中,功能分散在多个微服务中,如果我们使用相同的集中式数据库,则微服务将不再彼此独立(例如,如果数据库架构已从给定的微服务中更改,则将中断其他几种服务)。因此,每个微服务都必须具有自己的数据库。
每个微服务都有自己的私有数据库
这是在微服务架构中实施分散数据管理的关键方面。
每个微服务都可以具有一个私有数据库,以保留实现其提供的业务功能所需的数据。给定的微服务只能访问专用私有数据库,而不能访问其他微服务的数据库。在某些业务场景中,您可能必须为单个事务更新多个数据库。在这种情况下,其他微服务的数据库应仅通过其服务API进行更新(不允许直接访问该数据库)分散的数据管理为您提供了完全解耦的微服务,并且可以自由选择不同的数据管理技术(SQL或NoSQL等,每种服务使用不同的数据库管理系统)。但是,对于涉及多个微服务的复杂事务用例,必须使用每个服务提供的API来实现事务行为,并且逻辑位于客户端或中介(GW)级别。
分散治理
微服务架构支持分散式治理。
通常,“治理”是指建立和加强人员和解决方案如何共同协作以实现组织目标。在SOA的上下文中,SOA治理指导可重用服务的开发,确定如何设计和开发服务以及随着时间的推移这些服务将如何变化。它在服务的提供者和这些服务的消费者之间建立协议,告诉消费者他们可以期望什么以及提供者他们有义务提供什么。在SOA治理中,有两种常用的治理类型:
设计时治理-定义和控制服务创建,设计和服务策略的实现运行时治理-在执行过程中强制执行服务策略的能力那么,微服务环境中的治理到底意味着什么?在微服务架构中,微服务被构建为具有各种技术和平台的完全独立和解耦的服务。因此,无需为服务设计和开发定义通用标准。因此,我们可以将微服务的去中心化治理功能总结如下:
在微服务架构中,不需要进行集中的设计时治理。微服务可以自行决定其设计和实现。微服务架构促进了公共/可重用服务的共享。某些运行时管理方面(例如SLA,限制,监视,通用安全要求和服务发现)可以在API-GW级别实现。服务注册和服务发现
在微服务架构中,您需要处理的微服务数量非常多。而且,由于微服务的快速和敏捷开发/部署性质,它们的位置会动态变化。因此,您需要在运行时找到微服务的位置。解决此问题的方法是使用服务注册表。
服务注册
服务注册表包含微服务实例及其位置。微服务实例在启动时在服务注册表中注册,并在关机时注销。消费者可以通过服务注册表找到可用的微服务及其位置。
服务发现
为了找到可用的微服务及其位置,我们需要一种服务发现机制。服务发现机制有两种类型,客户端发现和服务器端发现。让我们仔细看看那些服务发现机制。
客户端发现-通过这种方法,客户端或API-GW通过查询服务注册表来获取服务实例的位置。
客户端发现
服务器端发现-使用这种方法,客户端/ API-GW将请求发送到在已知位置运行的组件(例如负载均衡器)。该组件调用服务注册表并确定微服务的绝对位置。
客户端发现
诸如Kubernetes(http://kubernetes.io/v1.1/docs/user-guide/services.html)之类的微服务部署解决方案提供了服务端发现机制。
部署方式
在微服务架构方面,微服务的部署起着至关重要的作用,并且具有以下关键要求:
能够独立于其他微服务部署/取消部署。必须能够在每个微服务级别进行扩展(给定的服务可能比其他服务获得更多的流量)。快速构建和部署微服务。一个微服务中的故障一定不会影响其他任何服务。Docker(一个开放源代码引擎,可让开发人员和系统管理员在Linux环境中部署自给自足的应用程序容器)提供了一种很好的方式来部署微服务以满足上述要求。涉及的关键步骤如下:
将微服务打包为(Docker)容器映像。将每个服务实例部署为一个容器。根据更改容器实例的数量来完成扩展。当我们使用Docker容器时,构建,部署和启动微服务将更快(比常规VM快得多)Kubernetes通过允许将Linux容器集群作为一个系统进行管理,跨多个主机管理和运行Docker容器,提供容器的共置,服务发现和复制控制,扩展了Docker的功能。如您所见,大多数这些功能在我们的微服务环境中也是必不可少的。因此,使用Kubernetes(在Docker之上)进行微服务部署已成为一种极其强大的方法,尤其是对于大规模微服务部署。
将微服务构建和部署为容器。
在上图中,它显示了零售应用程序的微服务部署的概述。每个微服务实例都作为一个容器部署,每个主机有两个容器。您可以任意更改在给定主机上运行的容器的数量。
安全
在实际场景中使用微服务时,保护微服务是非常普遍的要求。在进入微服务安全性之前,让我们快速看一下我们通常如何在整体应用程序级别上实现安全性。
在典型的单片应用程序中,安全性在于找到“谁是呼叫者”,“呼叫者能做什么”以及“我们如何传播该信息”。这通常是在一个公共安全组件上实现的,该组件位于请求处理链的开始,并且该组件使用底层用户存储库(或用户存储)填充所需的信息。那么,我们可以直接将此模式转换为微服务架构吗?是的,但这需要在每个微服务级别实现的安全组件,该组件正在与集中式/共享用户存储库进行对话并检索所需的信息。这是解决微服务安全性问题的非常乏味的方法。相反,我们可以利用广泛使用的API安全标准(例如OAuth2和OpenID Connect)为我们的微服务安全问题找到更好的解决方案。在深入探讨这一点之前,让我概述一下每个标准的目的以及如何使用它们。
OAuth2-是访问委派协议。客户端通过授权服务器进行身份验证,并获得一个不透明的令牌,称为“访问令牌”。访问令牌的用户/客户端信息为零。它仅具有对只能由授权服务器检索的用户信息的引用。因此,这被称为“参考令牌”,即使在公共网络/互联网中也可以安全地使用此令牌。OpenID Connect的行为类似于OAuth,但除了访问令牌外,授权服务器还会发布一个ID令牌,其中包含有关用户的信息。这通常由JWT(JSON Web令牌)实现,并由授权服务器签名。因此,这确保了授权服务器和客户端之间的信任。因此,JWT令牌被称为“按值令牌”,因为它包含用户的信息,并且显然在内部网络之外使用它是不安全的。现在,让我们看看在零售示例中如何使用这些标准来保护微服务。
具有OAuth2和OpenID Connect的微服务安
如上图所示,这些是实现微服务安全性涉及的关键步骤:
保留对OAuth和OpenID Connect服务器(授权服务器)的身份验证,以便在有人有权使用数据的情况下,微服务成功提供访问权限。
使用API-GW样式,其中所有客户端请求都有一个入口点。
客户端连接到授权服务器并获取访问令牌(按引用令牌)。然后将访问令牌与请求一起发送到API-GW。
网关处的令牌转换-API-GW提取访问令牌并将其发送到授权服务器以检索JWT(按值令牌)。
然后,GW将此JWT连同请求一起传递到微服务层。
JWT包含帮助存储用户会话等的必要信息。如果每个服务都可以理解JSON Web令牌,则您已经分发了身份机制,该机制允许您在整个系统中传输身份。
在每个微服务层,我们可以有一个处理JWT的组件,这是一个非常简单的实现。
交易次数
微服务中的交易支持如何?实际上,支持跨多个微服务的分布式事务是非常复杂的任务。微服务架构本身鼓励服务之间的无事务协调。
想法是,给定的服务是完全独立的,并且基于单一责任原则。跨多个微服务进行分布式事务的需求通常是微服务体系结构中设计缺陷的征兆,通常可以通过重构微服务的范围来解决。但是,如果必须在多个服务之间分配事务,则可以通过在每个微服务级别引入“补偿操作”来实现这种情况。关键思想是,给定的微服务基于单一职责原则,如果给定的微服务无法执行给定的操作,我们可以认为这是整个微服务的失败。
失败的设计
微服务架构引入了一组分散的服务,并且与单片设计相比,增加了在每个服务级别出现故障的可能性。给定的微服务可能会由于网络问题,基础资源的不可用等原因而失败。不可用或无响应的微服务不应导致整个基于微服务的应用程序崩溃。因此,微服务应该是容错的,能够在可能的情况下恢复,并且客户端必须妥善处理它。
另外,由于服务随时可能发生故障,因此能够快速检测(实时监视)故障并在可能的情况下自动恢复服务也很重要。
在微服务上下文中,有几种常用的模式来处理错误。
断路器
当您对微服务进行外部调用时,您需要为每次调用配置一个故障监视器组件,并且当故障达到某个阈值时,该组件将停止对该服务的任何进一步调用(使电路跳闸)。在一定数量的请求处于打开状态(您可以配置)之后,将电路更改回关闭状态。
这种模式非常有用,可避免不必要的资源消耗,由于超时而导致的请求延迟,并且还使我们有机会监视系统(基于活动的开路状态)。
舱壁
由于微服务应用程序包含微服务的数量,因此基于微服务的应用程序一部分的故障不应影响其余的应用程序。隔板模式是关于隔离应用程序的不同部分的,因此应用程序中服务的故障不会影响任何其他服务。
超时
超时模式是一种机制,当您认为微服务不会响应时,它可以让您停止等待微服务的响应。您可以在此处配置希望等待的时间间隔。
那么,我们在哪里以及如何在微服务中使用这些模式?在大多数情况下,这些模式中的大多数都适用于网关级别。这意味着当微服务不可用或没有响应时,在网关级别,我们可以决定是否使用断路器或超时模式将请求发送到微服务。同样,在网关级别实现诸如隔板等模式也是非常重要的,因为它是所有客户端请求的单个入口点,因此赠与服务的失败不应影响其他微服务的调用。
另外,网关可以用作中心点,当通过网关调用每个微服务时,我们可以获得状态并监视每个微服务。