微服务简介
微服务是过去几年中IT界出现的最热门趋势之一。它们为什么会日益受到欢迎呢?要理解这一点其实相当简单,因为它们的优点和缺点都是众所周知的,而所谓的缺点却可以使用正确的工具轻松解决。它们所具有的优势包括可扩展性、灵活性和独立交付,这些都是其迅速普及的原因。另外,还有一些早期的IT趋势也对微服务的普及有所影响,这里所说的趋势是指常见的基于云的环境的使用,以及从关系数据库到NoSQL的迁移等。
本章将要讨论的主题包括:
使用SpringCloud进行云原生开发。基于微服务架构中最重要的元素。服务间通信模型。断路器和后备模式介绍。
微服务的优点
微服务(Microservices)的概念定义了IT系统体系结构的方法,该方法将应用程序划分为实现业务需求的松散耦合服务的集合。实际上,这是面向服务的架构(Service-OrientedArchitecture,SOA)概念的变体。迁移到基于微服务的体系结构的最重要的好处之一是能够连续交付大型复杂应用程序。
截至目前,开发人员可能已经有很多机会阅读到有关微服务的书籍或文章,大多数资料都会详细描述它们的优点和缺点。在笔者看来,使用微服务确实有许多优点。首先,对于项目中的新开发人员来说,微服务相对较小且易于理解。开发人员通常希望确保在一个地方执行的代码修改不会对应用程序的所有其他模块产生不良影响。采用微服务之后,开发人员对这一点可谓深具信心,因为微服务可以做到仅实现一个业务领域,这和一体化(Monolithic)应用程序是不一样的,一体化应用程序有时甚至需要将各种看似无关的功能和插件等都整合到同一个软件包里。当然,这不是全部,笔者还注意到,一般来说,在小型微服务中维护高质量代码比在一体化应用程序中要容易得多,因为一体化应用程序通常会有许多开发人员介入修改。
关于微服务架构,值得嘉许的第二件事是其业务划分。到目前为止,当开发人员不得不处理复杂的企业系统时,总是会将系统划分为子系统,而这个划分是根据其他子系统完成的。例如,电信企业总是有一个计费子系统,开发人员会创建一个隐藏计费复杂性的子系统并对外提供应用程序编程接口(ApplicationProgrammingInterface,API),然后开发人员会发现自己需要数据,但这些数据无法存储在计费系统中,因为它不容易自定义,所以开发人员需要再创建另一个子系统,这将导致开发人员只能构建一个复杂的子系统网格,这个子系统网格很不容易理解,对于企业中的新员工来说尤其如此。使用微服务则不会遇到这样的问题,如果它们设计得很好,那么每个微服务都应该负责整个选定的业务领域。在某些情况下,无论企业活跃的部门如何,这些领域都是相似的。
使用SpringFramework构建微服务
尽管微服务的概念多年来一直是一个重要的主题,但仍然没有很多稳定的框架支持运行完整微服务环境所需的所有功能。自从笔者开始使用微服务之后,就一直在努力跟上最新的框架并找出针对微服务需求而开发的功能。当然还有一些其他有趣的解决方案,如Vert.x或ApacheCamel,但它们都不是SpringFramework的良配。
SpringCloud实现了基于微服务架构中使用的所有经过验证的模式,如服务注册表(ServiceRegistries)、配置服务器(ConfigurationServer)、断路器(CircuitBreakers),云总线(CloudBuses)、OAuth2模式(OAuth2Patterns)和API网关(APIGateways)。它拥有强大的网络讨论社区,因此其新功能会以高频率发布。它基于Spring的开放式编程模型,该模型被全球数百万Java开发人员使用。它也有编写得非常优秀的说明文档。在线查找许多可用的SpringFramework使用示例时,开发人员不会遇到任何问题。
云原生应用程序开发方法
微服务本质上与云计算平台相关联,但微服务的实际概念并不是什么新鲜事。这种方法已经在IT开发领域应用了很多年,现在,通过云解决方案的普及,它已经发展到更高的水平。所以,不难理解这种受欢迎程度的原因。与企业的内部部署(On-Premise)解决方案相比,云的使用为开发人员提供了可扩展性、可靠性和低维护成本等优点,这导致云原生应用程序开发方法(Cloud-nativeApplicationDevelopmentApproach)的兴起,旨在为开发人员提供类似云的弹性扩展、不可变部署和一次性实例具有的所有优势。这一切都归结为一件事——减少满足新要求所需的时间和成本。如今,软件系统和应用程序正在不断改进。如果开发人员采用传统的开发方法,即基于一体化开发方式,则代码库会持续增长并且变得过于复杂,以至于无法进行修改和维护,引入新功能、框架和技术也变得很难,这反过来会影响创新并抑制新想法。在这一点上,开发人员无疑深有同感。
这枚硬币还有另一面。今天几乎每个人都在考虑迁移到云端,部分原因是它显得很时尚。每个人都需要这个吗?当然不是。那些不确定是否要将应用程序迁移到远程云提供商(如AWS、Azure或Google)的人,至少希望拥有内部部署的私有云或Docker容器(DockerContainer)。但是,无论是迁移到远程云端还是部署私有云服务器,都需要不小的成本开支,它真的能带来足够的好处吗?在讨论云原生开发和云平台之前,值得思考这个问题。
我并不是想阻止开发人员使用SpringCloud,恰恰相反,我们必须彻底了解云原生的开发才能做到物超所值。以下是对于云原生应用程序开发的一个非常好的定义:
“原生云应用程序是专为云计算环境设计的程序,而不是简单地迁移到云计算。”
Spring旨在加速开发人员的云原生开发。使用SpringBoot构建应用程序非常快。本书第2章将详细介绍如何做到这一点。SpringCloud实现了微服务架构模式,并且可以帮助开发人员使用该领域最流行的解决方案。使用这些框架开发的应用程序可以轻松地调整为部署在PivotalCloudFoundry(PCF)或Docker容器上,但它们也可以按传统方式在一台或多台计算机上作为独立进程启动,并且开发人员将拥有微服务方法的优势。接下来不妨深入探讨一下微服务架构。
了解微服务架构
现在假设有一个客户要求设计一个解决方案。他们需要某种银行应用程序来保证整个系统内的数据一致性。到目前为止,该客户一直在使用Oracle数据库,并且还购买了技术支持。在不考虑太多的情况下,我们可以选择基于关系数据模型设计一个一体化应用程序。现在先来看一个系统设计的简化示意图,如图1.1所示。
有4个实体映射到数据库中的表:
口第一个是客户(Customer)实体,它将存储和检索活动客户端列表。每个客户可以有一个或多个账户,由账户(Account)实体操作。
图1.1系统设计简化示意图
转移(Transfer)实体负责在系统内的账户之间执行所有资金转移汇兑。
还有一个产品(Product)实体,创建该实体可用于存储客户的存款和分配给客户的信用额度等信息。
在没有进一步详细说明的情况下,该应用程序公开了API,该API提供了在设计的数据库上实现功能所需的所有操作。当然,该实现将符合三层模型。
一致性不再是最重要的要求,它甚至不是强制性的。客户需要一个解决方案,但并不希望这个解决方案的开发强行要求重新部署整个应用程序。它应该是可扩展的,并且应该能够轻松扩展新的模块和功能。此外,客户也不会向开发人员施加压力,要求他们必须使用Oracle或其他关系数据库——不仅如此,他还乐于避免使用它。这些理由足以决定迁移到微服务吗?我们假设已经足够。接下来,开发人员可以将一体化应用程序划分为4个独立的微服务,每个微服务都有一个专用的数据库。在某些情况下,它仍然可以是关系数据库,而在其他情况下,它也可以是NoSQL数据库。现在,我们的系统包含许多独立构建的服务,并将在我们的环境中运行。随着微服务数量的增加,系统复杂性也在不断提高。我们希望隐藏外部API客户端的复杂性,外部API客户端不应该知道它与服务X而不是服务Y进行通信。网关负责将所有请求动态路由到不同的端点。在这里,动态(Dynamically)一词意味着它应该基于服务发现(ServiceDiscovery)中的条目,本章后面的第1.4.1节“理解服务发现的必要性”将对此有详细的讨论。
隐藏特定服务或动态路由的调用并不是API网关的唯一功能。由于它是我们系统的入口点,因此它可以是跟踪重要数据、收集请求的指标性数据以及计算其他统计数据的好地方。它可以丰富请求头(RequestHeader)或响应头(ResponseHeader),以便包含可供系统内应用程序使用的其他一些信息。它应该执行一些安全操作,如身份验证和授权,并且应该能够检测每个资源的需求并拒绝不满足它们的请求。如图1.2所示就是这样一个系统示例,它由4个独立的微服务组成,它们对于在API网关后面的外部客户端来说是隐藏的。
理解服务发现的必要性
现在假设开发人员已经将一体化应用程序划分为更小的独立服务。从外部看,我们的系统看起来仍然和以前一样,因为它的复杂性隐藏在API网关之后。实际上,目前的微服务并不算多,但以后也可能会有更多。此外,每个微服务都可以与其他微服务交互。
这意味着每个微服务都必须保留有关其他微服务的网络地址的信息。保持这样的配置可能非常麻烦,特别是当涉及手动覆盖每个配置时。如果这些地址在重启后动态变化了该怎么办?图1.3显示了我们的示例微服务之间的调用路由。
示例微服务之间的调用路由示意图示例微服务之间的调用路由示意图
服务发现(ServiceDiscovery)可以自动检测这些设备在计算机网络上提供的设备和服务。在基于微服务架构的情况下,这是必要的机制。启动后的每个服务都应该在一个中心位置注册,这个中心位置可以由所有其他服务访问。注册键(RegistrationKey)应该是服务或标识符的名称,在整个系统中必须是唯一的,以便其他微服务能够使用该名称查找和调用该服务。具有给定名称的每个键都分配了一些值。在最常见的情况下,这些属性(Attribute)将指示服务的网络位置。更准确地说,它们表示微服务的一个实例,因为它可以作为在不同机器或端口上运行的独立应用程序而倍增。有时也可以发送一些其他信息,但这取决于具体的服务发现提供程序。但是,这里重要的是在一个键下,可以注册同一个服务的多个实例。除注册外,每个服务还将获取在特定发现服务器上注册的其他服务的完整列表。不仅如此,每个微服务都必须了解注册列表中的任何更改。这可以通过定期更新之前从远程服务器收集的配置来实现。
某些解决方案会将服务发现的使用与服务器配置功能相结合。当开发人员真正面对它时就会明白,这两种方法非常相似。通过服务器配置,开发人员可以集中管理系统中的所有配置文件。一般来说,这样的配置就是服务器作为表述性状态传递(RepresentationalStateTransfer,REST)Web服务。在启动之前,每个微服务都会尝试连接到服务器并获取专门为其准备的参数。其中一种方法是将这种配置存储在版本控制系统中——如Git(Git是目前最为先进的分布式版本控制系统),然后配置服务器更新其Git工作副本并将所有属性作为JSON提供。在另一种方法中,我们可以使用存储键值对(Key-ValuePairs)的解决方案,并在服务发现过程中履行提供者的角色。最受欢迎的工具是Consul和Zookeeper。图1.4给出了一个系统的架构,该系统由一些带有数据库后端的微服务组成,这些服务在一个称为发现服务(DiscoveryService)的中央服务中注册。
示例微服务均将在发现服务中注册
服务之间的通信
为了保证系统的可靠性,开发人员不能允许每个服务只有一个实例运行的情况。一般来说,每个服务至少需要有两个实例运行,以防其中一个实例出现故障。当然,也可能会有更多,但出于性能考虑,开发人员会保持较低水平。无论如何,同一服务的多个实例必须对传入的请求使用负载均衡。首先,负载均衡器(LoadBalancer)通常内置在API网关中。此负载均衡器应从发现服务器获取已注册实例的列表。如果没有理由不这样做,那么开发人员通常会使用轮询调度规则(Round-RobinRule)来平衡所有正在运行的实例之间的传入流量为50/50。同样的规则也适用于微服务端的负载均衡器。
图1.5说明了两个示例微服务的多个实例之间的服务间通信中所涉及的最重要的组件。
两个示例微服务的负载均衡器两个示例微服务的负载均衡器
大多数人在听到微服务时会认为它包含带有JSON表示法的RESTfulWeb服务,但这只是其中一种可能性。我们可以使用其他一些交互方式,当然,它们不仅适用于基于微服务的架构。应该执行的第一个分类是一对一或一对多通信。在一对一交互中,每个传入请求仅由一个服务实例处理,而在一对多交互中,它将由多个服务实例处理。但最流行的划分标准则为调用是同步的还是异步的。另外,异步通信还可以划分为通知信息。当客户端向服务发送请求但不期望回复时,它只要执行一个简单的异步调用即可,该异步调用不会阻塞线程并以异步方式进行回复。
此外,值得一提的还有响应式微服务(ReactiveMicroservices)。从版本5开始,Spring也已经支持这种类型的编程。还有一些库也支持响应式微服务,并且可以使用NoSQL数据库(如MongoDB或Cassandra)进行交互。最后一个众所周知的通信类型是发布-订阅(Publish-Subscribe)。这是一对多交互类型,其中由客户端发布消息,然后由所有侦听服务使用。一般来说,此模型将使用消息代理(MessageBroker)实现,常见的消息代理包括ApacheKafka、RabbitMQ和ActiveMQ等。
故障和断路器
前文已经讨论了与微服务架构相关的大多数重要概念。这些机制(包括服务发现、API网关和配置服务器等)是帮助开发人员创建可靠且高效的系统的有用元素。当然,即使在设计系统架构时考虑过这些问题的许多方面,开发人员也应该始终为失败做好准备。在许多情况下,失败的原因完全超出了开发人员的控制范围,如网络或数据库问题。对于基于微服务的系统,这种错误可能特别严重,因为该系统中的一个输入请求可能需要在许多后续调用中被处理。应该优先考虑的良好做法是在等待响应时始终使用网络超时。如果单个服务存在性能问题,则应尽量减少对其余服务的影响。发送一个出错的响应消息比长时间等待回复,阻塞其他线程要好得多。
网络超时问题的一个有趣的解决方案可能是断路器模式(CircuitBreakerPattern)。这是一个与微服务方法密切相关的概念。断路器负责计算成功和失败的请求。如果错误率超过假定阈值,则它会跳闸(这里采用了与电路相关的概念和术语)并导致所有进一步的尝试立即失败。在特定时间段之后,API客户端应该返回发送请求,如果成功,则关闭断路器。如果每个服务有多个实例可用,并且其中一个服务的工作速度比其他服务慢,那么结果就是在负载平衡过程中忽略了它。处理部分网络故障的第二种常用机制是回退逻辑(FallbackLogic),这是在请求失败时必须执行的逻辑。例如,服务可以返回已缓存的数据、默认值或空结果列表。就个人而言,笔者不是这个解决方案的忠实粉丝,笔者宁愿将错误代码传递到其他系统,而不是返回已缓存数据或默认值。
小结
SpringCloud的一大优势是它支持本章所讨论过的所有模式和机制。与其他一些框架不同,它们也是稳定的实现。在本书第3章“SpringCloud概述”中将详细介绍SpringCloud项目所支持的模式。
本章讨论了与微服务架构相关的最重要的概念,如云原生应用程序开发、服务发现、分布式配置、API网关和断路器模式等。本章还以企业应用程序的开发为例,提出了对这种方法的优缺点的看法。然后,在第1.4节中描述了与微服务相关的主要模式和解决方案。其中一些是众所周知的模式,它们已经存在多年,但仍在某种程度上被视为IT世界的新事物。在本小结中,我们要强调的是,微服务本质上是云原生的。也就是说,它天然地适应云平台开发模式。SpringBoot和SpringCloud等框架都可以帮助开发人员加速云原生开发。
迁移到云原生开发的主要动机是能够在保持高质量的同时更快地实现和交付应用程序。在许多情况下,微服务都可以帮助开发人员实现这一目标,但有时一体化应用程序方法也并不是一个糟糕的选择。
虽然微服务是小而独立的单元,但它们是集中管理的。诸如网络位置、配置、日志文件和指标等信息都应该存储在一个中心位置。有各种类型的工具和解决方案可以提供所有这些功能。本书后面的几乎所有章节都将详细讨论它们。SpringCloud项目旨在帮助开发人员整合所有内容。衷心希望本文所提供的内容能够有效地帮助读者掌握SpringCloud。
本文给大家讲解的内容是学习并理解基于微服务和一体化应用程序之间的区别
下篇文章给大家讲解的是详细说明如何有效地使用SpringBoot框架来创建微服务应用程序;觉得文章不错的朋友可以转发此文