Calling an HTTP service

You can create a WebClient with a base URI and send a request with a relative path.

import com.linecorp.armeria.client.WebClient;
import com.linecorp.armeria.common.AggregatedHttpResponse;
import com.linecorp.armeria.common.HttpHeaderNames;
import com.linecorp.armeria.common.HttpMethod;
import com.linecorp.armeria.common.RequestHeaders;

WebClient webClient = WebClient.of("http://example.com/");

// Send a simple GET request.
AggregatedHttpResponse res1 = webClient.get("/foo/bar.txt").aggregate().join();

// Send a GET request with an additional header.
RequestHeaders getJson = RequestHeaders.of(HttpMethod.GET, "/foo/bar.json",
                                           HttpHeaderNames.ACCEPT, "application/json");

AggregatedHttpResponse res2 = webClient.execute(getJson).aggregate().join();

// Send a simple POST request encoded in UTF-8.
AggregatedHttpResponse res3 = webClient.post("/upload", "{ \"foo\": \"bar\" }")
                                       .aggregate().join();

You can also create a WebClient without a base URI and send a request with an absolute URI. The ad-hoc WebClient would be useful in the following cases:

  • Sending requests to arbitrary endpoints
  • Building a proxy server
  • Handling redirected requests
import com.linecorp.armeria.common.HttpStatus;
import com.linecorp.armeria.common.HttpHeaderNames;

// Create a WebClient without a base URI.
WebClient webClient = WebClient.of();

// Send a request with an absolute URI.
AggregatedHttpResponse res1 = webClient.get("http://example.com/foo/bar.txt")
                                       .aggregate()
                                       .join();

// Send a GET request with an authority header.
HttpRequest request = HttpRequest.of(RequestHeaders.builder()
                                                   .scheme(SessionProtocol.HTTP)
                                                   .authority("example.com")
                                                   .method(HttpMethod.GET)
                                                   .path("/foo/bar.txt")
                                                   .build());
AggregatedHttpResponse res2 = webClient.execute(request).aggregate().join();

// Handle a redirected request
AggregatedHttpResponse redirected = webClient.get("http://example.com/redirect")
                                             .aggregate()
                                             .join();
assert redirected.status() == HttpStatus.TEMPORARY_REDIRECT;
String location = redirected.headers().get(HttpHeaderNames.LOCATION);
AggregatedHttpResponse actual = webClient.get(location).aggregate().join();

Decorating a WebClient

You can enrich your WebClient by decorating it with decorators. A decorator wraps another client to intercept an outgoing request or an incoming response. A lot of core features such as logging, metrics and distributed tracing are implemented as decorators.

import brave.http.HttpTracing;
import com.linecorp.armeria.client.brave.BraveClient;
import com.linecorp.armeria.client.circuitbreaker.CircuitBreaker;
import com.linecorp.armeria.client.circuitbreaker.CircuitBreakerClient;
import com.linecorp.armeria.client.circuitbreaker.CircuitBreakerRule;
import com.linecorp.armeria.client.cookie.CookieClient;
import com.linecorp.armeria.client.encoding.DecodingClient;
import com.linecorp.armeria.client.limit.ConcurrencyLimitingClient;
import com.linecorp.armeria.client.logging.ContentPreviewingClient;
import com.linecorp.armeria.client.logging.LoggingClient;
import com.linecorp.armeria.client.metric.MetricCollectingClient;
import com.linecorp.armeria.client.retry.RetryRule;
import com.linecorp.armeria.client.retry.RetryingClient;
import com.linecorp.armeria.common.metric.MeterIdPrefixFunction;

HttpTracing tracing = ...;
WebClient.builder()
         .decorator(BraveClient.newDecorator(tracing))
         .decorator(CircuitBreakerClient.newDecorator(
             CircuitBreaker.ofDefaultName(), CircuitBreakerRule.onServerErrorStatus()))
         .decorator(ConcurrencyLimitingClient.newDecorator(64))
         .decorator(ContentPreviewingClient.newDecorator(128))
         .decorator(CookieClient.newDecorator())
         .decorator(DecodingClient.newDecorator())
         .decorator(MetricCollectingClient.newDecorator(
             MeterIdPrefixFunction.ofDefault("armeria.client")))
         .decorator(LoggingClient.newDecorator())
         .decorator(RetryingClient.newDecorator(RetryRule.onUnprocessed()))
         ...
         .build();

Please see Decorating a client to learn how it works.

Service discovery with WebClient

Armeria provides the various service discovery mechanisms with EndpointGroup, from Kubernetes-style DNS records to Consul. Requests are sent to dynamically retrieved Endpoints by specifying an EndpointGroup to a WebClient.

import com.linecorp.armeria.client.endpoint.dns.DnsServiceEndpointGroup;

// Retrieve the Endpoint list from SRV records
DnsServiceEndpointGroup group =
    DnsServiceEndpointGroup.of("k8s.default.svc.cluster.local.");

// Filter out unhealthy endpoints
HealthCheckedEndpointGroup healthCheckedGroup =
        HealthCheckedEndpointGroup.of(group, "/monitor/l7check");

WebClient.builder(SessionProtocol.HTTP, healthCheckedGroup)
         ...
         .build();

Please check Client-side load balancing and service discovery for more information.

Configuring ClientFactory

A ClientFactory manages connections and protocol-specific properties. You can build your own ClientFactory using ClientFactoryBuilder and set it through WebClientBuilder.factory().

import com.linecorp.armeria.client.ClientFactory;
// Create a customized ClientFactory
ClientFactory clientFactory =
    ClientFactory.builder()
                 // Increase the connect timeout from 3.2s to 10s.
                 .connectTimeout(Duration.ofSeconds(10))
                 .
                 .build();

WebClient client =
        WebClient.builder()
                 .factory(clientFactory)
                 .build();

Please check Customizing a ClientFactory with ClientFactoryBuilder for the details.

See also