From 73a6f3a4f1068fcafe704583078129a7a4e9bcad Mon Sep 17 00:00:00 2001 From: spencergibb Date: Wed, 13 Sep 2023 18:34:15 -0400 Subject: [PATCH] Split files --- .../ROOT/pages/spring-cloud-zookeeper.adoc | 627 ------------------ .../pages/spring-cloud-zookeeper/config.adoc | 143 ++++ .../spring-cloud-zookeeper/dependencies.adoc | 208 ++++++ .../dependency-watcher.adoc | 53 ++ .../spring-cloud-zookeeper/discovery.adoc | 105 +++ .../pages/spring-cloud-zookeeper/install.adoc | 54 ++ .../other-componentes.adoc | 15 + .../spring-cloud-zookeeper/quick-start.adoc | 5 + .../service-registry.adoc | 45 ++ 9 files changed, 628 insertions(+), 627 deletions(-) create mode 100644 docs/modules/ROOT/pages/spring-cloud-zookeeper/config.adoc create mode 100644 docs/modules/ROOT/pages/spring-cloud-zookeeper/dependencies.adoc create mode 100644 docs/modules/ROOT/pages/spring-cloud-zookeeper/dependency-watcher.adoc create mode 100644 docs/modules/ROOT/pages/spring-cloud-zookeeper/discovery.adoc create mode 100644 docs/modules/ROOT/pages/spring-cloud-zookeeper/install.adoc create mode 100644 docs/modules/ROOT/pages/spring-cloud-zookeeper/other-componentes.adoc create mode 100644 docs/modules/ROOT/pages/spring-cloud-zookeeper/quick-start.adoc create mode 100644 docs/modules/ROOT/pages/spring-cloud-zookeeper/service-registry.adoc diff --git a/docs/modules/ROOT/pages/spring-cloud-zookeeper.adoc b/docs/modules/ROOT/pages/spring-cloud-zookeeper.adoc index 00149108..a92e944a 100644 --- a/docs/modules/ROOT/pages/spring-cloud-zookeeper.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-zookeeper.adoc @@ -6,630 +6,3 @@ include::intro.adoc[] include::_attributes.adoc[] -[[quick-start]] -== Quick Start - -include::quickstart.adoc[] - -[[spring-cloud-zookeeper-install]] -== Install Zookeeper -See the https://zookeeper.apache.org/doc/current/zookeeperStarted.html[installation -documentation] for instructions on how to install Zookeeper. - -Spring Cloud Zookeeper uses Apache Curator behind the scenes. -While Zookeeper 3.5.x is still considered "beta" by the Zookeeper development team, -the reality is that it is used in production by many users. -However, Zookeeper 3.4.x is also used in production. -Prior to Apache Curator 4.0, both versions of Zookeeper were supported via two versions of Apache Curator. -Starting with Curator 4.0 both versions of Zookeeper are supported via the same Curator libraries. - -In case you are integrating with version 3.4 you need to change the Zookeeper dependency -that comes shipped with `curator`, and thus `spring-cloud-zookeeper`. -To do so simply exclude that dependency and add the 3.4.x version like shown below. - -.maven -[source,xml,indent=0] ----- - - org.springframework.cloud - spring-cloud-starter-zookeeper-all - - - org.apache.zookeeper - zookeeper - - - - - org.apache.zookeeper - zookeeper - 3.4.12 - - - org.slf4j - slf4j-log4j12 - - - ----- - -.gradle -[source,groovy,indent=0] ----- -compile('org.springframework.cloud:spring-cloud-starter-zookeeper-all') { - exclude group: 'org.apache.zookeeper', module: 'zookeeper' -} -compile('org.apache.zookeeper:zookeeper:3.4.12') { - exclude group: 'org.slf4j', module: 'slf4j-log4j12' -} ----- - -[[spring-cloud-zookeeper-discovery]] -== Service Discovery with Zookeeper - -Service Discovery is one of the key tenets of a microservice based architecture. Trying to -hand-configure each client or some form of convention can be difficult to do and can be -brittle. https://curator.apache.org[Curator](A Java library for Zookeeper) provides Service -Discovery through a https://curator.apache.org/curator-x-discovery/[Service Discovery -Extension]. Spring Cloud Zookeeper uses this extension for service registration and -discovery. - -[[activating]] -=== Activating - -Including a dependency on -`org.springframework.cloud:spring-cloud-starter-zookeeper-discovery` enables -autoconfiguration that sets up Spring Cloud Zookeeper Discovery. - -NOTE: For web functionality, you still need to include -`org.springframework.boot:spring-boot-starter-web`. - -CAUTION: When working with version 3.4 of Zookeeper you need to change -the way you include the dependency as described <>. - -[[registering-with-zookeeper]] -=== Registering with Zookeeper - -When a client registers with Zookeeper, it provides metadata (such as host and port, ID, -and name) about itself. - -The following example shows a Zookeeper client: - -[source,java,indent=0] ----- -@SpringBootApplication -@RestController -public class Application { - - @RequestMapping("/") - public String home() { - return "Hello world"; - } - - public static void main(String[] args) { - new SpringApplicationBuilder(Application.class).web(true).run(args); - } - -} ----- - -NOTE: The preceding example is a normal Spring Boot application. - -If Zookeeper is located somewhere other than `localhost:2181`, the configuration must -provide the location of the server, as shown in the following example: - -[source,yml,indent=0] -.application.yml ----- -spring: - cloud: - zookeeper: - connect-string: localhost:2181 ----- - -CAUTION: If you use <>, the -values shown in the preceding example need to be in `bootstrap.yml` instead of -`application.yml`. - -The default service name, instance ID, and port (taken from the `Environment`) are -`${spring.application.name}`, the Spring Context ID, and `${server.port}`, respectively. - -Having `spring-cloud-starter-zookeeper-discovery` on the classpath makes the app into both -a Zookeeper "`service`" (that is, it registers itself) and a "`client`" (that is, it can -query Zookeeper to locate other services). - -If you would like to disable the Zookeeper Discovery Client, you can set -`spring.cloud.zookeeper.discovery.enabled` to `false`. - -[[using-the-discoveryclient]] -=== Using the DiscoveryClient - -Spring Cloud has support for -https://github.com/spring-cloud/spring-cloud-netflix/blob/master/docs/src/main/asciidoc/spring-cloud-netflix.adoc#spring-cloud-feign[Feign] -(a REST client builder), -https://github.com/spring-cloud/spring-cloud-netflix/blob/master/docs/src/main/ascii[Spring -`RestTemplate`] and -https://cloud.spring.io/spring-cloud-commons/reference/html/#loadbalanced-webclient[Spring WebFlux], using logical service names instead of physical URLs. - -You can also use the `org.springframework.cloud.client.discovery.DiscoveryClient`, which -provides a simple API for discovery clients that is not specific to Netflix, as shown in -the following example: - -[source,java,indent=0] ----- -@Autowired -private DiscoveryClient discoveryClient; - -public String serviceUrl() { - List list = discoveryClient.getInstances("STORES"); - if (list != null && list.size() > 0 ) { - return list.get(0).getUri().toString(); - } - return null; -} ----- - -[[spring-cloud-zookeeper-other-componentes]] -== Using Spring Cloud Zookeeper with Spring Cloud Components - -Feign, Spring Cloud Gateway and Spring Cloud LoadBalancer all work with Spring Cloud Zookeeper. - -[[spring-cloud-loadbalancer-with-zookeeper]] -=== Spring Cloud LoadBalancer with Zookeeper - -Spring Cloud Zookeeper provides an implementation of Spring Cloud LoadBalancer `ServiceInstanceListSupplier`. -When you use the `spring-cloud-starter-zookeeper-discovery`, Spring Cloud LoadBalancer is autoconfigured to use the -`ZookeeperServiceInstanceListSupplier` by default. - -TIP: If you were previously using the StickyRule in Zookeeper, its replacement in the current stack -is the `SameInstancePreferenceServiceInstanceListSupplier` in SC LoadBalancer. You can read on how to set it up in the https://docs.spring.io/spring-cloud-commons/docs/current/reference/html/#spring-cloud-loadbalancer[Spring Cloud Commons documentation]. - -[[spring-cloud-zookeeper-service-registry]] -== Spring Cloud Zookeeper and Service Registry - -Spring Cloud Zookeeper implements the `ServiceRegistry` interface, letting developers -register arbitrary services in a programmatic way. - -The `ServiceInstanceRegistration` class offers a `builder()` method to create a -`Registration` object that can be used by the `ServiceRegistry`, as shown in the following -example: - -[source,java,indent=0] ----- -@Autowired -private ZookeeperServiceRegistry serviceRegistry; - -public void registerThings() { - ZookeeperRegistration registration = ServiceInstanceRegistration.builder() - .defaultUriSpec() - .address("anyUrl") - .port(10) - .name("/a/b/c/d/anotherservice") - .build(); - this.serviceRegistry.register(registration); -} ----- - -[[instance-status]] -=== Instance Status - -Netflix Eureka supports having instances that are `OUT_OF_SERVICE` registered with the server. -These instances are not returned as active service instances. -This is useful for behaviors such as blue/green deployments. -(Note that the Curator Service Discovery recipe does not support this behavior.) Taking advantage of the flexible payload has let Spring Cloud Zookeeper implement `OUT_OF_SERVICE` by updating some specific metadata and then filtering on that metadata in the Spring Cloud LoadBalancer `ZookeeperServiceInstanceListSupplier`. -The `ZookeeperServiceInstanceListSupplier` filters out all non-null instance statuses that do not equal `UP`. -If the instance status field is empty, it is considered to be `UP` for backwards compatibility. -To change the status of an instance, make a `POST` with `OUT_OF_SERVICE` to the `ServiceRegistry` -instance status actuator endpoint, as shown in the following example: - -[source,sh,indent=0] ----- -$ http POST http://localhost:8081/service-registry status=OUT_OF_SERVICE ----- - -NOTE: The preceding example uses the `http` command from https://httpie.org. - -[[spring-cloud-zookeeper-dependencies]] -== Zookeeper Dependencies - -The following topics cover how to work with Spring Cloud Zookeeper dependencies: - -* <> -* <> -* <> -* <> - -[[spring-cloud-zookeeper-dependencies-using]] -=== Using the Zookeeper Dependencies - -Spring Cloud Zookeeper gives you a possibility to provide dependencies of your application -as properties. As dependencies, you can understand other applications that are registered -in Zookeeper and which you would like to call through -https://github.com/spring-cloud/spring-cloud-netflix/blob/master/docs/src/main/asciidoc/spring-cloud-netflix.adoc#spring-cloud-feign[Feign] -(a REST client builder), -https://github.com/spring-cloud/spring-cloud-netflix/blob/master/docs/src/main/ascii[Spring -`RestTemplate`] and -https://cloud.spring.io/spring-cloud-commons/reference/html/#loadbalanced-webclient[Spring WebFlux]. - -You can also use the Zookeeper Dependency Watchers functionality to control and monitor -the state of your dependencies. - -[[spring-cloud-zookeeper-dependencies-activating]] -=== Activating Zookeeper Dependencies - -Including a dependency on -`org.springframework.cloud:spring-cloud-starter-zookeeper-discovery` enables -autoconfiguration that sets up Spring Cloud Zookeeper Dependencies. Even if you provide -the dependencies in your properties, you can turn off the dependencies. To do so, set the -`spring.cloud.zookeeper.dependency.enabled` property to false (it defaults to `true`). - -[[spring-cloud-zookeeper-dependencies-setting-up]] -=== Setting up Zookeeper Dependencies - -Consider the following example of dependency representation: - -[source,yml,indent=0] -.application.yml ----- -spring.application.name: yourServiceName -spring.cloud.zookeeper: - dependencies: - newsletter: - path: /path/where/newsletter/has/registered/in/zookeeper - loadBalancerType: ROUND_ROBIN - contentTypeTemplate: application/vnd.newsletter.$version+json - version: v1 - headers: - header1: - - value1 - header2: - - value2 - required: false - stubs: org.springframework:foo:stubs - mailing: - path: /path/where/mailing/has/registered/in/zookeeper - loadBalancerType: ROUND_ROBIN - contentTypeTemplate: application/vnd.mailing.$version+json - version: v1 - required: true ----- - -The next few sections go through each part of the dependency one by one. The root property -name is `spring.cloud.zookeeper.dependencies`. - -[[spring-cloud-zookeeper-dependencies-setting-up-aliases]] -==== Aliases - -Below the root property you have to represent each dependency as an alias. -This is due to the constraints of Spring Cloud LoadBalancer, which requires that the application ID be placed in the URL. -Consequently, you cannot pass any complex path, suchas `/myApp/myRoute/name`). -The alias is the name you use instead of the `serviceId` for `DiscoveryClient`, `Feign`, or -`RestTemplate`. - -In the previous examples, the aliases are `newsletter` and `mailing`. -The following example shows Feign usage with a `newsletter` alias: - -[source,java,indent=0] ----- -@FeignClient("newsletter") -public interface NewsletterService { - @RequestMapping(method = RequestMethod.GET, value = "/newsletter") - String getNewsletters(); -} ----- - -[[path]] -==== Path - -The path is represented by the `path` YAML property and is the path under which the dependency is registered under Zookeeper. -As described in the -<>, Spring Cloud LoadBalancer operates on URLs. -As a result, this path is not compliant with its requirement. -That is why Spring Cloud Zookeeper maps the alias to the proper path. - -[[load-balancer-type]] -==== Load Balancer Type - -The load balancer type is represented by `loadBalancerType` YAML property. - -If you know what kind of load-balancing strategy has to be applied when calling this particular dependency, you can provide it in the YAML file, and it is automatically applied. -You can choose one of the following load balancing strategies: - -* STICKY: Once chosen, the instance is always called. -* RANDOM: Picks an instance randomly. -* ROUND_ROBIN: Iterates over instances over and over again. - -[[content-type-template-and-version]] -==== `Content-Type` Template and Version - -The `Content-Type` template and version are represented by the `contentTypeTemplate` and -`version` YAML properties. - -If you version your API in the `Content-Type` header, you do not want to add this header -to each of your requests. Also, if you want to call a new version of the API, you do not -want to roam around your code to bump up the API version. That is why you can provide a -`contentTypeTemplate` with a special `$version` placeholder. That placeholder will be filled by the value of the -`version` YAML property. Consider the following example of a `contentTypeTemplate`: - ----- -application/vnd.newsletter.$version+json ----- - -Further consider the following `version`: - ----- -v1 ----- - -The combination of `contentTypeTemplate` and version results in the creation of a -`Content-Type` header for each request, as follows: - ----- -application/vnd.newsletter.v1+json ----- - -[[default-headers]] -==== Default Headers - -Default headers are represented by the `headers` map in YAML. - -Sometimes, each call to a dependency requires setting up of some default headers. To not -do that in code, you can set them up in the YAML file, as shown in the following example -`headers` section: - -[source,yml,indent=0] ----- -headers: - Accept: - - text/html - - application/xhtml+xml - Cache-Control: - - no-cache ----- - -That `headers` section results in adding the `Accept` and `Cache-Control` headers with -appropriate list of values in your HTTP request. - -[[required-dependencies]] -==== Required Dependencies - -Required dependencies are represented by `required` property in YAML. - -If one of your dependencies is required to be up when your application boots, you can set -the `required: true` property in the YAML file. - -If your application cannot localize the required dependency during boot time, it throws an -exception, and the Spring Context fails to set up. In other words, your application cannot -start if the required dependency is not registered in Zookeeper. - -You can read more about Spring Cloud Zookeeper Presence Checker -<>. - -[[stubs]] -==== Stubs - -You can provide a colon-separated path to the JAR containing stubs of the dependency, as -shown in the following example: - -`stubs: org.springframework:myApp:stubs` - -where: - -* `org.springframework` is the `groupId`. -* `myApp` is the `artifactId`. -* `stubs` is the classifier. (Note that `stubs` is the default value.) - -Because `stubs` is the default classifier, the preceding example is equal to the following -example: - -`stubs: org.springframework:myApp` - -[[spring-cloud-zookeeper-dependencies-configuring]] -=== Configuring Spring Cloud Zookeeper Dependencies - -You can set the following properties to enable or disable parts of Zookeeper Dependencies functionalities: - -* `spring.cloud.zookeeper.dependencies`: If you do not set this property, you cannot use Zookeeper Dependencies. -* `spring.cloud.zookeeper.dependency.loadbalancer.enabled` (enabled by default): Turns on Zookeeper-specific custom load-balancing strategies, including `ZookeeperServiceInstanceListSupplier` and dependency-based load-balanced `RestTemplate` setup. -* `spring.cloud.zookeeper.dependency.headers.enabled` (enabled by default): This property registers a `FeignBlockingLoadBalancerClient` that automatically appends appropriate headers and content types with their versions, as presented in the Dependency configuration. -Without this setting, those two parameters do not work. -* `spring.cloud.zookeeper.dependency.resttemplate.enabled` (enabled by default): When enabled, this property modifies the request headers of a `@LoadBalanced`-annotated -`RestTemplate` such that it passes headers and content type with the version set in dependency configuration. -Without this setting, those two parameters do not work. - -[[spring-cloud-zookeeper-dependency-watcher]] -== Spring Cloud Zookeeper Dependency Watcher - -The Dependency Watcher mechanism lets you register listeners to your dependencies. The -functionality is, in fact, an implementation of the `Observator` pattern. When a -dependency changes, its state (to either UP or DOWN), some custom logic can be applied. - -[[activating]] -=== Activating - -Spring Cloud Zookeeper Dependencies functionality needs to be enabled for you to use the -Dependency Watcher mechanism. -// TODO: How can the reader do that? - -[[registering-a-listener]] -=== Registering a Listener - -To register a listener, you must implement an interface called -`org.springframework.cloud.zookeeper.discovery.watcher.DependencyWatcherListener` and -register it as a bean. The interface gives you one method: - -[source,java,indent=0] ----- -void stateChanged(String dependencyName, DependencyState newState); ----- - -If you want to register a listener for a particular dependency, the `dependencyName` would -be the discriminator for your concrete implementation. `newState` provides you with -information about whether your dependency has changed to `CONNECTED` or `DISCONNECTED`. - -[[spring-cloud-zookeeper-dependency-watcher-presence-checker]] -=== Using the Presence Checker - -Bound with the Dependency Watcher is the functionality called Presence Checker. It lets -you provide custom behavior when your application boots, to react according to the state -of your dependencies. - -The default implementation of the abstract -`org.springframework.cloud.zookeeper.discovery.watcher.presence.DependencyPresenceOnStartupVerifier` -class is the -`org.springframework.cloud.zookeeper.discovery.watcher.presence.DefaultDependencyPresenceOnStartupVerifier`, -which works in the following way. - -. If the dependency is marked us `required` and is not in Zookeeper, when your application -boots, it throws an exception and shuts down. -. If the dependency is not `required`, the -`org.springframework.cloud.zookeeper.discovery.watcher.presence.LogMissingDependencyChecker` -logs that the dependency is missing at the `WARN` level. - -Because the `DefaultDependencyPresenceOnStartupVerifier` is registered only when there is -no bean of type `DependencyPresenceOnStartupVerifier`, this functionality can be -overridden. - -[[spring-cloud-zookeeper-config]] -== Distributed Configuration with Zookeeper - -Zookeeper provides a -https://zookeeper.apache.org/doc/current/zookeeperOver.html#sc_dataModelNameSpace[hierarchical namespace] -that lets clients store arbitrary data, such as configuration data. Spring Cloud Zookeeper -Config is an alternative to the -https://github.com/spring-cloud/spring-cloud-config[Config Server and Client]. -Configuration is loaded into the Spring Environment during the special "`bootstrap`" -phase. Configuration is stored in the `/config` namespace by default. Multiple -`PropertySource` instances are created, based on the application's name and the active -profiles, to mimic the Spring Cloud Config order of resolving properties. For example, an -application with a name of `testApp` and with the `dev` profile has the following property -sources created for it: - -* `config/testApp,dev` -* `config/testApp` -* `config/application,dev` -* `config/application` - -The most specific property source is at the top, with the least specific at the bottom. -Properties in the `config/application` namespace apply to all applications that use -zookeeper for configuration. Properties in the `config/testApp` namespace are available -only to the instances of the service named `testApp`. - -Configuration is currently read on startup of the application. Sending a HTTP `POST` -request to `/refresh` causes the configuration to be reloaded. Watching the configuration -namespace (which Zookeeper supports) is not currently implemented. - -[[activating]] -=== Activating - -Including a dependency on -`org.springframework.cloud:spring-cloud-starter-zookeeper-config` enables -autoconfiguration that sets up Spring Cloud Zookeeper Config. - -CAUTION: When working with version 3.4 of Zookeeper you need to change -the way you include the dependency as described <>. - -[[config-data-import]] -=== Spring Boot Config Data Import - -Spring Boot 2.4 introduced a new way to import configuration data via the `spring.config.import` property. This is now the default way to get configuration from Zookeeper. - -To optionally connect to Zookeeper for configuration set the following in application.properties: - -.application.properties -[source,properties] ----- -spring.config.import=optional:zookeeper: ----- - -This will connect to Zookeeper at the default location of "localhost:2181". Removing the `optional:` prefix will cause Zookeeper Config to fail if it is unable to connect to Zookeeper. To change the connection properties of Zookeeper Config either set `spring.cloud.zookeeper.connect-string` or add the connect string to the `spring.config.import` statement such as, `spring.config.import=optional:zookeeper:myhost:2818`. The location in the import property has precedence over the `connect-string` property. - -Zookeeper Config will try to load values from four automatic contexts based on `spring.cloud.zookeeper.config.name` (which defaults to the value of the `spring.application.name` property) and `spring.cloud.zookeeper.config.default-context` (which defaults to `application`). If you want to specify the contexts rather than using the computed ones, you can add that information to the `spring.config.import` statement. - -.application.properties -[source,properties] ----- -spring.config.import=optional:zookeeper:myhost:2181/contextone;/context/two ----- - -This will optionally load configuration only from `/contextone` and `/context/two`. - -NOTE: A `bootstrap` file (properties or yaml) is *not* needed for the Spring Boot Config Data method of import via `spring.config.import`. - -[[customizing]] -=== Customizing - -Zookeeper Config may be customized by setting the following properties: - -[source,yml,indent=0] ----- -spring: - cloud: - zookeeper: - config: - enabled: true - root: configuration - defaultContext: apps - profileSeparator: '::' ----- - -* `enabled`: Setting this value to `false` disables Zookeeper Config. -* `root`: Sets the base namespace for configuration values. -* `defaultContext`: Sets the name used by all applications. -* `profileSeparator`: Sets the value of the separator used to separate the profile name in -property sources with profiles. - -CAUTION: If you have set `spring.cloud.bootstrap.enabled=true` or `spring.config.use-legacy-processing=true`, or included `spring-cloud-starter-bootstrap`, then the above values will need to be placed in `bootstrap.yml` instead of `application.yml`. - -[[access-control-lists-acls]] -=== Access Control Lists (ACLs) - -You can add authentication information for Zookeeper ACLs by calling the `addAuthInfo` -method of a `CuratorFramework` bean. One way to accomplish this is to provide your own -`CuratorFramework` bean, as shown in the following example: - -[source,java,indent=0] ----- -@BoostrapConfiguration -public class CustomCuratorFrameworkConfig { - - @Bean - public CuratorFramework curatorFramework() { - CuratorFramework curator = new CuratorFramework(); - curator.addAuthInfo("digest", "user:password".getBytes()); - return curator; - } - -} ----- -Consult -https://github.com/spring-cloud/spring-cloud-zookeeper/blob/master/spring-cloud-zookeeper-core/src/main/java/org/springframework/cloud/zookeeper/ZookeeperAutoConfiguration.java[the ZookeeperAutoConfiguration class] -to see how the `CuratorFramework` bean's default configuration. - -Alternatively, you can add your credentials from a class that depends on the existing -`CuratorFramework` bean, as shown in the following example: - -[source,java,indent=0] ----- -@BoostrapConfiguration -public class DefaultCuratorFrameworkConfig { - - public ZookeeperConfig(CuratorFramework curator) { - curator.addAuthInfo("digest", "user:password".getBytes()); - } - -} ----- - -The creation of this bean must occur during the boostrapping phase. You can register -configuration classes to run during this phase by annotating them with -`@BootstrapConfiguration` and including them in a comma-separated list that you set as the -value of the `org.springframework.cloud.bootstrap.BootstrapConfiguration` property in the -`resources/META-INF/spring.factories` file, as shown in the following example: - -.resources/META-INF/spring.factories ----- -org.springframework.cloud.bootstrap.BootstrapConfiguration=\ -my.project.CustomCuratorFrameworkConfig,\ -my.project.DefaultCuratorFrameworkConfig ----- diff --git a/docs/modules/ROOT/pages/spring-cloud-zookeeper/config.adoc b/docs/modules/ROOT/pages/spring-cloud-zookeeper/config.adoc new file mode 100644 index 00000000..3cb03c26 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-zookeeper/config.adoc @@ -0,0 +1,143 @@ +[[spring-cloud-zookeeper-config]] += Distributed Configuration with Zookeeper + +Zookeeper provides a +https://zookeeper.apache.org/doc/current/zookeeperOver.html#sc_dataModelNameSpace[hierarchical namespace] +that lets clients store arbitrary data, such as configuration data. Spring Cloud Zookeeper +Config is an alternative to the +https://github.com/spring-cloud/spring-cloud-config[Config Server and Client]. +Configuration is loaded into the Spring Environment during the special "`bootstrap`" +phase. Configuration is stored in the `/config` namespace by default. Multiple +`PropertySource` instances are created, based on the application's name and the active +profiles, to mimic the Spring Cloud Config order of resolving properties. For example, an +application with a name of `testApp` and with the `dev` profile has the following property +sources created for it: + +* `config/testApp,dev` +* `config/testApp` +* `config/application,dev` +* `config/application` + +The most specific property source is at the top, with the least specific at the bottom. +Properties in the `config/application` namespace apply to all applications that use +zookeeper for configuration. Properties in the `config/testApp` namespace are available +only to the instances of the service named `testApp`. + +Configuration is currently read on startup of the application. Sending a HTTP `POST` +request to `/refresh` causes the configuration to be reloaded. Watching the configuration +namespace (which Zookeeper supports) is not currently implemented. + +[[activating]] +== Activating + +Including a dependency on +`org.springframework.cloud:spring-cloud-starter-zookeeper-config` enables +autoconfiguration that sets up Spring Cloud Zookeeper Config. + +CAUTION: When working with version 3.4 of Zookeeper you need to change +the way you include the dependency as described <>. + +[[config-data-import]] +== Spring Boot Config Data Import + +Spring Boot 2.4 introduced a new way to import configuration data via the `spring.config.import` property. This is now the default way to get configuration from Zookeeper. + +To optionally connect to Zookeeper for configuration set the following in application.properties: + +.application.properties +[source,properties] +---- +spring.config.import=optional:zookeeper: +---- + +This will connect to Zookeeper at the default location of "localhost:2181". Removing the `optional:` prefix will cause Zookeeper Config to fail if it is unable to connect to Zookeeper. To change the connection properties of Zookeeper Config either set `spring.cloud.zookeeper.connect-string` or add the connect string to the `spring.config.import` statement such as, `spring.config.import=optional:zookeeper:myhost:2818`. The location in the import property has precedence over the `connect-string` property. + +Zookeeper Config will try to load values from four automatic contexts based on `spring.cloud.zookeeper.config.name` (which defaults to the value of the `spring.application.name` property) and `spring.cloud.zookeeper.config.default-context` (which defaults to `application`). If you want to specify the contexts rather than using the computed ones, you can add that information to the `spring.config.import` statement. + +.application.properties +[source,properties] +---- +spring.config.import=optional:zookeeper:myhost:2181/contextone;/context/two +---- + +This will optionally load configuration only from `/contextone` and `/context/two`. + +NOTE: A `bootstrap` file (properties or yaml) is *not* needed for the Spring Boot Config Data method of import via `spring.config.import`. + +[[customizing]] +== Customizing + +Zookeeper Config may be customized by setting the following properties: + +[source,yml,indent=0] +---- +spring: + cloud: + zookeeper: + config: + enabled: true + root: configuration + defaultContext: apps + profileSeparator: '::' +---- + +* `enabled`: Setting this value to `false` disables Zookeeper Config. +* `root`: Sets the base namespace for configuration values. +* `defaultContext`: Sets the name used by all applications. +* `profileSeparator`: Sets the value of the separator used to separate the profile name in +property sources with profiles. + +CAUTION: If you have set `spring.cloud.bootstrap.enabled=true` or `spring.config.use-legacy-processing=true`, or included `spring-cloud-starter-bootstrap`, then the above values will need to be placed in `bootstrap.yml` instead of `application.yml`. + +[[access-control-lists-acls]] +== Access Control Lists (ACLs) + +You can add authentication information for Zookeeper ACLs by calling the `addAuthInfo` +method of a `CuratorFramework` bean. One way to accomplish this is to provide your own +`CuratorFramework` bean, as shown in the following example: + +[source,java,indent=0] +---- +@BoostrapConfiguration +public class CustomCuratorFrameworkConfig { + + @Bean + public CuratorFramework curatorFramework() { + CuratorFramework curator = new CuratorFramework(); + curator.addAuthInfo("digest", "user:password".getBytes()); + return curator; + } + +} +---- +Consult +https://github.com/spring-cloud/spring-cloud-zookeeper/blob/master/spring-cloud-zookeeper-core/src/main/java/org/springframework/cloud/zookeeper/ZookeeperAutoConfiguration.java[the ZookeeperAutoConfiguration class] +to see how the `CuratorFramework` bean's default configuration. + +Alternatively, you can add your credentials from a class that depends on the existing +`CuratorFramework` bean, as shown in the following example: + +[source,java,indent=0] +---- +@BoostrapConfiguration +public class DefaultCuratorFrameworkConfig { + + public ZookeeperConfig(CuratorFramework curator) { + curator.addAuthInfo("digest", "user:password".getBytes()); + } + +} +---- + +The creation of this bean must occur during the boostrapping phase. You can register +configuration classes to run during this phase by annotating them with +`@BootstrapConfiguration` and including them in a comma-separated list that you set as the +value of the `org.springframework.cloud.bootstrap.BootstrapConfiguration` property in the +`resources/META-INF/spring.factories` file, as shown in the following example: + +.resources/META-INF/spring.factories +---- +org.springframework.cloud.bootstrap.BootstrapConfiguration=\ +my.project.CustomCuratorFrameworkConfig,\ +my.project.DefaultCuratorFrameworkConfig +---- diff --git a/docs/modules/ROOT/pages/spring-cloud-zookeeper/dependencies.adoc b/docs/modules/ROOT/pages/spring-cloud-zookeeper/dependencies.adoc new file mode 100644 index 00000000..37a626c9 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-zookeeper/dependencies.adoc @@ -0,0 +1,208 @@ +[[spring-cloud-zookeeper-dependencies]] += Zookeeper Dependencies + +The following topics cover how to work with Spring Cloud Zookeeper dependencies: + +* <> +* <> +* <> +* <> + +[[spring-cloud-zookeeper-dependencies-using]] +== Using the Zookeeper Dependencies + +Spring Cloud Zookeeper gives you a possibility to provide dependencies of your application +as properties. As dependencies, you can understand other applications that are registered +in Zookeeper and which you would like to call through +https://github.com/spring-cloud/spring-cloud-netflix/blob/master/docs/src/main/asciidoc/spring-cloud-netflix.adoc#spring-cloud-feign[Feign] +(a REST client builder), +https://github.com/spring-cloud/spring-cloud-netflix/blob/master/docs/src/main/ascii[Spring +`RestTemplate`] and +https://cloud.spring.io/spring-cloud-commons/reference/html/#loadbalanced-webclient[Spring WebFlux]. + +You can also use the Zookeeper Dependency Watchers functionality to control and monitor +the state of your dependencies. + +[[spring-cloud-zookeeper-dependencies-activating]] +== Activating Zookeeper Dependencies + +Including a dependency on +`org.springframework.cloud:spring-cloud-starter-zookeeper-discovery` enables +autoconfiguration that sets up Spring Cloud Zookeeper Dependencies. Even if you provide +the dependencies in your properties, you can turn off the dependencies. To do so, set the +`spring.cloud.zookeeper.dependency.enabled` property to false (it defaults to `true`). + +[[spring-cloud-zookeeper-dependencies-setting-up]] +== Setting up Zookeeper Dependencies + +Consider the following example of dependency representation: + +[source,yml,indent=0] +.application.yml +---- +spring.application.name: yourServiceName +spring.cloud.zookeeper: + dependencies: + newsletter: + path: /path/where/newsletter/has/registered/in/zookeeper + loadBalancerType: ROUND_ROBIN + contentTypeTemplate: application/vnd.newsletter.$version+json + version: v1 + headers: + header1: + - value1 + header2: + - value2 + required: false + stubs: org.springframework:foo:stubs + mailing: + path: /path/where/mailing/has/registered/in/zookeeper + loadBalancerType: ROUND_ROBIN + contentTypeTemplate: application/vnd.mailing.$version+json + version: v1 + required: true +---- + +The next few sections go through each part of the dependency one by one. The root property +name is `spring.cloud.zookeeper.dependencies`. + +[[spring-cloud-zookeeper-dependencies-setting-up-aliases]] +=== Aliases + +Below the root property you have to represent each dependency as an alias. +This is due to the constraints of Spring Cloud LoadBalancer, which requires that the application ID be placed in the URL. +Consequently, you cannot pass any complex path, suchas `/myApp/myRoute/name`). +The alias is the name you use instead of the `serviceId` for `DiscoveryClient`, `Feign`, or +`RestTemplate`. + +In the previous examples, the aliases are `newsletter` and `mailing`. +The following example shows Feign usage with a `newsletter` alias: + +[source,java,indent=0] +---- +@FeignClient("newsletter") +public interface NewsletterService { + @RequestMapping(method = RequestMethod.GET, value = "/newsletter") + String getNewsletters(); +} +---- + +[[path]] +=== Path + +The path is represented by the `path` YAML property and is the path under which the dependency is registered under Zookeeper. +As described in the +<>, Spring Cloud LoadBalancer operates on URLs. +As a result, this path is not compliant with its requirement. +That is why Spring Cloud Zookeeper maps the alias to the proper path. + +[[load-balancer-type]] +=== Load Balancer Type + +The load balancer type is represented by `loadBalancerType` YAML property. + +If you know what kind of load-balancing strategy has to be applied when calling this particular dependency, you can provide it in the YAML file, and it is automatically applied. +You can choose one of the following load balancing strategies: + +* STICKY: Once chosen, the instance is always called. +* RANDOM: Picks an instance randomly. +* ROUND_ROBIN: Iterates over instances over and over again. + +[[content-type-template-and-version]] +=== `Content-Type` Template and Version + +The `Content-Type` template and version are represented by the `contentTypeTemplate` and +`version` YAML properties. + +If you version your API in the `Content-Type` header, you do not want to add this header +to each of your requests. Also, if you want to call a new version of the API, you do not +want to roam around your code to bump up the API version. That is why you can provide a +`contentTypeTemplate` with a special `$version` placeholder. That placeholder will be filled by the value of the +`version` YAML property. Consider the following example of a `contentTypeTemplate`: + +---- +application/vnd.newsletter.$version+json +---- + +Further consider the following `version`: + +---- +v1 +---- + +The combination of `contentTypeTemplate` and version results in the creation of a +`Content-Type` header for each request, as follows: + +---- +application/vnd.newsletter.v1+json +---- + +[[default-headers]] +=== Default Headers + +Default headers are represented by the `headers` map in YAML. + +Sometimes, each call to a dependency requires setting up of some default headers. To not +do that in code, you can set them up in the YAML file, as shown in the following example +`headers` section: + +[source,yml,indent=0] +---- +headers: + Accept: + - text/html + - application/xhtml+xml + Cache-Control: + - no-cache +---- + +That `headers` section results in adding the `Accept` and `Cache-Control` headers with +appropriate list of values in your HTTP request. + +[[required-dependencies]] +=== Required Dependencies + +Required dependencies are represented by `required` property in YAML. + +If one of your dependencies is required to be up when your application boots, you can set +the `required: true` property in the YAML file. + +If your application cannot localize the required dependency during boot time, it throws an +exception, and the Spring Context fails to set up. In other words, your application cannot +start if the required dependency is not registered in Zookeeper. + +You can read more about Spring Cloud Zookeeper Presence Checker +<>. + +[[stubs]] +=== Stubs + +You can provide a colon-separated path to the JAR containing stubs of the dependency, as +shown in the following example: + +`stubs: org.springframework:myApp:stubs` + +where: + +* `org.springframework` is the `groupId`. +* `myApp` is the `artifactId`. +* `stubs` is the classifier. (Note that `stubs` is the default value.) + +Because `stubs` is the default classifier, the preceding example is equal to the following +example: + +`stubs: org.springframework:myApp` + +[[spring-cloud-zookeeper-dependencies-configuring]] +== Configuring Spring Cloud Zookeeper Dependencies + +You can set the following properties to enable or disable parts of Zookeeper Dependencies functionalities: + +* `spring.cloud.zookeeper.dependencies`: If you do not set this property, you cannot use Zookeeper Dependencies. +* `spring.cloud.zookeeper.dependency.loadbalancer.enabled` (enabled by default): Turns on Zookeeper-specific custom load-balancing strategies, including `ZookeeperServiceInstanceListSupplier` and dependency-based load-balanced `RestTemplate` setup. +* `spring.cloud.zookeeper.dependency.headers.enabled` (enabled by default): This property registers a `FeignBlockingLoadBalancerClient` that automatically appends appropriate headers and content types with their versions, as presented in the Dependency configuration. +Without this setting, those two parameters do not work. +* `spring.cloud.zookeeper.dependency.resttemplate.enabled` (enabled by default): When enabled, this property modifies the request headers of a `@LoadBalanced`-annotated +`RestTemplate` such that it passes headers and content type with the version set in dependency configuration. +Without this setting, those two parameters do not work. + diff --git a/docs/modules/ROOT/pages/spring-cloud-zookeeper/dependency-watcher.adoc b/docs/modules/ROOT/pages/spring-cloud-zookeeper/dependency-watcher.adoc new file mode 100644 index 00000000..24017e10 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-zookeeper/dependency-watcher.adoc @@ -0,0 +1,53 @@ +[[spring-cloud-zookeeper-dependency-watcher]] += Spring Cloud Zookeeper Dependency Watcher + +The Dependency Watcher mechanism lets you register listeners to your dependencies. The +functionality is, in fact, an implementation of the `Observator` pattern. When a +dependency changes, its state (to either UP or DOWN), some custom logic can be applied. + +[[activating]] +== Activating + +Spring Cloud Zookeeper Dependencies functionality needs to be enabled for you to use the +Dependency Watcher mechanism. +// TODO: How can the reader do that? + +[[registering-a-listener]] +== Registering a Listener + +To register a listener, you must implement an interface called +`org.springframework.cloud.zookeeper.discovery.watcher.DependencyWatcherListener` and +register it as a bean. The interface gives you one method: + +[source,java,indent=0] +---- +void stateChanged(String dependencyName, DependencyState newState); +---- + +If you want to register a listener for a particular dependency, the `dependencyName` would +be the discriminator for your concrete implementation. `newState` provides you with +information about whether your dependency has changed to `CONNECTED` or `DISCONNECTED`. + +[[spring-cloud-zookeeper-dependency-watcher-presence-checker]] +== Using the Presence Checker + +Bound with the Dependency Watcher is the functionality called Presence Checker. It lets +you provide custom behavior when your application boots, to react according to the state +of your dependencies. + +The default implementation of the abstract +`org.springframework.cloud.zookeeper.discovery.watcher.presence.DependencyPresenceOnStartupVerifier` +class is the +`org.springframework.cloud.zookeeper.discovery.watcher.presence.DefaultDependencyPresenceOnStartupVerifier`, +which works in the following way. + +. If the dependency is marked us `required` and is not in Zookeeper, when your application +boots, it throws an exception and shuts down. +. If the dependency is not `required`, the +`org.springframework.cloud.zookeeper.discovery.watcher.presence.LogMissingDependencyChecker` +logs that the dependency is missing at the `WARN` level. + +Because the `DefaultDependencyPresenceOnStartupVerifier` is registered only when there is +no bean of type `DependencyPresenceOnStartupVerifier`, this functionality can be +overridden. + diff --git a/docs/modules/ROOT/pages/spring-cloud-zookeeper/discovery.adoc b/docs/modules/ROOT/pages/spring-cloud-zookeeper/discovery.adoc new file mode 100644 index 00000000..b10ed7eb --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-zookeeper/discovery.adoc @@ -0,0 +1,105 @@ +[[spring-cloud-zookeeper-discovery]] += Service Discovery with Zookeeper + +Service Discovery is one of the key tenets of a microservice based architecture. Trying to +hand-configure each client or some form of convention can be difficult to do and can be +brittle. https://curator.apache.org[Curator](A Java library for Zookeeper) provides Service +Discovery through a https://curator.apache.org/curator-x-discovery/[Service Discovery +Extension]. Spring Cloud Zookeeper uses this extension for service registration and +discovery. + +[[activating]] +== Activating + +Including a dependency on +`org.springframework.cloud:spring-cloud-starter-zookeeper-discovery` enables +autoconfiguration that sets up Spring Cloud Zookeeper Discovery. + +NOTE: For web functionality, you still need to include +`org.springframework.boot:spring-boot-starter-web`. + +CAUTION: When working with version 3.4 of Zookeeper you need to change +the way you include the dependency as described <>. + +[[registering-with-zookeeper]] +== Registering with Zookeeper + +When a client registers with Zookeeper, it provides metadata (such as host and port, ID, +and name) about itself. + +The following example shows a Zookeeper client: + +[source,java,indent=0] +---- +@SpringBootApplication +@RestController +public class Application { + + @RequestMapping("/") + public String home() { + return "Hello world"; + } + + public static void main(String[] args) { + new SpringApplicationBuilder(Application.class).web(true).run(args); + } + +} +---- + +NOTE: The preceding example is a normal Spring Boot application. + +If Zookeeper is located somewhere other than `localhost:2181`, the configuration must +provide the location of the server, as shown in the following example: + +[source,yml,indent=0] +.application.yml +---- +spring: + cloud: + zookeeper: + connect-string: localhost:2181 +---- + +CAUTION: If you use <>, the +values shown in the preceding example need to be in `bootstrap.yml` instead of +`application.yml`. + +The default service name, instance ID, and port (taken from the `Environment`) are +`${spring.application.name}`, the Spring Context ID, and `${server.port}`, respectively. + +Having `spring-cloud-starter-zookeeper-discovery` on the classpath makes the app into both +a Zookeeper "`service`" (that is, it registers itself) and a "`client`" (that is, it can +query Zookeeper to locate other services). + +If you would like to disable the Zookeeper Discovery Client, you can set +`spring.cloud.zookeeper.discovery.enabled` to `false`. + +[[using-the-discoveryclient]] +== Using the DiscoveryClient + +Spring Cloud has support for +https://github.com/spring-cloud/spring-cloud-netflix/blob/master/docs/src/main/asciidoc/spring-cloud-netflix.adoc#spring-cloud-feign[Feign] +(a REST client builder), +https://github.com/spring-cloud/spring-cloud-netflix/blob/master/docs/src/main/ascii[Spring +`RestTemplate`] and +https://cloud.spring.io/spring-cloud-commons/reference/html/#loadbalanced-webclient[Spring WebFlux], using logical service names instead of physical URLs. + +You can also use the `org.springframework.cloud.client.discovery.DiscoveryClient`, which +provides a simple API for discovery clients that is not specific to Netflix, as shown in +the following example: + +[source,java,indent=0] +---- +@Autowired +private DiscoveryClient discoveryClient; + +public String serviceUrl() { + List list = discoveryClient.getInstances("STORES"); + if (list != null && list.size() > 0 ) { + return list.get(0).getUri().toString(); + } + return null; +} +---- + diff --git a/docs/modules/ROOT/pages/spring-cloud-zookeeper/install.adoc b/docs/modules/ROOT/pages/spring-cloud-zookeeper/install.adoc new file mode 100644 index 00000000..d34003d7 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-zookeeper/install.adoc @@ -0,0 +1,54 @@ +[[spring-cloud-zookeeper-install]] += Install Zookeeper + +See the https://zookeeper.apache.org/doc/current/zookeeperStarted.html[installation +documentation] for instructions on how to install Zookeeper. + +Spring Cloud Zookeeper uses Apache Curator behind the scenes. +While Zookeeper 3.5.x is still considered "beta" by the Zookeeper development team, +the reality is that it is used in production by many users. +However, Zookeeper 3.4.x is also used in production. +Prior to Apache Curator 4.0, both versions of Zookeeper were supported via two versions of Apache Curator. +Starting with Curator 4.0 both versions of Zookeeper are supported via the same Curator libraries. + +In case you are integrating with version 3.4 you need to change the Zookeeper dependency +that comes shipped with `curator`, and thus `spring-cloud-zookeeper`. +To do so simply exclude that dependency and add the 3.4.x version like shown below. + +.maven +[source,xml,indent=0] +---- + + org.springframework.cloud + spring-cloud-starter-zookeeper-all + + + org.apache.zookeeper + zookeeper + + + + + org.apache.zookeeper + zookeeper + 3.4.12 + + + org.slf4j + slf4j-log4j12 + + + +---- + +.gradle +[source,groovy,indent=0] +---- +compile('org.springframework.cloud:spring-cloud-starter-zookeeper-all') { + exclude group: 'org.apache.zookeeper', module: 'zookeeper' +} +compile('org.apache.zookeeper:zookeeper:3.4.12') { + exclude group: 'org.slf4j', module: 'slf4j-log4j12' +} +---- + diff --git a/docs/modules/ROOT/pages/spring-cloud-zookeeper/other-componentes.adoc b/docs/modules/ROOT/pages/spring-cloud-zookeeper/other-componentes.adoc new file mode 100644 index 00000000..a2897cf8 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-zookeeper/other-componentes.adoc @@ -0,0 +1,15 @@ +[[spring-cloud-zookeeper-other-componentes]] += Using Spring Cloud Zookeeper with Spring Cloud Components + +Feign, Spring Cloud Gateway and Spring Cloud LoadBalancer all work with Spring Cloud Zookeeper. + +[[spring-cloud-loadbalancer-with-zookeeper]] +== Spring Cloud LoadBalancer with Zookeeper + +Spring Cloud Zookeeper provides an implementation of Spring Cloud LoadBalancer `ServiceInstanceListSupplier`. +When you use the `spring-cloud-starter-zookeeper-discovery`, Spring Cloud LoadBalancer is autoconfigured to use the +`ZookeeperServiceInstanceListSupplier` by default. + +TIP: If you were previously using the StickyRule in Zookeeper, its replacement in the current stack +is the `SameInstancePreferenceServiceInstanceListSupplier` in SC LoadBalancer. You can read on how to set it up in the https://docs.spring.io/spring-cloud-commons/docs/current/reference/html/#spring-cloud-loadbalancer[Spring Cloud Commons documentation]. + diff --git a/docs/modules/ROOT/pages/spring-cloud-zookeeper/quick-start.adoc b/docs/modules/ROOT/pages/spring-cloud-zookeeper/quick-start.adoc new file mode 100644 index 00000000..d79042e1 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-zookeeper/quick-start.adoc @@ -0,0 +1,5 @@ +[[quick-start]] += Quick Start + +include:../:quickstart.adoc[] + diff --git a/docs/modules/ROOT/pages/spring-cloud-zookeeper/service-registry.adoc b/docs/modules/ROOT/pages/spring-cloud-zookeeper/service-registry.adoc new file mode 100644 index 00000000..88e56c06 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-zookeeper/service-registry.adoc @@ -0,0 +1,45 @@ +[[spring-cloud-zookeeper-service-registry]] += Spring Cloud Zookeeper and Service Registry + +Spring Cloud Zookeeper implements the `ServiceRegistry` interface, letting developers +register arbitrary services in a programmatic way. + +The `ServiceInstanceRegistration` class offers a `builder()` method to create a +`Registration` object that can be used by the `ServiceRegistry`, as shown in the following +example: + +[source,java,indent=0] +---- +@Autowired +private ZookeeperServiceRegistry serviceRegistry; + +public void registerThings() { + ZookeeperRegistration registration = ServiceInstanceRegistration.builder() + .defaultUriSpec() + .address("anyUrl") + .port(10) + .name("/a/b/c/d/anotherservice") + .build(); + this.serviceRegistry.register(registration); +} +---- + +[[instance-status]] +== Instance Status + +Netflix Eureka supports having instances that are `OUT_OF_SERVICE` registered with the server. +These instances are not returned as active service instances. +This is useful for behaviors such as blue/green deployments. +(Note that the Curator Service Discovery recipe does not support this behavior.) Taking advantage of the flexible payload has let Spring Cloud Zookeeper implement `OUT_OF_SERVICE` by updating some specific metadata and then filtering on that metadata in the Spring Cloud LoadBalancer `ZookeeperServiceInstanceListSupplier`. +The `ZookeeperServiceInstanceListSupplier` filters out all non-null instance statuses that do not equal `UP`. +If the instance status field is empty, it is considered to be `UP` for backwards compatibility. +To change the status of an instance, make a `POST` with `OUT_OF_SERVICE` to the `ServiceRegistry` +instance status actuator endpoint, as shown in the following example: + +[source,sh,indent=0] +---- +$ http POST http://localhost:8081/service-registry status=OUT_OF_SERVICE +---- + +NOTE: The preceding example uses the `http` command from https://httpie.org. +