Implementing a Custom Stork Service Registrar
プレビューThis guide explains how to implement a custom service registrar for SmallRye Stork and use it to programmatically register service instances at startup.
Stork を初めて使用する場合は、Stork 入門ガイド をお読みください。
|
この技術は、previewと考えられています。 preview では、下位互換性やエコシステムでの存在は保証されていません。具体的な改善には設定や API の変更が必要になるかもしれませんが、 stable になるための計画は現在進行中です。フィードバックは メーリングリスト や GitHub の課題管理 で受け付けています。 とりうるステータスの完全なリストについては、 FAQの項目 を参照してください。 |
前提条件
このガイドを完成させるには、以下が必要です:
-
約15分
-
IDE
-
JDK 17+がインストールされ、
JAVA_HOMEが適切に設定されていること -
Apache Maven 3.9.15
-
使用したい場合は、 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 https://github.com/quarkusio/quarkus-quickstarts.git 、または archive をダウンロードする。
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:
Windowsユーザーの場合:
-
cmdを使用する場合、(バックスラッシュ
\を使用せず、すべてを同じ行に書かないでください)。 -
Powershellを使用する場合は、
-Dパラメータを二重引用符で囲んでください。例:"-DprojectArtifactId=stork-programmatic-custom-registration-quickstart"
生成されたプロジェクトに、以下の依存関係を追加します:
<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 の設定
Now we need to configure Stork to use our custom registrar.
src/main/resources/application.properties に以下を追加します:
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
そして実行します:
> 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
このアプリケーションをネイティブ実行可能ファイルにコンパイルして、起動することができます。
quarkus build --native
./mvnw install -Dnative
./gradlew build -Dquarkus.native.enabled=true
まとめ
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.