Armeria v0.96.0 Release Notes
Release Date: 2019-11-23 // over 4 years ago-
๐ฑ ๐ฎ What is Armeria?
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.
๐ฑ ๐ New features
๐ The type signature of clients and services has been greatly simplified. Note that this involves various breaking changes unfortunately. See the 'Breaking changes' section for the details. #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> { ... }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;
You can now specify service-level settings for annotated services, just like other service types. #2180 #2222
Server server =Server.builder() .annotatedService().pathPrefix("/api") .requestTimeoutMillis(5000) .exceptionHandler((ctx, req, cause) -> ...) .build(new Object() { ... })) .build();
0๏ธโฃ
RequestContext
now has its ownRequestId
, which is a 64-bit random integer by default. #2001 #2174 #2203 #2224Server server =Server.builder() .service("/", (ctx, req) -> { return HttpResponse.of("Request ID: %s", ctx.id().text()); }) .build();
You can now add a cause to an
HttpStatusException
. #2253HttpService service = (ctx, req) -> { try (FileInputStream in = new FileInputStream(...)) { ... } catch (FileNotFoundException e) { throw HttpStatusException.of(HttpStatus.NOT_FOUND, e); } };
Armeria server now always has a fallback service which is matched when no routes match a request. #1625 #2255
- As a result, a route decorator bound at
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();
- As a result, a route decorator bound at
๐ฒ You can now determine the log level of
LoggingClient
andLoggingService
dynamically. #2250 #2258LoggingClient.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 theClientFactory
settings. #2230ClientFactory factory = ...;boolean pipelining = factory.options().useHttp1Pipelining();
0๏ธโฃ You can now disable Armeria's Netty-based asynchronous DNS resolver by specifying
-Dcom.linecorp.armeria.useJdkDnsResolver=true
JVM option. Use it only when the default resolver does not work. #2261
๐ฑ ๐ช Improvements
- Armeria client does not block a request anymore when DNS resolution takes long time. #2017 #2217 #2231
You do not have to prepend
none+
prefix to non-RPC URIs anymore. #2219 #2241// Before:Clients.of("none+https://...", ...);// After:Clients.of("https://...", ...);
- The behavior of
Scheme.parse()
andtryParse()
has been improved in the same manner:
// Scheme.parse() now uses SerializationFormat.NONE automatically:assert Scheme.parse("http") == Scheme.parse("none+http");
- The behavior of
๐ ๏ธ Bug fixes
- ๐ง Armeria Spring auto-configuration now works correctly even when only
GrpcServiceRegistrationBean
s are given. #2234 RequestContext
is not pushed more often than necessary anymore when notifyingRequestLogListener
s. #2227DynamicEndpointGroup
now handles the case where an endpoint address does not change but only a weight. #2240- ๐
HttpStatusException
now respectsFlags.verboseExceptionSampler()
. #2253 AccessLogWriter
does not fail with aClassCastException
anymore. #2259- ๐ Fixed a bug where a service receives an
OPTIONS
request even if the service did not opt-in forOPTIONS
method. #2263
๐ฑ ๐๏ธ Deprecations
- ๐
ClientFactory.DEFAULT
has been deprecated in favor ofClientFactory.ofDefault()
. ๐ The getters for host-level settings in
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()); };
๐ Continuing the migration to static
builder()
factory methods, the following classes have switched from constructors to staticbuilder()
methods: #2221ClientCacheControl
ClientConnectionTimings
ClientDecoration
ClientFactory
ClientOptions
ClientRequestContext
EndpointInfo
FieldInfo
HttpClient
LoggingClient
LoggingService
RequestContextCurrentTraceContext
RetrofitMeterIdPrefixFunction
ServerCacheControl
ServiceRequestContext
๐ฑ โข๏ธ Breaking changes
- The signatures of
RequestContext.newDerivedContext()
have been changed so they always requireRequestId
,HttpRequest
andRpcRequest
for less ambiguity. #2209 #2224 ๐
Scheme.parse()
andtryParse()
do not fail anymore even if the scheme does not start withnone+
: #2219 #2241// Scheme.parse() now uses SerializationFormat.NONE automatically:assert Scheme.parse("http") == Scheme.parse("none+http");
ClientBuilderParams.uri()
does not return a URI withnone+
prefix anymore. #2219 #2241HttpClient
interface has been renamed toWebClient
.HttpClient
is now a different interface that extendsClient<HttpRequest, HttpResponse>
. #2254// Doesn't work:HttpClient client = HttpClient.of("https://www.google.com/");// Good:WebClient client = WebClient.of("https://www.google.com/");
Services must implement
HttpService
orRpcService
and clients must implementHttpClient
orRpcClient
: #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 { ... }
- You'll have to change any
Service<...>
andClient<...>
usages in your code intoHttpService
,RpcService
,HttpClient
orRpcClient
, 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) { ... }
- Similarly, you must use
SimpleDecoratingHttpClient
orSimpleDecoratingRpcClient
instead ofSimpleDecoratingClient
, andSimpleDecoratingHttpService
orSimpleDecoratingRpcService
instead ofSimpleDecoratingService
.
// 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); } ...}
- If you implemented your decorator without extending the
SimpleDecorating{Http,Rpc}Service
orSimpleDecorating{Http,Rpc}Client
, then you must make sure that it implementsHttpService
,RpcRequest
,HttpClient
orRpcClient
.
// 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; } ...}
- You'll have to change any
Service.decorate()
has been pushed down toHttpService
andRpcService
: #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!
The type parameters of decorator
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;
Some types, which could be used at both HTTP- and RPC- level, have been split into two different types. For example:
DecoratingClientFunction
has been split intoDecoratingHttpClientFunction
andDecoratingRpcClientFunction
.DecoratingServiceFunction
has been split intoDecoratingHttpServiceFunction
andDecoratingRpcServiceFunction
.LoggingClient
has been split intoLoggingClient
andLoggingRpcClient
.ServiceWithRoutes
has been split intoHttpServiceWithRoutes
andRpcServiceWithRoutes
.TransientService
has been split intoTransientHttpService
andTransientRpcService
.
The following services are now provided only at HTTP-level. Please let us know if you need an RPC-level version of them.
LoggingService
MetricCollectingService
StructuredLoggingService
KafkaStructuredLoggingService
๐ฑ โ Dependencies
- Brave 5.8.0 -> 5.9.0
- gRPC 1.24.1 -> 1.25.0
- Jackson 2.10.0 -> 2.10.1
- Micrometer 1.3.0 -> 1.3.1
- Netty TCNative BoringSSL 2.0.26 -> 2.0.27
- Protobuf 3.9.1 -> 3.10.0
- RxJava 2.2.13 -> 2.2.14
- SLF4J 1.7.28 -> 1.7.29
- Spring Boot 2.1.9 -> 2.1.10
๐ฑ ๐ Thank you
๐ This release was possible thanks to the following contributors who shared their brilliant ideas and awesome pull requests: