1.27.0 release notes
30th January 2024
🌟 New features
xDS-based EndpointGroup: You can utilize xDS protocol to create
XdsEndpointGroup
for service discovery. #5342 #5336XdsBootstrap bootstrap = XdsBootstrap.of(...); EndpointGroup endpointGroup = XdsEndpointGroup.of(boostrap.listenerRoot("my-envoy-service")); WebClient client = WebClient.of(SessionProtocol.HTTP, endpointGroup);
GraphQL Subscriptions over WebSocket: You can now enable GraphQL subscriptions over WebSocket for live updates to your clients. #4999 #5280
GraphqlService .builder() .enableWebSocket(true) .webSocketServiceCustomizer(ws -> { // Customize the underlying WebSocket service. }) .build();
Service-specific EventLoopGroup: You can now assign a different
EventLoopGroup
to each service for finer grained resource control. #4099 #5233Server .builder() .serviceWorkerGroup(globalServiceWorkerGroup) .route() .path("/user") // Use userServiceWorkerGroup for the user service. .serviceWorkerGroup(userServiceWorkerGroup) .build(userService) .route() .path("/api") // Use apiServiceWorkerGroup for the API service. .serviceWorkerGroup(apiServiceWorkerGroup) .build(apiService) .build();
Thrift Service Decorators: You can now use
@Decorator
annotations to conveniently apply custom decorators to Thrift services. #4230 #5356@Decorator(ClassDecorator.class) class MyHelloService implements HelloService.AsyncIface { @Decorator(MethodDecorator.class) @Override public void hello(String name, AsyncMethodCallback<String> handler) throws TException { ... } }
Sensitive Header Masking: You can now easily mask request and response headers using
HeadersSanitizer
. #5175 #5188HeadersSanitizer<String> headersSanitizer = HeadersSanitizer .builderForText() .sensitiveHeaders("Authorization", "API-Key", "Proxy-Authorization") ... .build(); LogFormatter .builderForText() .requestHeadersSanitizer(headersSanitizer) ...
Custom Exception Handling for gRPC clients: You can customize how your gRPC clients respond to exceptions by implementing
GrpcExceptionHandlerFunction
. #5347 #5351GrpcClients .builder("https://my-grpc-server.com") .exceptionHandler((ctx, cause, metadata) -> { // Handle the exception. return Status.INTERNAL.withDescription("Something went wrong"); })
Kubernetes Client Integration: You can now use the popular Fabric Kubernetes client seamlessly on top of Armeria when using the
armeria-kubernetes
module. #5167WebSocket Service Fallback: You can now set up a fallback service for
WebSocketService
to handle both WebSocket and non-WebSocket requests on the same path withWebSocketServiceBuilder.fallbackService()
#5368WebSocketService webSocketService = WebSocketService .builder() // The fallback service is used when the request // is not a WebSocket request. .fallbackService(httpService) .build(); Server .builder() .service("/ws-or-http", webSocketService) .build();
WebSocket Frame Aggregation: You can now enable to aggregate WebSocket continuation frames for simplified message processing using
WebSocketServiceBuilder.aggregateContinuation()
. #5357WebSocketService .builder() // Aggregate continuation frames into a single frame. .aggregateContinuation(true) .build();
Active Connection Monitoring: You can monitor the number of active connections with
armeria.client.connections#value{state="active"}
for your clients when usingConnectionPoolListener.metricCollecting()
#4277 #5288ConnectionPoolListener connectionPoolListener = ConnectionPoolListener.metricCollecting(meterRegistry); ClientFactory clientFactory = ClientFactory .builder() .connectionPoolListener(connectionPoolListener) .build();
Graceful Executor Shutdown: You can now gracefully terminate a custom
ExecutorService
in sync with theServer
's shutdown procedure. #5031 #5087ServerListener .builder() // Shutdown the executor service when the server is stopping. .shutdownWhenStopping(preStopExecutorService) // Shutdown the executor service when the server is stopped. .shutdownWhenStopped(postStopExecutorService) .build()
Session Protocol in RoutingContext: You can now acquire
SessionProtocol
directly fromRoutingContext.sessionProtocol()
for enhanced routing andExchangeType
decisions.Eureka Health Check Customization: You can override the default health check URL when registering a service with Eureka. #5369
EurekaUpdatingListener .builder() // Override the default health check path provided by HealthCheckService. .healthCheckUrlPath("/healthcheck") .build();
📈 Improvements
- Annotated services now support
HttpResult
wrapped in RxJava objects. #5380 #5386 - You can now create
HttpFile
with Spring Boot nested JAR URL. #5390 #5391 WebSocketService
is restructured as an interface, allowing for custom implementations. #5368- Introduce
PreTlsHandshakeException
to better report exceptions occurring before a TLS handshake initiates. #5344 - Error messages for gRPC client stub generation have been clarified. #5387
🛠️ Bug fixes
- The completions of
ClientRequestContext.whenResponseCancelling()
andClientRequestContext.whenResponseCancelled()
are now guaranteed even ifRequestContext.cancel()
is invoked before the initialization of the timeout scheduler. #5212 - Fixed a leak within
WebSocketClient
when an invalid response is received. #5412 json_name
pseudo-option in protobuf files is now correctly respected for matching query parameters during gRPC HTTP/JSON transcoding. #5193 #5366- Implicit request objects are now correctly converted. #5389 #5393
CloseWebSocketFrame
with empty reason phrases are now correctly handled. #5388Flags
now correctly logs all messages whenRequestContextExportingAppender
is configured inlogback.xml
. #5327 #5361- You no longer see
EndpointSelectionTimeoutException
whenHealthCheckedEndpointGroup
is initialized with duplicateEndpoints
. #5343 HttpHeaders.containsBoolean()
now checks all header entries with the same name. #5319GrpcService
provides more consistentServerCall.Listener
behavior under error conditions. #5273
📃 Documentation
- Updated Production Checklist with guidance on
using
ClientFactoryBuilder.maxNumEventLoopsPerEndpoint()
. This addition will help you mitigate potential client-side bottlenecks. #5297
🏚️ Deprecations
- N/A
☢️ Breaking changes
- N/A
⛓ Dependencies
- Apache Curator 5.5.0 → 5.6.0
- Brave 5.16.0 → 6.0.0
- Brotli4j 1.12.0 → 1.15.0
- Envoy control plane 1.0.42 (new)
- Dropwizard 2.1.8 → 2.1.10
- Dropwizard Metrics 4.2.21 → 4.2.24
- GraphQL Java 20.4 → 21.3
- Fabric Kubernetes client 6.10.0 (new)
- gRPC Java 1.58.0 → 1.61.0
- gRPC Kotlin 1.4.0 → 1.4.1
- Guava 32.1.3 → 33.0.0
- Jackson 2.15.3 → 2.16.1
- Jetty 9.4.51 → 9.4.52, 10.0.17 → 10.0.19, 11.0.17 → 11.0.19, 12.0.5 (new)
- JUnit 5.10.0 → 5.10.1
- Kafka client 3.6.0 → 3.6.1
- Kotlin 1.9.0 → 1.9.22
- krotoDC 1.0.5 - 1.0.6
- Logback 1.2.11 → 1.2.13, 1.3.11 → 1.3.14, 1.4.11 → 1.4.14
- Micrometer 1.11.5 → 1.12.2
- Micrometer Tracing 1.1.6 → 1.2.2
- Netty 4.1.100 → 4.1.106
- Netty io_uring 0.0.23 → 0.0.24
- Protobuf 3.24.0 → 3.25.1
- Reactor 3.5.11 → 3.6.2
- Resilience4j 2.1.0 → 2.2.0
- ScalaPB 0.11.13 → 0.11.15
- Spring 6.0.11 → 6.1.3
- Spring Boot 2.7.14 → 2.7.18, 3.1.2 → 3.2.2
- Tomcat 8.5.94 → 8.5.98, 9.0.82 → 9.0.85, 10.1.15 → 10.1.18