Service registration for discovery

You can use ServerListener to register the information of Armeria servers when they start so that a client uses the information to distribute its requests to the servers autonomously, unlike traditional server-side load balancing where the requests go through a dedicated load balancer such as L4 and L7 switches.

Armeria provides 2 ServerListener implementations for service registration:

ZooKeeper-based service registration with ZooKeeperUpdatingListener

First, You need the armeria-zookeeper3 dependency:

build.gradle
dependencies {
    implementation platform('com.linecorp.armeria:armeria-bom:1.31.0')

    ...
    implementation 'com.linecorp.armeria:armeria-zookeeper3'
}

Then, use ZooKeeperUpdatingListener and ZooKeeperRegistrationSpec to register your server to a ZooKeeper cluster:

import com.linecorp.armeria.server.Server;
import com.linecorp.armeria.server.zookeeper.ZooKeeperRegistrationSpec;
import com.linecorp.armeria.server.zookeeper.ZooKeeperUpdatingListener;

String zkConnectionStr = "myZooKeeperHost:2181";
String znodePath = "/myProductionEndpoints";
String serviceName = "catalog";
ZooKeeperRegistrationSpec registrationSpec =
        ZooKeeperRegistrationSpec.curator(serviceName);
ZooKeeperUpdatingListener listener =
        ZooKeeperUpdatingListener.builder(zkConnectionStr, znodePath, registrationSpec)
                                 .sessionTimeoutMillis(10000)
                                 .build();
Server server = ...
server.addListener(listener);
server.start();

The ZooKeeperRegistrationSpec is used to convert the information of your server to a binary representation. The ZooKeeperUpdatingListener registers the binary representation to the specified znode as a member of the cluster when your server starts up. Each server will represent itself as an EPHEMERAL node, which means when a server stops or a network partition between your server and ZooKeeper cluster occurs, the node of the server that became unreachable will be deleted automatically by ZooKeeper.

ZooKeeperRegistrationSpec.curator() uses the format of Curator Service Discovery which is compatible with Spring Cloud Zookeeper. You can use ZooKeeperRegistrationSpec.serverSets() that applies the format of Finagle ServerSets. If you want to use your own format, you can implement the ZooKeeperRegistrationSpec.

In the example above, we used the ZooKeeper connection string("myZooKeeperHost:2181") to connect to the ZooKeeper cluster. Instead, you can use an existing CuratorFramework instance.

import org.apache.curator.framework.CuratorFramework;

CuratorFramework client = ...
String znodePath = ...
ZooKeeperRegistrationSpec registrationSpec = ...
ZooKeeperUpdatingListener listener =
        ZooKeeperUpdatingListener.builder(client, znodePath, registrationSpec)
                                 .build();
Server server = ...
server.addListener(listener);
server.start();

For more information, please refer to the API documentation of the com.linecorp.armeria.server.zookeeper package.

Eureka-based service registration with EurekaUpdatingListener

First, You need the armeria-eureka dependency:

build.gradle
dependencies {
    implementation platform('com.linecorp.armeria:armeria-bom:1.31.0')

    ...
    implementation 'com.linecorp.armeria:armeria-eureka'
}

Then, use EurekaUpdatingListener to register your server to the Eureka:

import com.linecorp.armeria.server.Server;
import com.linecorp.armeria.server.ServerBuilder;
import com.linecorp.armeria.server.eureka.EurekaUpdatingListener;

EurekaUpdatingListener listener =
        EurekaUpdatingListener.of("https://eureka.com:8001/eureka/v2");

ServerBuilder sb = Server.builder();
sb.serverListener(listener);
sb.build().start();

If you want to specify the properties of the Server, use EurekaUpdatingListenerBuilder:

import com.linecorp.armeria.server.eureka.EurekaUpdatingListenerBuilder;

EurekaUpdatingListenerBuilder builder =
        EurekaUpdatingListener.builder("https://eureka.com:8001/eureka/v2")
                              .instanceId("i-00000000")
                              .hostname("armeria.service.1")
                              // If ipAddr is not specified, it's automatically filled
                              // using SystemInfo.defaultNonLoopbackIpV4Address().
                              .ipAddr("192.168.10.10")
                              .vipAddress("armeria.service.com:8080");

EurekaUpdatingListener listener = builder.build();
ServerBuilder sb = Server.builder();
sb.serverListener(listener);
sb.build().start();

For more information, please refer to the API documentation of the com.linecorp.armeria.server.eureka package.

Consul-based service registration with ConsulUpdatingListener

First, You need the armeria-consul dependency:

build.gradle
dependencies {
    implementation platform('com.linecorp.armeria:armeria-bom:1.31.0')

    ...
    implementation 'com.linecorp.armeria:armeria-consul'
}

Then, use ConsulUpdatingListener to register your server to the Consul:

import com.linecorp.armeria.server.Server;
import com.linecorp.armeria.server.ServerBuilder;
import com.linecorp.armeria.server.consul.ConsulUpdatingListener;

ConsulUpdatingListener listener =
    ConsulUpdatingListener.of("http://my-consul.com:8500", "my-service");

ServerBuilder sb = Server.builder();
sb.serverListener(listener);
sb.build().start();

If you want to specify an HTTP check or tags to the Server, use ConsulUpdatingListenerBuilder:

import com.linecorp.armeria.server.consul.ConsulUpdatingListenerBuilder;

ConsulUpdatingListenerBuilder builder =
    ConsulUpdatingListener.builder("http://my-consul.com:8500", "my-service")
                          .check("http://my.hostname.local:8080/internal/l7check")
                          .tags("production", "v1");

ConsulUpdatingListener listener = builder.build();
ServerBuilder sb = Server.builder();
sb.serverListener(listener);
sb.build().start();

For more information, please refer to the API documentation of the com.linecorp.armeria.server.consul package.