# Vert.x Service Resolver

The Service Resolver library is a plugin that lets Vert.x clients call services using logical service names instead of network addresses. The service resolver is also able to perform client side load balancing with the usual strategies.

You can use a service resolver as a client to query a resolver or integrated natively with a Vert.x client.

## Service resolver client

You can query a service resolver with the service resolver client.

    ServiceResolverClient client = ServiceResolverClient.create(vertx, new KubeResolverOptions());

    // Resolve a service endpoint
    Future<Endpoint> fut = client.resolveEndpoint(ServiceAddress.of("the-service"));

    fut.onSuccess(endpoint -> {

      // Print physical servers details
      List<ServerEndpoint> servers = endpoint.servers();
      for (ServerEndpoint server : servers) {
        System.out.println("Available server: " + server.address());
      }
    });

You can also let the load balancer select nodes

    ServiceResolverClient client = ServiceResolverClient.create(vertx, new KubeResolverOptions());

    // Resolve a service endpoint
    Future<Endpoint> fut = client.resolveEndpoint(ServiceAddress.of("the-service"));

    fut.onSuccess(endpoint -> {

      // Print physical servers details
      List<ServerEndpoint> servers = endpoint.servers();
      for (ServerEndpoint server : servers) {
        System.out.println("Available server: " + server.address());
      }
    });

## Client integration

The service resolver is integrated with the Vert.x HTTP and Web clients.

### Getting started with the Vert.x HTTP Client

Given a resolver, you can configure a Vert.x HTTP Client to use it thanks to an `HttpClientBuilder`.

    HttpClient client = vertx.httpClientBuilder()
      .withAddressResolver(resolver)
      .build();

A service is addressed with a `ServiceAddress` instead of a `SocketAddress`.

    ServiceAddress serviceAddress = ServiceAddress.of("the-service");

    Future<HttpClientRequest> requestFuture = client.request(new RequestOptions()
      .setMethod(HttpMethod.GET)
      .setURI("/")
      .setServer(serviceAddress));

    Future<Buffer> resultFuture = requestFuture.compose(request -> request
      .send()
      .compose(response -> {
        if (response.statusCode() == 200) {
          return response.body();
        } else {
          return Future.failedFuture("Invalid status response:" + response.statusCode());
        }
      }));

### Getting started with the Vert.x Web Client

Given a resolver, you can configure a Vert.x Web Client to use it thanks to an `HttpClientBuilder`.

    HttpClient httpClient = vertx.httpClientBuilder()
      .withAddressResolver(resolver)
      .build();
    WebClient webClient = WebClient.wrap(httpClient);

A service is addressed with a `ServiceAddress`.

    ServiceAddress serviceAddress = ServiceAddress.create("the-service");

    Future<HttpResponse<Buffer>> future = webClient
      .request(HttpMethod.GET, new RequestOptions().setServer(serviceAddress))
      .send();

### Client side load balancing

The default load balancing behavior is round-robin, you can change the load balancer to use:

    HttpClient client = vertx.httpClientBuilder()
      .withAddressResolver(resolver)
      .withLoadBalancer(LoadBalancer.LEAST_REQUESTS)
      .build();

## Service resolver implementations

The service resolver integrates with a few discovery services such as Kubernetes and DNS SRV records.

### Kubernetes resolver

The Kubernetes resolver locates services within a Kubernetes cluster.

| The Kubernetes resolver requires the `endpoints` resource to be accessible using the service account configured for the pod. Here is an example configuration of role and role binding for the `default` service account:
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      name: observe-endpoints
      namespace: default
    rules:
      - apiGroups: [""]
        resources: ["endpoints"]
        verbs: ["get", "watch", "list"]
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: observe-endpoints
      namespace: default
    roleRef:
      kind: Role
      name: observe-endpoints
      apiGroup: rbac.authorization.k8s.io
    subjects:
      - kind: ServiceAccount
        name: default
        namespace: default

    KubeResolverOptions options = new KubeResolverOptions();

    KubeResolver resolver = KubeResolver.create(options);

    HttpClient client = vertx.httpClientBuilder()
      .withAddressResolver(resolver)
      .build();

The default resolver options values are loaded from the pod environment - `KUBERNETES_SERVICE_HOST` - `KUBERNETES_SERVICE_PORT` - `/var/run/secrets/kubernetes.io/serviceaccount/token` - `/var/run/secrets/kubernetes.io/serviceaccount/ca.crt` - `/var/run/secrets/kubernetes.io/serviceaccount/namespace`

You can override these settings.

You can deal with ephemeral tokens using the `tokenProvider`.

    KubeResolverOptions options = new KubeResolverOptions();

    KubeResolver resolver = KubeResolver.create(options)
      .tokenProvider(() -> loadToken());

    HttpClient client = vertx.httpClientBuilder()
      .withAddressResolver(resolver)
      .build();

The resolver calls the provider when it needs a fresh token. The token is cached by the resolver until it is detetected stale by the resolver (upon a `401` server response code).

#### Matching specific service ports

When a service exposes more than one port, the resolver retains only a single port, it might not be the expected port.

You can build a specific service address for a given service port number.

    ServiceAddress serviceAddress = KubernetesServiceAddressBuilder
      .of("the-service")
      .withPortNumber(8080)
      .build();

Or a given service port name.

    ServiceAddress serviceAddress = KubernetesServiceAddressBuilder
      .of("the-service")
      .withPortName(portName)
      .build();

### SRV resolver

The SRV resolver uses DNS SRV records to resolve and locate services.

    SrvResolverOptions options = new SrvResolverOptions()
      .setServer(SocketAddress.inetSocketAddress(dnsPort, dnsServer));

    AddressResolver resolver = SrvResolver.create(options);

    HttpClient client = vertx.httpClientBuilder()
      .withAddressResolver(resolver)
      .build();