From d16143ab60f0bb75d7d1666f1bd198fc651d8ab0 Mon Sep 17 00:00:00 2001 From: sourabh_sourabh Date: Mon, 17 Jun 2024 19:45:00 +0100 Subject: [PATCH] Handle high frequency (faster then DMI) of passthrough request in NCMP - Added pendingAcquireMaxCount property into appliaction.yml. - Added findbugs google annotations dependency. - Supressed "BC_UNCONFIRMED_CAST_OF_RETURN_VALUE" spotbugs low error for reactor.netty.resources.ConnectionProvider. Issue-ID: CPS-2262 Change-Id: Ie755e40282473933f2052fbe7654e7090bb9b337 Signed-off-by: sourabh_sourabh --- cps-application/src/main/resources/application.yml | 2 + cps-dependencies/pom.xml | 5 + cps-ncmp-service/pom.xml | 4 + .../api/impl/config/DmiWebClientConfiguration.java | 122 ++++++++++++--------- .../api/impl/config/HttpClientConfiguration.java | 46 ++++---- .../impl/config/HttpClientConfigurationSpec.groovy | 3 + .../src/test/resources/application.yml | 2 + 7 files changed, 113 insertions(+), 71 deletions(-) diff --git a/cps-application/src/main/resources/application.yml b/cps-application/src/main/resources/application.yml index 4f08bb61a..6f0807113 100644 --- a/cps-application/src/main/resources/application.yml +++ b/cps-application/src/main/resources/application.yml @@ -194,12 +194,14 @@ ncmp: readTimeoutInSeconds: 30 writeTimeoutInSeconds: 30 maximumConnectionsTotal: 100 + pendingAcquireMaxCount: 50 maximumInMemorySizeInMegabytes: 16 model-services: connectionTimeoutInSeconds: 30 readTimeoutInSeconds: 30 writeTimeoutInSeconds: 30 maximumConnectionsTotal: 100 + pendingAcquireMaxCount: 50 maximumInMemorySizeInMegabytes: 16 auth: username: ${DMI_USERNAME:cpsuser} diff --git a/cps-dependencies/pom.xml b/cps-dependencies/pom.xml index a50a420b0..dbb112884 100644 --- a/cps-dependencies/pom.xml +++ b/cps-dependencies/pom.xml @@ -128,6 +128,11 @@ spotbugs 4.2.3 + + com.google.code.findbugs + annotations + 3.0.1 + com.google.code.gson gson diff --git a/cps-ncmp-service/pom.xml b/cps-ncmp-service/pom.xml index 55abffc9b..8a8f7d02a 100644 --- a/cps-ncmp-service/pom.xml +++ b/cps-ncmp-service/pom.xml @@ -73,6 +73,10 @@ ${project.groupId} cps-path-parser + + com.google.code.findbugs + annotations + com.hazelcast hazelcast-spring diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/DmiWebClientConfiguration.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/DmiWebClientConfiguration.java index 08885a9e0..3a861a68b 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/DmiWebClientConfiguration.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/DmiWebClientConfiguration.java @@ -20,9 +20,12 @@ package org.onap.cps.ncmp.api.impl.config; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.netty.channel.ChannelOption; import io.netty.handler.timeout.ReadTimeoutHandler; import io.netty.handler.timeout.WriteTimeoutHandler; +import io.netty.resolver.DefaultAddressResolverGroup; +import java.time.Duration; import java.util.concurrent.TimeUnit; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; @@ -35,8 +38,9 @@ import reactor.netty.http.client.HttpClient; import reactor.netty.resources.ConnectionProvider; /** - * Configures and creates a WebClient bean that triggers an initialization (warmup) of the host name resolver and - * loads the necessary native libraries to avoid the extra time needed to load resources for first request. + * Configures and creates WebClient beans for various DMI services including data, model, and health check services. + * The configuration utilizes Netty-based HttpClient with custom connection settings, read and write timeouts, + * and initializes WebClient with these settings to ensure optimal performance and resource management. */ @Configuration @RequiredArgsConstructor @@ -44,82 +48,98 @@ public class DmiWebClientConfiguration { private final HttpClientConfiguration httpClientConfiguration; + private static final Duration DEFAULT_RESPONSE_TIMEOUT = Duration.ofSeconds(30); + /** - * Configures and create a WebClient bean for DMI data service. + * Configures and creates a WebClient bean for DMI data services. * - * @return a WebClient instance for data services. + * @return a WebClient instance configured for data services. */ @Bean public WebClient dataServicesWebClient() { - final HttpClientConfiguration.DataServices httpClientConfiguration - = this.httpClientConfiguration.getDataServices(); - - final HttpClient httpClient = createHttpClient("dataConnectionPool", - httpClientConfiguration.getMaximumConnectionsTotal(), - httpClientConfiguration.getConnectionTimeoutInSeconds(), - httpClientConfiguration.getReadTimeoutInSeconds(), - httpClientConfiguration.getWriteTimeoutInSeconds()); - return buildAndGetWebClient(httpClient, httpClientConfiguration.getMaximumInMemorySizeInMegabytes()); + final HttpClientConfiguration.DataServices dataServiceConfig = httpClientConfiguration.getDataServices(); + final ConnectionProvider dataServicesConnectionProvider + = getConnectionProvider(dataServiceConfig.getConnectionProviderName(), + dataServiceConfig.getMaximumConnectionsTotal(), dataServiceConfig.getPendingAcquireMaxCount()); + final HttpClient dataServicesHttpClient = createHttpClient(dataServiceConfig, dataServicesConnectionProvider); + return buildAndGetWebClient(dataServicesHttpClient, dataServiceConfig.getMaximumInMemorySizeInMegabytes()); } /** - * Configures and creates a WebClient bean for DMI model service. + * Configures and creates a WebClient bean for DMI model services. * - * @return a WebClient instance for model services. + * @return a WebClient instance configured for model services. */ @Bean public WebClient modelServicesWebClient() { - final HttpClientConfiguration.ModelServices httpClientConfiguration - = this.httpClientConfiguration.getModelServices(); - - final HttpClient httpClient = createHttpClient("modelConnectionPool", - httpClientConfiguration.getMaximumConnectionsTotal(), - httpClientConfiguration.getConnectionTimeoutInSeconds(), - httpClientConfiguration.getReadTimeoutInSeconds(), - httpClientConfiguration.getWriteTimeoutInSeconds()); - return buildAndGetWebClient(httpClient, httpClientConfiguration.getMaximumInMemorySizeInMegabytes()); + final HttpClientConfiguration.ModelServices modelServiceConfig = httpClientConfiguration.getModelServices(); + final ConnectionProvider modelServicesConnectionProvider + = getConnectionProvider(modelServiceConfig.getConnectionProviderName(), + modelServiceConfig.getMaximumConnectionsTotal(), + modelServiceConfig.getPendingAcquireMaxCount()); + final HttpClient modelServicesHttpClient + = createHttpClient(modelServiceConfig, modelServicesConnectionProvider); + return buildAndGetWebClient(modelServicesHttpClient, modelServiceConfig.getMaximumInMemorySizeInMegabytes()); } /** - * Configures and creates a WebClient bean for DMI health service. + * Configures and creates a WebClient bean for DMI health check services. * - * @return a WebClient instance for health checks. + * @return a WebClient instance configured for health check services. */ @Bean public WebClient healthChecksWebClient() { - final HttpClientConfiguration.HealthCheckServices httpClientConfiguration - = this.httpClientConfiguration.getHealthCheckServices(); + final HttpClientConfiguration.HealthCheckServices healthCheckServiceConfig + = httpClientConfiguration.getHealthCheckServices(); + final ConnectionProvider healthChecksConnectionProvider + = getConnectionProvider(healthCheckServiceConfig.getConnectionProviderName(), + healthCheckServiceConfig.getMaximumConnectionsTotal(), + healthCheckServiceConfig.getPendingAcquireMaxCount()); + final HttpClient healthChecksHttpClient + = createHttpClient(healthCheckServiceConfig, healthChecksConnectionProvider); + return buildAndGetWebClient(healthChecksHttpClient, + healthCheckServiceConfig.getMaximumInMemorySizeInMegabytes()); + } - final HttpClient httpClient = createHttpClient("healthConnectionPool", - httpClientConfiguration.getMaximumConnectionsTotal(), - httpClientConfiguration.getConnectionTimeoutInSeconds(), - httpClientConfiguration.getReadTimeoutInSeconds(), - httpClientConfiguration.getWriteTimeoutInSeconds()); - return buildAndGetWebClient(httpClient, httpClientConfiguration.getMaximumInMemorySizeInMegabytes()); + /** + * Provides a WebClient.Builder bean for creating WebClient instances. + * + * @return a WebClient.Builder instance. + */ + @Bean + public WebClient.Builder webClientBuilder() { + return WebClient.builder(); } - private static HttpClient createHttpClient(final String connectionProviderName, - final Integer maximumConnectionsTotal, - final Integer connectionTimeoutInSeconds, - final Integer readTimeoutInSeconds, - final Integer writeTimeoutInSeconds) { - final ConnectionProvider dmiWebClientConnectionProvider = ConnectionProvider.create(connectionProviderName, - maximumConnectionsTotal); + private static HttpClient createHttpClient(final HttpClientConfiguration.ServiceConfig serviceConfig, + final ConnectionProvider connectionProvider) { + return HttpClient.create(connectionProvider) + .responseTimeout(DEFAULT_RESPONSE_TIMEOUT) + .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, serviceConfig.getConnectionTimeoutInSeconds() * 1000) + .doOnConnected(connection -> connection.addHandlerLast(new ReadTimeoutHandler( + serviceConfig.getReadTimeoutInSeconds(), TimeUnit.SECONDS)).addHandlerLast( + new WriteTimeoutHandler(serviceConfig.getWriteTimeoutInSeconds(), TimeUnit.SECONDS))) + .resolver(DefaultAddressResolverGroup.INSTANCE) + .compress(true); + } - return HttpClient.create(dmiWebClientConnectionProvider) - .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectionTimeoutInSeconds * 1000) - .doOnConnected(connection -> connection.addHandlerLast(new ReadTimeoutHandler(readTimeoutInSeconds, - TimeUnit.SECONDS)).addHandlerLast(new WriteTimeoutHandler(writeTimeoutInSeconds, - TimeUnit.SECONDS))); + @SuppressFBWarnings("BC_UNCONFIRMED_CAST_OF_RETURN_VALUE") + private static ConnectionProvider getConnectionProvider(final String connectionProviderName, + final int maximumConnectionsTotal, + final int pendingAcquireMaxCount) { + return ConnectionProvider.builder(connectionProviderName) + .maxConnections(maximumConnectionsTotal) + .pendingAcquireMaxCount(pendingAcquireMaxCount) + .build(); } - private static WebClient buildAndGetWebClient(final HttpClient httpClient, - final Integer maximumInMemorySizeInMegabytes) { - return WebClient.builder() + private WebClient buildAndGetWebClient(final HttpClient httpClient, + final int maximumInMemorySizeInMegabytes) { + return webClientBuilder() .defaultHeaders(header -> header.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)) .defaultHeaders(header -> header.set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)) .clientConnector(new ReactorClientHttpConnector(httpClient)) - .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize( - maximumInMemorySizeInMegabytes * 1024 * 1024)).build(); + .codecs(configurer -> configurer.defaultCodecs() + .maxInMemorySize(maximumInMemorySizeInMegabytes * 1024 * 1024)).build(); } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/HttpClientConfiguration.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/HttpClientConfiguration.java index 62432f6ca..0acbabbba 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/HttpClientConfiguration.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/HttpClientConfiguration.java @@ -23,11 +23,11 @@ package org.onap.cps.ncmp.api.impl.config; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.stereotype.Component; +import org.springframework.context.annotation.Configuration; @Getter @Setter -@Component +@Configuration @ConfigurationProperties(prefix = "ncmp.dmi.httpclient") public class HttpClientConfiguration { @@ -37,30 +37,36 @@ public class HttpClientConfiguration { @Getter @Setter - public static class DataServices { - private Integer maximumConnectionsTotal = 100; - private Integer connectionTimeoutInSeconds = 30; - private Integer readTimeoutInSeconds = 30; - private Integer writeTimeoutInSeconds = 30; - private Integer maximumInMemorySizeInMegabytes = 1; + public static class DataServices extends ServiceConfig { + private String connectionProviderName = "dataConnectionPool"; } @Getter @Setter - public static class ModelServices { - private Integer maximumConnectionsTotal = 100; - private Integer connectionTimeoutInSeconds = 30; - private Integer readTimeoutInSeconds = 30; - private Integer writeTimeoutInSeconds = 30; - private Integer maximumInMemorySizeInMegabytes = 1; + public static class ModelServices extends ServiceConfig { + private String connectionProviderName = "modelConnectionPool"; + } + + @Getter + @Setter + public static class HealthCheckServices extends ServiceConfig { + private String connectionProviderName = "healthConnectionPool"; + private int maximumConnectionsTotal = 10; + private int pendingAcquireMaxCount = 5; } + /** + * Base configuration properties for all services. + */ @Getter - public static class HealthCheckServices { - private final Integer maximumConnectionsTotal = 10; - private final Integer connectionTimeoutInSeconds = 30; - private final Integer readTimeoutInSeconds = 30; - private final Integer writeTimeoutInSeconds = 30; - private final Integer maximumInMemorySizeInMegabytes = 1; + @Setter + public static class ServiceConfig { + private String connectionProviderName = "cpsConnectionPool"; + private int maximumConnectionsTotal = 100; + private int pendingAcquireMaxCount = 50; + private Integer connectionTimeoutInSeconds = 30; + private long readTimeoutInSeconds = 30; + private long writeTimeoutInSeconds = 30; + private int maximumInMemorySizeInMegabytes = 1; } } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/HttpClientConfigurationSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/HttpClientConfigurationSpec.groovy index b7ced2382..228f41277 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/HttpClientConfigurationSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/HttpClientConfigurationSpec.groovy @@ -43,6 +43,7 @@ class HttpClientConfigurationSpec extends Specification { assert readTimeoutInSeconds == 789 assert writeTimeoutInSeconds == 30 assert maximumConnectionsTotal == 100 + assert pendingAcquireMaxCount == 22 assert maximumInMemorySizeInMegabytes == 7 } } @@ -54,6 +55,7 @@ class HttpClientConfigurationSpec extends Specification { assert readTimeoutInSeconds == 30 assert writeTimeoutInSeconds == 30 assert maximumConnectionsTotal == 111 + assert pendingAcquireMaxCount == 44 assert maximumInMemorySizeInMegabytes == 8 } } @@ -65,6 +67,7 @@ class HttpClientConfigurationSpec extends Specification { assert readTimeoutInSeconds == 30 assert writeTimeoutInSeconds == 30 assert maximumConnectionsTotal == 10 + assert pendingAcquireMaxCount == 5 assert maximumInMemorySizeInMegabytes == 1 } } diff --git a/cps-ncmp-service/src/test/resources/application.yml b/cps-ncmp-service/src/test/resources/application.yml index e35f47100..5b10e7376 100644 --- a/cps-ncmp-service/src/test/resources/application.yml +++ b/cps-ncmp-service/src/test/resources/application.yml @@ -38,9 +38,11 @@ ncmp: dmi: httpclient: data-services: + pendingAcquireMaxCount: 22 connectionTimeoutInSeconds: 123 maximumInMemorySizeInMegabytes: 7 model-services: + pendingAcquireMaxCount: 44 connectionTimeoutInSeconds: 456 maximumInMemorySizeInMegabytes: 8 auth: -- 2.16.6