Spring Boot 与 Docker
观察 GraphQL 的实际运行

本指南将引导您通过使用 Spring Integration 通道适配器Google Cloud Pub/Sub 作为底层消息交换机制,在程序的不同部分或不同程序之间交换消息的过程。

你将构建的内容

一个 Spring Boot Web 应用程序,它向自身发送消息并处理这些消息。

所需准备

如何完成本指南

与大多数 Spring 入门指南一样,您可以从头开始并完成每个步骤,或者可以跳过您已经熟悉的基本设置步骤。无论哪种方式,您最终都会得到可运行的代码。

从头开始,请继续阅读使用 Gradle 构建

跳过基础步骤,请执行以下操作:

完成后,您可以将您的结果与 gs-messaging-gcp-pubsub/complete 中的代码进行对比。

使用 Gradle 构建

使用 Gradle 构建

首先,您需要设置一个基本的构建脚本。在使用 Spring 构建应用程序时,您可以使用任何喜欢的构建系统,但这里包含了与 GradleMaven 配合使用的代码。如果您对它们不熟悉,请参考 使用 Gradle 构建 Java 项目使用 Maven 构建 Java 项目

创建目录结构

在您选择的项目目录中,创建以下子目录结构;例如,在 *nix 系统上使用 mkdir -p src/main/java/hello

└── src
    └── main
        └── java
            └── hello

创建一个 Gradle 构建文件

以下是 初始 Gradle 构建文件

build.gradle

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:2.6.7")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

bootJar {
    baseName = 'gs-spring-cloud-gcp'
    version =  '0.1.0'
}

repositories {
    mavenCentral()
}

sourceCompatibility = 1.8
targetCompatibility = 1.8

dependencies {
    compile("org.springframework.boot:spring-boot-starter-web")
    testCompile("org.springframework.boot:spring-boot-starter-test")
}

Spring Boot gradle 插件 提供了许多便捷的功能:

  • 它收集类路径上的所有 jar 文件,并构建一个单独的可运行的 "über-jar",这使得执行和传输您的服务更加方便。

  • 它会搜索 public static void main() 方法,并将其标记为可运行的类。

  • 它提供了一个内置的依赖解析器,该解析器会设置版本号以匹配 Spring Boot 依赖项。您可以覆盖任何您希望的版本,但默认情况下它会使用 Spring Boot 选择的版本集。

使用 Maven 构建

使用 Maven 构建

首先,您需要设置一个基本的构建脚本。使用 Spring 构建应用程序时,您可以选择任何您喜欢的构建系统,但此处包含了使用 Maven 所需的代码。如果您不熟悉 Maven,请参考 使用 Maven 构建 Java 项目

创建目录结构

在您选择的项目目录中,创建以下子目录结构;例如,在 *nix 系统上使用 mkdir -p src/main/java/hello 命令:

└── src
    └── main
        └── java
            └── hello

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.springframework</groupId>
    <artifactId>gs-spring-cloud-gcp</artifactId>
    <version>0.1.0</version>

    <properties>
        <java.version>1.8</java.version>
        <spring-boot-release.version>2.3.4.RELEASE</spring-boot-release.version>
    </properties>

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

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot-release.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Spring Boot Maven 插件 提供了许多便捷的功能:

  • 它收集类路径上的所有 jar 文件,并构建一个单独的可运行“über-jar”,这使得执行和传输您的服务更加方便。

  • 它搜索 public static void main() 方法,将其标记为可运行类。

  • 它提供了一个内置的依赖解析器,用于设置版本号以匹配 Spring Boot 依赖。您可以覆盖任何您希望的版本,但默认情况下它将使用 Boot 选择的版本集。

使用您的 IDE 构建

使用您的 IDE 构建

添加所需的依赖项

如果您使用的是 Maven,请将以下内容添加到您的 pom.xml 文件中:

<dependencies>
    ...
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-gcp-starter-pubsub</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.integration</groupId>
        <artifactId>spring-integration-core</artifactId>
    </dependency>
    ...
</dependencies>

或者,如果您使用的是 Gradle:

dependencies {
    ...
    compile("org.springframework.cloud:spring-cloud-gcp-starter-pubsub:1.2.5.RELEASE")
    compile("org.springframework.integration:spring-integration-core")
    ...
}

如果您正在使用 Maven,强烈建议您使用 Spring Cloud GCP 材料清单来控制依赖项的版本:

<properties>
    ...
    <spring-cloud-gcp.version>1.2.5.RELEASE</spring-cloud-gcp.version>
    ...
</properties>

<dependencyManagement>
    <dependencies>
       ...
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-gcp-dependencies</artifactId>
            <version>${spring-cloud-gcp.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        ...
    </dependencies>
</dependencyManagement>

设置 Google Cloud Pub/Sub 环境

您需要一个主题和一个订阅来发送和接收来自 Google Cloud Pub/Sub 的消息。您可以在 Google Cloud Console 中创建它们,或者使用 PubSubAdmin 类以编程方式创建。

在这个练习中,请创建一个名为 "testTopic" 的主题,并为该主题创建一个名为 "testSubscription" 的订阅。

创建应用程序文件

您需要一个类来包含通道适配器和消息配置。创建一个带有 @SpringBootApplication 注解的 PubSubApplication 类,这与典型的 Spring Boot 应用程序一样。

src/main/java/hello/PubSubApplication.java

@SpringBootApplication
public class PubSubApplication {

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

}

此外,由于您正在构建一个Web应用程序,创建一个WebAppController类来区分控制器和配置逻辑。

src/main/java/hello/WebAppController.java

@RestController
public class WebAppController {
}

我们仍然缺少用于 HTML 和 properties 的两个文件。

src/main/resources/static/index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Spring Integration GCP sample</title>
</head>
<body>
<div name="formDiv">
  <form action="/publishMessage" method="post">
    Publish message: <input type="text" name="message" /> <input type="submit" value="Publish!"/>
  </form>
</div>
</body>
</html>

src/main/resources/application.properties

#spring.cloud.gcp.project-id=[YOUR_GCP_PROJECT_ID_HERE]
#spring.cloud.gcp.credentials.location=file:[LOCAL_FS_CREDENTIALS_PATH]

Spring Cloud GCP Core Boot starter 可以自动配置这两个属性并使它们成为可选项。属性文件中的属性总是优先于 Spring Boot 配置。Spring Cloud GCP Core Boot starter 与 Spring Cloud GCP Pub/Sub Boot starter 捆绑在一起。

GCP 项目 ID 是从 GOOGLE_CLOUD_PROJECT 环境变量中自动配置的,还有其他多个来源。OAuth2 凭证是从 GOOGLE_APPLICATION_CREDENTIALS 环境变量中自动配置的。如果安装了 Google Cloud SDK,通过在与应用程序相同进程或其父进程中运行 gcloud auth application-default login 命令,可以轻松配置该环境变量。

创建入站通道适配器

入站通道适配器监听来自 Google Cloud Pub/Sub 订阅的消息,并将它们发送到应用程序中的 Spring 通道。

实例化入站通道适配器需要一个 PubSubTemplate 实例和一个现有订阅的名称。PubSubTemplate 是 Spring 用于订阅 Google Cloud Pub/Sub 主题的抽象。Spring Cloud GCP Pub/Sub Boot 启动器提供了一个自动配置的 PubSubTemplate 实例,您可以简单地将其作为方法参数注入。

src/main/java/hello/PubSubApplication.java

  @Bean
  public PubSubInboundChannelAdapter messageChannelAdapter(
    @Qualifier("pubsubInputChannel") MessageChannel inputChannel,
    PubSubTemplate pubSubTemplate) {
  PubSubInboundChannelAdapter adapter =
    new PubSubInboundChannelAdapter(pubSubTemplate, "testSubscription");
  adapter.setOutputChannel(inputChannel);
  adapter.setAckMode(AckMode.MANUAL);

  return adapter;
  }

默认情况下,适配器中的消息确认模式设置为自动。此行为可以被覆盖,如示例所示。

在实例化通道适配器之后,必须配置一个输出通道,适配器将接收到的消息发送到该通道。

src/main/java/hello/PubSubApplication.java

  @Bean
  public MessageChannel pubsubInputChannel() {
  return new DirectChannel();
  }

附加到入站通道的是一个服务激活器,用于处理传入的消息。

src/main/java/hello/PubSubApplication.java

  @Bean
  @ServiceActivator(inputChannel = "pubsubInputChannel")
  public MessageHandler messageReceiver() {
  return message -> {
    LOGGER.info("Message arrived! Payload: " + new String((byte[]) message.getPayload()));
    BasicAcknowledgeablePubsubMessage originalMessage =
    message.getHeaders().get(GcpPubSubHeaders.ORIGINAL_MESSAGE, BasicAcknowledgeablePubsubMessage.class);
    originalMessage.ack();
  };
  }

ServiceActivator 的输入通道名称(例如 "pubsubInputChannel")必须与输入通道方法名称匹配。每当有新消息到达该通道时,它将由返回的 MessageHandler 处理。

在此示例中,消息的处理方式是通过记录其内容并确认它。在手动确认中,消息是通过 BasicAcknowledgeablePubsubMessage 对象进行确认的,该对象附加在 Message 的头部,并且可以使用 GcpPubSubHeaders.ORIGINAL_MESSAGE 键来提取。

创建出站通道适配器

出站通道适配器监听来自 Spring 通道的新消息,并将它们发布到 Google Cloud Pub/Sub 主题中。

实例化出站通道适配器需要一个 PubSubTemplate 和一个现有主题的名称。PubSubTemplate 是 Spring 用于向 Google Cloud Pub/Sub 主题发布消息的抽象。Spring Cloud GCP Pub/Sub Boot 启动器提供了一个自动配置的 PubSubTemplate 实例。

src/main/java/hello/PubSubApplication.java

  @Bean
  @ServiceActivator(inputChannel = "pubsubOutputChannel")
  public MessageHandler messageSender(PubSubTemplate pubsubTemplate) {
  return new PubSubMessageHandler(pubsubTemplate, "testTopic");
  }

您可以使用 MessageGateway 将消息写入通道并发布到 Google Cloud Pub/Sub。

src/main/java/hello/PubSubApplication.java

  @MessagingGateway(defaultRequestChannel = "pubsubOutputChannel")
  public interface PubsubOutboundGateway {

  void sendToPubsub(String text);
  }

从这段代码中,Spring 自动生成了一个对象,然后该对象可以被自动注入到应用程序的私有字段中。

src/main/java/hello/WebAppController.java

  @Autowired
  private PubsubOutboundGateway messagingGateway;

添加控制器逻辑

在您的控制器中添加逻辑,以便写入 Spring 频道:

src/main/java/hello/WebAppController.java

package hello;

import hello.PubSubApplication.PubsubOutboundGateway;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.view.RedirectView;

@RestController
public class WebAppController {

  // tag::autowireGateway[]
  @Autowired
  private PubsubOutboundGateway messagingGateway;
  // end::autowireGateway[]

  @PostMapping("/publishMessage")
  public RedirectView publishMessage(@RequestParam("message") String message) {
  messagingGateway.sendToPubsub(message);
  return new RedirectView("/");
  }
}

认证

您的应用程序必须通过 GOOGLE_APPLICATION_CREDENTIALS 环境变量或 spring.cloud.gcp.credentials.location 属性进行身份验证。

如果您已安装 Google Cloud SDK,可以使用 gcloud auth application-default login 命令登录您的用户账户。

或者,您可以从 Google Cloud Console 下载服务账户凭据文件,并在 application.properties 文件中将 spring.cloud.gcp.credentials.location 属性指向该文件。

作为 Spring Resourcespring.cloud.gcp.credentials.location 也可以从文件系统以外的位置获取,例如 URL、类路径等。

使应用程序可执行

尽管可以将此服务打包为传统的 WAR 文件以部署到外部应用服务器,但下面展示的更简单的方法将创建一个独立应用程序。您将所有内容打包到一个可执行的 JAR 文件中,由 Java 的 main() 方法驱动。此外,您使用 Spring 对嵌入 Tomcat servlet 容器的支持作为 HTTP 运行时,而不是部署到外部实例。

@SpringBootApplication 是一个便捷的注解,它添加了以下所有内容:

  • @Configuration: 将该类标记为应用程序上下文的 bean 定义源。

  • @EnableAutoConfiguration: 告诉 Spring Boot 根据类路径设置、其他 bean 和各种属性设置开始添加 bean。例如,如果类路径上有 spring-webmvc,该注解会将应用程序标记为 Web 应用程序,并激活关键行为,例如设置 DispatcherServlet

  • @ComponentScan: 告诉 Spring 在 hello 包中查找其他组件、配置和服务,使其能够找到控制器。

main() 方法使用 Spring Boot 的 SpringApplication.run() 方法来启动应用程序。您是否注意到没有一行 XML 代码?也没有 web.xml 文件。这个 Web 应用程序是 100% 纯 Java 的,您无需处理任何配置管道或基础设施。

构建可执行的 JAR

您可以使用 Gradle 或 Maven 从命令行运行该应用程序。您还可以构建一个包含所有必要依赖项、类和资源的可执行 JAR 文件并运行它。构建可执行 JAR 文件使得在整个开发生命周期中、跨不同环境等场景下,能够轻松地打包、版本化和部署该服务作为应用程序。

如果您使用 Gradle,可以通过 ./gradlew bootRun 来运行该应用程序。或者,您可以使用 ./gradlew build 构建 JAR 文件,然后按如下方式运行该 JAR 文件:

java -jar build/libs/gs-messaging-gcp-pubsub-0.1.0.jar

如果使用 Maven,您可以通过使用 ./mvnw spring-boot:run 来运行应用程序。或者,您可以使用 ./mvnw clean package 构建 JAR 文件,然后按如下方式运行该 JAR 文件:

java -jar target/gs-messaging-gcp-pubsub-0.1.0.jar

这里描述的步骤会创建一个可运行的 JAR 文件。您也可以构建一个经典的 WAR 文件

日志输出已显示。服务应在几秒内启动并运行。

测试应用程序

现在应用程序已经运行,您可以进行测试。打开http://localhost:8080,在输入文本框中输入一条消息,按下“发布!”按钮,并验证消息是否正确记录在您的进程终端窗口中。

总结

恭喜!您刚刚开发了一个使用 Spring Integration GCP Pub/Sub 通道适配器来交换消息的 Spring 应用程序!

本页目录