You're seeing the release note of an old version. Check out the latest release note.
v1.23.0
April 7, 2023
🌸 Highlights
DocServicenow supports auto-completion for gRPC and Thrift services. #4516- Check out this awesome demo video by @Dogacel to learn more.
🌟 New features
- You can now write a circuit breaker or retry rule that makes a decision based on gRPC trailers. #4496 #4535
final RetryRule rule =
RetryRule.builder()
.onGrpcTrailers((ctx, trailers) -> trailers.containsInt(
"grpc-status", 13)) // Retry for internal error.
.thenBackoff(backoff);
-
Unhandled exceptions are logged periodically out of the box. #4324 #4687
- You can change the interval of the periodic logging
using
ServerBuilder.unhandledExceptionsReportIntervalMillis().
ServerBuilder sb = ...// Log every 10 seconds.sb.unhandledExceptionsReportIntervalMillis(10000L); - You can change the interval of the periodic logging
using
-
You can now use Spring Boot 3 with Armeria. #4574
- Use
com.linecorp.armeria:armeria-spring-boot3-autoconfigure:1.23.0dependency for the integration.
- Use
-
You can now disconnect connections gracefully using
ClientRequestContext.initiateConnectionShutdown(). #4555 #4708ClientRequestContext ctx = ...// The connection will be gracefully shut down after a request is finished.ctx.initiateConnectionShutdown(); -
You can now use
ServiceErrorHandlerto configure the per-service or per-virtual host error handlers. #3421 #4716ServiceErrorHandler handler = (ctx, cause) -> {if (cause instanceof IllegalArgumentException) {return HttpResponse.of(HttpStatus.BAD_REQUEST);}// Return null to let the default error handler handle the exception.return null;};ServerBuilder sb = ...sb.route().get("/foo").errorHandler(handler)....build(); -
You can now handle an HTTP/1 request with an absolute URI using
ServerBuilder.absoluteUriTransformer(). #4681 #4726ServerBuilder sb = ...sb.absoluteUriTransformer(absoluteUri -> {// https://foo.com/bar -> /barreturn absoluteUri.replaceFirst("^https://\\.foo\\.com/", "/");// Or store the original URI in a query and use it in the proxy service.// return "/proxy?uri=" + URLEncoder.encode(absoluteUri);}); -
You can now listen to the result of DNS queries using
DnsQueryListener. #4690 #4715DnsAddressEndpointGroup.builder("foo.com").addDnsQueryListeners(new DnsQueryListener() {@Overridevoid onSuccess(...) {...}@Overridepublic void onFailure(...) {logger.warn("DNS query failed");}}).build(); -
You can now create a
BlockingTaskExecutorby wrapping aScheduledExecutorService. #4760- Additionally, you can create context-aware or context-propagating
ExecutorandBlockingTaskExecutor.
ScheduledExecutorService executorService = ...// Wrap a ScheduledExecutorService.BlockingTaskExecutor blockingTaskExecutor =BlockingTaskExecutor.of(executorService);RequestContext ctx = ...BlockingTaskExecutor blockingTaskExecutor = ...// context-awareContextAwareBlockingTaskExecutor.of(context, executor);// context-propagatingRequestContext.makeContextPropagating(blockingTaskExecutor); - Additionally, you can create context-aware or context-propagating
-
You can now use
StreamMessage.of()to convert anInputStreamtoStreamMessage. #3937 #4703InputStream in = ...// The input stream is divided into chunks of 8192 bytes by default.ByteStreamMessage stream = StreamMessage.of(in); -
You can now use
StreamMessage.streaming()to createStreamWriterfor writing streaming messages. #4253 #4696StreamWriter<String> writer = StreamMessage.streaming();writer.write("foo");writer.write("bar");writer.close();// Subscribe to the writer.writer.subscribe(...); -
You can now generate a
RequestIdusing the information ofRoutingContext. #4362 #4691- Additionally, the request ID generator can be configured per-service or per-virtual host. #4730 #4752
ServerBuilder sb = ...sb.requestIdGenerator((routingCtx, req) -> {// Create a request ID from the trace ID of an OpenTelemetry headers.return RequestId.of(requestId(routingCtx.headers().get("traceparent")));}); -
You can now collect metrics of open and closed client connections using
ConnectionPoolListener.metricCollecting(). #4685 #4686MeterRegistry registry = ...ConnectionPoolListener listener = ConnectionPoolListener.metricCollecting(registry);ClientFactory factory = ClientFactory.builder().connectionPoolListener(listener).build(); -
ClientRequestContext.authority()can now be used to get the authority that will be sent to the server. #4697ClientRequestContext ctx = ...String authority = ctx.authority(); -
You can now use
ManagementServerPropertiesto configure the management server when using Spring integration. #4560 #4574armeria:ports:- port: 8080# Use a different port with a custom base path:management:server:port: 8443base-path: /fooendpoints:web:exposure:include: health, loggers, prometheus -
You can now execute gRPC
ServerInterceptorasynchronously in Kotlin usingCoroutineServerInterceptor. #4669 #4724class AuthInterceptor : CoroutineServerInterceptor {private val authorizer = ...override suspend fun <ReqT, RespT> suspendedInterceptCall(call: ServerCall<ReqT, RespT>,headers: Metadata,next: ServerCallHandler<ReqT, RespT>): ServerCall.Listener<ReqT> {val result = authorizer.authorize(ServiceRequestContext.current(), headers).await()if (result) {return next.startCall(call, headers)} else {throw AnticipatedException("Invalid access")}}}
📈 Improvements
- The debug page of
DocServiceis now a pop-up for easier access. #4599 - You can now use
Connection: closeheader to close a connection after sending or receiving a response. #4131 #4454 #4471 #4531 - Armeria now exports the gauge metrics of
CommonPools.workerGroup(). #4675 #4750- The metric names:
armeria.netty.common.event.loop.workersarmeria.netty.common.event.loop.pending.tasks
- The metric names:
- If the mapping for a HEAD request is not found, the request is rerouted to the service bound to the GET method on the same path. #4038 #4706
🛠️ Bug fixes
- Armeria client now handles the fragment part of URI consistently. #4789
- The
RetryConfigfromRetryConfigMappingis retrieved only once for a retry session and reused. #4753 #4778 - Fixed a memory leak of the response body for HEAD requests. #4771
- A
#character in a request path is now always percent-encoded on the server side. #4751 #4765 Self-suppressionexception is not raised anymore in HTTP/1.1 encoder. #4749 #4758ConcurrentModificationExceptionis not raised anymore when usingAsyncServerInterceptor. #4729RequestHeaders.path()now returns percent-encoded value likeRequestContext.path()does when used in an Armeria server. #4694 #4721ByteStreamMessage.range()of a path stream message now handles the overflow of the range correctly. #4705- The authority header is now correctly set for retried requests. #4697
- You no longer see
DnsTimeoutExceptionwhen an IPv6 network interface is enabled. #4695 - No more deadlocks occur in Thrift 0.14 or older when loading
DocService. #4688 HttpStatusExceptionis not raised anymore when there is no matching route for a request. #4548 #4552- Normal 404 Not Found response is sent instead.
- A gRPC call is now correctly terminated with
Status.INTERLALwhen an invalid response is received. #4808 - A server-side
RequestLogis completed correctly when an empty response is sent. #4807
🏚️ Deprecations
ClientFactory.setMeterRegistry()is now deprecated in favor ofFlagsProvider.meterRegistry(). #4785
☢️ Breaking changes
-
Spring Boot 1 integration is no longer supported. #4651 #4787
-
The type of
blockingTaskExecutorproperty such asServiceConfig.blockingTaskExecutor()is changed fromScheduledExecutorServicetoBlockingTaskExecutor. #4760- Simply recompiling your code should be enough in most cases because
BlockingTaskExecutoris aScheduledExecutorService.
- Simply recompiling your code should be enough in most cases because
-
The return types of
makeContextAwaremethods onRequestContextare changed to the context-aware types. #4760makeContextAware(Executor)returnsContextAwareExecutorinstead ofExecutor.makeContextAware(ScheduledExecutorService)returnsContextAwareScheduledExecutorServiceinstead ofScheduledExecutorService.
-
The signatures of
UserClient.execute()have been changed. #4789- It now contains the newly added
RequestTargetas a parameter instead of thepath,query, andfragment.
- It now contains the newly added
-
The names of path cache meters have been changed. #4789
- The old meter name:
armeria.server.parsed.path.cache - New meter names:
armeria.path.cache{type=client}armeria.path.cache{type=server}
- The old meter name:
-
Armeria client doesn't normalize consecutive slashes (e.g.
foo//bar) in a client request path anymore. #4789tipPlease make sure your server or service is capable of handling consecutive slashes (
//) in request paths correctly prior to upgrading. In some instances, consecutive slashes may cause unexpected behavior or routing issues if not handled properly.Fortunately, Armeria server automatically cleans up and normalizes request paths containing consecutive slashes for you.
⛓ Dependencies
- Brotli4j 1.9.0 → 1.11.0
- Dropwizard 2.1.4 → 2.1.5
- Dropwizard Metrics 4.2.15 → 4.2.18
- fastutil 8.5.11 → 8.5.12
- GraphQL Kotlin 6.3.5 → 6.4.0
- java-jwt 4.2.2 → 4.3.0
- Micrometer 1.10.3 → 1.10.5
- Netty 4.1.87.Final → 4.1.91.Final
- Reactor Kotlin 1.2.1 → 1.2.2
- Sangria 3.5.0 → 3.5.3
- Spring 5.3.25 → 6.0.6
- Spring Boot
- 3.0.5
- 2.7.8 → 2.7.10
🙇 Thank you
This release was possible thanks to the following contributors who shared their brilliant ideas and awesome pull requests:






























