Your go-to microservice framework for any situation, from the creator of Netty et al. You can build any type of microservice leveraging your favorite technologies, including gRPC, Thrift, Kotlin, Retrofit, Reactive Streams, Spring Boot and Dropwizard.
APACHE-2.0 License
Bot releases are visible (Hide)
See the release notes for the complete change list.
See the release notes for the complete change list.
See the release notes for the complete change list.
See the release notes for the complete change list.
See the release notes for the complete change list.
Armeria is an open-source asynchronous HTTP/2 RPC/REST client/server library built on top of Java 8, Netty, Thrift and gRPC. Its primary goal is to help engineers build high-performance asynchronous microservices that use HTTP/2 as a session layer protocol. Visit the official web site and follow @armeria_project to check out many cool features you can't find in the official gRPC/Thrift implementation or other microservice frameworks.
ConcurrencyLimitingClient
now disables the limit when maxConcurrency
is Integer.MAX_VALUE
. #2667IllegalStateException
while handling a failed connection attempt. #2671This release was possible thanks to the following contributors who shared their brilliant ideas and awesome pull requests:
Armeria is an open-source asynchronous HTTP/2 RPC/REST client/server library built on top of Java 8, Netty, Thrift and gRPC. Its primary goal is to help engineers build high-performance asynchronous microservices that use HTTP/2 as a session layer protocol. Visit the official web site and follow @armeria_project to check out many cool features you can't find in the official gRPC/Thrift implementation or other microservice frameworks.
RetryStrategy.onException()
#2652OPTIONS * HTTP/1
request as an alternative to an HTTP/2 PING frame. #2636
com.linecorp.armeria.defaultPingIntervalMillis
system propertyClientFactoryBuilder.pingIntervalMillis()
.DocService
debug form now prettifies any JSON responses. #2635grpc-timeout
gRPC client header is generated correctly now. #2643QueryParams.toQueryString()
does not raise an exception anymore. #2644HealthCheckedEndpointGroup
does not raise aConcurrentModificationException
anymore. #2647MoreNamingConventions.prometheus()
now appends the seconds
suffix for LongTaskTimer
correctly, when using legacy meter names. #2646DocService
sends a request with a wrong HTTP method if a service specification contains duplicate methods. #2657com.linecorp.armeria.defaultHttp2PingTimeoutMillis
flag has been replaced with com.linecorp.armeria.defaultPintIntervalMillis
.com.linecorp.armeria.defaultUseHttp2PingWhenNoActiveStreams
flag has been removed without a replacement, because:
OPTIONS * HTTP/1.1
when there is no active stream.ClientFactoryBuilder.http2PingTimeoutMillis()
has been replaced with ClientFactoryBuilder.pingIntervalMillis()
.ServerBuilder.http2PingTimeoutMillis()
has been replaced with ServerBuilder.pingIntervalMillis()
.This release was possible thanks to the following contributors who shared their brilliant ideas and awesome pull requests:
Armeria is an open-source asynchronous HTTP/2 RPC/REST client/server library built on top of Java 8, Netty, Thrift and gRPC. Its primary goal is to help engineers build high-performance asynchronous microservices that use HTTP/2 as a session layer protocol. Visit the official web site and follow @armeria_project to check out many cool features you can't find in the official gRPC/Thrift implementation or other microservice frameworks.
This release was possible thanks to the following contributors who shared their brilliant ideas and awesome pull requests:
Armeria is an open-source asynchronous HTTP/2 RPC/REST client/server library built on top of Java 8, Netty, Thrift and gRPC. Its primary goal is to help engineers build high-performance asynchronous microservices that use HTTP/2 as a session layer protocol. Visit the official web site and follow @armeria_project to check out many cool features you can't find in the official gRPC/Thrift implementation or other microservice frameworks.
EncodingSerivce
when the HTTP status indicates the content is empty. #2631This release was possible thanks to the following contributors who shared their brilliant ideas and awesome pull requests:
Armeria is an open-source asynchronous HTTP/2 RPC/REST client/server library built on top of Java 8, Netty, Thrift and gRPC. Its primary goal is to help engineers build high-performance asynchronous microservices that use HTTP/2 as a session layer protocol. Visit the official web site and follow @armeria_project to check out many cool features you can't find in the official gRPC/Thrift implementation or other microservice frameworks.
ClientFactory.builder()
.proxyConfig(ProxyConfig.socks4(socksProxyServer.address()))
.build();
PING
on idle HTTP/2 connections. #1263 #2409
ClientFactory.builder()
.http2PingTimeoutMillis(3000)
.build();
RequestContextExportingAppender
. #2512 #2531
<export>request_id=req.id</export> <!-- request_id is the alias for the built-in property. -->
<export>my_foo_bar=attr:com.example.Foo#ATTR_BAR</export> <!-- my_foo_bar is the alias. -->
DocService
. #2195 #2546sb.route().addRoute(...).defaultLogName("name").build(service);
RequestLogAccess.parent()
. #2599CallCredentials
. #2619 #2622ThrottlingService
using ThrottlingAcceptHandler
and ThrottlingRejectHandler
. #2482 #2503RequestContext
. #2514 #2610
// You should implement `RequestContextStorageProvider` and use SPI to return the `RequestContextStorage`.
public class CustomizedStorageProvider implements RequestContextStorageProvider {
@Override
public RequestContextStorage newStorage() {
return new CustomizedStorage();
}
}
TimeoutMode.
#2535 #2571
// Before
ctx.extendResponseTimeoutMillis(10000);
ctx.setResponseTimeoutAfterMillis(10000);
// After
ctx.setResponseTimeoutMillis(TimeoutMode.EXTEND, 10000);
ctx.setResponseTimeoutMillis(TimeoutMode.SET_FROM_NOW, 10000);
set(Request|Response)Timeout
is now undeprecated.queryTimeoutMillis
. #2623Single<HttpResponse>
or Maybe<HttpResponse>
in an annotated service. #2624Scheduler
with Prometheus in Armeria Server. #2612RequestLog.name
and some other properties are copied to the child log in RetyringClient
. #2551 #2560ClosedStreamException
due to the stream creation reversal. #2597IllegalStateException
is not raised anymore while building a WebFlux response. #2613ZooKeeperUpdatingListener.nodeValueCodec()
in favor of ZooKeeperUpdatingListener.codec()
. #2552Most of the deprecated APIs are gone. #2543
armeria-dropwizard
is now for Dropwizard 2. If you want to use Dropwizard 1.3.x
, use armeria-dropwizard1
. #2556
armeria-rxjava
is now for RxJava 3. If you want to use RxJava 2, use armeria-rxjava2
. #2378 #2501
ClientBuilder.path(path)
is now gone. #2525 #2538
/
, you should specify it when creating ClientBuilder
.
// before
Clients.builer(SessionProtocol.HTTP, Endpoint.of("127.0.0.1"))
.path("/foo");
// after
Clients.builer(SessionProtocol.HTTP, Endpoint.of("127.0.0.1"), "/foo");
ClosedSessionException
now extends ClosedStreamException
. #2596 #2616
AuthSuccessHandler<HttpRequest, HttpResponse>
and AuthFailureHandler<HttpRequest, HttpResponse>
now don't have type parameters.
The meter name armeriaBlockingTaskExecutor
is now blockingTaskExecutor
with the prefix armeria.executor
.
RequestContext.set*TimeoutAfter*()
or extend*Timeout*()
methods have been removed. Call set*Timeout*()
with a TimeoutMode
specified instead.
This release was possible thanks to the following contributors who shared their brilliant ideas and awesome pull requests:
Armeria is an open-source asynchronous HTTP/2 RPC/REST client/server library built on top of Java 8, Netty, Thrift and gRPC. Its primary goal is to help engineers build high-performance asynchronous microservices that use HTTP/2 as a session layer protocol. Visit the official web site and follow @armeria_project to check out many cool features you can't find in the official gRPC/Thrift implementation or other microservice frameworks.
UnprocessedRequestException
in a certain case. #2594This release was possible thanks to the following contributors who shared their brilliant ideas and awesome pull requests:
Armeria is an open-source asynchronous HTTP/2 RPC/REST client/server library built on top of Java 8, Netty, Thrift and gRPC. Its primary goal is to help engineers build high-performance asynchronous microservices that use HTTP/2 as a session layer protocol. Visit the official web site and follow @armeria_project to check out many cool features you can't find in the official gRPC/Thrift implementation or other microservice frameworks.
TextFormatter.socketAddress()
and inetAddress()
that convert a SocketAddress
or an InetAddress
into a String
without repeating an IP address twice. #2591RequestContext.toString()
now returns a String
that includes its RequestId
. #2591Sampler.random(0.01)
never samples. #2592Logger
returned by RequestContext.makeContextAware(Logger)
now pushes the context whenever logging a message, so that RequestContextExporter
can retrieve the current context. #2587This release was possible thanks to the following contributors who shared their brilliant ideas and awesome pull requests:
Armeria is an open-source asynchronous HTTP/2 RPC/REST client/server library built on top of Java 8, Netty, Thrift and gRPC. Its primary goal is to help engineers build high-performance asynchronous microservices that use HTTP/2 as a session layer protocol. Visit the official web site and follow @armeria_project to check out many cool features you can't find in the official gRPC/Thrift implementation or other microservice frameworks.
UUID
as a parameter in your annotated service. #2573 #2577
public class MyAnnotatedService {
@Get("/user/by-uuid/{uuid}")
public User findUserByUuid(@Param UUID uuid) { ... }
}
LogLevel.OFF
and LogLevel.log(Logger, String, Object...)
have been added. #2532ClosedSessionException
and ClosedStreamException
now have a specific cause or message to help you understand why a connection or a stream has been closed. #2580ConcurrencyLimitingClient
does not fail with an 'illegal context' error under load anymore. #2579RetryingClient
and RetryingRpcClient
handles negative System.nanoTime()
values correctly now. #2584RetryingClient
and RetryingRpcClient
do not complete a request when RetryStrategy
or RetryStrategyWithContent
throws an exception. #2581EventLoopCheckingFuture
and its subtypes do not warn about blocking operation anymore when they are done already. #2564This release was possible thanks to the following contributors who shared their brilliant ideas and awesome pull requests:
Armeria is an open-source asynchronous HTTP/2 RPC/REST client/server library built on top of Java 8, Netty, Thrift and gRPC. Its primary goal is to help engineers build high-performance asynchronous microservices that use HTTP/2 as a session layer protocol. Visit the official web site and follow @armeria_project to check out many cool features you can't find in the official gRPC/Thrift implementation or other microservice frameworks.
HealthCheckService
does not leak the event loop tasks it scheduled anymore. #2557ServiceRequestContext.setRequestTimeout()
and ClientRequestContext.setResponseTimeout()
now schedules a timeout correctly even if no timeout was scheduled before. #2537Subscriber.onError()
is invoked rather than onComplete()
when a StreamMessage
has been aborted. #2539Subscriber
implementations. #2533This release was possible thanks to the following contributors who shared their brilliant ideas and awesome pull requests:
Armeria is an open-source asynchronous HTTP/2 RPC/REST client/server library built on top of Java 8, Netty, Thrift and gRPC. Its primary goal is to help engineers build high-performance asynchronous microservices that use HTTP/2 as a session layer protocol. Visit the official web site and follow @armeria_project to check out many cool features you can't find in the official gRPC/Thrift implementation or other microservice frameworks.
RequestContextExporter
and RequestContextExportingAppender
for Logback can now export the current request ID into MDC. #2511
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} %X{req.id} %msg%n</pattern>
</encoder>
</appender>
<appender name="RCEA" class="com.linecorp.armeria.common.logback.RequestContextExportingAppender">
<appender-ref ref="CONSOLE" />
<export>req.id</export>
</appender>
</configuration>
ClientOption
and ClientFactoryOption
) has been revamped. #2523
ClientOptions.get()
and ClientFactoryOptions.get()
never throw an exception. A default value is returned for an unspecified option. Therefore, other getter methods such as getOrElse()
have been removed.ClosedStreamException
instead of Http2Exception
when your HTTP/2 connection was closed due to an RST_STREAM
frame. #2508*ClientBuilder.options(ClientOptions)
now overrides only the specified options. #2516
ClientFactory factory = ClientFactory.insecure();
ClientOptions options = ClientOptions.builder()
.responseTimeMillis(5000)
.build();
WebClient client = WebClient.builder("http://foo.com")
.factory(factory)
.options(options)
.build();
// This assertion does not fail anymore.
assert client.options().factory() == ClientFactory.insecure();
DocService
throws the java.lang.IllegalStateException: zip file closed
exception while starting up. #2518 #2519MetricCollectingClient
does not count the request that failed initially due to a connection refused
error and then succeeded after a retry as failure anymore. #2517LoggingClient
and LoggingSerivce
does not push the current context when logging. #2528RequestContextExporter
and RequestContextExportingAppender
can now export a single custom attribute into multiple MDC properties. #2521RequestContextExporter
and RequestContextExportingAppender
does not export outdated custom attributes anymore. #2520armeria.server.pendingResponses
into armeria.server.pending.responses
. #2506Date
header is now encoded properly even when System.nanoTime()
returns a negative value. #2530GrpcStatus.fromThrowable()
now treats ContentTooLargeException
as RESOURCE_EXHAUSTED
rather than INTERNAL_ERROR
. #2523ClientOptions.getOrElse()
and getOrNull()
have been removed. get()
always returns a non-null value now. #2523ClientOptions.asMap()
and ClientFactoryOptions.asMap()
now return only the options overridden by a user. You can get the Set
of available options using ClientOptions.allOptions()
or ClientFactoryOptions.allOptions()
: #2516 #2523
ClientOptions myOptions = ...;
Map<ClientOption<?>, Object> map = new IdentityHashMap<>();
for (ClientOption o : ClientOptions.allOptions()) {
map.put(myOptions.get(o));
}
ClientOptions.valueOf()
and ClientFactoryOptions.valueOf()
have been split into two methods: define()
and of()
#2523.
of()
if you're getting an existing option.define()
if you're defining a new option.armeria.server.pendingResponses
, which has been renamed to armeria.server.pending.responses
in this release. #2506This release was possible thanks to the following contributors who shared their brilliant ideas and awesome pull requests:
Armeria is an open-source asynchronous HTTP/2 RPC/REST client/server library built on top of Java 8, Netty, Thrift and gRPC. Its primary goal is to help engineers build high-performance asynchronous microservices that use HTTP/2 as a session layer protocol. Visit the official web site and follow @armeria_project to check out many cool features you can't find in the official gRPC/Thrift implementation or other microservice frameworks.
ArmeriaRetrofitBuilder
, because it extends AbstractClientOptionsBuilder
. #2483
Retrofit retrofit = ArmeriaRetrofit.builder("http://example.com")
.factory(...)
.decorator(...)
.responseTimeout(...)
.build();
FallthroughException
is not a part of the internal API anymore, so you can refer to it when testing your annotation service extensions. #2495MAX_CONCURRENT_STREAMS
setting enforced by an HTTP/2 server anymore. #2256 #2374Server
fails to read PKCS#5 a private key since 0.98.0 #2485RequestContextExporter
does not export an entry whose value is null
anymore. #2492DocService
does not fail with a ReflectionsException
on startup anymore. #2491 #2494This release was possible thanks to the following contributors who shared their brilliant ideas and awesome pull requests:
Armeria is an open-source asynchronous HTTP/2 RPC/REST client/server library built on top of Java 8, Netty, Thrift and gRPC. Its primary goal is to help engineers build high-performance asynchronous microservices that use HTTP/2 as a session layer protocol. Visit the official web site and follow @armeria_project to check out many cool features you can't find in the official gRPC/Thrift implementation or other microservice frameworks.
FileService
does not warn about blocking operations anymore. #2473 #2478HealthCheckedEndpointGroup
does not fail to check the healthiness of old Armeria servers anymore. #2474This release was possible thanks to the following contributors who shared their brilliant ideas and awesome pull requests:
Armeria is an open-source asynchronous HTTP/2 RPC/REST client/server library built on top of Java 8, Netty, Thrift and gRPC. Its primary goal is to help engineers build high-performance asynchronous microservices that use HTTP/2 as a session layer protocol. Visit the official web site and follow @armeria_project to check out many cool features you can't find in the official gRPC/Thrift implementation or other microservice frameworks.
This release contains more breaking changes and deprecations than usual, which were necessary for the preparation of 1.0 release and the long term evolution of our API after 1.0. We'd like to apologize for any inconveniences caused by the breaking changes. Please don't forget we're always here for you and do let us know if you have any trouble upgrading, so we can help you!
@UnstableApi
annotation. #2445RequestContext
has been revamped for simplicity. #2322
AttributeKey<String> MY_ATTR = AttributeKey.valueOf("MY_ATTR");
RequestContext ctx = ...;
ctx.setAttr(MY_ATTR, "foo");
assert "foo".equals(ctx.attr(MY_ATTR));
RequestContext
is derived or inherited from another RequestContext
, the parent context's attributes are now visible from the derived or inherited context: #2322
AttributeKey<String> MY_ATTR = AttributeKey.valueOf("MY_ATTR");
ServiceRequestContext parentCtx = ...;
ClientRequestContext childCtx = ...;
assert childCtx.root() == parentCtx;
parentCtx.setAttr(MY_ATTR, "foo");
// Root context's attributes are visible from its children.
assert "foo".equals(childCtx.attr(MY_ATTR));
// Not visible anymore if the attribute is overwritten.
childCtx.setAttr(MY_ATTR, "bar");
assert "foo".equals(parentCtx.attr(MY_ATTR));
assert "bar".equals(childCtx.attr(MY_ATTR));
{Client,Service}RequestContext
now provides more useful ways to schedule request or response timeout: #2343
Server.builder()
.service("/svc", myService.decorate((delegate, ctx, req) -> {
if (req.headers().contains("x-extend-timeout")) {
// Extend the timeout by 10 seconds.
ctx.extendRequestTimeout(Duration.ofSeconds(10));
}
return delegate.serve(ctx, req);
}));
RequestLog
API has been revamped for safety and usability. #2342
RequestContext ctx = ...;
// Asynchronous retrieval:
ctx.log().whenRequestComplete().thenAccept(log -> {
// Can't access response properties at compilation level.
assert log instanceof RequestOnlyLog;
System.err.println(log.toStringRequestOnly());
})
ctx.log().whenComplete().thenAccept(log -> {
// Can access all properties.
assert log instanceof RequestLog;
System.err.println(log.toStringResponseOnly());
});
ctx.log().whenAvailable(RequestLogProperty.SESSION).thenAccept(log -> {
// Advanced use case:
// Raises an exception if accessing an unavailable property.
System.err.println(log.channel());
});
// Accessing a property ensuring availability:
ctx.log().ensureRequestComplete().requestEndTimeNanos();
ctx.log().ensureComplete().responseEndTimeNanos();
ctx.log().ensureAvailable(RequestLogProperty.RESPONSE_CONTENT)
.responseContent();
RequestLog
also has a new property NAME
, which can be used as a method name in an RPC call, a span name in distributed tracing or any other human-readable string that can be used for identifying a request. #2413QueryParams params = QueryParams.builder()
.add("foo", "1")
.add("bar", "2")
.build();
// Encoding
String queryString = params.toQueryString();
assert "foo=1&bar=2".equals(queryString);
// Decoding
QueryParams decodedParams = QueryParams.fromQueryString("foo=1&bar=2");
assert decodedParams.equals(params);
// Mutation
QueryParams newParams = params.toBuilder()
.add("baz", "3")
.build();
assert "foo=1&bar=2&baz=3".equals(newParams.toQueryString());
HttpStatus
: #2435
assert HttpStatus.CONTINUE.isInformational();
assert HttpStatus.OK.isSuccess();
assert HttpStatus.FOUND.isRedirect();
assert HttpStatus.BAD_REQUEST.isClientError();
assert HttpStatus.SERVICE_UNAVAILABLE.isServerError();
// No need to write like this anymore
assert HttpStatus.OK.codeClass() == HttpStatusClass.SUCCESS;
MediaTypeNames
which provides String
version of well known MediaType
s, which is useful when writing an annotated service: #2438
class MyAnnotatedService {
@Get("/download/zip")
@Produces(MediaTypeNames.ZIP)
HttpResponse downloadArchive() { ... }
}
{Request,Response}ConverterFunction
and ExceptionHandlerFunction
to all annotated services in your Server
easily. #2316
Server.builder()
.annotatedService("/users", userService)
.annotatedService("/posts", postService)
.annotatedService("/files", fileService)
// Applies all extensions to all 3 annotated services.
.annotatedServiceExtensions(
commonRequestConverters,
commonResponseConverters,
commonExceptionHandlers)
.build();
Server.builder()
// Route to 'myService' only when:
// - 'x-must-exist' header exists,
// - and 'bar' query parameter exists.
.route().get("/foo")
.matchesHeaders("x-must-exist")
.matchesParams("bar")
.build(myService)
SslContext
created from ServerBuilder.tlsSelfSigned()
or VirtualHost.tlsSelfSigned()
: #2340
Server.builder()
.tlsSelfSigned()
.tlsCustomizer(sslCtxBuilder -> {
sslCtxBuilder.ciphers(...);
})
EndpointGroup
asynchronously: #2430
DnsAddressEndpointGroup group =
DnsAddressEndpointGroup.of("cluster.com", 8080);
group.whenClosed().thenRun(() -> {
System.err.println("Closed!");
});
group.closeAsync();
EndpointGroup
to EndpointGroupRegistry
for client-side load balancing. Just specify it when you build a client: #2381
EndpointGroup group = EndpointGroup.of(
Endpoint.of("node1.cluster.com"),
Endpoint.of("node2.cluster.com"));
// Thrift
HelloService.Iface client =
Clients.builder("tbinary+http", group)
.path("/api/thrift/hello")
.build(HelloService.Iface.class);
// gRPC
HelloServiceBlockingStub client =
Clients.builder("gproto+http", group)
.build(HelloServiceBlockingStub.class);
// Web
WebClient client =
WebClient.of(SessionProtocol.HTTP, group);
HealthCheckedEndpointGroup
, which is very useful when there are many candidate endpoints in the group but you want to send requests to only a few of them, to avoid unnecessarily large number of outbound connections: #2177
HealthCheckedEndpointGroup group =
HealthCheckedEndpointGroup.builder(delegateGroup, "/health")
.maxEndpointCount(3)
.build();
ClientRequestContext
of your client call with ClientRequestContextCaptor
: #2344
WebClient client = WebClient.of("http://foo.com/");
try (ClientRequestContextCaptor ctxCaptor = Clients.newContextCaptor()) {
HttpResponse res = client.get("/");
ClientRequestContext ctx = ctxCaptor.get();
...
}
ClientFactory.insecure()
and ClientFactoryBuilder.tlsNoVerify()
to simplify testing SSL/TLS connections with self-signed certificates: #2340
// Using the default insecure factory
WebClient.builder("https://127.0.0.1:8443")
.factory(ClientFactory.insecure())
.build();
// Using a custom insecure factory
WebClient.builder("https://127.0.0.1:8443")
.factory(ClientFactory.builder()
.tlsNoVerify()
...
.build())
.build();
ClientFactory
is now part of ClientOptions
for easier creation of derived clients. #2384
ClientFactory factory = ...;
WebClient client =
WebClient.builder(...)
.factory(factory)
.build();
WebClient clientCopy =
WebClient.builder(...)
.options(client.options())
.build();
// Note that ClientFactory is copied as well.
assert client.factory() == clientCopy.factory();
RetrofitMeterIdPrefixFunction
is now capable of adding HTTP method and request path pattern if you specify a Retrofit service class: #2356armeria-dropwizard
provides the integration with Dropwizard, which allows you to leverage the best of the both worlds. #2236
DocService
when integrating with Spring framework by injecting DocServiceConfigurator
: #2327
@Bean
public DocServiceConfigurator docServiceConfigurator() {
// Exclude all Thrift services from DocService.
return docServiceBuilder -> {
docServiceBuilder.exclude(DocServiceFilter.ofThrift());
};
}
ServerRule
(JUnit 4) and ServerExtension
(JUnit 5) now have more getters: #2449
Endpoint
getters:
endpoint(SessionProtocol)
, httpEndpoint()
and httpsEndpoint()
URI
getters:
uri(SessionProtocol)
, uri(SessionProtocol, SerializationFormat)
, httpUri()
, httpUri(SerializationFormat)
, httpsUri()
and httpsUri(SerializationFormat)
String
instead of URI
.InetSocketAddress
getters:
socketAddress(SessionProtocol)
CompletableFuture
s returned by our API will leave a warning log like the following when you perform a blocking operation in an event loop thread: #2275
Calling a blocking method on CompletableFuture from an event loop or
non-blocking thread. You should never do this ...
java.lang.IllegalStateException: Blocking event loop, don't do this.
You can disable this functionality by specifying the -Dcom.linecorp.armeria.reportBlockedEventLoop=false
JVM option.ThriftCall
, ThriftReply
, TMessage
and TBase
into TTEXT JSON using ThriftJacksonModule
. #2439
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new ThriftJacksonModule());
Logger
context-aware with RequestContext.makeContextAware(Logger)
: #2341
// Prints 'Hello!'
Logger logger = ...;
logger.info("Hello!");
// Prints '[<current context>] Hello!'
Logger ctxAwareLogger = ctx.makeContextAware(logger);
ctxAwareLogger("Hello!");
RequestContextExporter
is now part of the core API, allowing you to integrate with other logging frameworks than Logback, such as Log4J2. #2314-Dcom.linecorp.armeria.validateHeaders=false
JVM option.Logging{Client,Service}
#2341UnknownHostException
raised by Armeria now explains what DNS query has failed. #2332WebClient
now accepts a URI that starts with none+
as well. #2361HealthCheckedEndpointGroup
now logs a helpful warning message when it receives a 4XX response. #2401B
and SELF
anymore. #2454SSLException
instead of ClosedSessionException
if a connection attempt fails with an SSL/TLS handshake error. #2338WebClient
does not omit a request query string when sending a request to an absolute URI. #2309Server
with a misconfigured SslContext
anymore. #2325RequestContext
even if the contexts are nested. #1083Clients.withHttpHeaders()
and withContextCustomizer()
now work with gRPC calls. #2344ClientRequestContext.path()
now returns a correct path for gRPC client calls. #2344DocService
client. #2357HttpResponseWriter
. #2359ClientFactoryOptions
does not raise a NullPointerException
anymore. #2387AsyncMethodCallback
specified when making an async Thrift call now has thread-local ClientRequestContext
set properly. #2383Server
can be started back after close()
is called. #2406com.linecorp.armeria.versions.properties
file. #2398cannot start a new stream with a DATA frame
errors anymore. #2429RequestLog.requestStartTime
property now includes the time taken for making a connection attempt and the time taken by decorators. #2436-Dcom.linecorp.armeria.dumpOpenSslInfo=true
JVM option does not trigger a StackOverflowError
anymore. #2418DocService
client sidebar. #2470HttpParameters
has been deprecated in favor of QueryParams
#2307ServerBuilder.tls()
methods that require a Consumer
have been deprecated. Use tlsCustomizer(Consumer)
instead. #2340
// Before:
Server.builder()
.tls(..., sslCtxBuilder -> { ... });
// After:
Server.builder()
.tls(...)
.tlsCustomizer(sslCtxBuilder -> { ... });
Http
in their names have been deprecated in favor of those without Http
, e.g. #2323
RetryingHttpClient
-> RetryingClient
HttpFileService
-> FileService
builder()
static methods, e.g. #1719
new ClientBuilder()
-> Clients.builder()
new ArmeriaRetrofitBuilder()
-> ArmeriaRetrofit.builder()
new ServerCacheControlBuilder()
-> ServerCacheControl.builder()
EndpointSelectionStrategy.ROUND_ROBIN
-> roundRogin()
NodeValueCodec.DEFAULT
-> ofDefault()
AuthTokenExtractors.BASIC
-> basic()
MoreNamingConventions
has been deprecated because we follow Micrometer's recommended naming convention. #2367Version.identify()
has been deprecated in favor of getAll()
#2398ServiceRequestContext.setRequestTimeout*()
has been deprecated in favor of extendRequestTimeout*()
, setRequestTimeoutAfter*()
, setRequestTimeoutAt*()
and clearRequestTimeout()
. #2343ClientRequestContext.setResponseTimeout*()
has been deprecated in favor of extendResponseTimeout*()
, setResponseTimeoutAfter*()
, setResponseTimeoutAt*()
and clearResponseTimeout()
. #2343CompletableFuture
have been renamed from *Future()
to when*()
, e.g. #2427
HttpRequest.completionFuture()
-> whenComplete()
HttpResponseWriter.onDemand()
-> whenConsumed()
EndpointGroup.initialEndpointsFuture()
-> whenReady()
ServerRule
and ServerExtension
have been deprecated in favor of the new methods that do not require a path parameter: #2449
ServerExtension server = ...;
// Before
server.httpUri("/");
server.httpUri("/foo");
// After
server.httpUri();
server.httpUri().resolve("/foo");
THttpService.allowedSerializationFormats()
has been deprecated in favor of supportedSerializationFormats()
for consistency with GrpcService
. #2453Service.decorate(Class)
has been deprecated in favor of other decorate()
methods that require a function.ClosedPublisherException
has been deprecated in favor of ClosedStreamException
. #2468RequestLog
RequestContext
ContentPreviewingClient
and ContentPreviewingService
.RequestContext
anymore because we found this feature results in poor performance and confusing behavior in many cases. We may want to revisit this feature if there is a valid use case for it.{Server,VirtualHost}Builder.tls()
methods do not throw an SSLException
anymore. build()
will throw an IllegalStateException
instead. As a result, any SSL configuration failure will be known when a user calls build()
, rather than tls()
.Http
from class names.ServiceRequestContext.logger()
has been removed due to performance issues with Log4J2.RequestContext.isTimedOut()
has been removed.armeria-grpc-protocol
have been reorganized into multiple packages.-Dcom.linecorp.armeria.useLegacyMeterNamed=true
JVM option. However, please keep in mind this option will eventually go away, because the new naming convention is recommended by Micrometer.Optional
anymore. They are all @Nullable
now. If you wish to continue using Optional
, just wrap the return value with Optional.ofNullable()
.EndpointGroupRegistry
has been removed, because you can now just specify an EndpointGroup
directly when creating a client.
EndpointSelectionStrategy
when building an EndpointGroup
. If unspecified, EndpointSelectionStrategy.weightedRoundRobin()
is used.MeterIdPrefixFunction
is not a functional interface anymore. You must implement two methods explicitly: activeRequestPrefix()
and completeRequestPrefix()
.ClientFactory
is a part of ClientOption
, the following code will not work as expected, because options(options)
will overwrite the factory.
ClientOptions options = ClientOptions.builder().maxResponseLength(...).build();
ClientFactory factory = ClientFactory.builder().useHttp2Preface(false).build();
WebClient client = WebClient.builder(...)
.factory(factory)
.options(options)
.build();
// This will fail!
assert client.options().factory() == factory;
To fix this, you must call options()
first, and then override the individual properties:
WebClient client = WebClient.builder(...)
.options(options)
.factory(factory)
.build();
StreamMessage.subscribe(..., boolean withPooledObjects)
has been removed. Use subscribe(..., SubscriptionOption.WITH_POOLED_OBJECTS)
.StreamMessage.drainAll(..., boolean withPooledObjects)
has been removed. Use drainAll(..., SubscriptionOption.WITH_POOLED_OBJECTS)
.HttpRequestDuplicator
and HttpResponseDuplicator
are now interfaces. Use HttpRequest.toDuplicator()
and HttpResponse.toDuplicator()
to create a duplicator.StructuredLog
and StructuredLoggingService
have been removed. Use AccessLogWriter
.ThriftStructuredLogJsonFormat
has been removed. Register ThriftJacksonModule
to ObjectMapper
to serialize or deserialize Thrift objects.This release was possible thanks to the following contributors who shared their brilliant ideas and awesome pull requests:
Armeria is an open-source asynchronous HTTP/2 RPC/REST client/server library built on top of Java 8, Netty, Thrift and gRPC. Its primary goal is to help engineers build high-performance asynchronous microservices that use HTTP/2 as a session layer protocol. Visit the official web site and follow @armeria_project to check out many cool features you can't find in the official gRPC/Thrift implementation or other microservice frameworks.
SameSite
attribute support, which replaces Netty's cookie API. #1567 #2286
Cookie c1 = Cookie.of("foo", "bar")
Cookie c2 = Cookie.builder("alice", "bob")
.domain("foo.com")
.path("/bar")
.maxAge(3600)
.sameSite("Strict")
.build();
// Encoding
ResponseHeaders resHeaders =
ResponseHeaders.builder(HttpStatus.OK)
.add(HttpHeaderNames.SET_COOKIE,
Cookie.toSetCookieHeaders(c1, c2))
.build();
// Decoding
Cookies cookies =
Cookie.fromSetCookieHeaders(
resHeaders.getAll(HttpHeaderNames.SET_COOKIE));
HttpRequest
more conveniently using HttpRequest.withHeaders(RequestHeaders)
. #2278 #2283
HttpRequest req = HttpRequest.of(HttpMethod.POST, "/post",
MediaType.PLAIN_TEXT_UTF_8,
"Hello!");
RequestHeaders newHeaders = RequestHeaders.of(HttpMethod.PUT, "/put",
MediaType.PLAIN_TEXT_UTF_8);
HttpRequest newReq = req.withHeaders(newHeaders);
ServiceRequestContext.blockingTaskExecutor()
is now a ScheduledExecutorService
instead of ExecutorService
, which means you can schedule delayed or periodic tasks. #2269
// A service that sends `PING!` every second.
HttpService service = (ctx, req) -> {
HttpResponse res = HttpResponse.streaming();
res.write(ResponseHeaders.of(200));
AtomicReference<ScheduledFuture<?>> futureHolder = new AtomicReference<>();
futureHolder.set(ctx.blockingTaskExecutor().scheduleWithFixedDelay(() -> {
boolean success = res.tryWrite(HttpData.ofUtf8("PING!\r\n"));
if (!success) {
if (futureHolder.get() != null) {
futureHolder.get().cancel(false);
}
}
}, 1, TimeUnit.SECONDS));
return res;
};
Server server =
Server.builder()
.annotatedService().requestConverters(converterA, converterB)
.responseConverters(converterC, converterD)
.exceptionHandlers(handlerA, handlerB)
.build(myAnnotatedService)
.build();
ServiceRequestContext.clientAddress()
via ServerBuilder.clientAddressMapper()
. #1631 #2294
Server.builder()
.clientAddressMapper(proxiedAddrs -> {
InetSocketAddress srcAddr = proxiedAddrs.sourceAddress();
List<InetSocketAddress> destAddrs = proxiedAddrs.destinationAddresses();
if (destAddrs.isEmpty()) {
// No proxy servers involved.
return srcAddr;
} else {
// When there are more than one proxy server involved,
// trust only the address given by the last proxy server.
return destAddrs.get(destAddrs.size() - 1);
}
});
Logger
than the default one when using LoggingClient
or LoggingService
. #2220 #2237
Logger myLogger = LoggerFactory.getLogger(MyService.class);
Server server =
Server.builder()
.service("/",
myService.decorate(LoggingService.builder()
.logger(myLogger)
.newDecorator()))
.build();
BraveClient
now adds connection timing information to a span. #2271 #2273
connection-acquire.start
and connection-acquire.end
dns-resolve.start
and dns-resolve.end
socket-connect.start
and socket-connect.end
connection-reuse.start
and connection-reuse.end
withDeadlineAfter
is now supported for gRPC client stubs. #2284GrpcService
now allows controlling whether to respect the "grpc-timeout"
header sent by a gRPC client. #2284
GrpcServiceBuilder.useClientTimeoutHeader()
in an insecure environment.THttpService
and ThriftCallService
now supports adding more than one Thrift service at the same endpoint. #2164 #2285
class MyFooService implements FooService.Iface ... {
public void foo() { ... }
}
class MyBarService implements BarService.Iface ... {
public void bar() { ... }
}
// "foo" call goes to MyFooService and
// "bar" call goes to MyBarService.
Server server =
Server.builder()
.service("/thrift",
THttpService.builder()
.addService(new MyFooService())
.addService(new MyBarService())
.build())
.build();
armeria-spring-boot-actuator-autoconfigure
now supports the official CORS settings. #2063
# application.yml
management.endpoints.web.cors.allowed-origins: https://example.com
management.endpoints.web.cors.allowed-methods: GET,POST
HttpRequest.of(RequestHeaders, Publisher)
now handles the case where the Publisher
is actually an HttpRequest
. #2278 #2283Server
failed to start. #2288Server
now sends a redirect response correctly for the requests without a trailing slash (/
) even when you bound a fallback service at prefix:/
. #2116 #2292"Cookie"
header when injecting Cookies
, because multiple "Cookie"
headers must be prohibited according to RFC 6265. #2286GrpcService
now respects gRPC stub deadline. #2008 #2284HttpRequest.of(HttpRequest, RequestHeaders)
in favor ofHttpRequest.withHeaders()
#2283HttpRequest.of(AggregatedHttpRequest)
andHttpResponse.of(AggregatedHttpResponse)
in favor ofAggregatedHttpRequest.toHttpRequest()
andAggregatedHttpResponse.toHttpResponse()
. #2283
AggregatedHttpMessage
some time ago due to ambiguity, but now we are reviving them because we have separate types for requests and responses.Cookies.copyOf()
has been deprecated in favor of Cookies.of()
. #2286AnnotatedServiceBuildingBuilder.requestConverter()
, responseConverter()
and exceptionHandler()
have been deprecated in favor of requestConverters()
, responseConverters()
and exceptionHandlers()
respectively. #2242Sampler.rateLimited()
has been deprecated in favor of rateLimiting()
. #2122 #2289THttpService.of()
and ofFormats()
factory methods that require a Map<String, ?>
have been deprecated. Use THttpService.builder()
and call THttpService.addService(String, Object)
multiple times instead. #2285ServerBuilder.blockingTaskExecutor()
now requires a ScheduledExecutorService
instead of an ExecutorService
. #2269/
) has been changed (in a good way). #2116 #2292
/docs
, the following server routed the request to TomcatService
:
Server server =
Server.builder()
.serviceUnder("/docs", new DocService())
.serviceUnder("/", TomcatSerivce.forClassPath(...))
.build();
TomcatService
but the server will issue a redirect response to /docs/
.ProxiedAddresses
class has been redesigned to handle the case of more than one intermediary proxy server. #1631 #2294Cookies
now uses Armeria's own Cookie
. #2286Cookies
now may contain the cookies with the same name. #2286
"Cookie"
header when decoding cookies. Sending multiple "Cookie"
headers was a violation of RFC 6265 anyway. #2286ThriftCallService
factory method with signature of(Map<String, ? extends Iterable<?>>)
has been changed to of(Map<String, ? extends Iterable<?>>)
#2285This release was possible thanks to the following contributors who shared their brilliant ideas and awesome pull requests:
Armeria is an open-source asynchronous HTTP/2 RPC/REST client/server library built on top of Java 8, Netty, Thrift and gRPC. Its primary goal is to help engineers build high-performance asynchronous microservices that use HTTP/2 as a session layer protocol. Visit the official web site and follow @armeria_project to check out many cool features you can't find in the official gRPC/Thrift implementation or other microservice frameworks.
// Before:
public class FooHttpService implements Service<HttpRequest, HttpResponse> { ... }
public class FooRpcService implements Service<RpcRequest, RpcResponse> { ... }
public class FooHttpClient implements Client<HttpRequest, HttpResponse> { ... }
public class FooRpcClient implements Client<RpcRequest, RpcResponse> { ... }
Service<HttpRequest, HttpResponse> foo(Service<HttpRequest, HttpResponse> bar) { ... }
Function<Service<HttpRequest, HttpResponse>,
? extends Service<HttpRequest, HttpResponse>> httpServiceDecorator;
Function<Service<RpcRequest, RpcResponse>,
? extends Service<RpcRequest, RpcResponse>> rpcServiceDecorator;
Function<Client<HttpRequest, HttpResponse>,
? extends Client<HttpRequest, HttpResponse>> httpClientDecorator;
Function<Client<RpcRequest, RpcResponse>,
? extends Client<RpcRequest, RpcResponse>> rpcClientDecorator;
// After:
public class FooHttpService implements HttpService { ... }
public class FooRpcService implements RpcService { ... }
public class FooHttpClient implements HttpClient { ... }
public class FooRpcClient implements RpcClient { ... }
HttpService foo(HttpService bar) { ... }
Function<? super HttpService, ? extends HttpService> httpServiceDecorator;
Function<? super RpcService, ? extends RpcService> rpcServiceDecorator;
Function<? super HttpClient, ? extends HttpClient> httpClientDecorator;
Function<? super RpcClient, ? extends RpcClient> rpcClientDecorator;
Server server =
Server.builder()
.annotatedService().pathPrefix("/api")
.requestTimeoutMillis(5000)
.exceptionHandler((ctx, req, cause) -> ...)
.build(new Object() { ... }))
.build();
RequestContext
now has its own RequestId
, which is a 64-bit random integer by default. #2001 #2174 #2203 #2224
Server server =
Server.builder()
.service("/", (ctx, req) -> {
return HttpResponse.of("Request ID: %s", ctx.id().text());
})
.build();
HttpStatusException
. #2253
HttpService service = (ctx, req) -> {
try (FileInputStream in = new FileInputStream(...)) {
...
} catch (FileNotFoundException e) {
throw HttpStatusException.of(HttpStatus.NOT_FOUND, e);
}
};
prefix:/
can intercept any requests, even the requests not handled by any services:
// CorsService will intercept any requests,
// even the ones not handled by any services.
Server server =
Server.builder()
.service("/foo", fooService)
.service("/bar", barService)
.routeDecorator().pathPrefix("/")
.build(CorsService.newDecorator(...))
.build();
LoggingClient
and LoggingService
dynamically. #2250 #2258
LoggingClient.builder()
.requestLogLevel(log -> ...)
.responseLogLevel(log -> {
if (log.responseCause() == null ||
log.responseCause() instanceof HarmlessException) {
return LogLevel.INFO;
} else {
return LogLevel.WARN;
}
});
ClientFactoryOptions
has been added to allow programmatic access to the ClientFactory
settings. #2230
ClientFactory factory = ...;
boolean pipelining = factory.options().useHttp1Pipelining();
-Dcom.linecorp.armeria.useJdkDnsResolver=true
JVM option. Use it only when the default resolver does not work. #2261none+
prefix to non-RPC URIs anymore. #2219 #2241
// Before:
Clients.of("none+https://...", ...);
// After:
Clients.of("https://...", ...);
Scheme.parse()
and tryParse()
has been improved in the same manner:
// Scheme.parse() now uses SerializationFormat.NONE automatically:
assert Scheme.parse("http") == Scheme.parse("none+http");
GrpcServiceRegistrationBean
s are given. #2234RequestContext
is not pushed more often than necessary anymore when notifying RequestLogListener
s. #2227DynamicEndpointGroup
now handles the case where an endpoint address does not change but only a weight. #2240HttpStatusException
now respects Flags.verboseExceptionSampler()
. #2253AccessLogWriter
does not fail with a ClassCastException
anymore. #2259OPTIONS
request even if the service did not opt-in for OPTIONS
method. #2263ClientFactory.DEFAULT
has been deprecated in favor of ClientFactory.ofDefault()
.ServerConfig
have been deprecated. #2244 #2246
// Before:
HttpService service = (ctx, req) -> {
return HttpResponse.of("maxRequestLength: %d",
ctx.server().maxRequestLength());
};
// After:
HttpService service = (ctx, req) -> {
return HttpResponse.of("maxRequestLength: %d",
ctx.virtualHost().maxRequestLength());
};
builder()
factory methods, the following classes have switched from constructors to static builder()
methods: #2221
ClientCacheControl
ClientConnectionTimings
ClientDecoration
ClientFactory
ClientOptions
ClientRequestContext
EndpointInfo
FieldInfo
HttpClient
LoggingClient
LoggingService
RequestContextCurrentTraceContext
RetrofitMeterIdPrefixFunction
ServerCacheControl
ServiceRequestContext
RequestContext.newDerivedContext()
have been changed so they always require RequestId
, HttpRequest
and RpcRequest
for less ambiguity. #2209 #2224Scheme.parse()
and tryParse()
do not fail anymore even if the scheme does not start with none+
: #2219 #2241
// Scheme.parse() now uses SerializationFormat.NONE automatically:
assert Scheme.parse("http") == Scheme.parse("none+http");
ClientBuilderParams.uri()
does not return a URI with none+
prefix anymore. #2219 #2241HttpClient
interface has been renamed to WebClient
. HttpClient
is now a different interface that extends Client<HttpRequest, HttpResponse>
. #2254
// Doesn't work:
HttpClient client = HttpClient.of("https://www.google.com/");
// Good:
WebClient client = WebClient.of("https://www.google.com/");
HttpService
or RpcService
and clients must implement HttpClient
or RpcClient
: #2239 #2254
// Before:
public class FooHttpService implements Service<HttpRequest, HttpResponse> { ... }
public class FooRpcService implements Service<RpcRequest, RpcResponse> { ... }
public class FooHttpClient implements Client<HttpRequest, HttpResponse> { ... }
public class FooRpcClient implements Client<RpcRequest, RpcResponse> { ... }
// After:
public class FooHttpService implements HttpService { ... }
public class FooRpcService implements RpcService { ... }
public class FooHttpClient implements HttpClient { ... }
public class FooRpcClient implements RpcClient { ... }
Service<...>
and Client<...>
usages in your code into HttpService
, RpcService
, HttpClient
or RpcClient
, unless you intended to express both HTTP- and RPC- level types.
// Before:
Service<HttpRequest, HttpResponse> foo(Service<HttpRequest, HttpResponse> bar) { ... }
// After:
HttpService foo(HttpService bar) { ... }
SimpleDecoratingHttpClient
or SimpleDecoratingRpcClient
instead of SimpleDecoratingClient
, and SimpleDecoratingHttpService
or SimpleDecoratingRpcService
instead of SimpleDecoratingService
.
// Does not work:
class MyDecoratorService extends SimpleDecoratingService<HttpRequest, HttpResponse> {
MyDecoratorService(Service<HttpRequest, HttpResponse> delegate) {
super(delegate);
}
...
}
// Good:
class MyDecoratorService extends SimpleDecoratingHttpService {
// Note the constructor parameter change.
MyDecoratorService(HttpService delegate) {
super(delegate);
}
...
}
SimpleDecorating{Http,Rpc}Service
or SimpleDecorating{Http,Rpc}Client
, then you must make sure that it implements HttpService
, RpcRequest
, HttpClient
or RpcClient
.
// Does not work:
class MyDecoratorService implements Service<HttpRequest, HttpResponse> {
final Service<HttpRequest, HttpResponse> delegate;
MyDecoratorService(Service<HttpRequest, HttpResponse> delegate) {
this.delegate = delegate;
}
...
}
// Good:
class MyDecoratorService implements HttpService {
// Note the type change.
final HttpService delegate;
// Note the constructor parameter change.
MyDecoratorService(HttpService delegate) {
this.delegate = delegate;
}
...
}
Service.decorate()
has been pushed down to HttpService
and RpcService
: #2239 #2254
// Does not work:
Service<HttpRequest, HttpResponse> service = (ctx, req) -> ...;
service.decorate(myDecorator); // No such method
// Good:
HttpService service (ctx, req) -> ...;
service.decorate(myDecorator); // OK!
Function
s have been changed. #2239 #2254
// Before:
Function<Service<HttpRequest, HttpResponse>,
? extends Service<HttpRequest, HttpResponse>> httpServiceDecorator;
Function<Service<RpcRequest, RpcResponse>,
? extends Service<RpcRequest, RpcResponse>> rpcServiceDecorator;
Function<Client<HttpRequest, HttpResponse>,
? extends Client<HttpRequest, HttpResponse>> httpClientDecorator;
Function<Client<RpcRequest, RpcResponse>,
? extends Client<RpcRequest, RpcResponse>> rpcClientDecorator;
// After:
Function<? super HttpService, ? extends HttpService> httpServiceDecorator;
Function<? super RpcService, ? extends RpcService> rpcServiceDecorator;
Function<? super HttpClient, ? extends HttpClient> httpClientDecorator;
Function<? super RpcClient, ? extends RpcClient> rpcClientDecorator;
DecoratingClientFunction
has been split into DecoratingHttpClientFunction
and DecoratingRpcClientFunction
.DecoratingServiceFunction
has been split into DecoratingHttpServiceFunction
and DecoratingRpcServiceFunction
.LoggingClient
has been split into LoggingClient
and LoggingRpcClient
.ServiceWithRoutes
has been split into HttpServiceWithRoutes
and RpcServiceWithRoutes
.TransientService
has been split into TransientHttpService
and TransientRpcService
.LoggingService
MetricCollectingService
StructuredLoggingService
KafkaStructuredLoggingService
This release was possible thanks to the following contributors who shared their brilliant ideas and awesome pull requests: