编辑此页面

编写 Dev Service

了解如何在扩展中开发 Dev Service,以便在开发模式下替换外部服务。

先决条件

要完成本指南,您需要

  • 大约 15 分钟

  • 一个 IDE

  • 已安装 JDK 17+ 并正确配置了 JAVA_HOME

  • Apache Maven 3.9.9

  • 一个正常工作的容器运行时(Docker 或 Podman

  • 已就位的扩展结构

  • 外部服务的容器化版本(并非所有 Dev Service 都依赖容器,但大多数依赖)

创建 Dev Service

如果您的扩展提供用于连接到外部服务的 API,则最好提供 Dev Service 实现。

首先,您必须将以下依赖项添加到构建文件的 deployement 模块中

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-devservices-deployment</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-devservices-deployment")

然后,在扩展处理器类中添加一个新的构建步骤,该步骤返回 DevServicesResultBuildItem。 在这里,使用了 hello-world 镜像,但您应该为您的服务设置正确的镜像。

@BuildStep(onlyIfNot = IsNormal.class, onlyIf = DevServicesConfig.Enabled.class)
public DevServicesResultBuildItem createContainer() {
    DockerImageName dockerImageName = DockerImageName.parse("hello-world");
    GenericContainer container = new GenericContainer<>(dockerImageName)
                                     .withExposedPorts(SERVICE_PORT, OTHER_SERVICE_PORT)
                                     .waitingFor(Wait.forLogMessage(".*Started.*", 1))
                                     .withReuse(true);

    container.start();

    String newUrl = "http://%s:%d".formatted(container.getHost(),
            container.getMappedPort(SERVICE_PORT));
    Map<String, String> configOverrides = Map.of("some-service.base-url", newUrl);

    return new DevServicesResultBuildItem.RunningDevService(FEATURE,
            container.getContainerId(),
            container::close,
            configOverrides).toBuildItem();
}

使用此代码,如果将扩展添加到测试应用程序并运行 quarkus dev,您应该能够看到您的容器启动。 但是,应用程序将无法连接到它,因为没有暴露端口。 要暴露端口,请将 withExposedPorts 添加到容器构造中。 例如,

GenericContainer container = new GenericContainer<>(dockerImageName)
                                 .withExposedPorts(SERVICE_PORT, OTHER_SERVICE_PORT);

Testcontainers 会将这些端口映射到主机上的随机端口。 这避免了端口冲突,但也带来了一个新问题 – 应用程序如何连接到容器中的服务?

为了允许应用程序连接,扩展应该使用映射的端口覆盖服务的默认配置。 这必须在启动容器后完成。 例如,

container.start();
String serviceUrl = "http://%s:%d".formatted(container.getHost(),
        container.getMappedPort(SERVICE_PORT));
Map<String, String> configOverrides = Map.of("some-service.base-url",
    serviceUrl);

其他配置覆盖可以包含在同一映射中。

等待容器启动

您应该向容器构造添加一个 .waitingFor 调用,以等待容器启动。 例如

container.waitingFor(Wait.forLogMessage(".*Started.*", 1));

等待端口打开是另一种选择。 有关等待策略的完整讨论,请参阅 Testcontainers 文档

配置 Dev Service

要配置 Dev Service 启动过程,您的构建步骤可以在其构造函数中接受 ConfigPhase.BUILD_TIME 配置类。 例如,

@BuildStep(onlyIfNot = IsNormal.class, onlyIf = GlobalDevServicesConfig.Enabled.class)
public DevServicesResultBuildItem createContainer(MyConfig config) {}

您可能希望使用此配置来设置固定端口或设置镜像名称,例如。

if (config.port.isPresent()) {
    String portBinding = "%d:%d".formatted(config.port.get(), SERVICE_PORT);
    container.setPortBindings(List.of(portBinding));
}

控制重用

在具有实时重载的开发模式下,Quarkus 可能会频繁重启。 默认情况下,这也将重启测试容器。 Quarkus 重启通常非常快,但容器可能需要更长的时间才能重启。 要防止容器在每次代码更改时都重启,您可以将容器标记为可重用

container.withReuse(true);

某些 Dev Service 实现了复杂的重用逻辑,它们在处理器本身中跟踪容器的状态。 如果您的服务有更复杂的要求,或者需要在实例之间共享,您可能需要这个。

相关内容