微服务架构具有以下特征。
在基于Java的应用程序构建中,Spring已经成为了事实上的标准开发框架。Spring的核心是建立在依赖注入的概念上的。在普通的Java应用程序中,应用程序被分解成为类,其中每个类与应用程序中的其他类经常有明显的联系,这些联系是在代码中直接调用类的构造器,一旦代码被编译,这些联系点将无法修改。
这在大型项目中是有问题的,因为这些外部联系是脆弱的,并且进行修改可能会对其他下游代码造成多重影响。依赖注入框架(如Spring),允许用户通过约定(以及注解)将应用程序对象之间的关系外部化,而不是在对象内部彼此硬编码实例化代码,以便更轻松地管理大型Java项目。Spring在应用程序的不同的Java类之间充当一个中间人,管理着它们的依赖关系。Spring本质上就是让用户像玩乐高积木一样将自己的代码组装在一起。
Spring能够快速引入特性的特点推动了它的实际应用,使用J2EE技术栈开发应用的企业级Java开发人员迅速采用它作为一个轻量级的替代方案。J2EE栈虽然功能强大,但许多人认为它过于庞大,甚至许多特性从未被应用程序开发团队使用过。此外,J2EE应用程序强制用户使用成熟的(和沉重的)Java应用程序服务器来部署自己的应用程序。
Spring框架的迷人之处在于它能够与时俱进并进行自我改造——它已经向开发社区证明了这一点。Spring团队发现,许多开发团队正在从将应用程序的展现、业务和数据访问逻辑打包在一起并部署为单个制品的单体应用程序模型中迁移,正转向高度分布式的模型,服务能够被构建成可以轻松部署到云端的小型分布式服务。为了响应这种转变,Spring开发团队启动了两个项目,即Spring Boot和Spring Cloud。
Spring Boot是对Spring框架理念重新思考的结果。虽然Spring Boot包含了Spring的核心特性,但它剥离了Spring中的许多“企业”特性,而提供了一个基于Java的、面向REST[1]的微服务框架。只需一些简单的注解,Java开发者就能够快速构建一个可打包和部署的REST 微服务,这个微服务并不需要外部的应用容器。
注意
虽然本书会在第2章中更详细地介绍REST,但REST背后最为核心的概念是,服务应该使用HTTP动词(GET、POST、PUT和DELETE)来代表服务中的核心操作,并且使用轻量级的面向Web的数据序列化协议(如JSON)来从服务请求数据和从服务接收数据。
在构建基于云的应用时,微服务已经成为更常见的架构模式之一,因此Spring社区为开发者提供了Spring Cloud。Spring Cloud框架使实施和部署微服务到私有云或公有云变得更加简单。Spring Cloud在一个公共框架之下封装了多个流行的云管理微服务框架,并且让这些技术的使用和部署像为代码添加注解一样简便。本章随后将介绍Spring Cloud中的不同组件。
我一直以来都持有这样一个观点:如果一个软件开发框架通过了被我亲切地称为“卡内尔猴子测试”[[2]](javascript:void(0))的试验,我就认为它是经过深思熟虑和易于使用的。如果一只像我(作者)这样的“猴子”能够在10 min或者更少时间内弄明白一个框架,那么这个框架就通过了这个试验。这就是我第一次写Spring Boot服务示例的感觉。我希望读者也有同样的体验和快乐,所以,让我们花一点儿时间,看看如何使用Spring编写一个简单的“Hello World”REST服务。
在本节中,我们不会详细介绍大部分代码。这个例子的目标是让读者体会一下编写Spring Boot服务的感受。第2章中会深入更多的细节。
图1-3展示了这个服务将会做什么,以及Spring Boot微服务将会如何处理用户请求的一般流程。
图1-3 Spring Boot抽象出了常见的REST微服务任务(路由到业务逻辑、从URL中解析HTTP参数、JSON与对象相互映射),并让开发人员专注于服务的业务逻辑
这个例子并不详尽,甚至没有说明应该如何构建一个生产级别的微服务,但它同样值得我们注意,因为它只需要写很少的代码。在第2章之前,我不打算介绍如何设置项目构建文件或代码的细节。如果读者想要查看Maven pom.xml文件以及实际代码,可以在第1章对应的代码中找到它。第1章中的所有源代码都能在本书的GitHub存储库找到。
在这个例子中,创建一个名为Application
的Java类(在simpleservice/src/com/thoughtmechanix/application/simpleservice/Application.java
的Java类,它公开了一个名为/hello
的REST端点。Application
类的代码,如代码清单1-1所示。
代码清单1-1 使用Spring Boot的Hello World:一个简单的Spring微服务
package com.thoughtmechanix.simpleservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.PathVariable;
@SpringBootApplication ⇽--- 告诉Spring Boot框架,该类是Spring Boot服务的入口点
@RestController ⇽--- 告诉Spring Boot,要将该类中的代码公开为Spring RestController类
@RequestMapping(value="hello") ⇽--- 此应用程序中公开的所有URL将以/ hello前缀开头
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@RequestMapping(value="/{firstName}/{lastName}", ⇽--- Spring Boo公开为一个基于GET方法的REST端点,它将使用两个参数,即firstName和lastName
➥ method = RequestMethod.GET)
public String hello( @PathVariable("firstName") String firstName, ⇽--- 将URL中传入的firstName和lastName参数映射为传递给hello方法的两个变量
➥ @PathVariable("lastName") String lastName) {
return String.format("{\"message\":\"Hello %s %s\"}", ⇽--- 返回一个手动构建的简单JSON字符串。在第2章中,我们不需要创建任何JSON
➥ firstName, lastName);
}
}
代码清单1-1中主要公开了一个GET HTTP端点,该端点将在URL上取两个参数(firstName和lastName),然后返回一个包含消息“Hello firstName lastName”的净荷的简单JSON字符串。如果在服务上调用了端点/hello/john/carnell,返回的结果(马上展示)将会是:
{“message”:“Hello john carnell”}
让我们启动服务。为此,请转到命令提示符并输入以下命令:
mvn spring-boot:run
这条mvn命令将使用Spring Boot插件,然后使用嵌入式Tomcat服务器启动应用程序。
我们正处于历史的拐点。现代社会的几乎所有方面都可以通过互联网连接在一起。习惯于为当地市场服务的公司突然发现,他们可以接触到全球的客户群,全球更大的客户群一起涌进来的同时也带来了全球竞争。这些竞争压力意味着以下力量正在影响开发人员考虑构建应用程序的方式。
为了满足这些期望,作为应用开发人员,我们不得不接受这样一个悖论:构建高可伸缩性和高度冗余的应用程序。我们需要将应用程序分解成可以互相独立构建和部署的小型服务。如果将应用程序“分解”为小型服务,并将它们从单体制品中转移出来,那么就可以构建具有下面这些特性的系统。
为此,当我们开始讨论微服务时,请记住下面一句话:小型的、简单的和解耦的服务=可伸缩的、有弹性的和灵活的应用程序。
术语“云”已经被过度使用了。每个软件供应商都有云,每个软件供应商的平台都是支持云的,但是如果穿透这些天花乱坠的广告宣传,我们就会发现云计算有3种基本模式。它们是:
为了更好地理解这些概念,让我们将每天的任务映射到不同的云计算模型中。当你想吃饭时,你有4种选择
1)在家做饭;
(2)去食品杂货店买一顿预先做好的膳食,然后你加热并享用它;
(3)叫外卖送到家里;
(4)开车去餐厅吃饭。
图1-6展示了各种模型。
图1-6 不同的云计算模型归结于云供应商或你各自要负责什么
这些选择之间的区别是谁负责烹饪这些膳食,以及在哪里烹饪。在内部自建模型中,想要在家里吃饭就需要自己做所有的工作,还要使用家里面的烤箱和食材。商店购买的食物就像使用基础设施即服务(IaaS)计算模型一样,使用店内的厨师和烤箱预先烘烤餐点,但你仍然有责任加热膳食并在家里吃(然后清洗餐具)。
在平台即服务(PaaS)模型中,你仍然需要负责烹饪膳食,但同时依靠供应商来负责与膳食制作相关的核心任务。例如,在PaaS模型中,你提供盘子和家具,但餐厅老板提供烤箱、食材和厨师来做饭。在“软件即服务”(SaaS)模型中,你去到一家餐厅,在那里,所有食物都已为你准备好。你在餐厅吃饭,然后在吃完后买单,你也不需要自己去准备或清洗餐具。
每个模型中的关键项都是控制:由谁来负责维护基础设施,以及构建应用程序的技术选择是什么?在IaaS模型中,云供应商提供基础设施,但你需要选择技术并构建最终的解决方案;而在SaaS模型中,你就是供应商所提供的服务的被动消费者,无法对技术进行选择,同时也没有任何责任来维护应用程序的基础设施。
新兴的云平台
本书已经介绍了目前正在使用的3种核心云平台类型(即IaaS、PaaS和SaaS)。然而,新的云平台类型正在出现。这些新平台包括“函数即服务”(Functions as a Service,FaaS)和“容器即服务”(Container as a Service,CaaS)。基于FaaS的应用程序会使用像亚马逊的Lambda技术和Google Cloud函数这样的设施,应用会将代码块以“无服务器”(serverless)的形式部署,这些代码会完全在云提供商的平台计算设施上运行。使用FaaS平台,无需管理任何服务器基础设施,只需支付执行函数所需的计算周期。
使用容器即服务模型,开发人员将微服务作为便携式虚拟容器(如Docker)进行构建并部署到云供应商。与IaaS模型不同,使用IaaS的开发人员必须管理部署服务的虚拟机,而CaaS则是将服务部署在轻量级的虚拟容器中。云供应商会提供运行容器的虚拟服务器,以及用于构建、部署、监控和伸缩容器的综合工具。亚马逊的弹性容器服务(Amazon’s Elastic Container Service,Amazon ECS)就是一个基于CaaS平台的例子。在第10章中,我们将看到如何部署已构建的微服务到Amazon ECS。
需要重点注意的是,使用云计算的FaaS和CaaS模型,开发人员仍然可以构建基于微服务的架构。请记住,微服务概念的重点在于构建有限职责的小型服务,并使用基于HTTP的接口进行通信。新兴的云计算平台(如FaaS和CaaS)是部署微服务的替代基础设施机制。
微服务架构的核心概念之一就是每个服务都被打包和部署为离散的独立制品。服务实例应该迅速启动,服务的每一个实例都是完全相同的。
作为编写微服务的开发人员,我们迟早要决定是否将服务部署到下列某个环境之中。
基于云的微服务的优势是以弹性的概念为中心。云服务供应商允许开发人员在几分钟内快速启动新的虚拟机和容器。如果服务容量需求下降,开发人员可以关闭虚拟服务器,而不会产生任何额外的费用。使用云供应商部署微服务可以显著地提高应用程序的水平可伸缩性(添加更多的服务器和服务实例)。服务器弹性也意味着应用程序可以更具弹性。如果其中一台微服务遇到问题并且处理能力正在不断地下降,那么启动新的服务实例可以让应用程序保持足够长的存活时间,让开发团队能够从容而优雅地解决问题。
本书会使用Docker容器将所有的微服务和相应的服务基础设施部署到基于IaaS的云供应商。下面列出的是用于微服务的常见部署拓扑结构。
为什么不是基于PaaS的微服务
本章前面讨论了3种云平台(基础设施即服务、平台即服务和软件即服务)。对于本书,我选择专注于使用基于IaaS的方法构建微服务。虽然某些云供应商可以让开发人员抽象出微服务的部署基础设施,但我选择保持独立于供应商并部署应用程序的所有部分(包括服务器)。
例如,亚马逊、Cloud Foundry和Heroku可以让开发人员无需知道底层应用程序容器即可部署服务。它们提供了一个Web接口和API,以允许将应用程序部署为WAR或JAR文件。设置和调优应用程序服务器和相应的Java容器被抽象了出来。虽然这很方便,但每个云供应商的平台与其各自的PaaS解决方案有着不同的特点。
IaaS方案虽然需要更多的工作,但可跨多个云供应商进行移植,并允许开发人员通过产品覆盖更广泛的受众。就个人而言,我发现基于PaaS的云解决方案可以快速启动开发工作,但一旦应用程序拥有足够多的微服务,开发人员就会开始需要云服务商提供的IaaS风格的灵活性。
本章前面提到过新的云计算平台,如函数即服务(FaaS)和容器即服务(CaaS)。如果不小心,基于FaaS的平台就会将代码锁定到一个云供应商平台上,因为代码会被部署到供应商特定的运行时引擎上。使用基于FaaS的模型,开发人员可能会使用通用的编程语言(Java、Python、JavaScript等)编写服务,但开发人员仍然会将自己严格束缚在底层供应商的API和部署函数的运行时引擎上。
本书中构建的服务都会打包为Docker容器。本书选择Docker的原因之一是,作为容器技术,Docker可以部署到所有主要的云供应商之中。稍后在第10章中,本书将演示如何使用Docker打包微服务,然后将这些容器部署到亚马逊云平台。
尽管构建单个微服务的概念很易于理解,但运行和支持健壮的微服务应用程序(尤其是在云中运行)不只是涉及为服务编写代码。编写健壮的服务需要考虑几个主题。图1-7强调了这些主题。
图1-7 微服务不只是业务逻辑,还需要考虑服务的运行环境以及服务的伸缩性和弹性
下面我们来更详细地了解一下图1-7中提及的要点。
本书采用基于模式的方法来回答这些问题。通过基于模式的方法,本书列出可以跨不同技术实现来使用的通用设计。虽然本书选择了使用Spring Boot和Spring Cloud来实现本书中所使用的模式,但开发人员完全可以把这些概念和其他技术平台一起使用。具体来说,本书涵盖以下6类微服务模式:
让我们深入了解一下这些模式。
核心微服务开发模式解决了构建微服务的基础问题,图1-8突出了我们将要讨论的基本服务设计的主题。
图1-8 在设计微服务时必须考虑服务是如何通信以及被消费的
微服务路由模式负责处理希望消费微服务的客户端应用程序,使客户端应用程序发现服务的位置并路由到服务。在基于云的应用程序中,可能会运行成百上千个微服务实例。需要抽象这些服务的物理IP地址,并为服务调用提供单个入口点,以便为所有服务调用持续强制执行安全和内容策略。
服务发现和路由回答了这个问题:如何将客户的服务请求发送到服务的特定实例?
图1-9 服务发现和路由是所有大规模微服务应用的关键部分
因为微服务架构是高度分布式的,所以必须对如何防止单个服务(或服务实例)中的问题级联暴露给服务的消费者十分敏感。为此,这里将介绍4种客户端弹性模式。
图1-10展示了这些模式如何在服务表现不佳时,保护服务消费者不受影响。第5章将会介绍这些主题。
图1-10 使用微服务时,必须保护服务调用者远离表现不佳的服务。记住,
慢速或无响应的服务所造成的中断并不仅仅局限于直接关联的服务
写一本微服务的书绕不开微服务安全性。在第7章中我们将介绍3种基本的安全模式。这3种模式具体如下。
图1-11展示了如何实现上述3种模式来构建可以保护微服务的验证服务。
图1-11 使用基于令牌的安全方案,可以实现服务验证和授权,而无需传递客户端凭据
本书现在不会太深入图1-11中的细节。需要一整章来介绍安全是有原因的(实际上它本身就可以是一本书)。
微服务架构的优点是单体应用程序被分解成可以彼此独立部署的小的功能部件,而它的缺点是调试和跟踪应用程序和服务中发生的事情要困难得多。
因此,本书将介绍以下3种核心日志记录和跟踪模式。
图1-12展示了这些模式如何配合在一起。第9章中将会更加详细地介绍日志记录和跟踪模式。
微服务架构的核心原则之一是,微服务的每个实例都应该和其他所有实例相同。“配置漂移”(某些文件在部署到服务器之后发生了一些变化)是不允许出现的,因为这可能会导致应用程序不稳定。
一句经常说的话
“我在交付准备服务器上只做了一个小小的改动,但是我忘了在生产服务器中也做这样的改动。”多年来,我在紧急情况团队中工作时,许多宕机系统的解决方案通常是从开发人员或系统管理员的这些话开始的。工程师(和大多数人一般)是以良好的意图在操作。工程师并不是故意犯错误或使系统崩溃,相反,他们尽可能做到最好,但他们会变得忙碌或者分心。他们调整了一些服务器上的东西,打算回去在所有环境中做相同的调整。
在以后某个时间点里,出现了中断状况,每个人都在搔头挠耳,想要知道其他环境与生产环境之间有什么不同。我发现,微服务的小规模与有限范围的特点创造了一个绝佳机会——将“不可变基础设施”概念引入组织:一旦部署服务,其运行的基础设施就再也不会被人触碰。
不可变基础设施是成功使用微服务架构的关键因素,因为在生产中必须要保证开发人员为特定微服务启动的每个微服务实例与其他微服务实例相同。
为此,本书的目标是将基础设施的配置集成到构建部署过程中,这样就不再需要将软件制品(如Java WAR或EAR)部署到已经在运行的基础设施中。相反,开发人员希望在构建过程中构建和编译微服务并准备运行微服务的虚拟服务器镜像。部署微服务时,服务器运行所需的整个机器镜像都会进行部署。
图1-13阐述了这个过程。本书最后将介绍如何更改构建和部署管道,以便将微服务及运行的服务器部署为单个工作单元。第10章将介绍以下模式和主题。
图1-13 开发人员希望微服务及其运行所需的服务器成为在不同环境间作为整体部署的原子制件
使用这些模式和主题的目的是,在配置漂移影响到上层环境(如交付准备环境或生产环境)之前,尽可能快地公开并消除配置漂移。
注意
本书中的代码示例(除了第10章)都将在本地机器上运行。前两章的代码可以直接从命令行运行,从第3章开始,所有代码将被编译并作为Docker容器运行。
本节将简要介绍在构建微服务时会使用的Spring Cloud技术。这是一个高层次的概述。在书中使用各项技术时,我们会根据需要为读者讲解这些技术的细节。
从零开始实现所有这些模式将是一项巨大的工作。幸好,Spring团队将大量经过实战检验的开源项目整合到一个称为Spring Cloud的Spring子项目中。
Spring Cloud将Pivotal、HashiCorp和Netflix等开源公司的工作封装在一起。Spring Cloud简化了将这些项目设置和配置到Spring应用程序中的工作,以便开发人员可以专注于编写代码,而不会陷入配置构建和部署微服务应用程序的所有基础设施的细节中。
图1-14将1.9节中列出的模式映射到实现它们的Spring Cloud项目。
图1-14 可以将这些直接可用的技术与本章探讨的微服务模式对应起来
下面让我们更详细地了解一下这些技术。
Spring Boot是微服务实现中使用的核心技术。Spring Boot通过简化构建基于REST的微服务的核心任务,大大简化了微服务开发。Spring Boot还极大地简化了将HTTP类型的动词(GET、PUT、POST和DELETE)映射到URL、JSON协议序列化与Java对象的相互转化,以及将Java异常映射回标准HTTP错误代码的工作。
Spring Cloud Config通过集中式服务来处理应用程序配置数据的管理,因此应用程序配置数据(特别是环境特定的配置数据)与部署的微服务完全分离。这确保了无论启动多少个微服务实例,这些微服务实例始终具有相同的配置。Spring Cloud Config拥有自己的属性管理存储库,也可以与以下开源项目集成。
通过Spring Cloud服务发现,开发人员可以从客户端消费的服务中抽象出部署服务器的物理位置(IP或服务器名称)。服务消费者通过逻辑名称而不是物理位置来调用服务器的业务逻辑。Spring Cloud服务发现也处理服务实例的注册和注销(在服务实例启动和关闭时)。Spring Cloud服务发现可以使用Consul和Eureka作为服务发现引擎。
Spring Cloud与Netflix的开源项目进行了大量整合。对于微服务客户端弹性模式,Spring Cloud封装了Netflix Hystrix库和Netflix Ribbon项目,开发人员可以轻松地在微服务中使用它们。
使用Netflix Hystrix库,开发人员可以快速实现服务客户端弹性模式,如断路器模式和舱壁模式。
虽然Netflix Ribbon项目简化了与诸如Eureka这样的服务发现代理的集成,但它也为服务消费者提供了客户端对服务调用的负载均衡。即使在服务发现代理暂时不可用时,客户端也可以继续进行服务调用。
Spring Cloud使用Netflix Zuul项目为微服务应用程序提供服务路由功能。Zuul是代理服务请求的服务网关,确保在调用目标服务之前,对微服务的所有调用都经过一个“前门”。通过集中的服务调用,开发人员可以强制执行标准服务策略,如安全授权验证、内容过滤和路由规则。
Spring Cloud Stream(https://cloud.spring.io/spring-cloud-stream/)是一种可让开发人员轻松地将轻量级消息处理集成到微服务中的支持技术。借助Spring Cloud Stream,开发人员能够构建智能的微服务,它可以使用在应用程序中出现的异步事件。此外,使用Spring Cloud Stream可以快速将微服务与消息代理进行整合,如RabbitMQ和Kafka。
Spring Cloud Sleuth允许将唯一跟踪标识符集成到应用程序所使用的HTTP调用和消息通道(RabbitMQ、Apache Kafka)之中。这些跟踪号码(有时称为关联ID或跟踪ID)能够让开发人员在事务流经应用程序中的不同服务时跟踪事务。有了Spring Cloud Sleuth,这些跟踪ID将自动添加到微服务生成的任何日志记录中。
Spring Cloud Sleuth与日志聚合技术工具(如Papertrail)和跟踪工具(如Zipkin)结合时,能够展现出真正的威力。Papertail是一个基于云的日志记录平台,用于将日志从不同的微服务实时聚合到一个可查询的数据库中。Zipkin可以获取Spring Cloud Sleuth生成的数据,并允许开发人员可视化单个事务涉及的服务调用流程。
Spring Cloud Security是一个验证和授权框架,可以控制哪些人可以访问服务,以及他们可以用服务做什么。Spring Cloud Security是基于令牌的,允许服务通过验证服务器发出的令牌彼此进行通信。接收调用的每个服务可以检查HTTP调用中提供的令牌,以确认用户的身份以及用户对该服务的访问权限。
此外,Spring Cloud Security支持JSON Web Token。JSON Web Token(JWT)框架标准化了创建OAuth2令牌的格式,并为创建的令牌进行数字签名提供了标准。
要实现代码供应,我们将会转移到其他的技术栈。Spring框架是面向应用程序开发的,它(包括Spring Cloud)没有用于创建“构建和部署”管道的工具。要实现一个“构建和部署”管道,开发人员需要使用Travis CI和Docker这两样工具,前者可以作为构建工具,而后者可以构建包含微服务的服务器镜像。
为了部署构建好的Docker容器,本书的最后将通过一个例子,阐述如何将整个应用程序栈部署到亚马逊云上。
在本章最后这一节中,我们概要回顾一下要使用的各种Spring Cloud技术。因为每一种技术都是独立的服务,要详细介绍这些服务,整整一章的内容都不够。在总结这一章时,我想留给读者一个小小的代码示例,它再次演示了将这些技术集成到微服务开发工作中是多么容易。
与代码清单1-1 中的第一个代码示例不同,这个代码示例不能运行,因为它需要设置和配置许多支持服务才能使用。不过,不要担心,在设置服务方面,这些Spring Cloud服务(配置服务,服务发现)的设置是一次性的。一旦设置完成,微服务就可以不断使用这些功能。在本书的开头,我们无法将所有的精华都融入一个代码示例中。
代码清单1-2中的代码快速演示了如何将远程服务的服务发现、断路器、舱壁以及客户端负载均衡集成到“Hello World”示例中。
代码清单1-2 Hello World Service使用Spring Cloud
package com.thoughtmechanix.simpleservice;
// 为了简洁,省略了其他import语句
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
@SpringBootApplication
@RestController
@RequestMapping(value="hello")
@EnableCircuitBreaker ⇽--- 使服务能够使用Hystrix和Ribbon库
@EnableEurekaClient
public class Application { ⇽--- 告诉服务,它应该使用Eureka服务发现代理注册自身,并且服务调用是使用服务发现来“查找”远程服务的位置的
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@HystrixCommand(threadPoolKey = "helloThreadPool") ⇽--- 包装器使用Hystrix断路器调用helloRemoteServiceCall方法
public String helloRemoteServiceCall(String firstName, String lastName){
ResponseEntity<String> restExchange =
➥ restTemplate.exchange(
➥ "http://logical-service-id/name/ ⇽--- 使用一个装饰好的RestTemplate类来获取一个“逻辑”服务ID,Eureka在幕后查找服务的物理位置
➥ [ca]{firstName}/{lastName}",
➥ HttpMethod.GET,
➥ null, String.class, firstName, lastName);
return restExchange.getBody();
}
@RequestMapping(value="/{firstName}/{lastName}", method = RequestMethod.GET)
public String hello(@PathVariable("firstName") String firstName,
➥ @PathVariable("lastName") String lastName) {
return helloRemoteServiceCall(firstName, lastName);
}
}
这段代码包含了很多内容,让我们慢慢分析。记住,这个代码清单只是一个例子,在第1章的GitHub仓库源代码中是找不到的。把它放在这里,是为了让读者了解本书后面的内容。
开发人员首先应该要注意的是@EnableCircuitBreaker
和@EnableEurekaClient
注解。@EnableCircuitBreaker
注解告诉Spring微服务,将要在应用程序使用Netflix Hystrix库。@EnableEurekaClient
注解告诉微服务使用Eureka服务发现代理去注册它自己,并且将要在代码中使用服务发现去查询远程REST服务端点。注意,配置是在一个属性文件中的,该属性文件告诉服务要进行通信的Eureka服务器的地址和端口号。读者第一次看到使用Hystrix是在声明hello
方法时:
@HystrixCommand(threadPoolKey = "helloThreadPool")
public String helloRemoteServiceCall(String firstName,String lastName)
@HystrixCommand
注解做两件事。第一件事是,在任何时候调用helloRemoteService Call
方法,该方法都不会被直接调用,这个调用会被委派给由Hystrix管理的线程池。如果调用时间太长(默认为1 s),Hystrix将介入并中断调用。这是断路器模式的实现。第二件事是创建一个由Hystrix管理的名为helloThreadPool
的线程池。所有对helloRemoteServiceCall
方法的调用只会发生在此线程池中,并且将与正在进行的任何其他远程服务调用隔离。
最后要注意的是helloRemoteServiceCall
方法中发生的事情。@EnableEurekaClient
的存在告诉Spring Boot,在使用REST服务调用时,使用修改过的RestTemplate
类(这不是标准的Spring RestTemplate
的工作方式)。这个RestTemplate
类允许用户传入自己想要调用的服务的逻辑服务ID:
ResponseEntity restExchange = restTemplate.exchange
➥ (http://logical-service-id/name/{firstName}/{lastName}
在幕后,RestTemplate
类将与Eureka服务进行通信,并查找一个或多个“name”服务实例的实际位置。作为服务的消费者,开发人员的代码永远不需要知道服务的位置。
另外,RestTemplate
类使用Netflix的Ribbon库。Ribbon将会检索与服务有关的所有物理端点的列表。每当客户端调用该服务时,它不必经过集中式负载均衡器就可以对客户端上不同服务实例进行轮询(round-robin)。通过消除集中式负载平衡器并将其移动到客户端,可以消除应用程序基础设施中的其他故障点(故障的负载平衡器)。
我希望此刻读者会印象深刻,因为只需要几个注解就可以为微服务添加大量的功能。这就是Spring Cloud背后真正的美。作为开发人员,我们可以利用Netflix和Consul等知名的云计算公司的微服务功能,这些功能是久经考验的。如果在Spring Cloud之外使用这些功能,可能会很复杂并且难以设置。Spring Cloud简化了它们的使用,仅仅是使用一些简单的Spring Cloud注解和配置条目。
我想要确保本书提供的示例都是与开发人员的工作息息相关的。为此,我将围绕一家名为ThoughtMechanix的虚构公司的冒险(不幸事件)来组织本书的章节以及对应的代码示例。
ThoughtMechanix是一家软件开发公司,其核心产品EagleEye提供企业级软件资产管理应用程序。该产品覆盖了所有关键要素:库存、软件交付、许可证管理、合规、成本以及资源管理。其主要目标是使组织获得准确时间点的软件资产的描述。
该公司成立了大概10年,尽管营收增长强劲,但在内部,他们正在讨论是否应该革新其核心产品,将它从一个单体内部部署的应用程序转移到云端。对该公司来说,与EagleEye相关的平台革新是“生死”时刻。
该公司正在考虑在新架构上重构其核心产品EagleEye。虽然应用程序的大部分业务逻辑将保持原样,但应用程序本身将从单体设计中分解为更小的微服务设计,其部件可以独立部署到云端。本书中的示例不会构建整个ThoughtMechanix应用程序。相反,读者将从问题领域构建特定的微服务,然后使用各种Spring Cloud(和一些非Spring Cloud)技术来构建支持这些服务的基础设施。
成功采用基于云的微服务架构的能力将影响技术组织的所有成员。这包括架构团队、工程团队、测试团队和运维团队。每个团队都需要投入,最终,当团队重新评估他们在这个新环境中的职责时,他们可能需要重组。让我们开始与ThoughtMechanix的旅程,读者将开始一些基础工作——识别和构建EagleEye中使用的几个微服务,然后使用Spring Boot构建这些服务。
[1] 虽然本书在稍后的第2章中会介绍REST,但是Roy Fielding阐述如何基于REST构建应用的博士论文仍然值得一读(http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm)。在对REST概念的阐述上,它依然是最棒的材料之一。
端弹性模式、安全模式、日志记录和跟踪模式以及构建和部署模式。
[1] 虽然本书在稍后的第2章中会介绍REST,但是Roy Fielding阐述如何基于REST构建应用的博士论文仍然值得一读(http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm)。在对REST概念的阐述上,它依然是最棒的材料之一。