如何使用 Spring Boot、Spring Cloud、Docker 和 Netflix 的一些开源工具设置微服务架构。
本文通过一个使用 Spring Boot、Spring Cloud 和 Docker 构建的概念验证应用程序的示例,为理解常见的微服务架构模式提供了一个起点。
代码在 Github上可用,图像在 Docker Hub 上可用。您只需一个命令即可启动整个系统。
作为这个系统的基础,我选择了一个旧项目,它的后端曾经是一个单体应用程序。该应用程序提供了一种处理个人财务、组织收入和支出、管理储蓄、分析统计数据和创建简单预测的方法。
功能服务
单体应用程序被分解为三个核心微服务。所有这些都是可独立部署的应用程序,围绕某些业务功能进行组织。
账户服务
包含一般用户输入逻辑和验证:收入/支出项目、储蓄和帐户设置
统计局
对主要统计参数执行计算并捕获每个帐户的时间序列。数据点包含标准化为基础货币和时间段的值。此数据可用于跟踪帐户生命周期中的现金流动态。
通知服务
存储用户的联系信息和通知设置(如提醒和备份频率)。计划工作人员从其他服务收集所需信息,并向订阅的客户发送电子邮件消息。
笔记
- 每个微服务都有自己的数据库,因此无法绕过 API 直接访问持久性数据。
- 对于这个项目,我使用 MongoDB 作为每个服务的主数据库。拥有多语言持久性架构也可能有意义(选择最适合服务需求的数据库类型)。
- 服务到服务的通信非常简单:微服务只使用同步 REST API 进行通信。现实世界系统中的常见做法是使用交互风格的组合。例如,执行同步 GET 请求以检索数据,并通过消息代理使用异步方法进行创建/更新操作,以解耦服务和缓冲消息。然而,这给我们带来了最终的一致性世界。
基础设施服务
分布式系统中有很多通用模式,它们可以帮助我们使描述的核心服务工作。Spring Cloud 提供了强大的工具来增强 Spring Boot 应用程序的行为以实现这些模式。我将简要介绍它们。
配置服务
Spring Cloud Config 是分布式系统的水平可扩展集中配置服务。它使用当前支持本地存储、Git 和 Subversion 的可插拔存储库层。
在这个项目中,我使用 native profile
,它只是从本地类路径加载配置文件。您可以在配置服务资源中看到 shared
目录 。现在,当 Notification-service 请求它的配置时,Config 服务使用 and 响应 (在所有客户端应用程序之间共享)。shared/notification-service.ymlshared/application.yml
客户端使用
只需构建具有 spring-cloud-starter-config
依赖关系的 Spring Boot 应用程序,其余的将由自动配置完成。
现在您的应用程序中不需要任何嵌入式属性。只需提供 bootstrap.yml
应用程序名称和配置服务 url:
spring:
application:
name: notification-service
cloud:
config:
uri: http://config:8888
fail-fast: true
使用 Spring Cloud Config,您可以动态更改应用程序配置
例如, EmailService bean 用 @RefreshScope
. 这意味着您可以更改电子邮件文本和主题行,而无需重新构建和重新启动通知服务应用程序。
首先,更改配置服务器中所需的属性。然后,对 Notification 服务执行刷新请求: curl -H "Authorization: Bearer #token#" -XPOST http://127.0.0.1:8000/notifications/refresh
您还可以使用webhook 自动执行此过程。
笔记
- 但是动态刷新有一些限制。
@RefreshScope
不适用于@Configuration
类,也不能影响@Scheduled
方法。 fail-fast
属性意味着 Spring Boot 应用程序如果无法连接到 Config Service 将立即启动失败。当您同时启动所有应用程序时,这非常有用。- 下面有重要的安全说明。
认证服务
授权职责被完全提取到单独的服务器,它为后端资源服务授予OAuth2 令牌 。Auth Server 用于用户授权以及边界内的安全机器对机器通信。
在这个项目中,我将 Password credentials
其用作用户授权的授权类型(因为它仅由本机应用程序 UI 使用)和 Client Credentials
微服务授权的授权类型。
Spring Cloud Security 提供了方便的注解和自动配置,使得这在服务器端和客户端都非常容易实现。您可以在文档中了解有关它的更多信息, 并在Auth Server 代码 中查看配置详细信息 。
从客户端来看,一切都与传统的基于会话的授权完全相同。Principal
您可以使用基于表达式的访问控制和 @PreAuthorize
注释从请求中检索 对象、检查用户角色和其他内容。
PiggyMetrics 中的每个客户端(帐户服务、统计服务、通知服务和浏览器)都有一个范围: server
用于后端服务,以及 ui
- 用于浏览器。所以我们也可以保护控制器免受外部访问,例如:
PreAuthorize("#oauth2.hasScope('server')")
@RequestMapping(value = "accounts/{name}", method = RequestMethod.GET)
public List<DataPoint> getStatisticsByAccountName(@PathVariable String name) {
return statisticsService.findByAccountName(name);
}
网关 API
如您所见,共有三个核心服务,它们将外部 API 暴露给客户端。在现实世界的系统中,这个数字会像整个系统的复杂性一样快速增长。实际上,渲染一个复杂的网页可能涉及数百个服务。
理论上,客户端可以直接向每个微服务发出请求。但显然这个选项存在挑战和限制,比如必须知道所有端点地址,分别为每个信息和平执行 http 请求,在客户端合并结果。另一个问题是可能在后端使用的非网络友好协议。
通常更好的方法是使用 API 网关。它是系统的单一入口点,用于通过将请求路由到适当的后端服务或调用多个后端服务并 聚合结果来处理请求。此外,它还可用于身份验证、洞察力、压力和金丝雀测试、服务迁移、静态响应处理、主动流量管理。
Netflix 开源 了这样一个边缘服务,现在有了 Spring Cloud,我们可以通过一个 @EnableZuulProxy
注解来启用它。在这个项目中,我使用 Zuul 来存储静态内容(UI 应用程序)并将请求路由到适当的微服务。这是通知服务的简单的基于前缀的路由配置:
zuul:
routes:
notification-service:
path: /notifications/**
serviceId: notification-service
stripPrefix: false
这意味着所有以 开头的请求 /notifications
都将被路由到通知服务。如您所见,没有硬编码的地址。Zuul 使用服务发现机制来定位通知服务实例以及断路器和负载均衡器,如下所述。
服务发现
另一种常见的架构模式是服务发现。它允许自动检测服务实例的网络位置,这些实例可能由于自动缩放、故障和升级而具有动态分配的地址。
服务发现的关键部分是注册表。我在这个项目中使用了 Netflix Eureka。Eureka 是客户端发现模式的一个很好的例子,当客户端负责确定可用服务实例的位置(使用注册表服务器)并在它们之间平衡请求时。
spring-cloud-starter-eureka-server
使用 Spring Boot,您可以使用依赖项、 @EnableEurekaServer
注释和简单的配置属性轻松构建 Eureka Registry 。
客户端支持通过 @EnableDiscoveryClient
注释和 bootstrap.yml
应用程序名称启用:
spring:
application:
name: notification-service
现在,在应用程序启动时,它将注册到 Eureka Server 并提供元数据,例如主机和端口、健康指标 URL、主页等。Eureka 从属于服务的每个实例接收心跳消息。如果心跳在可配置的时间表上失败,则将从注册表中删除该实例。
此外,Eureka 提供了一个简单的界面,您可以在其中跟踪正在运行的服务和可用实例的数量: http://localhost:8761
负载均衡器、断路器和 Http 客户端
Netflix OSS 提供了另一套很棒的工具。
丝带
Ribbon 是一个客户端负载均衡器,可让您对 HTTP 和 TCP 客户端的行为进行大量控制。与传统的负载均衡器相比,每次在线调用都不需要额外的跃点——您可以直接联系所需的服务。
开箱即用,它与 Spring Cloud 和 Service Discovery 原生集成。Eureka Client 提供了可用服务器的动态列表,因此 Ribbon 可以在它们之间进行平衡。
海斯特里克斯
Hystrix 是断路器模式的实现,它可以控制通过网络访问的依赖项的延迟和故障。主要思想是在具有大量微服务的分布式环境中停止级联故障。这有助于快速失败并尽快恢复——自我修复的容错系统的重要方面。
除了断路器控制之外,您还可以使用 Hystrix 添加一个备用方法,在主命令失败时调用该方法以获得默认值。
此外,Hystrix 会为每个命令生成有关执行结果和延迟的指标,我们可以使用这些指标来监控系统行为。
假装
Feign 是一个声明式 HTTP 客户端,与 Ribbon 和 Hystrix 无缝集成。实际上,通过一个 spring-cloud-starter-feign
依赖项和 @EnableFeignClients
注释,您就拥有了一整套负载均衡器、断路器和 HTTP 客户端,并具有明智的现成默认配置。
以下是 Account Service 的示例:
FeignClient(name = "statistics-service")
public interface StatisticsServiceClient {
@RequestMapping(method = RequestMethod.PUT, value = "/statistics/{accountName}", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
void updateStatistics(@PathVariable("accountName") String accountName, Account account);
}
- 您需要的只是一个界面
@RequestMapping
您可以在 Spring MVC 控制器和 Feign 方法之间共享 部分- 上面的示例仅指定了所需的服务 id -
statistics-service
,这要归功于通过 Eureka 进行的自动发现(但显然您可以使用特定的 url 访问任何资源)
监控仪表板
在这个项目配置中,每个带有 Hystrix 的微服务都通过 Spring Cloud Bus(使用 AMQP 代理)将指标推送到 Turbine。Monitoring 项目只是一个带有Turbine和Hystrix Dashboard的小型 Spring boot 应用程序。
让我们看看我们的系统在负载下的行为:帐户服务调用统计服务,它以不同的模拟延迟响应。响应超时阈值设置为 1 秒。
"table" data-size="normal" data-row-style="normal">
日志分析
在尝试识别分布式环境中的问题时,集中式日志记录非常有用。Elasticsearch、Logstash 和 Kibana 堆栈可让您轻松搜索和分析您的日志、利用率和网络活动数据。我的另一个项目中描述了现成的 Docker 配置 。
安全
高级安全配置超出了此概念验证项目的范围。为了更真实地模拟真实系统,请考虑使用 https 和 JCE 密钥库来加密微服务密码和配置服务器属性内容(有关详细信息,请参阅文档 )。
基础设施自动化
部署相互依赖的微服务是一个比部署单体应用程序复杂得多的过程。拥有完全自动化的基础架构非常重要。我们可以通过持续交付方法获得以下好处:
- 随时发布软件的能力。
- 任何构建最终都可能成为发布。
- 构建工件一次,根据需要进行部署。
这是一个简单的持续交付工作流程,在这个项目中实现:
在此 配置中,Travis CI 为每次成功的 Git 推送构建标记图像。因此, Docker Hublatest
上的每个微服务 始终有一个 镜像, 并且旧镜像使用 Git 提交哈希进行标记。如果需要,可以轻松部署它们中的任何一个并快速回滚。
如何运行所有的东西?
这真的很容易,我建议你尝试一下。请记住,您将启动 8 个 Spring Boot 应用程序、4 个 MongoDB 实例和 RabbitMq。确保您 4 Gb
的机器上有可用的 RAM。您始终可以通过 Gateway、Registry、Config、Auth Service 和 Account Service 运行重要的服务。
在你开始前
- 安装 Docker 和 Docker Compose。
- 导出环境变量:
CONFIG_SERVICE_PASSWORD
,NOTIFICATION_SERVICE_PASSWORD
,STATISTICS_SERVICE_PASSWORD
,ACCOUNT_SERVICE_PASSWORD
,MONGODB_PASSWORD
生产模式
在这种模式下,所有最新的镜像都将从 Docker Hub 中拉取。只需复制 docker-compose.yml
并点击 docker-compose up -d
。
发展模式
如果您想自己构建图像(例如,对代码进行一些更改),您必须克隆所有存储库并使用 Maven 构建工件。然后,运行 docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d
docker-compose.dev.yml
继承 docker-compose.yml
了在本地构建图像并公开所有容器端口以方便开发的额外可能性。
重要端点
- 本地主机:80 - 网关
- 本地主机:8761 - 尤里卡仪表板
- 本地主机:9000 - Hystrix 仪表板
- localhost:8989 - 涡轮流(Hystrix Dashboard 的来源)
- localhost:15672 - RabbitMq 管理
笔记
所有 Spring Boot 应用程序都需要已经运行Config Server 才能启动。但是由于 fail-fast
Spring Boot 属性和 restart: always
docker-compose 选项,我们可以同时启动所有容器。这意味着所有依赖容器都将尝试重新启动,直到 Config Server 启动并运行。
此外,服务发现机制在所有应用程序启动后都需要一些时间。在实例、Eureka 服务器和客户端在其本地缓存中都有相同的元数据之前,客户端无法发现任何服务,因此可能需要 3 次心跳。默认心跳周期为 30 秒。
说明:本文限于篇幅,故而只展示部分的面试内容,完整的Java面试学习文档小编已经帮你整理好了,有需要的朋友点赞+关注私信我777免费领取Java、大厂面试学习资料哦!