@Bean
@ConditionalOnMissingBean(name = "pricingClientHttpConnector")
public ClientHttpConnector pricingClientHttpConnector(
CommonInternalServiceClientConnectorFactory connectorFactory) {
return connectorFactory.createConnector();
}
Broadleaf utilizes centralized configurations and connection pools for managing outbound WebClient connections, both for internal service-to-service calls and external third-party integrations. This approach provides better visibility, secure defaults, and resource management across the framework.
since 2.0.8
Every Broadleaf service opens WebClient connections to its siblings (e.g., pricing, interlink, and others). To prevent a service from exhausting its outbound connections without visibility (which previously looked exactly like a slow downstream due to Reactor Netty’s anonymous global pool), Broadleaf utilizes a named connection pool for internal service-to-service traffic. Services build their connectors from a centralized factory, ensuring all internal traffic is pooled and reported in one place.
A ConnectionProvider bean named commonIntlSvcConnectionProvider backs internal service-to-service `WebClient`s. Its default maximum of 500 connections deliberately matches the Reactor Netty global pool, ensuring a high ceiling for busy services.
With io.micrometer:micrometer-core on the classpath, the pool publishes metrics such as reactor.netty.connection.provider.total.connections, .active.connections, .idle.connections, and .pending.connections.time, each tagged with the pool name and the remote address. Meters register lazily as connections are first opened. If micrometer is absent, the pool is built without metrics and logs a warning instead of failing startup.
Services keep their individually named connector beans (e.g., pricingClientHttpConnector, interlinkClientHttpConnector), allowing them to remain independently overridable. These beans are built using CommonInternalServiceClientConnectorFactory, which applies the shared pool and the common broadleaf.common.ssl-verification.disabled policy:
@Bean
@ConditionalOnMissingBean(name = "pricingClientHttpConnector")
public ClientHttpConnector pricingClientHttpConnector(
CommonInternalServiceClientConnectorFactory connectorFactory) {
return connectorFactory.createConnector();
}
If a connector needs the underlying client customized (e.g., response timeouts, wiretap logging, or a proxy), start from createHttpClient() and pass the result to createConnector(HttpClient). This keeps the connection on the shared pool while still applying the common SSL policy.
Configuration properties are available under broadleaf.common.webclient.connection-pool:
enabled (default true) — set to false to run on Reactor Netty’s global pool (at the cost of losing these metrics).
name (default blc-internal-service) — the value carried on the id tag of published metrics.
metrics-enabled (default true).
max-connections (default 500).
max-life-time, max-idle-time, pending-acquire-timeout, pending-acquire-max-count, evict-in-background — unset by default, leaving Reactor Netty’s defaults in place.
since 2.0.8
A client_credentials access token for an internal call can be resolved without an ambient request or security context.
Under Spring Boot 4, a service-to-service call issued from a background worker, an @Async method, a scheduled task, or a messaging listener requires context-free token lookup, because it cannot depend on thread-local state that only exists on a request thread.
CommonInternalServiceClientCredentialsExchangeFilterFunction resolves the token context-free. Register it on a WebClient builder ahead of oAuth2FilterFunctionSupplier.get().oauth2Configuration(). Call sites are unaffected and keep using .attributes(clientRegistrationId(…)).
The authorized-client manager is exposed as the bean commonInternalServiceAuthorizedClientManager, allowing the standard filter and the context-free filter to share one instance and a single access-token cache.
since 3.0.0
Integrations with external, third-party services require different configurations than setups used for internal service-to-service communication. As an example, internal calls disable SSL by default since traffic remains within the internal network. However, external calls (e.g., outbound requests to a payment gateway) require SSL to be enabled to ensure secure connections.
Additionally, to prevent external services from exhausting or overloading a shared internal connection pool, external integrations utilize a common independent connection pool separate from the internal services.
To facilitate this, an external setup similar to the internal centralized OAuth2 setup described above is provided.
For third-party integrations that do not require independent isolation and can share a pool, a default shared ClientHttpConnector bean named commonExtlSvcClientConnector is provided out-of-the-box.
External services have been configured to utilize this commonExtlSvcClientConnector. Custom external connection setups will look similar to:
@Bean(name = "myExternalClientHttpConnector")
@ConditionalOnMissingBean(name = "myExternalClientHttpConnector")
public ClientHttpConnector myExternalClientHttpConnector(
@Qualifier("commonExtlSvcClientConnector") ObjectProvider<ClientHttpConnector> sharedConnectorProvider,
ObjectProvider<CommonExternalServiceConnectionPoolProperties> poolPropertiesProvider) {
// Use the shared pool in production (default)
ClientHttpConnector shared = sharedConnectorProvider.getIfAvailable();
if (shared != null) {
return shared;
}
// Build unpooled connector for fallback in test slice contexts
return CommonExternalServiceClientConnectorFactory
.unpooled(poolPropertiesProvider.getIfAvailable())
// Note - this name will go unused in unpooled config
.createConnector("my-external-fallback-pool");
}
If customization on the connector is required, a common factory, commonExtlSvcClientConnectorFactory, is available to construct independent connection pools for individual third-party services:
@Bean
@ConditionalOnMissingBean(name = "myExternalClientHttpConnector")
public ClientHttpConnector myExternalClientHttpConnector(
CommonExternalServiceClientConnectorFactory connectorFactory) {
return connectorFactory.createConnector("my-external-service-pool");
}
The factory also supports full customization of the connection pool builder, baseline HttpClient customizations, and custom SSL context builders. The following is an example for a customization that needs a highly customized connector:
@Bean
@ConditionalOnMissingBean(name = "myExternalCustomizedClientHttpConnector")
public ClientHttpConnector myExternalCustomizedClientHttpConnector(
CommonExternalServiceClientConnectorFactory connectorFactory,
ConnectionProvider.Builder connectionProviderCustomizer,
HttpClient clientCustomizer,
SslContextBuilder sslCustomizer) {
connectionProviderCustomizer.name("my-external-provider");
clientCustomizer.responseTimeout(Duration.ofSeconds(5));
sslCustomizer.startTls(true);
return connectorFactory.createConnector(
"my-external-service-pool",
connectionProviderCustomizer,
clientCustomizer,
sslCustomizer
);
}
Similar to the internal connection pool, named external pools default to publishing Netty connection metrics to Micrometer’s global registry. Without additional setup, most external services connect to the blc-external-service pool.
If metrics or other connection settings need to be modified, properties are available. See External Service Connection Pool Properties for detailed documentation.
since 3.0.0
As part of the external connection pool consolidation, SSL logic was refactored into a CommonWebClientSslUtils utility to centralize and share the configuration of customized and disabled SSL contexts for HttpClient.
Both internal and external client setups route their SSL configuration through the shared CommonWebClientSslUtils class.
It provides getDisabledSSLClientConnector(…) for specifically disabling SSL and getCustomSSLClientConnector(…) for customizing SSL based on the input of a SslContextBuilder.
|
Note
|
Out of the box, the Connector Factory beans already utilize the SSL logic in order to modify the returned connector. Use the provided utility only if you are making a custom connector that cannot be based on the provided factory. |