You're seeing the release note of an old version. Check out the latest release note.
v1.3.0
November 30, 2020
🌟 New features
-
You can now use io_uring for efficient I/O processing in Linux. #3182
- Specify the
-Dcom.linecorp.armeria.transportType=io_uringJVM option to enable it. - Netty's
io_uringtransport is currently experimental, so you should be careful using the feature.
- Specify the
-
The metrics of requests to a
TransientServiceare not collected anymore by default. #3061 #3081- Access logs and service logs are not recorded as well.
- You should use
TransientServiceOptionto enable them.HealthCheckService.builder()
.transientServiceOptions(TransientServiceOption.WITH_METRIC_COLLECTION,
TransientServiceOption.WITH_SERVICE_LOGGING,
TransientServiceOption.WITH_ACCESS_LOGGING)
.build(); - Currently,
HealthCheckServiceandPrometheusExpositionServiceareTransientServices.
-
You can now use Protobuf's
Messageand ScalaPB'sGeneratedMessageas a request/response object in an annotated service. #3088 #3124 #3192- Use
armeria-protobuf,armeria-scalapb_2.12orarmeria-scalapb_2.13dependencies. - See Supporting ScalaPB in annotated services for more information.
- Use
-
You can now use Scala
Futurein an annotated service. #3189@Get("/items/{id}")
def items(@Param id: Int)(implicit ec: ExecutionContext): Future[String] = {
Future {
// Perform asynchronous task using Armeria's event loop.
...
}
} -
You can now use
HttpDeframerto conveniently decode a stream ofHttpObjectsto N objects. #2981HttpDeframerHandler<String> decoder = ...
HttpDeframer<String> deframer = HttpDeframer.of(decoder, ByteBufAllocator.DEFAULT);
HttpRequest request = ...;
request.subscribe(deframer);- See
HttpDeframerfor more information.
- See
-
You can now apply
CircuitBreakerper request path. #3134, #3135CircuitBreakerFactory factory = ...
// CircuitBreaker is applied per the combination of host and path.
CircuitBreakerMapping mapping = CircuitBreakerMapping.builder()
.perPath()
.perHost()
.build(factory);
CircuitBreakerRule rule = ...
CircuitBreakerClient.newDecorator(mapping, rule); -
You can now apply the different
maxTotalAttemptsandresponseTimeoutforRetryingClientusingRetryConfig. #3145BiFunction<ClientRequestContext, Request, String> keyFactory =
(ctx, req) -> ctx.endpoint().host();
BiFunction<ClientRequestContext, Request, RetryConfig<HttpResponse>> configFactory = (ctx, req) -> {
String host = ctx.endpoint().host();
RetryConfigBuilder builder = RetryConfig.<HttpResponse>builder(RetryRule.onException());
if (host.equals("host1")) {
builder.maxTotalAttempts(2);
} else if (host.equals("host2")) {
builder.maxTotalAttempts(4);
} else {
builder.maxTotalAttempts(1);
}
return builder.build();
};
RetryConfigMapping mapping = RetryConfigMapping.of(keyFactory, configFactory);
RetryingClient.newDecoratorWithMapping(mapping); -
You can now split the
ResponseHeadersand bodies usingHttpResponse.split(). #3038HttpResponse response = ...
SplitHttpResponse splitHttpResponse = response.split();
CompletableFuture<ResponseHeaders> headersFuture = splitHttpResponse.headers();
StreamMessage<HttpData> bodyStream = splitHttpResponse.body();
headersFuture.thenApply(headers -> {
if (headers.contentType() == MediaType.JSON_SEQ) {
// Subscribe to a stream of HttpData.
Flux.from(bodyStream)
.map(httpData -> {
// Convert HttpData to your domain object
});
...
}
}); -
You can now customize for mapping an exception to a gRPC status. #3197
GrpcService.builder()
.addExceptionMapping(AccessDeniedException.class, Status.UNAUTHENTICATED);
// Or, use GrpcStatusFunction.
GrpcService.builder()
.exceptionMapping(cause -> {
if (cause instanceof AccessDeniedException) {
return Status.UNAUTHENTICATED;
}
if (cause instanceof FileNotFoundException) {
return Status.NOT_FOUND;
}
return null; // Return null to use Armeria's default exception mapping.
}); -
You can now specify the Caffeine spec for the DNS resolver cache. #2970 #3007
-
You can now specify a prefix for MDC keys using the
<prefix>element. #3086 #3112<configuration>
...
<appender name="RCEA" class="com.linecorp.armeria.common.logback.RequestContextExportingAppender">
...
<!-- set the prefix of exports which is not wrapped with the <exportGroup> element -->
<prefix>armeria.</prefix>
<export>remote.id</export>
<export>req.headers.user-agent</export>
...
<exportGroup>
<!-- set the prefix of exports in this <exportGroup> -->
<prefix>some_prefix.</prefix>
<export>some_value=attr:com.example.AttrKeys#SOME_KEY</export>
...
</exportGroup>
<exportGroup>
<!-- if <prefix> is not defined, no prefix is added to exports -->
<export>tracking_id=attr:com.example.AttrKeys#TRACKING_ID_KEY</export>
...
</exportGroup>
</appender>
...
</configuration> -
You can now use the unsafe TLS cipher using
ClientFactoryBuilder.tlsAllowUnsafeCiphers(). #3157 #3172 -
You can now specify an arbitrary type for
@Headerand@Paramif the type has one of following static methods or the constructor. #2574 #3143 #3164public static T of(String) { ... }public static T valueOf(String) { ... }public static T fromString(String) { ... }public T(String) { ... } // constructor
public class UserService {
@Get("/api")
public HttpResponse get(@Param User user) {
...
}
private static class User {
User(String userId) { ... } // This constructor is used to create User.
...
}
} -
You can now build and execute an
HttpRequestfluently. #3110// Creates a POST HttpRequest whose URI is "/foo?q=bar"
// with headers "cookie: name=value" and "authorization: value" and a JSON body.
HttpRequest.builder()
.post("/{resource}")
.pathParam("resource", "foo")
.queryParam("q", "bar")
.cookie(Cookie.of("name", "value"))
.header("authorization", "value")
.content(MediaType.JSON, "{\"foo\":\"bar\"}"));
// You can also use WebClient.prepare().
WebClient client = ...
client.prepare()
.post("/{resource}")
.pathParam("resource", "foo")
.queryParam("q", "bar")
.cookie(Cookie.of("name", "value"))
.header("authorization", "value")
.content(MediaType.JSON, "{\"foo\":\"bar\"}")
.execute(); -
You can now easily handle cookies by applying
CookieClient.newDecorator(). #2637 #3118WebClient client = WebClient.builder()
.factory(factory)
.decorator(CookieClient.newDecorator())
.build();
client.get(...); // The cookies that are received from the origin server
// are added to the request headers. -
You can now use the custom Thrift protocol by using
ThriftProtocolFactoryProviderand SPI. #3183public class TTupleFactoryProvider extends ThriftProtocolFactoryProvider {
@Override
public Set<ThriftProtocolFactoryProvider.Entry> entries() {
return ImmutableSet.of(new ThriftProtocolFactoryProvider.Entry(
SerializationFormat.of("ttuple"), new TTupleProtocol.Factory()));
}
} -
You can now collect more detailed DNS metrics. #1887 #2935
armeria.client.dns.queries#count{...,result=success}armeria.client.dns.queries#count{...,result=failure}armeria.client.dns.queries.written#count{...}armeria.client.dns.queries.cancelled#count{...}armeria.client.dns.queries.redirected#count{...}armeria.client.dns.queries.cnamed#count{...}armeria.client.dns.queries.noanswer#count{...}
-
You can now customize the
HealthCheckServicewhen using Spring integration. #3144@Bean
public HealthCheckServiceConfigurator healthCheckServiceConfigurator() {
return builder -> builder.updatable(true);
} -
You can now use
RequestHeaders.acceptLanguage()to choose language. #3177 #3179
📈 Improvements
- Various improvements for
DocService. #3149 #3150 #3167 #3188
🛠️ Bug fixes
HealthCheckedEndpointGroup.endpoints()now returns healthy endpoints properly even whenEndpointGroup.orElse()is used. #3181ServletRequest.getProtocol()now returns the proper value when usingTomcatServiceandJettyService. #3194- The route decorators are now evaluated in the reverse order they applied. #3160 #3166
- The purpose of this change is to make sure we provide a consistent experience when decorating a service.
For example, when we decorate a service like the following:
we expect
myService
.decorate(decoratorA)
.decorate(decoratorB);decoratorAdecoratesmyServiceanddecoratorBdecoratesdecoratorA. Route decorators were breaking this expectation.
- The purpose of this change is to make sure we provide a consistent experience when decorating a service.
For example, when we decorate a service like the following:
- You now get the FORBIDDEN status if your service does not handle preflight requests regardless of route decorators. #3152
- A gRPC
ServerCallis now closed exactly only once. #3153 - You no longer see
AnnotatedConnectExceptionwhen theEndpointis created with an IPv6 scope ID. #3158 #3178 - Armeria server does not reject the request path whose first segment includes a colon anymore. #3154
🏚️ Deprecations
CircuitBreakerClient.newPerHostAndMethodDecorator()is now deprecated. #3135- Use
CircuitBreakerClient.newDecorator()with the customizedCircuitBreakerMappingusingCircuitBreakerMapping.builder().
- Use
- The response timeout and max total attempts setters in
RetryingClientBuilderare now deprecated. #3128 #3145- The static factory methods that take those parameters in
RetryingClientare now deprecated as well. - Use
RetryConfigMappingandRetryConfig.
- The static factory methods that take those parameters in
Route.apply(RoutingContext)is deprecated. #3152- The constructor of
PrometheusExpositionServiceis now deprecated. #3081
☢️ Breaking changes
- Route decorators are now evaluated in the reverse order they applied. #3160 #3166
- The purpose of this change is to make sure we provide a consistent experience when decorating a service.
For example, when we decorate a service like the following:
we expect
myService
.decorate(decoratorA)
.decorate(decoratorB);decoratorAdecoratesmyServiceanddecoratorBdecoratesdecoratorA. Route decorators were breaking this expectation.
- The purpose of this change is to make sure we provide a consistent experience when decorating a service.
For example, when we decorate a service like the following:
⛓ Dependencies
- Dropwizard 2.0.13 → 2.0.16
- Fastutil 8.4.2 → 8.4.3
- gRPC 1.33.0 → 1.33.1
- grpc-kotlin-stub 0.2.0 → 0.2.1
- Dropwizard Metrics 4.1.13 → 4.1.15
- Jackson 2.11.2 → 2.12.0
- JCTools 3.1.0 → 3.2.0
- javax.annotation-api 1.3.2
- jakarta-annotation-api 2.0 has been released with a breaking change so we use javax.annotation-api instead.
- Micrometer 1.5.5 → 1.6.1
- Netty 4.1.53.Final → 4.1.54.Final
- BouncyCastle 1.66 → 1.67
- Reactor 3.3.10.RELEASE → 3.4.0
- Spring Boot 2.3.4.RELEASE → 2.4.0
- Spring 5.2.9.RELEASE → 5.3.1
- Tomcat 9.0.39 → 9.0.40
🙇 Thank you
This release was possible thanks to the following contributors who shared their brilliant ideas and awesome pull requests:




















