Calling an HTTP service
Table of contents
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.
BraveClient
which traces outbound requests using Brave.CircuitBreakerClient
which opens a circuit on failed responses.CookieClient
which stores cookies of responses usingCookieJar
.ConcurrencyLimitingClient
which limits the concurrent number of active requests.ContentPreviewingClient
which previews the content of requests and responses.DecodingClient
which decompresses a compressed response.MetricCollectingClient
which collects metrics using Micrometer.LoggingClient
which logs requests and responses.RetryingClient
which automatically retries failed requests.
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.