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 <sourabh.sourabh@est.tech>
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 08885a9..3a861a6 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.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 @@
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 HttpClient httpClient = createHttpClient("healthConnectionPool",
- httpClientConfiguration.getMaximumConnectionsTotal(),
- httpClientConfiguration.getConnectionTimeoutInSeconds(),
- httpClientConfiguration.getReadTimeoutInSeconds(),
- httpClientConfiguration.getWriteTimeoutInSeconds());
- return buildAndGetWebClient(httpClient, httpClientConfiguration.getMaximumInMemorySizeInMegabytes());
+ 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());
}
- 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);
-
- 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)));
+ /**
+ * Provides a WebClient.Builder bean for creating WebClient instances.
+ *
+ * @return a WebClient.Builder instance.
+ */
+ @Bean
+ public WebClient.Builder webClientBuilder() {
+ return WebClient.builder();
}
- private static WebClient buildAndGetWebClient(final HttpClient httpClient,
- final Integer maximumInMemorySizeInMegabytes) {
- return WebClient.builder()
+ 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);
+ }
+
+ @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 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 62432f6..0acbabb 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 @@
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 @@
@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
- 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 HealthCheckServices extends ServiceConfig {
+ private String connectionProviderName = "healthConnectionPool";
+ private int maximumConnectionsTotal = 10;
+ private int pendingAcquireMaxCount = 5;
+ }
+
+ /**
+ * Base configuration properties for all services.
+ */
+ @Getter
+ @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 b7ced23..228f412 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 @@
assert readTimeoutInSeconds == 789
assert writeTimeoutInSeconds == 30
assert maximumConnectionsTotal == 100
+ assert pendingAcquireMaxCount == 22
assert maximumInMemorySizeInMegabytes == 7
}
}
@@ -54,6 +55,7 @@
assert readTimeoutInSeconds == 30
assert writeTimeoutInSeconds == 30
assert maximumConnectionsTotal == 111
+ assert pendingAcquireMaxCount == 44
assert maximumInMemorySizeInMegabytes == 8
}
}
@@ -65,6 +67,7 @@
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 e35f471..5b10e73 100644
--- a/cps-ncmp-service/src/test/resources/application.yml
+++ b/cps-ncmp-service/src/test/resources/application.yml
@@ -38,9 +38,11 @@
dmi:
httpclient:
data-services:
+ pendingAcquireMaxCount: 22
connectionTimeoutInSeconds: 123
maximumInMemorySizeInMegabytes: 7
model-services:
+ pendingAcquireMaxCount: 44
connectionTimeoutInSeconds: 456
maximumInMemorySizeInMegabytes: 8
auth: