vlambda博客
学习文章列表

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》飞机票系统

Chapter 7. Airline Ticket System

我们的最后一个项目——Twitter Consumers、Twitter Gathering 和 Twitter Dispatcher——非常出色。我们学到了几个令人兴奋的特性,它们是使用 Spring 5.0 中的新特性实现的。所有这些都在 Reactive Streams 中实现,并使用 Kotlin 作为编程语言。它们是 Spring 5.0 中最热门的功能;这是一个令人印象深刻的进步。

但是,这些项目明显缺少部分;我们考虑到了微服务需求。没有服务发现、分布式配置、API 网关、分布式跟踪和监控等基础设施服务。这些类型的服务在分布式系统(例如微服务架构)中是必需的。

有几个原因。首先,我们可以想到配置管理。让我们想象以下场景——在开发周期中,我们有三个环境:DEV、TST 和 PROD。这是在公司中发现的一个非常简单的标准。另外,我们有一个应用程序在 4 个微服务中解耦,那么使用最少的基础设施,我们就有 12 个服务实例;请记住,这是一个很好的场景,因为在真实情况下,我们可能会有多个微服务应用程序实例。

在前面的场景中,我们将为每个微服务维护至少三个配置文件,请记住我们需要保留三个环境的配置。然后,我们将有 12 versions 的设置。维护配置、保持文件同步和更新是一项艰巨的任务。这些文件可能包含敏感信息,例如数据库密码和消息代理的配置,不建议您将这些文件放在主机上。

在这种情况下,分布式配置可以轻松解决我们的问题。我们将在本章中学习配置服务器,以及其他基础设施服务。

让我们总结一下本章将要学习的内容:

  • How to create a Config Server 
  • Implementing a service discovery with Eureka
  • Monitoring applications with Spring Cloud Zipkin
  • Exposing the applications with the Spring Cloud Gateway

The Airline Ticket System


这些的最后几章中,我们将研究Airline Ticket System。该解决方案相当复杂,涉及大量 HTTP 集成和基于消息的解决方案。我们将探索我们从这本书的旅程中学到的东西。

我们将使用 Spring Messaging、Spring WebFlux 和 Spring Data 组件来创建解决方案。应用程序将拆分为多个微服务,以保证系统的可扩展性、弹性和容错性。 

此外,我们将提供一些基础设施服务来帮助我们提供高效的系统。将引入一些新模式,例如断路器和 OAuth。在基础设施层,我们将使用与 Spring Framework 生态系统集成的 Netflix OSS 组件。

我们应用程序的主要目的是销售机票,但要完成这个任务,我们需要构建一个完整的生态系统。我们将构建一个微服务来管理座位和飞机的特性。还将有一个微服务来管理可用的公司航班;基本思想是管理航班日期和路线。当然,我们将有一个微服务来管理乘客、票价、预订和付款。最后,我们将有一个 e-commerce API,最终用户将使用它购买机票。

Airline functionalities

我们将创建一些微服务来组成solution,然后我们将解决方案分解成小块,即微服务。为此,我们将使用限界上下文模式 < strong>领域驱动设计DDD)。

让我们看一下下面的 图,以了解我们将构建什么:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》飞机票系统

这是对我们将在这几章中所做的事情的总结;我们已经为每个微服务定义了基本功能。

现在,我们来看看组件;让我们进入下一节。

Solution diagram

以下图说明了整个解决方案,我们将在以下章节中实施:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》飞机票系统

正如我们所看到的,有不同种类的组件。一些组件将通过 网关 向最终用户(在我们的例子中是我们的客户)公开。公司用户将使用一个类别来注册航班,例如,这些微服务也将在 Gateway 上公开。

除了 Gateway 服务外,基础设施类别不会在 Internet 上公开。这些服务有助于解决方案基础架构,不应因为其中存在敏感数据而暴露。

有很多事情要做;让我们继续表演吧。

Note

DDD 使我们能够轻松处理微服务。一些 DDD 模式非常适合微服务架构风格。 Packt 目录中有很多有趣的书籍。

Spring Cloud Config Server


当我们采用微服务 architectural 风格时,有一些挑战需要解决。首先要解决的问题之一是如何管理集群中的微服务配置,以及如何使它们变得简单和分布式?

Spring Cloud Config 提供了一种 Spring 方式,基于 注解和 Spring bean。这是在生产就绪模块中解决此问题的一种简单方法。该模块包含三个主要组件,Configuration Repository,即版本控制系统,Config Server,它将提供配置,最后是 Configuration Client,它将使用来自 Config Server 的配置。

该模块通过 HTTP 接口提供配置文件。它是该项目提供的主要功能,它充当我们架构中配置的中央存储库。

我们想从类路径中删除 application.yaml 文件;我们不再需要类路径中的这个文件,因此我们将使用配置服务器为我们的应用程序提供这个文件。

现在,我们的微服务将没有配置文件,即 application.yaml。 在应用程序引导期间,应用程序将查看配置服务器以获取正确的配置,然后,应用程序将完成引导程序以使其启动并进入运行状态。

下图解释了 Config Server 和 Config Client:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》飞机票系统

我们可以看到,这里的基本思想是尝试通过Config Server分发配置。使用这种方法有一些优点。第一个将配置保存在中央存储库中。它使配置易于维护。第二个是配置使用标准协议提供服务,例如 HTTP。大多数开发人员都知道协议并使交互易于理解。最后,也是最重要的,当属性发生变化时,它可以立即反映在其他微服务中。

是时候实施它了。让我们去那里。

Note

如果我们部署在云环境中,Config Server 通常维护在私有网络上,尽管 Spring Cloud Config 支持基于对称或非对称密钥的加密和解密。请记住,微服务配置不应发布在公共网络上。

Creating the Config Server project

让我们用 Spring Initializr 创建我们的 project。转到 Spring Initializr (https://start.spring.io/) 并按照图片说明进行操作:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》飞机票系统

点击Generate Project,然后我们就可以在IDE上打开项目了。

Enabling Spring Cloud Config Server

我们将 Git 存储库用作 property 源,然后我们需要创建一个存储库来保存这些文件。但是,在此之前,让我们导航到 pom.xml 文件并查看一些有趣的东西。我们可以找到以下依赖:

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-config-server</artifactId>
</dependency>

它是一个配置服务器依赖项。它使我们能够在我们的应用程序中使用配置服务器。记住,我们需要把它放到 pom.xml 文件中来实现所需的Config Server。

Using GitHub as a repository

Spring Cloud Config Server 使我们能够使用不同的数据存储技术作为属性存储库。社区提供了一些选项,例如 Git 存储库、文件系统或 SVN 等。

我们将选择 Git 存储库,并使用 GitHub 作为主机。 

Note

我们将使用包含本书源代码的 Git 存储库。存储库位于:  https://GitHub.com/PacktPublishing/Spring-5.0-By-Example/tree/master/config-files。Spring Cloud Config Server 也支持私有仓库。为此,我们需要提供私钥/公钥。

Configuring the Spring Boot application

启用和运行配置服务器并提供我们的配置 HTTP 协议是小菜一碟。为了实现它,我们需要在我们的 Spring Boot starter 类中添加以下注解。实现如下:

package springfive.airline.configserver;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;

@EnableConfigServer
@SpringBootApplication
public class ConfigServerApplication {

public static void main(String[] args) {
    SpringApplication.run(ConfigServerApplication.class, args);
}

}

太棒了。 @EnableConfigServer 为我们创造了奇迹。它将启动配置服务器并使应用程序准备好连接。

Configuring the Git repository as a properties source

我们的配置服务器需要配置。为此,我们将使用 application.yaml 文件。该文件应该很简单,并且配置也最少。 configuration 文件应如下所示:

server:
  port: 5000

spring:
  cloud:
    config:
      name: configserver
server:
        git:
          uri: https://github.com/PacktPublishing/Spring-5.0-By-Example
search-paths: config-files*

我们已经配置了应用程序端口,这是一个常见的任务。我们命名了我们的配置服务器,最重要的部分是 server.git.uri 配置属性,它指示 Spring Framework 获取配置文件。

另一种配置是 search-paths;它允许我们在 git 存储库文件夹中搜索配置,而不是在存储库中搜索根地址。

 

 

Running the Config Server

好工作;我们的 configuration 服务器可以使用了。然后让我们运行它。我们可以使用 JAR 文件,也可以通过 IDE,由您选择所需的方式。

我们可以使用 Java 命令行或 IDE 来运行它。我更喜欢使用 IDE,因为它使我们能够调试并进行一些代码更改。

运行。

输出应如下所示:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》飞机票系统

Tomcat 启动成功;我们的配置服务器已启动并正在运行。我们可以在我们的配置服务器中找到一些不同的端点。这些端点被公开以服务于 configuration 文件。

Spring Cloud Config Server 也支持配置文件,为不同的环境提供不同的配置很重要。

Config Server 支持的模式如下:

<application-name>-<profile>.<properties|yaml>

牢记这一点非常重要。此外,必须在我们的微服务中声明 application.name 属性,以识别应用程序。

我们可以在应用引导程序上找到 Spring Cloud Config Server 提供的端点。看一下日志:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》飞机票系统

记住配置服务器支持环境;因此,端点上有一种正则表达式。查看 "/{name}-{profiles}.yml" 端点。

Testing our Config Server

我们能够通过 REST API 测试我们的配置 Server

让我们创建一个简单的 yaml 文件来创建测试;该文件应称为 dummy.yaml

info:
  message: "Testing my Config Server"
status: "It worked"

将其推送到 GitHub——如果您使用的是 GitHub 书籍,则无需执行此步骤。然后,我们可以使用以下命令调用 Config Server API:

curl http://localhost:5000/dummy/default | jq

该命令在配置文件 default 中查找 dummy 配置; URL 是不言自明的。应显示以下输出:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》飞机票系统

我们的配置服务器是完全可操作的。现在,我们将使用 Netflix Eureka 配置我们的服务发现。

Spring Cloud service discovery


服务发现是微服务架构的关键之一。微服务架构的基础是将单体应用程序解耦为具有明确边界的更小的软件。

这会影响我们在单片应用程序中的系统设计。通常,应用程序逻辑与代码有关。这意味着当应用程序运行时,在相同的上下文中调用过程或方法调用。

当我们采用微服务架构风格时,这些调用通常是外部的,换句话说,它们会通过 HTTP 调用来调用服务,例如在另一个应用程序上下文或 Web 服务器中。

那么,服务需要通过HTTP调用其他服务,但是如果这些服务的实例变化相当频繁,这些服务如何调用其他服务呢?请记住,我们正在创建分布式和可扩展的系统,其中服务实例可以根据系统使用情况增加。

这些服务需要知道其他服务在哪里运行才能调用它们。假设我们正在考虑将服务 IP 放入配置中;在此期间将很难管理并且无法跟踪机器更改。

服务发现模式解决了这一挑战。通常,该解决方案涉及一个服务注册中心,它知道所有正在运行的服务的位置。然后,客户端需要有一种 Service Registry Client 才能查询此 Service Registry 以获得所需服务的有效地址;然后服务注册中心将返回一个健康的地址,最后,客户端可以调用所需的服务。

我们来看下面的图:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》飞机票系统

Spring Cloud 服务发现支持一些服务发现实现,例如 Spring Cloud Consul 提供的 Hashicorp Consul,Spring Cloud Zookeeper 提供的 Apache Zookeeper。

我们正在使用 Netflix OSS 堆栈,我们将在其中使用由 Spring Netflix OSS 提供的 Eureka 服务器。它使我们能够将 Eureka 服务器用作托管 Spring bean。

Spring Eureka Client 提供了一个感知 Service Registry 的客户端,它可以通过几个注释和一些配置来完成——我们很快就会这样做。

我们将开始createconfigure 以下部分中的 Eureka 服务器。让我们这样做。

Note

Spring Cloud Consul 的完整文档可以在以下位置找到: https:// /cloud.spring.io/spring-cloud-consul,Spring Cloud Zookeeper 位于: https://cloud.spring.io/spring-cloud-zookeeper.

Creating Spring Cloud Eureka

为了在我们的基础设施中启用服务发现,我们需要创建一个服务实例来充当服务发现。 Spring Cloud Eureka 服务器使我们能够完成这项任务。让我们创建我们的项目。进入Spring Initializr并填写信息,如以下截图所示:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》飞机票系统

查看所需的依赖项。 Eureka 服务器是允许我们启动服务发现服务器的依赖项。

让我们在 IDE 上打开项目并开始配置它。我们将在下一节中执行此操作。

Creating the Eureka server main class

在开始配置之前,我们将创建 main 类。此类将启动 Spring Boot 应用程序。 Eureka 服务器嵌入在应用程序中。这是一个非常标准的 Spring Boot 应用程序,只有一个注解。

main 应用程序类应如下所示:

package springfive.airline.eureka;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {

public static void main(String[] args) {
    SpringApplication.run(EurekaApplication.class, args);
}

}

@EnableEurekaServer 注解将启动我们的嵌入式Eureka服务器应用程序并使其准备好使用。它还将在我们的应用程序中启用服务注册表。

Configuring the Spring Cloud Eureka server

我们的 Eureka 服务器需要使用前面部分中配置的 Spring Cloud Server 进行配置。然后,我们需要将 application.yaml 从我们的项目中移除,以便正确使用配置服务器。而不是 application.yaml, 我们需要放置 bootstrap.yaml 并在其上放置 Config Server 地址。

然后,我们需要:

  • Create discovery.yaml on GitHub
  • Create bootstrap.yaml file in the classpath project 

让我们从 discovery.yaml 文件开始。该文件应如下所示:

server:
  port: 8761

eureka:
  instance:
    hostname: localhost
    health-check-url-path: /actuator/health
    status-page-url-path: /actuator/info 
  client:
    registerWithEureka: false
    fetchRegistry: false
logging:
  level:
    com.netflix.discovery: 'ON'
    org.springframework.cloud: 'DEBUG'

有一些有趣的事情可以探索。我们使用 localhost 作为 hostname 因为我们在开发者机器上运行。关于 URL 健康检查和状态页面有几个配置 - 注意与服务器相关的配置。它们位于 eureka.instance YAML 节点下方。配置为health-check-url-pathstatus-page-url-path。我们也可以使用默认值,但是新的 Spring Boot Actuator 改变了这两个特性的 URL,所以我们需要正确配置它们。  

eureka.client YAML 节点是关于客户端配置的;在我们的例子中,我们将 registerWithEureka 设置为 false。我们不希望 Eureka 服务器也充当客户端。 fetchRegistry 配置也是如此,它是一个客户端配置,它会缓存 Eureka 注册中心的信息。

logging 节点是关于日志配置的。

太棒了——我们的 gateway.yaml 已经准备好了。

让我们在 Eureka 服务器项目类路径中创建我们的 bootstrap.yaml 文件。该文件应如下所示:

spring:
  application:
    name: discovery
cloud:
    config:
      uri: http://localhost:5000
label: master

Easy peasy – 我们已经配置了 spring.cloud.config。它指示配置服务器地址的 Spring。另外,我们配置了label,也就是我们使用 版本控制系统 (VCS) 作为存储库.

做得好。配置已准备就绪。是时候运行它了。让我们在下一节中进行。

Running the Spring Cloud Eureka server

Eureka server 可以使用了。我们将启动 Spring Boot 应用程序并将我们的 Eureka 服务器上线。我们可以使用 Java 命令行或 IDE 来运行它。我更喜欢使用 IDE,因为它使我们能够调试并进行一些代码更改。

Note

配置服务器需要运行,因为发现将找到配置文件以正确引导服务器。

运行!

我们应该在应用程序引导日志中看到以下行:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》飞机票系统

惊人的。查看日志的以下行:

2018-01-07 14:42:42.636  INFO 11191 --- [      Thread-32] e.s.EurekaServerInitializerConfiguration : Started Eureka Server

这意味着我们的 Eureka 服务器已经可以使用了。要查看解决方案,我们可以前往 Eureka 服务器主页。转到 http://localhost:8761/,会显示如下页面:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》飞机票系统

正如我们所见,还没有可用的服务实例。我们可以找到一些相关的信息比如服务器Uptime,当前数据中心 ,以及 当前时间。  General Info部分有一些信息,关于Eureka server 正在运行。

好工作。我们的服务发现服务正在运行。我们将很快使用这个基础设施。

Spring Cloud Zipkin server and Sleuth


我们的解决方案涉及一些微服务。它使我们的 解决方案易于部署且易于编写代码。每个解决方案都有一个特定的存储库和代码库。&nbsp;

在单体解决方案中,整个问题都在要部署的同一个工件中解决。通常,在 Java 中,这些工件是.jar.war.ear , 如果应用程序是按照 Java EE 5/6 规范编写的。

这些类型的应用程序的日志记录策略很容易使用(因此问题可以很容易地解决),因为一切都发生在同一个上下文中;请求是从具有业务组件的同一应用程序服务器或 Web 服务器接收的。现在,如果我们查看日志,我们可能会找到我们想要的日志条目。它使跟踪应用程序更容易发现错误和调试。

在微服务解决方案中,应用行为在分布式系统中被拆分;它大大增加了跟踪任务 因为请求可能到达 API 网关并进入微服务。他们将信息记录在不同的来源中。在这种情况下,我们需要一种日志聚合器和一种方法来识别服务之间的整个事务。

为此,Spring Cloud Sleuth 和 Spring Cloud Zipkin 可以帮助我们并使跟踪功能对开发人员来说更加舒适。

在本节中,我们将了解并了解它是如何在幕后工作的。

Infrastructure for the Zipkin server

在我们开始工作之前,我们需要配置一个Zipkin ser​​ver需要的服务。默认情况下,Zipkin ser​​ver 使用内存数据库,但不建议用于生产;通常,开发人员使用此功能来演示 Zipkin 功能。

我们将使用 MySQL 作为数据存储。 Zipkin 服务器还支持不同的来源,例如 Cassandra 和 Elasticsearch。

Spring Cloud Sleuth 支持同步和异步操作。同步操作通过 HTTP 协议进行,异步可以通过 RabbitMQ 或 Apache Kafka 完成。

要使用HTTP,即REST API,我们应该使用 @EnableZipkinServer,它将通过SpanStore<委托REST层的持久性/代码>接口。

我们将选择异步解决方案,因为它非常适合我们的项目,并且我们不希望跟踪收集器导致一些性能问题。异步解决方案使用 Spring Cloud Stream binder 来存储 Spans。我们选择 RabbitMQ 消息代理来执行此操作。它可以使用 @EnableZipkinStreamServer 注释来实现,这些注释将 Spring Sleuth 配置为使用流存储 Span

让我们创建 docker-compose-min.yaml 来引导我们的 RabbitMQ 和 MySQL 容器。该文件应如下所示:

version: '3'
services:

  rabbitmq:
    hostname: rabbitmq
image: rabbitmq:3.7.0-management-alpine
ports:
- "5672:5672"
- "15672:15672"
networks:
- airline

mysql:
    hostname: mysql
image: mysql:5.7.21
ports:
- "3306:3306"
environment:
- MYSQL_ROOT_PASSWORD=root
      - MYSQL_DATABASE=zipkin
networks:
- airline

mongo:
    hostname: mongo
image: mongo
ports:
- "27017:27017"
networks:
- airline  

redis:
    hostname: redis
image: redis:3.2-alpine
ports:
- "6379:6379"
networks:
- airline

networks:
  airline:
    driver: bridge

Note

docker-compose-min.yaml 文件可以在 GitHub,这里有MongoDB和Redis——下一章会用到。

这里没有什么特别的。我们已经声明了两个容器——RabbitMQ 和 MySQL——并暴露了主机上的端口。此外,我们还创建了 airline 网络;我们将使用这个网络来附加我们的基础设施微服务。

现在,我们可以创建我们的 Zipkin 服务器,我们将在下一节中进行。

Creating the Spring Cloud Zipkin server

我们将在 Spring Initializr 中创建我们的 Zipkin 面板 structure,然后我们需要按照说明进行操作:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》飞机票系统

太棒了——看看 Selected Dependencies部分,所有这些都是必需的。注意 Spring Boot 版本。我们选择 1.5.9,因为 Spring Boot 2 中不支持 Zipkin 服务器。这不是问题,因为我们不需要 Spring Boot 2 的特定功能。

点击 Generate Project按钮并等待下载完成。之后,在 IDE 中打开项目。

为了启用服务发现并将 Spans 存储在数据库中,我们需要将以下依赖项放入我们的 pom.xml 中:

<dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

<dependency>
 <groupId>mysql</groupId>
 <artifactId>mysql-connector-java</artifactId>
 <version>6.0.6</version>
</dependency>

第一个依赖项用于服务发现客户端,其他依赖项用于与 MySQL 的 JDBC 连接。它使我们的项目依赖项完全配置。 

让我们创建我们的 main 类来启动我们的 Zipkin 服务器。该类非常标准,但带有一些新注释:

package springfive.airline;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.sleuth.zipkin.stream.EnableZipkinStreamServer;

@SpringBootApplication
@EnableZipkinStreamServer
@EnableEurekaClient
public class ZipkinServerApplication {

public static void main(String[] args) {
  SpringApplication.run(ZipkinServerApplication.class, args);
}

}

@EnableEurekaClient 注解使应用程序能够连接到 Eureka 服务器。新注解@EnableZipkinStreamServer 指示框架与配置的代理连接以接收 Spans。请记住,它可以使用 Spring Cloud Stream Binder 来完成。

Configuring boostrap.yaml and application.yaml

在本节中,我们创建了 main 类。在我们运行它之前,我们应该创建我们的两个配置文件。 bootstrap.yamlsrc/main/resources 目录和 application.yaml< /code> 在我们的 GitHub 存储库中。它们将通过 Config Server 下载并由 Zipkin server 项目。

让我们从 bootstrap.yaml 开始:

spring:
  application:
    name: zipkin
cloud:
    config:
      uri: http://localhost:5000
label: master

没什么特别的,我们已经配置了我们的 Config Server 地址。

让我们跳到我们的 application.yaml

server:
  port: 9999

spring:
  rabbitmq:
    port: 5672
    host: localhost
  datasource:
    schema: classpath:/mysql.sql
    url: jdbc:mysql://${MYSQL_HOST:localhost}/zipkin?autoReconnect=true
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: root
    initialize: true
    continue-on-error: true
  sleuth:
    enabled: false

zipkin:
  storage:
    type: mysql

logging:
  level:
    ROOT: INFO

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

这里有一些有趣的事情。在 spring.rabbitmq 节点中,我们已经配置了我们的 RabbitMQ 代理连接。它将用于接收 Spans。在 spring.datasource 中,我们已经配置了 MySQL 连接。 Zipkin 服务器将使用它来存储数据。此外,我们还配置了如何执行 DDL 脚本来创建 zipkin 数据库。

spring.sleuth 节点被配置为不产生任何 Span 因为它是服务器,而不是客户端应用程序,我们将不在 Zipkin 服务器上执行跟踪。

zipkin 节点已用于配置 Zipkin 服务器存储类型 MySQL,在我们的例子中。

让我们运行它!

Running the Zipkin server

我们已经正确配置了 Zipkin 服务器,所以现在我们可以正常运行它了。

我们可以运行主类 ZipkinServerApplication。运行以下输出后,我们可以使用 IDE 或 Java 命令行:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》飞机票系统

干得好——Zipkin 服务器现在正在运行。我们可以看一下索引页面,看看它长什么样。 

转到 Zipkin 页面; 该页面应类似于以下屏幕截图:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》飞机票系统

此外,我们可以检查 RabbitMQ 面板以找到 Zipkin 服务器创建的队列。转到 RabbitMQ 队列 (http://localhost:15672/#/queues) 部分,页面应如下所示:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》飞机票系统

查看队列,项目创建了 sleuth.sleuth 队列,做得很好。

Zipkin 服务器已准备就绪。目前,我们没有任何 Span,因为没有应用程序向 Zipkin 发送数据。我们将在下一章中这样做。

Spring Cloud Gateway


API 网关模式帮助我们通过单个已知入口点公开我们的微服务。通常,它充当外部访问的入口点,并将调用重定向到内部微服务。

在我们的应用程序中采用 API 网关有很多好处。第一个很容易识别,它使客户端可以轻松使用 API,这意味着客户端不需要知道不同的微服务端点。

其他好处是第一个好处的结果。当我们有一个唯一的入口点时,我们也可以解决一些跨应用程序的问题,例如过滤、身份验证、节流和速率限制。

当我们采用微服务架构时,它是必不可少的部分。 

Spring Cloud Gateway 使我们能够在 Spring 管理的 bean 中以 Spring 方式使用依赖注入和 Spring Framework 提供的其他特性来拥有这些特性。 

该项目建立在 Spring Framework 5 之上,它使用 Project Reactor 作为基础。还提供了一些有趣的功能,例如 Hystrix 断路器集成以及与 Spring Cloud Discovery 客户端的集成。 

查看图表以了解 API 网关的好处:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》飞机票系统

Note

API 网关模式的完整文档可以在以下位置找到: http://microservices .io/patterns/apigateway.html

Creating the Spring Cloud Gateway project

我们将使用 Spring Initializr 创建我们的 Spring Cloud Gateway 项目;我们需要手动添加一些依赖项。让我们进入 Spring Initializr页面并创建我们的项目:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》飞机票系统

有一个全新的依赖网关,它使我们能够使用 Spring Cloud Gateway。然后点击Generate Project 等待下载完成。

之后,我们需要添加一个 missing 依赖。网关需要缺少的依赖才能与 Eureka 服务器交互;依赖的名字是 spring-cloud-starter-netflix-eureka-client。然后,让我们添加对 pom.xml 的依赖,我们需要添加以下代码段:

<dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

太好了,我们的项目配置正确,可以与 Eureka 服务器一起使用。在下一节中,我们将配置项目以与 Config Server 一起使用。

Creating the Spring Cloud Gateway main class

这部分没有秘密。 Spring Cloud Gateway 的工作方式与常见的 Spring Boot 应用程序相同。有一个 main 类将 start 嵌入式服务器并启动 整个应用程序。

我们的 main 类应该是这样的:

package springfive.airline.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@EnableEurekaClient
@SpringBootApplication
public class GatewayApplication {

public static void main(String[] args) {
  SpringApplication.run(GatewayApplication.class, args);
}

}

正如我们所见,它是一个非常标准的 Spring Boot 应用程序,配置了 @EnableEurekaClient 以与 Eureka 服务器一起作为服务发现实现。

Configuring the Spring Cloud Gateway project

主项目 structure 已准备就绪。我们将在本节中创建项目配置。为此,我们需要执行以下步骤:

  • Add a gateway.yaml file to GitHub
  • Create the bootstrap.yaml in the Gateway project

我们使用的是 Spring Cloud Config Server,因此需要在 GitHub 中创建新文件,因为 Config Server 会尝试在存储库中查找文件。在我们的例子中,我们使用 GitHub 作为存储库。

第二个任务是必要的,因为 bootstrap.yaml 文件是在应用程序完全准备好运行之前处理的。然后,在这个阶段,应用程序需要查找配置文件,为了实现这一点,应用程序需要知道 repository,在我们的例子中是 Config Server。记住 Config Server 的地址总是需要放在 bootstrap.yaml 上。

让我们创建我们的 gateway.yaml 文件——该文件应如下所示:

server:
  port: 8888
eureka:
  client:
    serviceUrl:
defaultZone: http://localhost:8761/eureka/
logging:
  level: debug

YAML 文件中的 eureka.client 节点负责配置 Eureka Client 的配置。我们需要配置我们的 Eureka 服务器地址实例。它应该指向正确的地址。

Note

Eureka 配置客户端属性有更多选项。完整的文档可以在 https://github.com/中找到Netflix/eureka/wiki/Configuring-Eureka; Netflix 团队维护 Eureka。

然后,我们需要在 Gateway 项目中创建 bootstrap.yaml 文件。该文件将指示 Spring Framework 在 Config Server 上查找配置文件,然后下载所需文件以完成应用程序引导。我们的文件应该是这样的:

spring:
  application:
      name: gateway
cloud:
    config:
      uri: http://localhost:5000
label: master

很简单。 application.name 是指示框架查找正确文件所必需的。通常,对于不同的应用程序和环境,也有许多配置文件。 

cloud.config 节点上,我们需要输入我们在前面部分中配置的 Spring Cloud Config Server 地址。

项目最终结构应如下所示:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》飞机票系统

看截图。类路径中没有 application.yaml。这给了我们几个优势;类路径项目中没有配置文件,这对我们管理微服务配置有很大帮助。

在下一节中,我们将运行它并解释整个应用程序引导过程。我们开始做吧。

Running the Spring Cloud Gateway

项目已经配置好,现在是time运行它了。 我们可以使用Java命令行或者IDE。两种方式都没有区别。 

Config Server 和 Eureka Server 需要熬夜;网关项目必须正常工作。然后,我们就可以运行项目了。

运行项目并查看日志。我们可以看到一些有趣的东西,比如项目连接到 Config Server 并下载配置,然后它连接到 Eureka 服务器并自行注册。下图解释了应用程序引导流程:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》飞机票系统

让我们看看不同的流程是什么并理解它们:

  1. The Gateway application requests the configuration file
  2. The Config Server serves the config file
  3. The Gateway application registers to the Eureka server

太棒了,我们的网关应用程序连接到我们的基础设施服务。

Checking the Eureka server

我们的网关正在运行。现在,我们可以查看 Eureka 服务器页面来确认这些信息。

转到 http://localhost:8761/, 查看 当前在Eureka注册的实例 部分。我们应该看到网关应用程序,如以下屏幕截图所示:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》飞机票系统

出色的。它运作良好。网关应用注册成功,可以通过服务发现查找。我们的网关将连接到 Eureka 服务器以获取可用服务并将请求的调用分发到正确的服务。

做得好。现在,我们可以在网关中创建路由。我们将在下一章创建航空公司微服务时执行此操作。

Creating our first route with Spring Cloud Gateway

我们的网关正在运行。在我们为我们的 Airline 应用程序开始真正的 routes 之前,让我们尝试使用一些假的 routes<一个 id="id326644720" class="indexterm"> 来测试 Spring Cloud Gateway 的行为。我们将使用 https://httpbin.org/ 站点,它可以帮助我们测试一些路线。

让我们创建一个带有@Configuration注解的类来提供routes 用于 Spring 容器。让我们创建一个名为 springfive.airline.gateway.infra.route的包,然后创建以下类:

package springfive.airline.gateway.infra.route;


import java.util.function.Function;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.PredicateSpec;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder.Builder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SampleRoute {

private Function<PredicateSpec, Builder> addCustomHeader = predicateSpec -> predicateSpec
      .path("/headers")
      .addRequestHeader("Book", "Spring 5.0 By Example")
      .uri("http://httpbin.org:80");

@Bean
public RouteLocator sample(RouteLocatorBuilder builder) {
return builder.routes()
        .route("custom-request-header", addCustomHeader)
        .route("add-query-param", r -> r.path("/get").addRequestParameter("book", "spring5.0")
            .uri("http://httpbin.org:80"))
        .route("response-headers", (r) -> r.path("/response-headers")
            .addResponseHeader("book","spring5.0")
            .uri("http://httpbin.org:80"))
        .route("combine-and-change", (r) -> r.path("/anything").and().header("access-key","AAA")
            .addResponseHeader("access-key","BBB")
            .uri("http://httpbin.org:80"))
        .build();
}

}

configure 路由有几种不同的类型;我们提取的第一个是名为 addCustomHeader, 的私有属性的函数,它将在custom-request-header<中使用/代码>路线。我们将使用 curl 来测试之前创建的一些路由。

我们将测试的第一个是 custom-request-header, route 被配置为路由到: http://httpbin.org:80 和路径将是 /headers。此服务将返回发送到服务器的请求标头。看一下addCustomHeader,我们已经将它配置为向请求添加自定义标头。它将以 Book 作为关键和 Spring 5.0 By Example, 作为值。让我们使用 curl 调用网关 URL:

curl http://localhost:8888/headers

输出应如下所示:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》飞机票系统

让我们分析一下输出。首先要看的是我们调用了 localhost 地址。 Request中的Host键显示 httpbin.org,表示Spring Cloud Gateway已经改变了地址。太棒了,但我们期待它。第二个是我们添加 Book 键的地方,宾果游戏,它在请求标头中。网关按预期工作,通过几行代码,我们做了一些有趣的事情。

让我们再做一个测试。我们将测试 combine-and-change,这个路由被配置为用Request /anything =“文字”>标题访问键:AAA ,因此命令行应为:

curl -v -H "access-key: AAA" http://localhost:8888/anything

正如我们所见,-v 参数 使 调用详细模式,它对于 debugging 目的很有用,并且 -H 表示请求标头。让我们看看输出:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》飞机票系统

惊人的。如果您查看 access-key 值,网关更改为请求值 BBB。干得好伙计们。有一些端点要测试,感觉 free 可以根据需要进行测试。

Note

您可以在以下位置找到 httpbin 文档: https://httpbin.org/。还有一些有趣的其他方法可以测试 HTTP。 

Putting the infrastructure on Docker


我们的基础设施已经准备就绪,它使我们能够开发应用程序。我们可以创建一个 Docker 组合文件来启动基础设施服务;在开发生命周期中,Eureka、Config Server、Trace Server 和 API Gateway 等组件不会发生变化,因为它们作为基础设施进行交互。

然后,它使我们能够创建组件映像并在 docker-compose.yaml 文件中使用它们。让我们列出我们的组件:

  • Config Server
  • Eureka
  • Zipkin
  • RabbitMQ
  • Redis

我们知道如何使用 Fabric8 Maven 插件创建 Docker 镜像,我们在前面的章节中已经做过几次了——让我们来做吧。

让我们以配置一个为例,记住我们需要对所有项目进行相同的配置,Eureka、Gateway、Config Server 和 Gateway。以下片段配置 docker-maven-plugin 以生成 Docker 映像:

<plugin>
  <groupId>io.fabric8</groupId>
  <artifactId>docker-maven-plugin</artifactId>
  <version>0.21.0</version>
  <configuration>
    <images>
      <image>
        <name>springfivebyexample/${project.build.finalName}</name>
        <build>
          <from>openjdk:latest</from>
          <entryPoint>java -Dspring.profiles.active=docker -jar /application/${project.build.finalName}.jar</entryPoint>
          <assembly>
            <basedir>/application</basedir>
            <descriptorRef>artifact</descriptorRef>
            <inline>
              <id>assembly</id>
              <files>
                <file>
                  <source>target/${project.build.finalName}.jar</source>
                </file>
              </files>
            </inline>
          </assembly>
          <tags>
            <tag>latest</tag>
          </tags>
          <ports>
            <port>8761</port>
          </ports>
        </build>
        <run>
          <namingStrategy>alias</namingStrategy>
        </run>
        <alias>${project.build.finalName}</alias>
      </image>
    </images>
  </configuration>
</plugin>

这是一个非常简单的配置。一个简单的 Maven 插件,有几个配置。然后,在插件配置之后,我们就可以生成 Docker 镜像了。生成 Docker 镜像的命令是:

mvn clean install docker:build

它将为我们生成一个 Docker 映像。

配置的项目可以在 GitHub 上找到;与前几章一样,有很多配置要做。我们需要配置 docker-maven-plugin 并生成 Docker 镜像。

Note

完全配置的项目可以在第 7 章文件夹中找到。 GitHub 仓库是: https:// /github.com/PacktPublishing/Spring-5.0-By-Example/tree/master/Chapter07.

创建图像后,我们可以创建一个定义整个事物的 Docker 组合文件。 docker-compose-infra-full.yaml 文件应如下所示:

version: '3'
services:

  config:
    hostname: config
image: springfivebyexample/config
ports:
- "5000:5000"
networks:
- airline

rabbitmq:
    hostname: rabbitmq
image: rabbitmq:3.7.0-management-alpine
ports:
- "5672:5672"
- "15672:15672"
networks:
- airline

mysql:
    hostname: mysql
image: mysql:5.7.21
ports:
- "3306:3306"
environment:
- MYSQL_ROOT_PASSWORD=root
      - MYSQL_DATABASE=zipkin
networks:
- airline

redis:
    hostname: redis
image: redis:3.2-alpine
ports:
- "6379:6379"
networks:
- airline

zipkin:
    hostname: zipkin
image: springfivebyexample/zipkin
ports:
- "9999:9999"
networks:
- airline

networks:
  airline:
    driver: bridge

这里有一些有趣的事情需要注意。将所有容器实例连接到名为airline 的同一个 Docker 网络非常重要。注意容器暴露的端口,在 Docker 中启用服务发现功能很重要。

然后,我们可以执行指令来spin 启动整个基础架构;可以使用以下命令完成:

docker-compose -f docker-compose-infra-full.yaml up -d

应出现以下输出:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》飞机票系统

此外,我们可以执行以下指令来检查容器的执行情况:

docker-compose -f docker-compose-infra-full.yaml ps

它将列出正在运行的容器,如以下屏幕截图所示:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》飞机票系统

所有应用程序都已启动并正在运行。做得好。

要删除容器,我们可以使用:

docker-compose -f docker-compose-infra-full.yaml down

它将从堆栈中删除容器。

干得好,我们的基础设施完全可以在 Docker 容器中运行。它是开始创建我们的微服务的基础。

Summary


在本章中,我们构建了采用微服务架构风格的基本基础设施服务。 

我们已经了解了 Spring Framework 如何从我们的微服务中消除基础设施代码,并使我们能够使用几个注释来创建这些服务。

我们了解它在后台是如何工作的;当应用程序在生产阶段出现一些错误时,调试和故障排除非常重要。

现在,我们准备创建可扩展,容错和响应式系统。我们已经建立了系统的基础。

在下一章中,我们将开始构建我们的机票系统,了解如何将新的微服务与整个基础设施连接起来,并启用服务发现和其他惊人的功能。

到时候那里见。