Implementing a custom Stork service registrar
previewThis guide explains how to implement a custom service registrar for SmallRye Stork and use it to programmatically register service instances at startup.
If you are new to Stork, please read the Stork Getting Started Guide.
|
这项技术被认为是preview。 在 preview(预览) 中,不保证向后兼容和在生态系统中的存在。具体的改进可能需要改变配置或API,并且正在计划变得 稳定 。欢迎在我们的 邮件列表 中提供反馈,或在我们的 GitHub问题列表 中提出问题。 For a full list of possible statuses, check our FAQ entry. |
先决条件
要完成这个指南,你需要:
-
大概15分钟
-
编辑器
-
JDK 17+ installed with
JAVA_HOMEconfigured appropriately -
Apache Maven 3.9.16
-
如果你愿意的话,还可以选择使用Quarkus CLI
-
如果你想构建原生可执行程序,可以选择安装Mandrel或者GraalVM,并正确配置(或者使用Docker在容器中进行构建)
应用结构
In this guide, we will build an application that:
-
Implements a custom service registrar by extending Stork’s SPI
-
Programmatically registers a service instance at startup
-
Configures the registrar via
application.properties
Beyond service discovery and load balancing, it also supports service registration. Service registration is the process of announcing a service instance to a registry so that other services can discover it. While Stork provides built-in registrars for Consul, Eureka, and others, you can implement your own by using the Stork SPI.
解决方案
我们建议您按照下一节的说明逐步创建应用程序。然而,您可以直接转到已完成的示例。
克隆 Git 仓库: git clone -b development https://github.com/quarkusio/quarkus-quickstarts.git ,或下载一个 存档 。
The solution is located in the stork-programmatic-custom-registration-quickstart directory.
创建项目
Create a Quarkus project importing the quarkus-rest, quarkus-smallrye-stork, and quarkus-rest-client-jackson extensions using your favorite approach:
For Windows users:
-
If using cmd, (don’t use backward slash
\and put everything on the same line) -
If using Powershell, wrap
-Dparameters in double quotes e.g."-DprojectArtifactId=stork-programmatic-custom-registration-quickstart"
In the generated project, also add the following dependencies:
<dependency>
<groupId>io.smallrye.stork</groupId>
<artifactId>stork-core</artifactId>
</dependency>
<dependency>
<groupId>io.smallrye.stork</groupId>
<artifactId>stork-configuration-generator</artifactId>
</dependency>
implementation("io.smallrye.stork:stork-core")
implementation("io.smallrye.stork:stork-configuration-generator")
stork-core provides the Stork API and SPI needed to implement a custom registrar.
stork-configuration-generator generates configuration classes at build time based on annotations on your registrar provider.
The Custom Service Registrar Provider
Stork uses a provider model. To create a custom registrar, you need two classes:
-
A provider implementing
ServiceRegistrarProvider— the factory that creates registrar instances. -
A registrar implementing
ServiceRegistrar— the actual registration logic.
Let’s start with the provider.
Create the src/main/java/org/acme/services/CustomServiceRegistrarProvider.java file with the following content:
package org.acme.services;
import io.smallrye.stork.api.Metadata;
import io.smallrye.stork.api.ServiceRegistrar;
import io.smallrye.stork.api.config.ServiceRegistrarAttribute;
import io.smallrye.stork.api.config.ServiceRegistrarType;
import io.smallrye.stork.spi.ServiceRegistrarProvider;
import io.smallrye.stork.spi.StorkInfrastructure;
import jakarta.enterprise.context.ApplicationScoped;
@ServiceRegistrarType(value = "custom", metadataKey = Metadata.DefaultMetadataKey.class)
@ServiceRegistrarAttribute(name = "host",
description = "Host name of the service registration server.", required = true)
@ServiceRegistrarAttribute(name = "port",
description = "Port of the service registration server.", required = false)
@ApplicationScoped
public class CustomServiceRegistrarProvider
implements ServiceRegistrarProvider<CustomRegistrarConfiguration, Metadata.DefaultMetadataKey> {
@Override
public ServiceRegistrar createServiceRegistrar(
CustomRegistrarConfiguration config,
String serviceName,
StorkInfrastructure storkInfrastructure) {
return new CustomServiceRegistrar(config);
}
}
There are a few important things to note:
-
The
@ServiceRegistrarTypeannotation declares the registrar type. This is the value used in thequarkus.stork.<service-name>.service-registrar.typeproperty. Here, it iscustom. -
The
@ServiceRegistrarAttributeannotations declare the configuration attributes for this registrar. Stork’s annotation processor (provided bystork-configuration-generator) uses these annotations to generate theCustomRegistrarConfigurationclass at compile time. You do not need to write this class yourself. -
The
@ApplicationScopedannotation makes this provider a CDI bean, so Stork can discover it automatically. -
The
createServiceRegistrarmethod is the factory that creates aCustomServiceRegistrarfrom the generated configuration.
The Custom Service Registrar
Now let’s implement the registrar itself.
Create the src/main/java/org/acme/services/CustomServiceRegistrar.java file with the following content:
package org.acme.services;
import io.smallrye.mutiny.Uni;
import io.smallrye.stork.api.Metadata;
import io.smallrye.stork.api.ServiceRegistrar;
import org.jboss.logging.Logger;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class CustomServiceRegistrar implements ServiceRegistrar {
private static final Logger LOGGER = Logger.getLogger(CustomServiceRegistrar.class.getName());
private final String backendHost;
private final int backendPort;
private final Map<String, String> registeredInstances = new ConcurrentHashMap<>();
public CustomServiceRegistrar(CustomRegistrarConfiguration configuration) {
this.backendHost = configuration.getHost();
this.backendPort = Integer.parseInt(configuration.getPort()!=null?configuration.getPort():"8080");
}
@Override
public Uni<Void> registerServiceInstance(String serviceName, Metadata metadata, String ipAddress, int defaultPort) {
String address = ipAddress + ":" + defaultPort;
LOGGER.info("Registering service: " + serviceName + " with ipAddress: " + ipAddress + " and port: " + defaultPort);
registeredInstances.put(serviceName, address);
return Uni.createFrom().voidItem();
}
@Override
public Uni<Void> deregisterServiceInstance(String serviceName) {
LOGGER.infof("Deregistering service '%s' from backend %s:%d", serviceName, backendHost, backendPort);
registeredInstances.remove(serviceName);
return Uni.createFrom().voidItem();
}
}
This registrar:
-
Receives its configuration (host and port) from the generated
CustomRegistrarConfigurationclass. -
Stores registered instances in a
ConcurrentHashMap. -
Implements
registerServiceInstanceto add a service instance to the map. -
Implements
deregisterServiceInstanceto remove a service instance on shutdown. -
Both methods return
Uni<Void>, since Stork uses Mutiny for reactive support.
Programmatic Service Registration at Startup
With the registrar in place, we can now register service instances programmatically.
Create the src/main/java/org/acme/services/Registration.java file with the following content:
package org.acme.services;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;
import io.quarkus.runtime.StartupEvent;
import io.smallrye.stork.Stork;
import io.vertx.mutiny.core.Vertx;
@ApplicationScoped
public class Registration {
/**
* Register our two services using custom registrar.
*
* Note: this method is called on a worker thread, and so it is allowed to block.
*/
public void init(@Observes StartupEvent ev, Vertx vertx) {
Stork.getInstance().getService("my-service").registerInstance("my-service", "localhost",
9000);
}
}
When the application starts, this CDI bean observes the StartupEvent and uses the Stork API to register a service instance.
The Stork.getInstance().getService("my-service") call retrieves the Stork service named my-service, and registerInstance
delegates to the custom registrar we created earlier.
Stork configuration
Now we need to configure Stork to use our custom registrar.
In the src/main/resources/application.properties, add:
quarkus.stork.my-service.service-registrar.type=custom
quarkus.stork.my-service.service-registrar.host=localhost
The quarkus.stork.my-service.service-registrar.type property tells Stork to use the custom registrar type.
This matches the value declared in the @ServiceRegistrarType annotation on CustomServiceRegistrarProvider.
The quarkus.stork.my-service.service-registrar.host property maps to the host attribute declared with @ServiceRegistrarAttribute on the provider.
This is a required attribute.
You can optionally set quarkus.stork.my-service.service-registrar.port as well, since the provider declares an optional port attribute.
If omitted, the registrar defaults to port 8080.
运行应用程序
Package the application:
quarkus build
./mvnw install
./gradlew build
And run it:
> java -jar target/quarkus-app/quarkus-run.jar
You should see the following log message in the output, confirming that the custom registrar is invoked:
INFO [org.acm.ser.CustomServiceRegistrar] Registering service: my-service with ipAddress: localhost and port: 9000
You can compile this application into a native executable:
quarkus build --native
./mvnw install -Dnative
./gradlew build -Dquarkus.native.enabled=true
Summary
In this guide, we have:
-
Implemented a custom
ServiceRegistrarProviderusing the@ServiceRegistrarTypeand@ServiceRegistrarAttributeannotations. -
Implemented a custom
ServiceRegistrarwithregisterServiceInstanceandderegisterServiceInstancemethods. -
Used the Stork API to programmatically register a service instance at startup.
-
Configured the custom registrar in
application.properties.
This pattern is useful when you need to integrate Stork with a service registry that is not supported out of the box.