Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions docs/modules/ROOT/pages/discovery-client.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,22 @@ spring.cloud.kubernetes.http.discovery.catalog.watcher.enabled=true
- Since http discovery has _two_ components : server and client, we strongly recommend to align versions between them, otherwise things might not work.
- If you decide to disable catalog watcher, you need to disable it in both server and client.

Since version `5.0.0`, there is the possibility to cache the responses from a discovery client (we do it via the `@Cacheable` annotation). There are two properties to keep in mind here:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ryanjbaxter is this OK documentation wise to you?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would state that by default the non-cacheable versions are enabled and to enable the cachable versions you need to enable one or the other of the below properties.


[source]
----
spring.cloud.kubernetes.discovery.cacheable.reactive.enabled
----

and

[source]
----
spring.cloud.kubernetes.discovery.cacheable.blocking.enabled
----

The first one enables the cacheable reactive client, and the second one, the cacheable blocking client.

By default, we use the `Endpoints`(see https://kubernetes.io/docs/concepts/services-networking/service/#endpoints) API to find out the current state of services. There is another way though, via `EndpointSlices` (https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/). Such support can be enabled via a property: `spring.cloud.kubernetes.discovery.use-endpoint-slices=true` (by default it is `false`). Of course, your cluster has to support it also. As a matter of fact, if you enable this property, but your cluster does not support it, we will fail starting the application. If you decide to enable such support, you also need proper Role/ClusterRole set-up. For example:

[source]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,29 +25,20 @@
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.models.V1Endpoints;
import io.kubernetes.client.openapi.models.V1Service;
import org.apache.commons.logging.LogFactory;

import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.client.ReactiveCommonsClientAutoConfiguration;
import org.springframework.cloud.client.discovery.composite.reactive.ReactiveCompositeDiscoveryClientAutoConfiguration;
import org.springframework.cloud.client.discovery.health.DiscoveryClientHealthIndicatorProperties;
import org.springframework.cloud.client.discovery.health.reactive.ReactiveDiscoveryClientHealthIndicator;
import org.springframework.cloud.client.discovery.simple.reactive.SimpleReactiveDiscoveryClientAutoConfiguration;
import org.springframework.cloud.kubernetes.commons.PodUtils;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryClientHealthIndicatorInitializer;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryPropertiesAutoConfiguration;
import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnDiscoveryCacheableReactiveDisabled;
import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnDiscoveryCacheableReactiveEnabled;
import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnSpringCloudKubernetesReactiveDiscovery;
import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.log.LogAccessor;

/**
* @author Ryan Baxter
Expand All @@ -62,13 +53,10 @@
KubernetesClientDiscoveryClientSpelAutoConfiguration.class })
final class KubernetesClientInformerReactiveDiscoveryClientAutoConfiguration {

private static final LogAccessor LOG = new LogAccessor(
LogFactory.getLog(KubernetesClientInformerReactiveDiscoveryClientAutoConfiguration.class));

@Bean
@ConditionalOnMissingBean
@ConditionalOnDiscoveryCacheableReactiveDisabled
KubernetesClientInformerReactiveDiscoveryClient kubernetesClientReactiveDiscoveryClient(
KubernetesClientInformerReactiveDiscoveryClient kubernetesClientInformerReactiveDiscoveryClient(
List<SharedInformerFactory> sharedInformerFactories, List<Lister<V1Service>> serviceListers,
List<Lister<V1Endpoints>> endpointsListers, List<SharedInformer<V1Service>> serviceInformers,
List<SharedInformer<V1Endpoints>> endpointsInformers, KubernetesDiscoveryProperties properties,
Expand All @@ -82,21 +70,10 @@ KubernetesClientInformerReactiveDiscoveryClient kubernetesClientReactiveDiscover
return new KubernetesClientInformerReactiveDiscoveryClient(blockingClient);
}

@Bean
@ConditionalOnBean(KubernetesClientInformerReactiveDiscoveryClient.class)
@ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer
ReactiveDiscoveryClientHealthIndicator kubernetesReactiveDiscoveryClientHealthIndicator(
KubernetesClientInformerReactiveDiscoveryClient reactiveClient,
DiscoveryClientHealthIndicatorProperties properties) {
return new ReactiveDiscoveryClientHealthIndicator(reactiveClient, properties);
}

// Above two beans are created when cacheable is disabled

@Bean
@ConditionalOnMissingBean
@ConditionalOnDiscoveryCacheableReactiveEnabled
KubernetesClientCacheableInformerReactiveDiscoveryClient kubernetesClientCacheableReactiveDiscoveryClient(
KubernetesClientCacheableInformerReactiveDiscoveryClient kubernetesClientCacheableInformerReactiveDiscoveryClient(
List<SharedInformerFactory> sharedInformerFactories, List<Lister<V1Service>> serviceListers,
List<Lister<V1Endpoints>> endpointsListers, List<SharedInformer<V1Service>> serviceInformers,
List<SharedInformer<V1Endpoints>> endpointsInformers, KubernetesDiscoveryProperties properties,
Expand All @@ -110,44 +87,4 @@ KubernetesClientCacheableInformerReactiveDiscoveryClient kubernetesClientCacheab
return new KubernetesClientCacheableInformerReactiveDiscoveryClient(blockingClient);
}

@Bean
@ConditionalOnMissingBean(KubernetesClientInformerReactiveDiscoveryClient.class)
@ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer
ReactiveDiscoveryClientHealthIndicator reactiveDiscoveryClientHealthIndicator(
List<SharedInformerFactory> sharedInformerFactories, List<Lister<V1Service>> serviceListers,
List<Lister<V1Endpoints>> endpointsListers, List<SharedInformer<V1Service>> serviceInformers,
List<SharedInformer<V1Endpoints>> endpointsInformers,
KubernetesDiscoveryProperties kubernetesDiscoveryProperties, CoreV1Api coreV1Api,
Predicate<V1Service> predicate, DiscoveryClientHealthIndicatorProperties properties) {

KubernetesClientInformerDiscoveryClient blockingClient = new KubernetesClientInformerDiscoveryClient(
sharedInformerFactories, serviceListers, endpointsListers, serviceInformers, endpointsInformers,
kubernetesDiscoveryProperties, coreV1Api, predicate);
blockingClient.afterPropertiesSet();

KubernetesClientInformerReactiveDiscoveryClient reactiveClient = new KubernetesClientInformerReactiveDiscoveryClient(
blockingClient);

return new ReactiveDiscoveryClientHealthIndicator(reactiveClient, properties);
}

// Above two beans are created when cacheable is enabled. In this case, we can't make
// KubernetesClientInformerDiscoveryClient a @Bean, since blocking discovery might be
// disabled and we do not want to allow wiring of it.
// Nevertheless, we still need an instance of KubernetesClientInformerDiscoveryClient
// in order to create the ReactiveDiscoveryClientHealthIndicator and
// KubernetesClientCacheableInformerReactiveDiscoveryClient.
// As such, we create two of such instances in each bean.

/**
* Post an event so that health indicator is initialized.
*/
@Bean
@ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer
KubernetesDiscoveryClientHealthIndicatorInitializer reactiveIndicatorInitializer(
ApplicationEventPublisher applicationEventPublisher, PodUtils<?> podUtils) {
LOG.debug(() -> "Will publish InstanceRegisteredEvent from reactive implementation");
return new KubernetesDiscoveryClientHealthIndicatorInitializer(podUtils, applicationEventPublisher);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Copyright 2019-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.cloud.kubernetes.client.discovery;

import java.util.List;
import java.util.function.Predicate;

import io.kubernetes.client.informer.SharedInformer;
import io.kubernetes.client.informer.SharedInformerFactory;
import io.kubernetes.client.informer.cache.Lister;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.models.V1Endpoints;
import io.kubernetes.client.openapi.models.V1Service;
import org.apache.commons.logging.LogFactory;

import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.client.ReactiveCommonsClientAutoConfiguration;
import org.springframework.cloud.client.discovery.composite.reactive.ReactiveCompositeDiscoveryClientAutoConfiguration;
import org.springframework.cloud.client.discovery.health.DiscoveryClientHealthIndicatorProperties;
import org.springframework.cloud.client.discovery.health.reactive.ReactiveDiscoveryClientHealthIndicator;
import org.springframework.cloud.client.discovery.simple.reactive.SimpleReactiveDiscoveryClientAutoConfiguration;
import org.springframework.cloud.kubernetes.commons.PodUtils;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryClientHealthIndicatorInitializer;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryPropertiesAutoConfiguration;
import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnSpringCloudKubernetesReactiveDiscovery;
import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.log.LogAccessor;

/**
* @author wind57
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnSpringCloudKubernetesReactiveDiscovery
@AutoConfigureBefore({ SimpleReactiveDiscoveryClientAutoConfiguration.class,
ReactiveCommonsClientAutoConfiguration.class })
@AutoConfigureAfter({ ReactiveCompositeDiscoveryClientAutoConfiguration.class,
KubernetesDiscoveryPropertiesAutoConfiguration.class,
KubernetesClientDiscoveryClientSpelAutoConfiguration.class,
KubernetesClientInformerReactiveDiscoveryClientAutoConfiguration.class })
class KubernetesClientInformerReactiveHealthAutoConfiguration {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it turns out that @ConditionalOnBean does not work the way I thought it does, so we need proper auto-configurations for the reactive health, that would have @@AutoConfigureAfter(KubernetesClientDiscoveryClientSpelAutoConfiguration)

essentially let the clients be created first, then the beans for the health

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've done this for all 3 clients


private static final LogAccessor LOG = new LogAccessor(
LogFactory.getLog(KubernetesClientInformerReactiveHealthAutoConfiguration.class));

@Bean
@ConditionalOnBean(KubernetesClientInformerReactiveDiscoveryClient.class)
@ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer
ReactiveDiscoveryClientHealthIndicator nonCacheableReactiveDiscoveryClientHealthIndicator(
KubernetesClientInformerReactiveDiscoveryClient reactiveClient,
DiscoveryClientHealthIndicatorProperties properties) {
return new ReactiveDiscoveryClientHealthIndicator(reactiveClient, properties);
}

@Bean
@ConditionalOnMissingBean(KubernetesClientInformerReactiveDiscoveryClient.class)
@ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer
ReactiveDiscoveryClientHealthIndicator cacheableReactiveDiscoveryClientHealthIndicator(
List<SharedInformerFactory> sharedInformerFactories, List<Lister<V1Service>> serviceListers,
List<Lister<V1Endpoints>> endpointsListers, List<SharedInformer<V1Service>> serviceInformers,
List<SharedInformer<V1Endpoints>> endpointsInformers,
KubernetesDiscoveryProperties kubernetesDiscoveryProperties, CoreV1Api coreV1Api,
Predicate<V1Service> predicate, DiscoveryClientHealthIndicatorProperties properties) {

KubernetesClientInformerDiscoveryClient blockingClient = new KubernetesClientInformerDiscoveryClient(
sharedInformerFactories, serviceListers, endpointsListers, serviceInformers, endpointsInformers,
kubernetesDiscoveryProperties, coreV1Api, predicate);
blockingClient.afterPropertiesSet();

KubernetesClientInformerReactiveDiscoveryClient reactiveClient = new KubernetesClientInformerReactiveDiscoveryClient(
blockingClient);

return new ReactiveDiscoveryClientHealthIndicator(reactiveClient, properties);
}

/**
* Post an event so that health indicator is initialized.
*/
@Bean
@ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer
KubernetesDiscoveryClientHealthIndicatorInitializer reactiveIndicatorInitializer(
ApplicationEventPublisher applicationEventPublisher, PodUtils<?> podUtils) {
LOG.debug(() -> "Will publish InstanceRegisteredEvent from reactive implementation");
return new KubernetesDiscoveryClientHealthIndicatorInitializer(podUtils, applicationEventPublisher);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ org.springframework.cloud.kubernetes.client.discovery.KubernetesClientInformerDi
org.springframework.cloud.kubernetes.client.discovery.KubernetesClientInformerAutoConfiguration
org.springframework.cloud.kubernetes.client.discovery.KubernetesClientInformerReactiveDiscoveryClientAutoConfiguration
org.springframework.cloud.kubernetes.client.discovery.KubernetesClientDiscoveryClientSpelAutoConfiguration
org.springframework.cloud.kubernetes.client.discovery.KubernetesClientInformerReactiveHealthAutoConfiguration
Loading
Loading