Skip to content

armeria-0.53.0

Choose a tag to compare

@trustin trustin released this 07 Sep 06:38

Breaking changes

  • #696 Replace PathMappings with Router and change the API of PathMapping

    • Improve the service router by using Trie-based data structure
    • It is possible to implement more complicated service routing such as content negotiation.
  • #717 #727 #729 #733 #737 #747 Use Micrometer as the primary metric collection library.

    • Replace (Dropwizard|Prometheus)MetricCollecting(Client|Service) with
      MetricCollecting(Client|Service)

      ServerBuilder sb = new ServerBuilder();
      sb.service(...., myService.decorate(
              MetricCollectingService.newDecorator(MeterIdFunction.ofDefault())));
    • Replace DropwizardMetricsCircuitBreakerListener with
      MetricCollectingCircuitBreakerListener

    • Replace PathMapping.metricName() with PathMapping.meterTag()

    • Add Server.meterRegistry() and ClientFactory.meterRegistry()

      • The default registry is Metrics.globalRegistry.

      • A user can override the default registry using ServerBuilder.meterRegistry(..), ClientFactoryBuilder.meterRegistry(..) and ClientFactory.setMeterRegistry(..).

        ServerBuilder sb = new ServerBuilder();
        sb.meterRegistry(NoopMeterRegistry.get()); // Disable metrics
    • Add RequestContext.meterRegistry()

    • Add various utilities for Micrometer to common.metric

  • #720 RetryStrategy.shouldRetry() now returns CompletableFuture<Optional<BackOff>> to allow a user to choose a different Backoff for a different failure cause.

    // Use fixed delay on 500 and exponential delay on 503.
    Backoff backoffOn500 = Backoff.fixed(3000);
    Backoff backoffOn503 = Backoff.exponential(1000, 60000);
    RetryStrategy<HttpRequest, HttpResponse> strategy = RetryStrategy.onStatus(httpStatus -> {
        if (httpStatus == HttpStatus.SERVICE_UNAVAILABLE) {
            return Optional.of(backoffOn503);
        } else if (httpStatus == HttpStatus.INTERNAL_SERVER_ERROR) {
            return Optional.of(backoffOn500);
        } else {
            return Optional.empty();
        }
    });
  • #749 Rename PrometheusExporterHttpService to PrometheusExpositionService

New features

  • #690 #710 Easier Server integration test with ServerRule

    • A new module armeria-testing contains a JUnit rule ServerRule, which enables easier integration testing of a Server.

      public class MyTest {
          @ClassRule // or @Rule if you want to start a new Server for each test method.
          public static final ServerRule server = new ServerRule() {
              @Override
              protected void configure(ServerBuilder sb) throws Exception {
                  sb.service("/myService", ...);
              }
          });
      
          @Test
          public void myTest() throws Exception {
              // ServerRule provides various utility methods for getting URIs and port numbers.
              HttpClient client = HttpClient.of(server.uri("/"));
              AggregatedHttpMessage response = client.get("/myService").aggregate().get();
              assertThat(response.content().toStringUtf8()).isEqualTo("Hello, world!");
          }
      }
  • #694 DnsAddressEndpointGroup and DnsServiceEndpointGroup which uses DNS A/AAAA and SRV records respectively.

  • #696 Media type negotiation for annotated services using @ConsumerType and @ProducerType

    ServerBuilder sb = new ServerBuilder();
    sb.annotatedService(new Object() {
        @Get("/greet")
        @ProduceType("application/json;charset=UTF-8")
        public HttpResponse greetGet(@Param("name") String name) {
            return HttpResponse.of(HttpStatus.OK, MediaType.JSON_UTF_8, "{\"name\":\"%s\"}", name);
        }
    
        @Post("/greet")
        @ConsumeType("application/x-www-form-urlencoded")
        public HttpResponse greetPost(@Param("name") String name) {
            return HttpResponse.of(HttpStatus.OK);
        }
    });
  • #701 Add StreamMessageDuplicator.duplicateStream(boolean) for potentially reduced memory footprint and more convenience.

    HttpRequestDuplicator dup = new HttpRequestDuplicator(req);
    HttpRequest firstDuplicate = dup.duplicateStream();
    HttpRequest lastDuplicate = dup.duplicateStream(true);
    // No need to call dup.close(); underlying buffered content will be 
    // removed automatically as the duplicate streams are consumed.
  • #711 Provide a simpler way to add gRPC services.

    • GrpcService now implements a new interface ServiceWithPathMappings which allows a user not to specify path mapping:

      ServerBuilder sb = new ServerBuilder();
      sb.service(new GrpcServiceBuilder()
              .addService(new MyGrpcServiceOne())
              .supportedSerializationFormats(GrpcSerializationFormats.values())
              .build())
        .service(new GrpcServiceBuilder()
              .addService(new MyGrpcServiceTwo())
              .addService(new MyGrpcServiceThree())
              .supportedSerializationFormats(GrpcSerializationFormats.values())
              .build(), authDecorator);
  • This doesn't work only for GrpcService but any ServiceWithPathMappings implementations.

  • #728 Add session-level options for client-side initial HTTP/2 settings

    ClientFactory cf = new ClientFactoryBuilder()
            .initialHttp2ConnectionWindowSize(1048576)
            .initialHttp2StreamWindowSize(131072)
            .http2MaxFrameSize(32768)
            .build();
  • #735 Allow specifying alternative timeout for retry attempts. e.g. Set the timeout of each attempt to 1 second while RetryingClient will give up 10 seconds after the initial attempt.

    // Retry when could not receive a resonse before timeout.
    RetryStrategy<HttpRequest, HttpResponse> strategy = new RetryStrategy<HttpRequest, HttpResponse>() {
        final Backoff backoff = Backoff.fixed(1000);
        @Override
        public CompletableFuture<Optional<Backoff>> shouldRetry(HttpRequest request,
                                                                HttpResponse response) {
            return response.aggregate().handle((result, cause) -> {
                if (cause instanceof ResponseTimeoutException) {
                    return Optional.of(backoff);
                }
                return Optional.empty();
            });
        }
    };
    
    HttpClient client = new HttpClientBuilder("http://example.com/")
            .decorator(RetryingHttpClient.newDecorator(
                    strategy, /* defaultMatAttempts */ 10, /* responseTimeoutForEachAttempt */ 1000))
            .build();

Improvements

  • #696 Service router now uses Caffeine as its caching layer, and its stats are exported to Micrometer.
  • #721 Implement Server.toString()
  • #750 Log stacktrace for error responses in LoggingService

Bug fixes

  • #579 #696 Inability to bind two annotated methods at the same path with different HTTP methods. e.g. one at /greet for GET and the other at /greet for POST
  • #714 #715 A request whose path contains an asterisk (*) is not handled correctly.
  • #722 #748 HttpResponse produced by TomcatService does not contain content-length header.
  • #731 Add missing service(ServiceWithPathMappings,...) to AbstractVirtualHostBuilder
  • #744 A race condition in DeferredStreamMessage causes a subscription leak.
  • #748 JettyService fails with IllegalStateException when ResourceHandler tries to serve the resource greated than 8192 bytes.

Dependencies

  • Brave 4.5.1 -> 4.6.0
  • Caffeine 2.5.3 -> 2.5.5
  • Dropwizard Metrics 3.2.3 -> 3.2.4
  • gRPC 1.4.0 -> 1.6.1
  • Guava 22.0 -> 23.0
  • Kafka 0.10.2.1 -> 0.11.0.0
  • Netty 4.1.13 -> 4.1.15
  • Reactive Streams 1.0.1-RC1 -> 1.0.1
  • Spring Boot 1.5.4 -> 1.5.6
  • Tomcat 8.5.16 -> 8.5.20 / 8.0.45 -> 8.0.46