如何使用 Spring Boot、Spring Cloud、Docker 和 Netflix 的一些开源工具设置微服务架构。
本文通过一个使用 Spring Boot、Spring Cloud 和 Docker 构建的概念验证应用程序的示例,为理解常见的微服务架构模式提供了一个起点。
代码在 Github上可用,图像在 Docker Hub 上可用。您只需一个命令即可启动整个系统。
作为这个系统的基础,我选择了一个旧项目,它的后端曾经是一个单体应用程序。该应用程序提供了一种处理个人财务、组织收入和支出、管理储蓄、分析统计数据和创建简单预测的方法。
单体应用程序被分解为三个核心微服务。所有这些都是可独立部署的应用程序,围绕某些业务功能进行组织。
对主要统计参数执行计算并捕获每个帐户的时间序列。数据点包含标准化为基础货币和时间段的值。此数据可用于跟踪帐户生命周期中的现金流动态。
存储用户的联系信息和通知设置(如提醒和备份频率)。计划工作人员从其他服务收集所需信息,并向订阅的客户发送电子邮件消息。
分布式系统中有很多通用模式,它们可以帮助我们使描述的核心服务工作。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
例如, 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 getStatisticsByAccountName(@PathVariable String name) {
return statisticsService.findByAccountName(name);
}
如您所见,共有三个核心服务,它们将外部 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
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 方法之间共享 部分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 运行重要的服务。
在你开始前
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
了在本地构建图像并公开所有容器端口以方便开发的额外可能性。
重要端点
笔记
所有 Spring Boot 应用程序都需要已经运行Config Server 才能启动。但是由于 fail-fast
Spring Boot 属性和 restart: always
docker-compose 选项,我们可以同时启动所有容器。这意味着所有依赖容器都将尝试重新启动,直到 Config Server 启动并运行。
此外,服务发现机制在所有应用程序启动后都需要一些时间。在实例、Eureka 服务器和客户端在其本地缓存中都有相同的元数据之前,客户端无法发现任何服务,因此可能需要 3 次心跳。默认心跳周期为 30 秒。
说明:本文限于篇幅,故而只展示部分的面试内容,完整的Java面试学习文档小编已经帮你整理好了,有需要的朋友点赞+关注私信我777免费领取Java、大厂面试学习资料哦!