Skip to main content
info

You're seeing the release note of an old version. Check out the latest release note.

v1.23.0

April 7, 2023

🌸 Highlights

  • DocService now 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

    ServerBuilder sb = ...
    // Log every 10 seconds.
    sb.unhandledExceptionsReportIntervalMillis(10000L);
  • You can now use Spring Boot 3 with Armeria. #4574

    • Use com.linecorp.armeria:armeria-spring-boot3-autoconfigure:1.23.0 dependency for the integration.
  • You can now disconnect connections gracefully using ClientRequestContext.initiateConnectionShutdown(). #4555 #4708

    ClientRequestContext ctx = ...
    // The connection will be gracefully shut down after a request is finished.
    ctx.initiateConnectionShutdown();
  • You can now use ServiceErrorHandler to configure the per-service or per-virtual host error handlers. #3421 #4716

    ServiceErrorHandler 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 #4726

    ServerBuilder sb = ...
    sb.absoluteUriTransformer(absoluteUri -> {
    // https://foo.com/bar -> /bar
    return 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 #4715

    DnsAddressEndpointGroup
    .builder("foo.com")
    .addDnsQueryListeners(new DnsQueryListener() {
    @Override
    void onSuccess(...) {...}

    @Override
    public void onFailure(...) {
    logger.warn("DNS query failed");
    }
    })
    .build();
  • You can now create a BlockingTaskExecutor by wrapping a ScheduledExecutorService. #4760

    • Additionally, you can create context-aware or context-propagating Executor and BlockingTaskExecutor.
    ScheduledExecutorService executorService = ...
    // Wrap a ScheduledExecutorService.
    BlockingTaskExecutor blockingTaskExecutor =
    BlockingTaskExecutor.of(executorService);

    RequestContext ctx = ...
    BlockingTaskExecutor blockingTaskExecutor = ...
    // context-aware
    ContextAwareBlockingTaskExecutor.of(context, executor);
    // context-propagating
    RequestContext.makeContextPropagating(blockingTaskExecutor);
  • You can now use StreamMessage.of() to convert an InputStream to StreamMessage. #3937 #4703

    InputStream 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 create StreamWriter for writing streaming messages. #4253 #4696

    StreamWriter<String> writer = StreamMessage.streaming();
    writer.write("foo");
    writer.write("bar");
    writer.close();

    // Subscribe to the writer.
    writer.subscribe(...);
  • You can now generate a RequestId using the information of RoutingContext. #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 #4686

    MeterRegistry 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. #4697

    ClientRequestContext ctx = ...
    String authority = ctx.authority();
  • You can now use ManagementServerProperties to configure the management server when using Spring integration. #4560 #4574

    armeria:
    ports:
    - port: 8080
    # Use a different port with a custom base path:
    management:
    server:
    port: 8443
    base-path: /foo
    endpoints:
    web:
    exposure:
    include: health, loggers, prometheus
  • You can now execute gRPC ServerInterceptor asynchronously in Kotlin using CoroutineServerInterceptor. #4669 #4724

    class 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 DocService is now a pop-up for easier access. #4599
  • You can now use Connection: close header 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.workers
      • armeria.netty.common.event.loop.pending.tasks
  • 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

🏚️ Deprecations

☢️ Breaking changes

  • Spring Boot 1 integration is no longer supported. #4651 #4787

  • The type of blockingTaskExecutor property such as ServiceConfig.blockingTaskExecutor() is changed from ScheduledExecutorService to BlockingTaskExecutor. #4760

    • Simply recompiling your code should be enough in most cases because BlockingTaskExecutor is a ScheduledExecutorService.
  • The return types of makeContextAware methods on RequestContext are changed to the context-aware types. #4760

  • The signatures of UserClient.execute() have been changed. #4789

    • It now contains the newly added RequestTarget as a parameter instead of the path, query, and fragment.
  • 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}
  • Armeria client doesn't normalize consecutive slashes (e.g. foo//bar) in a client request path anymore. #4789

    tip

    Please 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:

@Bue-von-hon@opus53@SeanWhoCodes@nao0811ta@yamamichid@echo304@trustin@ks-yim@doljae@mitrofmep@vkostyukov@taodo2291@jrhee17@mscheong01@chris-ryan-square@ghkim3221@seonwoo960000@chanho0912@wu-sheng@mauhiz@ikhoon@kojilin@heowc@hllee1021@ta7uw@di-seo@minwoox@Dogacel@cj848@injae-kim@tobias-

Like Armeria?
Star us ⭐️

×