Spring微服务

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 63w+ 字,讲解图 2808+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2200+ 小伙伴加入学习 ,欢迎点击围观

最初由 Paul Chapman 为 Spring 博客撰写


微服务 允许从多个协作组件构建大型系统。它在进程级别上所做的事情正是 Spring 在组件级别上所做的:松散耦合的进程而不是松散耦合的组件。

例如,想象一个在线商店为用户帐户、产品目录订单处理和购物车提供单独的微服务:

不可避免地,您必须设置和配置许多移动部件才能构建这样的系统。如何让它们协同工作并不明显——您需要非常熟悉 Spring Boot,因为 Spring Cloud 大量利用它,需要多个 Netflix 或其他 OSS 项目,当然,还有一些 Spring 配置“魔法”!

在本文中,我旨在通过逐步构建尽可能简单的系统来阐明事物的工作原理。因此,我将只实现大系统的一小部分——用户账户服务。

Web 应用程序 将使用 RESTful API 向 帐户服务 微服务发出请求。我们还需要添加一个 发现 服务——这样其他进程就可以找到彼此。

此应用程序的代码位于:https: //github.com/paulc4/microservices-demo

后续1:其他资源

本文只讨论一个最小的系统。有关更多信息,您可能想阅读 Josh Long 的博客文章 使用 Spring Cloud 和 Netflix 的 Eureka 进行微服务注册和发现 ,其中展示了在 Cloud Foundry 上运行完整的微服务系统。

Spring Cloud 项目在 这里

跟进 2:SpringOne 2GX 2015

尽快在华盛顿特区的 SpringOne2GX 预订您的位置——这是了解第一手所有活动并提供直接反馈的最佳机会。在 Spring Cloud 和 Cloud Native 应用程序上会有一个完整的轨道。

好的,让我们开始吧……

服务注册

当您有多个进程一起工作时,它们需要找到彼此。如果您曾经使用过 Java 的 RMI 机制,您可能还记得它依赖于中央注册表,以便 RMI 进程可以找到彼此。微服务有相同的要求。

Netflix 的开发人员在构建系统时遇到了这个问题,并创建了一个名为 Eureka 的注册服务器(希腊语“我找到了”)。对我们来说幸运的是,他们将他们的发现服务器开源,并且 Spring 已合并到 Spring Cloud 中,这使得运行 Eureka 服务器变得更加容易。这是 完整的 应用程序:


 @SpringBootApplication
@EnableEurekaServer
public class ServiceRegistrationServer {

public static void main(String[] args) { // Tell Boot to look for registration-server.yml System.setProperty("spring.config.name", "registration-server"); SpringApplication.run(ServiceRegistrationServer.class, args); } }

真的就是这么简单!

Spring Cloud 构建于 Spring Boot 之上,并利用父 POM 和启动 POM。 POM 的重要部分是:


 @SpringBootApplication
@EnableEurekaServer
public class ServiceRegistrationServer {

public static void main(String[] args) { // Tell Boot to look for registration-server.yml System.setProperty("spring.config.name", "registration-server"); SpringApplication.run(ServiceRegistrationServer.class, args); } }

注意: Angel.SR3 是当前的“发布序列”——一组协调的发布——请参阅 Spring Cloud 主页 上的注释(向下滚动到第二部分)。

默认情况下,Spring Boot 应用程序会查找 application.properties application.yml 文件进行配置。通过设置 spring.config.name 属性,我们可以告诉 Spring Boot 寻找不同的文件——如果你在同一个项目中有多个 Spring Boot 应用程序,这很有用——我很快就会这样做。

此应用程序查找 registration-server.properties registration-server.yml 。这是 registration-server.yml 的相关配置:


 @SpringBootApplication
@EnableEurekaServer
public class ServiceRegistrationServer {

public static void main(String[] args) { // Tell Boot to look for registration-server.yml System.setProperty("spring.config.name", "registration-server"); SpringApplication.run(ServiceRegistrationServer.class, args); } }

默认情况下,Eureka 在端口 8761 上运行,但在这里我们将使用端口 1111 。此外,通过在我的流程中包含注册码,我可能是服务器或客户端。配置指定我不是客户端并停止尝试向自身注册的服务器进程。


使用领事

Spring Cloud 还支持 Consul 作为 Eureka 的替代品。您使用脚本启动 Consul Agent(其注册服务器),然后客户端使用它来查找他们的微服务。详情见这篇博 或项目 主页

现在尝试运行 RegistrationServer (有关运行应用程序的帮助,请参见 参考资料)。您可以在此处打开 Eureka 仪表板: http://localhost:1111 ,显示应用程序的部分将为空。

从现在开始,我们将提及 发现服务器 ,因为它可能是 Eureka 或 Consul(请参阅侧面板)。

创建微服务: 账户服务

微服务是处理定义明确的需求的独立流程。

当使用 Spring 配置应用程序时,我们强调松散耦合和紧密内聚,这些不是新概念(Larry Constantine 在 1960 年代后期首先定义了这些 - 参考资料 )但现在我们正在应用它们,而不是交互组件(Spring Beans),但对于交互过程。

在此示例中,我有一个简单的帐户管理微服务,它使用 Spring Data 实现 JPA AccountRepository 和 Spring REST 以提供帐户信息的 RESTful 接口。在大多数方面,这是一个简单的 Spring Boot 应用程序。

它的特别之处在于它会在启动时向 发现服务器 注册自己。下面是Spring Boot的启动类:


 @SpringBootApplication
@EnableEurekaServer
public class ServiceRegistrationServer {

public static void main(String[] args) { // Tell Boot to look for registration-server.yml System.setProperty("spring.config.name", "registration-server"); SpringApplication.run(ServiceRegistrationServer.class, args); } }

注释完成以下工作:

  1. @EnableAutoConfiguration - 将其定义为 Spring Boot 应用程序。
  2. @EnableDiscoveryClient - 这将启用服务注册和发现。在这种情况下,此进程使用其应用程序名称(见下文)向 发现服务器 服务注册自己。
  3. @Import(AccountsWebApplication.class) - 这个 Java Configuration 类设置了其他所有内容(有关更多详细信息,请参见 )。

使它成为微服务的原因是通过 @EnableDiscoveryClient 发现服务器 注册,其 YML 配置完成设置:


 @SpringBootApplication
@EnableEurekaServer
public class ServiceRegistrationServer {

public static void main(String[] args) { // Tell Boot to look for registration-server.yml System.setProperty("spring.config.name", "registration-server"); SpringApplication.run(ServiceRegistrationServer.class, args); } }

注意这个文件

  1. 将应用程序名称设置为 accounts-service 。该服务以该名称注册,也可以通过该名称访问 - 见下文。
  2. 指定要侦听的自定义端口 (2222)。我所有的进程都在使用 Tomcat,它们不能全部监听 8080 端口。
  3. Eureka 服务进程的 URL - 来自上一节。

现在运行 AccountsService 应用程序并让它完成初始化。刷新仪表板 http://localhost:1111 ,您应该会看到 ACCOUNTS-SERVICE 列在应用程序下。有时注册可能需要 10-20 秒,所以请耐心等待 - 检查 RegistrationService 的日志输出

警告: 不要尝试使用 Eclipse/STS 的内部 Web 查看器显示 XML 输出,因为它不能这样做。请改用您最喜欢的网络浏览器。

有关更多详细信息,请转到此处: http://localhost:1111/eureka/apps/ ,您应该会看到如下内容:


 @SpringBootApplication
@EnableEurekaServer
public class ServiceRegistrationServer {

public static void main(String[] args) { // Tell Boot to look for registration-server.yml System.setProperty("spring.config.name", "registration-server"); SpringApplication.run(ServiceRegistrationServer.class, args); } }

或者转到 http://localhost:1111/eureka/apps/ACCOUNTS-SERVICE 并查看 AccountsService 的详细信息 - 如果它未注册,您将获得 404。

访问微服务: Web 服务

为了使用 RESTful 服务,Spring 提供了 RestTemplate 类。这允许您将 HTTP 请求发送到 RESTful 服务器并获取多种格式的数据 - 例如 JSON 和 XML。

注意: Accounts 微服务通过 HTTP 提供 RESTful 接口,但可以使用任何合适的协议。使用 AMQP 或 JMS 的消息传递是一个明显的选择。

可以使用哪些格式取决于类路径上是否存在封送处理类 - 例如 JAXB 始终会被检测到,因为它是 Java 的标准部分。如果类路径中存在 Jackson jars,则支持 JSON。

微服务(发现)客户端可以使用 RestTemplate ,Spring 会自动将其配置为微服务感知(稍后会详细介绍)。

封装微服务访问

这是我的 客户端 应用程序的 WebAccountService 的一部分:


 @SpringBootApplication
@EnableEurekaServer
public class ServiceRegistrationServer {

public static void main(String[] args) { // Tell Boot to look for registration-server.yml System.setProperty("spring.config.name", "registration-server"); SpringApplication.run(ServiceRegistrationServer.class, args); } }

请注意,我的 WebAccountService 只是 RestTemplate 从微服务获取数据的包装器。有趣的部分是 serviceUrl RestTemplate

访问微服务

serviceUrl 由主程序提供给 WebAccountController ,后者又将其传递给 WebAccountService (如上所示):


 @SpringBootApplication
@EnableEurekaServer
public class ServiceRegistrationServer {

public static void main(String[] args) { // Tell Boot to look for registration-server.yml System.setProperty("spring.config.name", "registration-server"); SpringApplication.run(ServiceRegistrationServer.class, args); } }

需要注意的几点:

  1. WebController 是典型的 Spring MVC 基于视图的控制器,返回 HTML。该应用程序使用 Thymeleaf 作为视图技术(用于生成动态 HTML)
  2. WebServer 也是一个 @EnableDiscoveryClient ,但在这种情况下,除了向 发现服务器 注册自己(这不是必需的,因为它不提供自己的服务),它还使用 Eureka 来定位帐户服务。
  3. 从 Spring Boot 继承的默认组件扫描器设置查找 @Component 类,在这种情况下,找到我的 WebAccountController 并尝试创建它。但是,我想自己创建它,所以我像这样禁用扫描器 @ComponentScan(useDefaultFilters=false)
  4. 我传递给 WebAccountController 的 service-url 是服务用于向 发现服务器 注册自身的名称——默认情况下,这与 spring.application.name 相同,该进程是 account-service - 请参阅 account-service.yml 上面的 account-service.yml 。不需要使用大写字母,但它确实有助于强调 ACCOUNTS-SERVICE 是逻辑主机(将通过发现获得)而不是实际主机。

负载均衡 RestTemplate

RestTemplate 已由 Spring Cloud 自动配置为使用自定义 HttpRequestClient ,该客户端使用 Netflix Ribbon 进行微服务查找。 Ribbon 也是负载均衡器,因此如果您有多个可用服务实例,它会为您选择一个。 (Eureka 和 Consul 都没有自己执行负载均衡,因此我们使用 Ribbon 来代替)。

如果查看 RibbonClientHttpRequestFactory ,您将看到以下代码:


 @SpringBootApplication
@EnableEurekaServer
public class ServiceRegistrationServer {

public static void main(String[] args) { // Tell Boot to look for registration-server.yml System.setProperty("spring.config.name", "registration-server"); SpringApplication.run(ServiceRegistrationServer.class, args); } }

loadBalancer 采用逻辑服务名称(在 发现服务器 中注册)并将其转换为所选微服务的实际主机名。

RestTemplate 实例是线程安全的,可用于访问应用程序不同部分中的任意数量的服务(例如,我可能有一个 CustomerService 包装同一个 RestTemplate 实例来访问客户数据微服务)。

配置

下面是来自 web-server.yml 的相关配置。它用于:

  1. 设置应用名称
  2. 定义访问发现服务器的 URL
  3. 将 Tomcat 端口设置为 3333

 @SpringBootApplication
@EnableEurekaServer
public class ServiceRegistrationServer {

public static void main(String[] args) { // Tell Boot to look for registration-server.yml System.setProperty("spring.config.name", "registration-server"); SpringApplication.run(ServiceRegistrationServer.class, args); } }

如何运行演示

该系统的一个小演示位于 http://github.com/paulc4/microservices-demo 。克隆它并加载到您最喜欢的 IDE 或直接使用 maven。有关如何运行演示的建议包含在项目主页的 自述文件 中。

附加说明

有关这些应用程序使用 Spring Boot 的一些说明。如果你不熟悉 Spring Boot,这解释了一些“魔法”!

查看模板引擎

Eureka 仪表板(在 RegistrationServer 内部)是使用 FreeMarker 模板实现的,但其他两个应用程序使用 Thymeleaf。为了确保每个都使用正确的视图引擎,每个 YML 文件中都有额外的配置。

这是在 registration-server.yml 的末尾禁用 Thymeleaf。


 @SpringBootApplication
@EnableEurekaServer
public class ServiceRegistrationServer {

public static void main(String[] args) { // Tell Boot to look for registration-server.yml System.setProperty("spring.config.name", "registration-server"); SpringApplication.run(ServiceRegistrationServer.class, args); } }

由于 AccountService WebService 都使用 thymeleaf,我们还需要将每个指向它们自己的模板。这是 account-server.yml 的一部分:


 @SpringBootApplication
@EnableEurekaServer
public class ServiceRegistrationServer {

public static void main(String[] args) { // Tell Boot to look for registration-server.yml System.setProperty("spring.config.name", "registration-server"); SpringApplication.run(ServiceRegistrationServer.class, args); } }

web-server.yml 类似,但其模板由


 @SpringBootApplication
@EnableEurekaServer
public class ServiceRegistrationServer {

public static void main(String[] args) { // Tell Boot to look for registration-server.yml System.setProperty("spring.config.name", "registration-server"); SpringApplication.run(ServiceRegistrationServer.class, args); } }

请注意每个 spring.thymeleaf.prefix 类路径末尾的 / - 这是 至关重要的

命令行执行

当从命令行调用时,jar 被编译为自动运行 io.pivotal.microservices.services.Main - 请参阅 Main.java

可以在 POM 中看到用于设置 start-class Spring Boot 选项:


 @SpringBootApplication
@EnableEurekaServer
public class ServiceRegistrationServer {

public static void main(String[] args) { // Tell Boot to look for registration-server.yml System.setProperty("spring.config.name", "registration-server"); SpringApplication.run(ServiceRegistrationServer.class, args); } }

AccountsWeb应用程序配置


 @SpringBootApplication
@EnableEurekaServer
public class ServiceRegistrationServer {

public static void main(String[] args) { // Tell Boot to look for registration-server.yml System.setProperty("spring.config.name", "registration-server"); SpringApplication.run(ServiceRegistrationServer.class, args); } }

这是 AccountService 的主要配置类,是使用 Spring Data 的经典 Spring Boot 应用程序。注释完成了大部分工作:

  1. @SpringBootApplication - 将其定义为 Spring Boot 应用程序。这个方便的注释结合了 @EnableAutoConfiguration @Configuration @ComponentScan (默认情况下,这会导致 Spring 在包含此类的包及其子包中搜索组件 - 潜在的 Spring Beans: AccountController AccountRepository )。
  2. @EntityScan("io.pivotal.microservices.accounts") - 因为我使用的是 JPA,所以我需要指定 @Entity 类的位置。通常这是您在 JPA 的 persistence.xml 中或在创建 LocalContainerEntityManagerFactoryBean 时指定的选项。 Spring Boot 将为我创建这个工厂 bean,因为 spring-boot-starter-data-jpa 依赖项位于类路径上。因此,另一种指定在哪里可以找到 @Entity 类的方法是使用 @EntityScan 。这将找到 Account
  3. @EnableJpaRepositories("io.pivotal.microservices.accounts") - 查找扩展 Spring Data 的 Repository 标记接口的类并使用 JPA 自动实现它们 - 请参阅 Spring Data JPA
  4. @PropertySource("classpath:db-config.properties") - 用于配置我的 DataSource 属性 – 请参阅 db-config.properties

请注意, AccountsWebApplication 本身可以作为独立应用程序运行,我发现这对测试很有用。它监听默认的 Tomcat 端口:8080,所以主页是 http://localhost:8080

配置属性

如上所述,Spring Boot 应用程序寻找 application.properties application.yml 来配置自己。由于此应用程序中使用的所有三个服务器都在同一个项目中,因此它们会自动使用相同的配置。

为避免这种情况,每个人都通过设置 spring.config.name 属性指定一个替代文件。

例如这里是 WebServer.java 的一部分。


 @SpringBootApplication
@EnableEurekaServer
public class ServiceRegistrationServer {

public static void main(String[] args) { // Tell Boot to look for registration-server.yml System.setProperty("spring.config.name", "registration-server"); SpringApplication.run(ServiceRegistrationServer.class, args); } }

在运行时,应用程序将在 src/main/resources 中查找并使用 web-server.yml

记录

Spring Boot 默认为 Spring 设置 INFO 级别的日志记录。由于我们需要检查日志以获取微服务正常运行的证据,因此我将级别提高到 WARN 以减少日志记录量。

为此,需要在每个 xxxx-server.yml 配置文件中指定日志记录级别。这通常是定义它们的最佳位置,因为 不能 在属性文件中指定日志记录属性(日志记录在处理 @PropertySource 指令之前已经初始化)。 Spring Boot 手册中对此有注释,但很容易遗漏。

我没有在每个 YAML 文件中复制日志记录配置,而是选择将其放在 logback 配置文件中,因为 Spring Boot 使用 logback - 请参阅 src/main/resources/logback.xml 。所有三个服务将共享相同的 logback.xml

相关文章