You're seeing the release note of an old version. Check out the latest release note.
v1.4.0
February 1, 2021
🌟 New features
-
You can now register/discover your servers to/from Consul using a new
armeria-consulmodule. #194 #2192 #3002 #3281// Registration
Server server = ...;
server.addListener(
ConsulUpdatingListener.builder(myConsulUri, "myService")
.consulToken(myConsulAccessToken)
.build());
server.start().join();
// Discovery
ConsulEndpointGroup endpointGroup =
ConsulEndpointGroup.builder(myConsulUri, "myService")
.consulToken(myConsulAccessToken)
.build(); -
You can now smoothly ramp up the weight of newly added
Endpointin anEndpointGroupusingEndpointSelectionStrategy.rampingUp()to protect a fresh node from getting too much traffic suddenly. #1757 #3217EndpointGroup endpointGroup =
DnsAddressEndpointGroup
.builder("my.hostname.local")
.selectionStrategy(EndpointSelectionStrategy.rampingUp())
.build();
// Customize as necessary.
EndpointSelectionStrategy
.builderForRampingUp()
// Ramp up a fresh endpoint for 60 seconds,
// increasing weight every second.
.rampingUpInterval(Duration.ofSeconds(1))
.numberSteps(60)
.build(); -
You can now start your server as unhealthy with
HealthCheckServiceBuilder.startUnhealthy(). #3260- This can be useful when you don't want to handle incoming requests immediately after startup but after some operational steps, such as warming up.
-
You can now add
HealthCheckUpdateListenerstoHealthCheckServiceto get notified when healthiness is updated externally, e.g., from aPOSTrequest handled byHealthCheckUpdateHandler. #3260- This can be useful when used in combination with
startUnhealthy()above.
- This can be useful when used in combination with
-
You can now specify a custom HTTP header naming rule for HTTP/1 connections, so that you can work around the interoperability issues with old HTTP/1 servers that don't recognize lower-cased headers such as
content-length. #3196 #3259ClientFactory factory =
ClientFactory.builder()
// Send 'Content-Length', not 'content-length'.
.http1HeaderNaming(http1HeaderNaming.traditional())
.build();
WebClient client =
WebClient.builder()
.factory(factory)
.build(); -
You can now close a connection after handling a certain number of requests or after a certain period of time, rather than keeping it open indefinitely. #203 #3267
-
v0.99.7 introduced this feature partially for the server side. We expanded it into both client and server side and also added a way to disconnect after handling a certain number of requests.
// For server
Server.builder()
// A connection will be closed after
// sending the 10000th request's response.
.maxNumRequestsPerConnection(10000);
// For client
ClientFactory.builder()
// A connection will be closed after
// receiving the 2000th request's response.
.maxNumRequestsPerConnection(2000);
-
-
You can now turn any service into a transient service by decorating it with
TransientHttpServiceorTransientRpcService. #3221- By making a service transient, you can disable metric collection by
MetricCollectingService, service logging byLoggingServiceor access logging byAccessLogWriter. - The following example disables metric collection, service and access logs for
HealthCheckService:Server.builder()
.serviceUnder(
"/internal/l7check",
HealthCheckService.of()
.decorate(TransientHttpService.newDecorator()))
.build()
- By making a service transient, you can disable metric collection by
-
You can now disable the check that rejects potentially unsafe TLS cipher suites when configuring server-side TLS, at your own risk. #3292
Server.builder()
.tlsAllowUnsafeCiphers()
.tlsCustomizer(sslCtxBuilder -> {
sslCtxBuilder.ciphers(Set.of(
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA" // Bad cipher suite
))
})
... -
You can now get, set and parse a
Content-Dispositionheader usingContentDisposition. #3253// Setting
HttpHeaders headers = HttpHeaders.of(
HttpHeaderNames.CONTENT_DISPOSITION,
ContentDisposition.of("attachment", "fileName", "file.png"));
// Getting
ContentDisposition disposition = headers.contentDisposition();
assert "attachment".equals(disposition.type());
assert "fileName".equals(disposition.name());
assert "file.png".equals(disposition.filename());
// Parsing
ContentDisposition parsed = ContentDisposition.parse(
"attachment; name=\"fileName\"; filename=\"file.png\"");
assert parsed.equals(disposition); -
You can now serve a file by returning an
HttpFilein an annotated service. #3258 #3287Server
.builder()
.annotatedService(new Object() {
@Get
@Head
@Path("/files/{fileName}")
HttpFile myFile(@Param String fileName) {
return HttpFile.of(new File("/var/www/" + fileName));
}
})
.build(); -
You can now invoke
RunnableorCallablewithRequestContextset in the thread-local storage usingRequestContext.run()andRequestContext.run(). #3311Runnable task = () -> {
logger.info("Current request: {}", ServiceRequestContext.current());
};
ctx.run(task); -
You can now concat multiple
Publishers into one usingStreamMessage.concat(). #3254 #3299StreamMessage<String> stream =
StreamMessage.concat(Mono.just("foo"),
Mono.just("bar"));
StepVerifier.create(stream)
.expectNext("foo")
.expectNext("bar")
.expectComplete()
.verify()
// You can also concat a `Publisher` of `Publisher`s.
StreamMessage<String> flat =
StreamMessage.concat(Flux.just(Mono.just("foo"),
Mono.just("bar"))); -
You can now create an
HttpResponsefrom aResponseHeadersand a stream ofHttpDatasusingHttpResponse.of(). #3089 #3237HttpResponse res = HttpResponse.of(
ResponseHeaders.of(200),
Flux.just("one\n", "two\n", "three\n")
.map(HttpData::ofUtf8)); -
You can now check which native transports are available or not and why they are unavailable using
TransportType.isAvailable()andTransportType.unavailabilityCause(). #3244if (!TransportType.EPOLL.isAvailable()) {
logger.warn("/dev/epoll support not available:",
TransportType.EPOLL.unavailabilityCause());
}- We also added methods like
serverChannelType(),socketChannelType()anddatagramChannelType(), which may be useful when you need to deal with Netty channels.
- We also added methods like
-
You can now decode the body of
HttpRequestorHttpResponseinto aStreamMessageusingHttpMessage.decode(). #3215- This feature can be useful when you need to reactively decode an HTTP body. For example, Armeria will use this feature to handle a multipart body.
-
Added
StreamMessage.demand()that returns the remaining demand of the current subscription. #3215 -
Added
withTags(Tag...)method toMeterIdPrefixFunction. #3241 #3242 -
You can now register a custom gRPC client stub factory via SPI or
GrpcClientOptions.GRPC_CLIENT_STUB_FACTORYoption. #3214 #3294 -
You can now keep using the old decoration ordering for route decorators by specifying the
-Dcom.linecorp.armeria.useLegacyRouteDecoratorOrdering=trueJVM option. #3279
📈 Improvements
- You can now run your Armeria application even if
netty-transport-native-epollandnetty-incubator-transport-native-io_uringare not in the class path. #3244 - You can now use
/dev/epolltransport on WSL 2. #3244 - You can now use
JettyServicewith Jetty 9.3. We previously supported Jetty 9.4 only. #3288 - We improved how we shut down a
Serverin our Spring Boot integration so it's more future-proof. #3266
🛠️ Bug fixes
- The future returned by
StreamWriter.whenConsumed()is now completed immediately even when the current demand is0. #3213 - Fixed the violation of Reactive Streams specification when
nullis published. #3212 #3216 TomcatServicedoes not raise aNullPointerExceptionwhen it stops. #3136 #3218- Fixed stability and interoperability issues in Eureka service discovery module. #3235 #3275 #3238 #3301
- The service name generated from a CGLIB-enhanced class does not have synthetic suffix anymore. #3103 #3240
HealthCheckedEndpointGroupnow refreshes itsEndpoints' status when their weights are changed. #3236- You'll no longer see a
NullPointerExceptionwhen retrying a gRPC request. #3251 - Fixed the incorrect return type of some builder methods. #3220
- Fixed a UI glitch in the go-to form of
DocServiceclient #3261 - You'll no longer see a harmless
Http2Exception: Stream N does not existwarning log message anymore. #3233- You'll see it if there was really a protocol violation, however.
- Fixed an issue where a
RequestLogis sometimes not completed immediately. #3291 - TLS is now configured only once in Spring Boot WebFlux integration. #3266
- Armeria now finds the gRPC
ServiceDescriptorcorrectly from the code generated with Reactive-gRPC. #3294 - A request refused with HTTP/2
REFUSED_STREAMerror code now fails with anUnprocessedRequestException, so it can be safely retried. #3298 THttpServicecan now handle the Thrift code generated with thefullcameloption. #3272ZooKeeperUpdatingListenerdoes not raise anIllegalStateExceptionanymore even if the given Curator client is started already. #3305
🏚️ Deprecations
*ChannelType()methods inEventLoopGroupshave been deprecated in favor of the new methods inTransportType. #3244CircuitBreakerRpcClient.newPerHostAndMethodDecorator()has been deprecated in favor ofCircuitBreakerRpcClient.newDecorator(). #3249
☢️ Breaking changes
armeria-tomcat9andarmeria-tomcat8don't depend ontomcat-embed-jasperanymore, which was pulled into runtime dependencies by mistake. #3256- You have to recompile your application because we changed the return type of some builder methods. #3220
- Changes in the API annotated with
@UnstableApi:HttpDeframerhas been replaced withHttpMessage.decode(). #3215
⛓ Dependencies
- Bouncy Castle 1.67 → 1.68
- Brave 5.13.1 → 5.13.3
- Dropwizard 2.0.16 -> 2.0.18, 1.3.25 → 1.3.29
- Dropwizard Metrics 4.1.15 → 4.1.17
- gRPC-Java 1.33.1 → 1.35.0
- gRPC-Kotlin 0.2.1 → 1.0.0
- java-jwt 3.11.0 → 3.12.1
- Jetty 9.4.35 → 9.4.36
- Micrometer 1.6.1 → 1.6.3
- Netty 4.1.54 → 4.1.58
- tcnative 2.0.34 → 2.0.36
- io_uring 0.0.1 → 0.0.3
- io_uring is now an optional dependency.
- Protobuf Jackson 1.1.0 → 1.2.0
- Project Reactor 3.4.0 → 3.4.2
- RxJava 3.0.7 → 3.0.9
- ScalaPB runtime 0.10.8 → 0.10.10
- ScalaPB json4s 0.10.1 → 0.10.3
- Spring Boot 2.4.0 → 2.4.1
- Tomcat 9.0.40 → 9.0.41, 8.5.58 → 8.5.61
- We don't depend on
tomcat-embed-jasperanymore. #3256
- We don't depend on
🙇 Thank you
This release was possible thanks to the following contributors who shared their brilliant ideas and awesome pull requests:



























