Switch from Ribbon to SC LoadBalancer (#240)
* WIP * Remove repeated dependency. * Gitignore fix. * Implement Spring Cloud LoadBalancer-based DependencyFeignClientAutoConfiguration and DependencyLoadBalancerAutoConfiguration. * Fix dependency-based configurations. Add ZookeeperServiceInstanceListSupplier. Add LoadBalancerZookeeperAutoConfiguration. * Adjust tests to changes in code. * Adjust tests to changes in code. Add missing license Strings. Refactor. * Add BlockingDependencyLoadBalancerConfiguration. Remove condition from ZookeeperDependenciesAutoConfiguration. * Switch EmptyResponse to chooseDefault(). Fix DependencyRestTemplateAutoConfiguration. Change return type for BlockingDependencyLoadBalancerConfiguration. * Fix zookeeper-specific BlockingLoadBalancerClient implementation. * Remove zookeeper-specific LoadBalancerClient and ReactorExchangeFilterFunction implementations following a team discussion. Remove Ribbon-specific entries from docs. Add javadocs. Co-authored-by: Marcin Grzejszczak <marcin@grzejszczak.pl>
This commit is contained in:
committed by
GitHub
parent
fe4a48c5ec
commit
59c0010be3
16
README.adoc
16
README.adoc
@@ -28,9 +28,8 @@ This project provides Zookeeper integrations for Spring Boot applications throug
|
||||
autoconfiguration and binding to the Spring Environment and other Spring programming model
|
||||
idioms. With a few annotations, you can quickly enable and configure the common patterns
|
||||
inside your application and build large distributed systems with Zookeeper based
|
||||
components. The provided patterns include Service Discovery and Configuration. Integration
|
||||
with Spring Cloud Netflix provides Intelligent Routing (Zuul), Client Side Load Balancing
|
||||
(Ribbon), and Circuit Breaker (Hystrix).
|
||||
components. The provided patterns include Service Discovery and Configuration. The project
|
||||
also provides client-side load-balancing via integration with Spring Cloud LoadBalancer.
|
||||
|
||||
|
||||
:doctype: book
|
||||
@@ -51,17 +50,16 @@ with Spring Cloud Netflix provides Intelligent Routing (Zuul), Client Side Load
|
||||
|
||||
== Zookeeper overview
|
||||
|
||||
ZooKeeper is a centralized service for maintaining configuration information, naming,
|
||||
providing distributed synchronization, and providing group services. See the
|
||||
https://zookeeper.apache.org[Zookeeper site] for more information. Spring Cloud Zookeeper
|
||||
also builds on the https://curator.apache.org[Apache Curator] project, which started life
|
||||
at Netflix.
|
||||
ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services.
|
||||
See the
|
||||
https://zookeeper.apache.org[Zookeeper site] for more information.
|
||||
Spring Cloud Zookeeper also builds on the https://curator.apache.org[Apache Curator] project, which started life at Netflix.
|
||||
|
||||
== Spring Cloud Zookeeper Features
|
||||
|
||||
Spring Cloud Zookeeper includes the following features:
|
||||
|
||||
* Spring Cloud `DiscoveryClient` implementation (supports Ribbon and Zuul)
|
||||
* Spring Cloud `DiscoveryClient` implementation (supports Spring Cloud LoadBalancer)
|
||||
* Zookeeper-based `PropertySource` loaded during the 'bootstrap' phase
|
||||
|
||||
== Running the Sample
|
||||
|
||||
@@ -6,17 +6,16 @@ include::_attributes.adoc[]
|
||||
|
||||
== Zookeeper overview
|
||||
|
||||
ZooKeeper is a centralized service for maintaining configuration information, naming,
|
||||
providing distributed synchronization, and providing group services. See the
|
||||
https://zookeeper.apache.org[Zookeeper site] for more information. Spring Cloud Zookeeper
|
||||
also builds on the https://curator.apache.org[Apache Curator] project, which started life
|
||||
at Netflix.
|
||||
ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services.
|
||||
See the
|
||||
https://zookeeper.apache.org[Zookeeper site] for more information.
|
||||
Spring Cloud Zookeeper also builds on the https://curator.apache.org[Apache Curator] project, which started life at Netflix.
|
||||
|
||||
== Spring Cloud Zookeeper Features
|
||||
|
||||
Spring Cloud Zookeeper includes the following features:
|
||||
|
||||
* Spring Cloud `DiscoveryClient` implementation (supports Ribbon and Zuul)
|
||||
* Spring Cloud `DiscoveryClient` implementation (supports Spring Cloud LoadBalancer)
|
||||
* Zookeeper-based `PropertySource` loaded during the 'bootstrap' phase
|
||||
|
||||
== Running the Sample
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
|spring.cloud.zookeeper.block-until-connected-unit | | The unit of time related to blocking on connection to Zookeeper.
|
||||
|spring.cloud.zookeeper.block-until-connected-wait | 10 | Wait time to block on connection to Zookeeper.
|
||||
|spring.cloud.zookeeper.connect-string | localhost:2181 | Connection string to the Zookeeper cluster.
|
||||
|spring.cloud.zookeeper.default-health-endpoint | | Default health endpoint that will be checked to verify that a dependency is alive.
|
||||
|spring.cloud.zookeeper.dependencies | | Mapping of alias to ZookeeperDependency. From Ribbon perspective the alias is actually serviceID since Ribbon can't accept nested structures in serviceID.
|
||||
|spring.cloud.zookeeper.dependencies | | Mapping of alias to ZookeeperDependency. From LoadBalancer perspective the alias is actually serviceID since SC LoadBalancer can't accept nested structures in serviceID.
|
||||
|spring.cloud.zookeeper.dependency-configurations | |
|
||||
|spring.cloud.zookeeper.dependency-names | |
|
||||
|spring.cloud.zookeeper.discovery.enabled | true |
|
||||
|
||||
@@ -5,6 +5,5 @@ This project provides Zookeeper integrations for Spring Boot applications throug
|
||||
autoconfiguration and binding to the Spring Environment and other Spring programming model
|
||||
idioms. With a few annotations, you can quickly enable and configure the common patterns
|
||||
inside your application and build large distributed systems with Zookeeper based
|
||||
components. The provided patterns include Service Discovery and Configuration. Integration
|
||||
with Spring Cloud Netflix provides Intelligent Routing (Zuul), Client Side Load Balancing
|
||||
(Ribbon), and Circuit Breaker (Hystrix).
|
||||
components. The provided patterns include Service Discovery and Configuration. The project
|
||||
also provides client-side load-balancing via integration with Spring Cloud LoadBalancer.
|
||||
|
||||
@@ -6,8 +6,8 @@ Spring Cloud Zookeeper provides http://zookeeper.apache.org/[Apache Zookeeper] i
|
||||
## Features
|
||||
|
||||
* Service Discovery: instances can be registered with Zookeeper and clients can discover the instances using Spring-managed beans
|
||||
* Supports Ribbon, the client side load-balancer via Spring Cloud Netflix
|
||||
* Supports Zuul, a dynamic router and filter via Spring Cloud Netflix
|
||||
* Supports Spring Cloud LoadBalancer - client-side load-balancing solution
|
||||
* Supports Spring Cloud OpenFeign
|
||||
* Distributed Configuration: using Zookeeper as a data store
|
||||
|
||||
## Quick Start
|
||||
|
||||
@@ -164,14 +164,14 @@ public String serviceUrl() {
|
||||
== Using Spring Cloud Zookeeper with Spring Cloud Netflix Components
|
||||
|
||||
Spring Cloud Netflix supplies useful tools that work regardless of which `DiscoveryClient`
|
||||
implementation you use. Feign, Turbine, Ribbon, and Zuul all work with Spring Cloud
|
||||
Zookeeper.
|
||||
implementation you use.
|
||||
Feign and Spring Cloud LoadBalancer also work with Spring Cloud Zookeeper.
|
||||
|
||||
=== Ribbon with Zookeeper
|
||||
=== Spring Cloud LoadBalancer with Zookeeper
|
||||
|
||||
Spring Cloud Zookeeper provides an implementation of Ribbon's `ServerList`. When you use
|
||||
the `spring-cloud-starter-zookeeper-discovery`, Ribbon is autoconfigured to use the
|
||||
`ZookeeperServerList` by default.
|
||||
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.
|
||||
|
||||
[[spring-cloud-zookeeper-service-registry]]
|
||||
== Spring Cloud Zookeeper and Service Registry
|
||||
@@ -201,15 +201,13 @@ public void registerThings() {
|
||||
|
||||
=== 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 Ribbon `ZookeeperServerList`. The `ZookeeperServerList`
|
||||
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`
|
||||
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]
|
||||
@@ -290,14 +288,14 @@ 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 Ribbon, 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
|
||||
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:
|
||||
In the previous examples, the aliases are `newsletter` and `mailing`.
|
||||
The following example shows Feign usage with a `newsletter` alias:
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
@@ -310,19 +308,18 @@ public interface NewsletterService {
|
||||
|
||||
==== 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-zookeeper-dependencies-setting-up-aliases,previous section>>, Ribbon
|
||||
operates on URLs. As a result, this path is not compliant with its requirement.
|
||||
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-zookeeper-dependencies-setting-up-aliases,previous section>>, 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
|
||||
|
||||
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:
|
||||
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.
|
||||
@@ -412,29 +409,15 @@ example:
|
||||
[[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:
|
||||
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.ribbon.enabled` (enabled by default): Ribbon requires
|
||||
either explicit global configuration or a particular one for a dependency. By turning on
|
||||
this property, runtime load balancing strategy resolution is possible, and you can use the
|
||||
`loadBalancerType` section of the Zookeeper Dependencies. The configuration that needs
|
||||
this property has an implementation of `LoadBalancerClient` that delegates to the
|
||||
`ILoadBalancer` presented in the next bullet.
|
||||
* `spring.cloud.zookeeper.dependency.ribbon.loadbalancer` (enabled by default): Thanks to
|
||||
this property, the custom `ILoadBalancer` knows that the part of the URI passed to Ribbon
|
||||
might actually be the alias that has to be resolved to a proper path in Zookeeper. Without
|
||||
this property, you cannot register applications under nested paths.
|
||||
* `spring.cloud.zookeeper.dependency.headers.enabled` (enabled by default): This property
|
||||
registers a `RibbonClient` 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.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
|
||||
|
||||
2
pom.xml
2
pom.xml
@@ -4,7 +4,6 @@
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-zookeeper</artifactId>
|
||||
<version>3.0.0.BUILD-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
@@ -169,6 +168,7 @@
|
||||
<spring-cloud-config.version>3.0.0.BUILD-SNAPSHOT</spring-cloud-config.version>
|
||||
<spring-cloud-netflix.version>3.0.0.BUILD-SNAPSHOT</spring-cloud-netflix.version>
|
||||
<spring-cloud-openfeign.version>3.0.0.BUILD-SNAPSHOT</spring-cloud-openfeign.version>
|
||||
<hibernate-validator.version>6.1.1.Final</hibernate-validator.version>
|
||||
</properties>
|
||||
|
||||
<profiles>
|
||||
|
||||
@@ -30,6 +30,10 @@
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-zookeeper-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.curator</groupId>
|
||||
<artifactId>curator-x-discovery</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-context</artifactId>
|
||||
|
||||
@@ -18,58 +18,62 @@ package org.springframework.cloud.zookeeper.config;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
/**
|
||||
* @author Enrique Recarte Llorens
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
public class ZookeeperPropertySourceLocatorFailFastTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException expectedException = ExpectedException.none();
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
public void setUp() {
|
||||
// This system property makes Curator fail faster, otherwise it takes 15 seconds
|
||||
// to trigger a retry
|
||||
System.setProperty("curator-default-connection-timeout", "0");
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
public void tearDown() {
|
||||
System.clearProperty("curator-default-connection-timeout");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailFastFalseLoadsTheApplicationContext() throws Exception {
|
||||
new SpringApplicationBuilder().sources(Config.class).web(WebApplicationType.NONE)
|
||||
.run("--spring.application.name=testZookeeperPropertySourceLocatorFailFast",
|
||||
"--spring.cloud.zookeeper.config.connectString=localhost:2188",
|
||||
"--spring.cloud.zookeeper.baseSleepTimeMs=0",
|
||||
"--spring.cloud.zookeeper.maxRetries=0",
|
||||
"--spring.cloud.zookeeper.maxSleepMs=0",
|
||||
"--spring.cloud.zookeeper.blockUntilConnectedWait=0",
|
||||
"--spring.cloud.zookeeper.config.failFast=false");
|
||||
public void testFailFastFalseLoadsTheApplicationContext() {
|
||||
assertThatCode(() -> {
|
||||
new SpringApplicationBuilder().sources(Config.class)
|
||||
.web(WebApplicationType.NONE)
|
||||
.run("--spring.application.name=testZookeeperPropertySourceLocatorFailFast",
|
||||
"--spring.cloud.zookeeper.config.connectString=localhost:2188",
|
||||
"--spring.cloud.zookeeper.baseSleepTimeMs=0",
|
||||
"--spring.cloud.zookeeper.maxRetries=0",
|
||||
"--spring.cloud.zookeeper.maxSleepMs=0",
|
||||
"--spring.cloud.zookeeper.blockUntilConnectedWait=0",
|
||||
"--spring.cloud.zookeeper.config.failFast=false");
|
||||
}).doesNotThrowAnyException();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailFastTrueDoesNotLoadTheApplicationContext() throws Exception {
|
||||
expectedException.expect(Exception.class);
|
||||
|
||||
new SpringApplicationBuilder().sources(Config.class).web(WebApplicationType.NONE)
|
||||
.run("--spring.application.name=testZookeeperPropertySourceLocatorFailFast",
|
||||
"--spring.cloud.zookeeper.config.connectString=localhost:2188",
|
||||
"--spring.cloud.zookeeper.baseSleepTimeMs=0",
|
||||
"--spring.cloud.zookeeper.maxRetries=0",
|
||||
"--spring.cloud.zookeeper.maxSleepMs=0",
|
||||
"--spring.cloud.zookeeper.blockUntilConnectedWait=0",
|
||||
"--spring.cloud.zookeeper.config.failFast=true");
|
||||
public void testFailFastTrueDoesNotLoadTheApplicationContext() {
|
||||
assertThatThrownBy(() -> {
|
||||
new SpringApplicationBuilder().sources(Config.class)
|
||||
.web(WebApplicationType.NONE)
|
||||
.run("--spring.application.name=testZookeeperPropertySourceLocatorFailFast",
|
||||
"--spring.cloud.zookeeper.config.connectString=localhost:2188",
|
||||
"--spring.cloud.zookeeper.baseSleepTimeMs=0",
|
||||
"--spring.cloud.zookeeper.maxRetries=0",
|
||||
"--spring.cloud.zookeeper.maxSleepMs=0",
|
||||
"--spring.cloud.zookeeper.blockUntilConnectedWait=0",
|
||||
"--spring.cloud.zookeeper.config.failFast=true");
|
||||
})
|
||||
.isNotNull();
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
|
||||
@@ -26,6 +26,15 @@
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>javax.validation</groupId>
|
||||
<artifactId>validation-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
<version>${hibernate-validator.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
@@ -50,6 +59,11 @@
|
||||
<artifactId>spring-cloud-commons</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.curator</groupId>
|
||||
<artifactId>curator-x-discovery</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.curator</groupId>
|
||||
<artifactId>curator-framework</artifactId>
|
||||
|
||||
@@ -38,6 +38,11 @@
|
||||
<artifactId>spring-cloud-commons</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
@@ -73,6 +78,11 @@
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.openfeign</groupId>
|
||||
<artifactId>feign-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure-processor</artifactId>
|
||||
@@ -88,11 +98,6 @@
|
||||
<artifactId>spring-cloud-config-server</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2015-2019 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.zookeeper.discovery;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
|
||||
/**
|
||||
* Wrapper annotation to enable Spring Cloud LoadBalancer for Zookeeper.
|
||||
*
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@ConditionalOnProperty(value = "spring.cloud.zookeeper.loadbalancer.enabled", matchIfMissing = true)
|
||||
public @interface ConditionalOnLoadBalancerForZookeeperEnabled {
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2015-2019 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.zookeeper.discovery;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer;
|
||||
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
|
||||
import org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfiguration;
|
||||
import org.springframework.cloud.zookeeper.ConditionalOnZookeeperEnabled;
|
||||
import org.springframework.cloud.zookeeper.discovery.dependency.ZookeeperDependenciesAutoConfiguration;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
* Auto-configuration} that sets up Spring Cloud LoadBalancer for Zookeeper.
|
||||
*
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties
|
||||
@ConditionalOnZookeeperEnabled
|
||||
@ConditionalOnBean(ReactiveLoadBalancer.Factory.class)
|
||||
@ConditionalOnLoadBalancerForZookeeperEnabled
|
||||
@AutoConfigureAfter({LoadBalancerAutoConfiguration.class, ZookeeperDependenciesAutoConfiguration.class})
|
||||
@LoadBalancerClients(defaultConfiguration = ZookeeperLoadBalancerConfiguration.class)
|
||||
public class LoadBalancerZookeeperAutoConfiguration {
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2015-2019 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.zookeeper.discovery;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.loadbalancer.cache.LoadBalancerCacheManager;
|
||||
import org.springframework.cloud.loadbalancer.core.CachingServiceInstanceListSupplier;
|
||||
import org.springframework.cloud.loadbalancer.core.DiscoveryClientServiceInstanceListSupplier;
|
||||
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
|
||||
import org.springframework.cloud.zookeeper.discovery.dependency.ZookeeperDependencies;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
/**
|
||||
* Zookeeper-specific {@link ServiceInstanceListSupplier} that provides a delegate that
|
||||
* filters available instances based on status retrieved from Zookeeper.
|
||||
*
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class ZookeeperLoadBalancerConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(DiscoveryClient.class)
|
||||
@ConditionalOnMissingBean
|
||||
public ServiceInstanceListSupplier zookeeperDiscoveryClientServiceInstanceListSupplier(
|
||||
DiscoveryClient discoveryClient, Environment env,
|
||||
ApplicationContext context,
|
||||
ZookeeperDependencies zookeeperDependencies) {
|
||||
DiscoveryClientServiceInstanceListSupplier firstDelegate = new DiscoveryClientServiceInstanceListSupplier(
|
||||
discoveryClient, env);
|
||||
ZookeeperServiceInstanceListSupplier secondDelegate = new ZookeeperServiceInstanceListSupplier(firstDelegate,
|
||||
zookeeperDependencies);
|
||||
ObjectProvider<LoadBalancerCacheManager> cacheManagerProvider = context
|
||||
.getBeanProvider(LoadBalancerCacheManager.class);
|
||||
if (cacheManagerProvider.getIfAvailable() != null) {
|
||||
return new CachingServiceInstanceListSupplier(secondDelegate,
|
||||
cacheManagerProvider.getIfAvailable());
|
||||
}
|
||||
return secondDelegate;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2015-2019 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.zookeeper.discovery;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
|
||||
import org.springframework.cloud.zookeeper.discovery.dependency.ZookeeperDependencies;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.springframework.cloud.zookeeper.support.StatusConstants.INSTANCE_STATUS_KEY;
|
||||
import static org.springframework.cloud.zookeeper.support.StatusConstants.STATUS_UP;
|
||||
|
||||
/**
|
||||
* A {@link ServiceInstanceListSupplier} implementation that filters available instances based on status retrieved from Zookeeper.
|
||||
*
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public class ZookeeperServiceInstanceListSupplier implements ServiceInstanceListSupplier {
|
||||
|
||||
private final ServiceInstanceListSupplier delegate;
|
||||
private final String serviceId;
|
||||
|
||||
public ZookeeperServiceInstanceListSupplier(ServiceInstanceListSupplier delegate,
|
||||
ZookeeperDependencies zookeeperDependencies) {
|
||||
this.delegate = delegate;
|
||||
this.serviceId = getServiceIdFromDepsOrClientName(delegate
|
||||
.getServiceId(), zookeeperDependencies);
|
||||
}
|
||||
|
||||
private String getServiceIdFromDepsOrClientName(String delegateServiceId,
|
||||
ZookeeperDependencies zookeeperDependencies) {
|
||||
String serviceIdFromDeps = zookeeperDependencies
|
||||
.getPathForAlias(delegateServiceId);
|
||||
return StringUtils.hasText(serviceIdFromDeps) ? serviceIdFromDeps
|
||||
: delegateServiceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceId() {
|
||||
return serviceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<List<ServiceInstance>> get() {
|
||||
return delegate.get().map(this::filteredByZookeeperStatusUp);
|
||||
}
|
||||
|
||||
private List<ServiceInstance> filteredByZookeeperStatusUp(List<ServiceInstance> serviceInstances) {
|
||||
ArrayList<ServiceInstance> filteredInstances = new ArrayList<>();
|
||||
for (ServiceInstance serviceInstance : serviceInstances) {
|
||||
if (serviceInstance instanceof ZookeeperServiceInstance) {
|
||||
org.apache.curator.x.discovery.ServiceInstance<ZookeeperInstance> zookeeperServiceInstance = ((ZookeeperServiceInstance) serviceInstance)
|
||||
.getServiceInstance();
|
||||
String instanceStatus = null;
|
||||
if (zookeeperServiceInstance.getPayload() != null
|
||||
&& zookeeperServiceInstance.getPayload().getMetadata() != null) {
|
||||
instanceStatus = zookeeperServiceInstance.getPayload().getMetadata()
|
||||
.get(INSTANCE_STATUS_KEY);
|
||||
}
|
||||
if (!StringUtils.hasText(instanceStatus) // backwards compatibility
|
||||
|| instanceStatus.equalsIgnoreCase(STATUS_UP)) {
|
||||
filteredInstances.add(serviceInstance);
|
||||
}
|
||||
}
|
||||
}
|
||||
return filteredInstances;
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015-2019 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.zookeeper.discovery.dependency;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* LoadBalancer that delegates to other rules depending on the provided load balancing
|
||||
* strategy in the {@link ZookeeperDependency#getLoadBalancerType()}.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class DependenciesBasedLoadBalancer /*extends DynamicServerListLoadBalancer*/ {
|
||||
|
||||
private static final Log log = LogFactory.getLog(DependenciesBasedLoadBalancer.class);
|
||||
|
||||
//private final Map<String, IRule> ruleCache = new ConcurrentHashMap<>();
|
||||
|
||||
private final ZookeeperDependencies zookeeperDependencies;
|
||||
|
||||
public DependenciesBasedLoadBalancer(ZookeeperDependencies zookeeperDependencies/*,
|
||||
ServerList<?> serverList, IClientConfig config, IPing iPing*/) {
|
||||
//super(config);
|
||||
this.zookeeperDependencies = zookeeperDependencies;
|
||||
//setServersList(serverList.getInitialListOfServers());
|
||||
//setPing(iPing);
|
||||
//setServerListImpl(serverList);
|
||||
}
|
||||
|
||||
// FIXME: 3.0.0
|
||||
//@Override
|
||||
//public Server chooseServer(Object key) {
|
||||
// String keyAsString;
|
||||
// if ("default".equals(key)) { // this is the default hint, use name instead
|
||||
// keyAsString = getName();
|
||||
// }
|
||||
// else {
|
||||
// keyAsString = (String) key;
|
||||
// }
|
||||
// ZookeeperDependency dependency = this.zookeeperDependencies
|
||||
// .getDependencyForAlias(keyAsString);
|
||||
// log.debug(String.format("Current dependencies are [%s]",
|
||||
// this.zookeeperDependencies));
|
||||
// if (dependency == null) {
|
||||
// log.debug(String.format(
|
||||
// "No dependency found for alias [%s] - will use the default rule which is [%s]",
|
||||
// keyAsString, this.rule));
|
||||
// return this.rule.choose(key);
|
||||
// }
|
||||
// cacheEntryIfMissing(keyAsString, dependency);
|
||||
// log.debug(String.format(
|
||||
// "Will try to retrieve dependency for key [%s]. Current cache contents [%s]",
|
||||
// keyAsString, this.ruleCache));
|
||||
// updateListOfServers();
|
||||
// return this.ruleCache.get(keyAsString).choose(key);
|
||||
//}
|
||||
//
|
||||
//private void cacheEntryIfMissing(String keyAsString, ZookeeperDependency dependency) {
|
||||
// if (!this.ruleCache.containsKey(keyAsString)) {
|
||||
// log.debug(String.format("Cache doesn't contain entry for [%s]", keyAsString));
|
||||
// this.ruleCache.put(keyAsString,
|
||||
// chooseRuleForLoadBalancerType(dependency.getLoadBalancerType()));
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//private IRule chooseRuleForLoadBalancerType(LoadBalancerType type) {
|
||||
// switch (type) {
|
||||
// case ROUND_ROBIN:
|
||||
// return getRoundRobinRule();
|
||||
// case RANDOM:
|
||||
// return getRandomRule();
|
||||
// case STICKY:
|
||||
// return getStickyRule();
|
||||
// default:
|
||||
// throw new IllegalArgumentException("Unknown load balancer type " + type);
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//private RoundRobinRule getRoundRobinRule() {
|
||||
// return new RoundRobinRule(this);
|
||||
//}
|
||||
//
|
||||
//private IRule getRandomRule() {
|
||||
// RandomRule randomRule = new RandomRule();
|
||||
// randomRule.setLoadBalancer(this);
|
||||
// return randomRule;
|
||||
//}
|
||||
//
|
||||
//private IRule getStickyRule() {
|
||||
// StickyRule stickyRule = new StickyRule(getRoundRobinRule());
|
||||
// stickyRule.setLoadBalancer(this);
|
||||
// return stickyRule;
|
||||
//}
|
||||
|
||||
}
|
||||
@@ -16,80 +16,99 @@
|
||||
|
||||
package org.springframework.cloud.zookeeper.discovery.dependency;
|
||||
|
||||
import feign.Client;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Response;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
|
||||
import org.springframework.cloud.loadbalancer.config.BlockingLoadBalancerClientAutoConfiguration;
|
||||
import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient;
|
||||
import org.springframework.cloud.openfeign.loadbalancer.FeignLoadBalancerAutoConfiguration;
|
||||
import org.springframework.cloud.zookeeper.ConditionalOnZookeeperEnabled;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
|
||||
/**
|
||||
* Configuration for ensuring that headers are set for a given dependency when Feign is
|
||||
* used.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnDependenciesPassed
|
||||
@ConditionalOnZookeeperEnabled
|
||||
@ConditionalOnProperty(value = "spring.cloud.zookeeper.dependency.headers.enabled", matchIfMissing = true)
|
||||
@ConditionalOnClass({ Client.class/*, LoadBalancerFeignClient.class*/ })
|
||||
/*@AutoConfigureAfter({ RibbonAutoConfiguration.class,
|
||||
FeignRibbonClientAutoConfiguration.class })*/
|
||||
@ConditionalOnClass({Client.class, FeignBlockingLoadBalancerClient.class})
|
||||
@ConditionalOnBean(BlockingLoadBalancerClient.class)
|
||||
@AutoConfigureAfter({FeignLoadBalancerAutoConfiguration.class, BlockingLoadBalancerClientAutoConfiguration.class})
|
||||
public class DependencyFeignClientAutoConfiguration {
|
||||
|
||||
// FIXME: 3.0.0
|
||||
//@Autowired(required = false)
|
||||
//private LoadBalancerFeignClient ribbonClient;
|
||||
//
|
||||
//@Autowired
|
||||
//private ZookeeperDependencies zookeeperDependencies;
|
||||
//
|
||||
//@Autowired
|
||||
//private CachingSpringLoadBalancerFactory loadBalancerFactory;
|
||||
//
|
||||
//@Autowired
|
||||
//private SpringClientFactory springClientFactory;
|
||||
//
|
||||
//@Bean
|
||||
//@Primary
|
||||
//Client dependencyBasedFeignClient() {
|
||||
// return new LoadBalancerFeignClient(new Client.Default(null, null),
|
||||
// this.loadBalancerFactory, this.springClientFactory) {
|
||||
//
|
||||
// @Override
|
||||
// public Response execute(Request request, Request.Options options)
|
||||
// throws IOException {
|
||||
// URI asUri = URI.create(request.url());
|
||||
// String clientName = asUri.getHost();
|
||||
// ZookeeperDependency dependencyForAlias = DependencyFeignClientAutoConfiguration.this.zookeeperDependencies
|
||||
// .getDependencyForAlias(clientName);
|
||||
// Map<String, Collection<String>> headers = getUpdatedHeadersIfPossible(
|
||||
// request, dependencyForAlias);
|
||||
// if (DependencyFeignClientAutoConfiguration.this.ribbonClient != null) {
|
||||
// return DependencyFeignClientAutoConfiguration.this.ribbonClient
|
||||
// .execute(request(request, headers), options);
|
||||
// }
|
||||
// return super.execute(request(request, headers), options);
|
||||
// }
|
||||
//
|
||||
// private Request request(Request request,
|
||||
// Map<String, Collection<String>> headers) {
|
||||
// return Request.create(request.method(), request.url(), headers,
|
||||
// request.body(), request.charset());
|
||||
// }
|
||||
//
|
||||
// private Map<String, Collection<String>> getUpdatedHeadersIfPossible(
|
||||
// Request request, ZookeeperDependency dependencyForAlias) {
|
||||
// if (dependencyForAlias != null) {
|
||||
// return Collections.unmodifiableMap(new HashMap<>(
|
||||
// dependencyForAlias.getUpdatedHeaders(request.headers())));
|
||||
// }
|
||||
// return request.headers();
|
||||
// }
|
||||
//
|
||||
// };
|
||||
//}
|
||||
private final FeignBlockingLoadBalancerClient feignLoadBalancerClient;
|
||||
|
||||
private final ZookeeperDependencies zookeeperDependencies;
|
||||
|
||||
private final BlockingLoadBalancerClient loadBalancerClient;
|
||||
|
||||
public DependencyFeignClientAutoConfiguration(@Autowired(required = false) FeignBlockingLoadBalancerClient feignLoadBalancerClient,
|
||||
ZookeeperDependencies zookeeperDependencies, BlockingLoadBalancerClient loadBalancerClient) {
|
||||
this.feignLoadBalancerClient = feignLoadBalancerClient;
|
||||
this.zookeeperDependencies = zookeeperDependencies;
|
||||
this.loadBalancerClient = loadBalancerClient;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
Client dependencyBasedFeignClient() {
|
||||
return new FeignBlockingLoadBalancerClient(new Client.Default(null, null),
|
||||
loadBalancerClient) {
|
||||
|
||||
@Override
|
||||
public Response execute(Request request, Request.Options options)
|
||||
throws IOException {
|
||||
URI asUri = URI.create(request.url());
|
||||
String clientName = asUri.getHost();
|
||||
ZookeeperDependency dependencyForAlias = DependencyFeignClientAutoConfiguration.this.zookeeperDependencies
|
||||
.getDependencyForAlias(clientName);
|
||||
Map<String, Collection<String>> headers = getUpdatedHeadersIfPossible(
|
||||
request, dependencyForAlias);
|
||||
if (DependencyFeignClientAutoConfiguration.this.feignLoadBalancerClient != null) {
|
||||
return DependencyFeignClientAutoConfiguration.this.feignLoadBalancerClient
|
||||
.execute(request(request, headers), options);
|
||||
}
|
||||
return super.execute(request(request, headers), options);
|
||||
}
|
||||
|
||||
private Request request(Request request,
|
||||
Map<String, Collection<String>> headers) {
|
||||
return Request.create(request.httpMethod(), request.url(), headers,
|
||||
request.body(), request.charset(), request.requestTemplate());
|
||||
}
|
||||
|
||||
private Map<String, Collection<String>> getUpdatedHeadersIfPossible(
|
||||
Request request, ZookeeperDependency dependencyForAlias) {
|
||||
if (dependencyForAlias != null) {
|
||||
return Collections.unmodifiableMap(new HashMap<>(
|
||||
dependencyForAlias.getUpdatedHeaders(request.headers())));
|
||||
}
|
||||
return request.headers();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,9 +26,11 @@ import java.util.Map;
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
|
||||
import org.springframework.cloud.zookeeper.ConditionalOnZookeeperEnabled;
|
||||
import org.springframework.cloud.zookeeper.discovery.ConditionalOnLoadBalancerForZookeeperEnabled;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpRequest;
|
||||
@@ -41,12 +43,15 @@ import org.springframework.web.client.RestTemplate;
|
||||
* Customizes RestTemplate to support passing of params from dependency.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnZookeeperEnabled
|
||||
@ConditionalOnLoadBalancerForZookeeperEnabled
|
||||
@ConditionalOnDependenciesPassed
|
||||
@ConditionalOnProperty(value = "spring.cloud.zookeeper.dependency.resttemplate.enabled", matchIfMissing = true)
|
||||
@ConditionalOnBean(RestTemplate.class)
|
||||
public class DependencyRestTemplateAutoConfiguration {
|
||||
|
||||
@Autowired
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015-2019 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.zookeeper.discovery.dependency;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
* @author Spencer Gibb
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public enum LoadBalancerType {
|
||||
|
||||
/**
|
||||
* Valid load balancer types.
|
||||
*/
|
||||
STICKY, RANDOM, ROUND_ROBIN
|
||||
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015-2019 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.zookeeper.discovery.dependency;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Load balancing rule that returns always the same instance.
|
||||
*
|
||||
* Ported from {@code org.apache.curator.x.discovery.strategies.StickyStrategy}
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class StickyRule /*extends AbstractLoadBalancerRule*/ {
|
||||
|
||||
private static final Log log = LogFactory.getLog(StickyRule.class);
|
||||
|
||||
//private final IRule masterStrategy;
|
||||
|
||||
//private final AtomicReference<Server> ourInstance = new AtomicReference<>(null);
|
||||
|
||||
private final AtomicInteger instanceNumber = new AtomicInteger(-1);
|
||||
|
||||
// FIXME: 3.0.0
|
||||
//public StickyRule(IRule masterStrategy) {
|
||||
// this.masterStrategy = masterStrategy;
|
||||
//}
|
||||
//
|
||||
//@Override
|
||||
//public void initWithNiwsConfig(IClientConfig iClientConfig) {
|
||||
//
|
||||
//}
|
||||
//
|
||||
//@Override
|
||||
//public Server choose(Object key) {
|
||||
// final List<Server> instances = getLoadBalancer().getServerList(true);
|
||||
// log.debug(String.format("Instances taken from load balancer [%s]", instances));
|
||||
// Server localOurInstance = this.ourInstance.get();
|
||||
// log.debug(String.format("Current saved instance [%s]", localOurInstance));
|
||||
// if (!instances.contains(localOurInstance)) {
|
||||
// this.ourInstance.compareAndSet(localOurInstance, null);
|
||||
// }
|
||||
// if (this.ourInstance.get() == null) {
|
||||
// Server instance = this.masterStrategy.choose(key);
|
||||
// if (this.ourInstance.compareAndSet(null, instance)) {
|
||||
// this.instanceNumber.incrementAndGet();
|
||||
// }
|
||||
// }
|
||||
// return this.ourInstance.get();
|
||||
//}
|
||||
|
||||
/**
|
||||
* Each time a new instance is picked, an internal counter is incremented. This way
|
||||
* you can track when/if the instance changes. The instance can change when the
|
||||
* selected instance is not in the current list of instances returned by the instance
|
||||
* provider
|
||||
* @return instance number
|
||||
*/
|
||||
public int getInstanceNumber() {
|
||||
return this.instanceNumber.get();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,7 +24,6 @@ import java.util.Map;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.zookeeper.discovery.dependency.StubsConfiguration.DependencyPath;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -35,6 +34,7 @@ import static org.springframework.cloud.zookeeper.discovery.DependencyPathUtils.
|
||||
* Representation of this service's dependencies in Zookeeper.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@ConfigurationProperties("spring.cloud.zookeeper")
|
||||
@@ -46,17 +46,11 @@ public class ZookeeperDependencies {
|
||||
private String prefix = "";
|
||||
|
||||
/**
|
||||
* Mapping of alias to ZookeeperDependency. From Ribbon perspective the alias is
|
||||
* actually serviceID since Ribbon can't accept nested structures in serviceID.
|
||||
* Mapping of alias to ZookeeperDependency. From LoadBalancer perspective the alias is
|
||||
* actually serviceID since SC LoadBalancer can't accept nested structures in serviceID.
|
||||
*/
|
||||
private Map<String, ZookeeperDependency> dependencies = new LinkedHashMap<>();
|
||||
|
||||
/**
|
||||
* Default health endpoint that will be checked to verify that a dependency is alive.
|
||||
*/
|
||||
@Value("${spring.cloud.zookeeper.dependency.ribbon.loadbalancer.defaulthealthendpoint:/health}")
|
||||
private String defaultHealthEndpoint;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
if (StringUtils.hasText(this.prefix)) {
|
||||
@@ -153,10 +147,6 @@ public class ZookeeperDependencies {
|
||||
return this.dependencies;
|
||||
}
|
||||
|
||||
public String getDefaultHealthEndpoint() {
|
||||
return this.defaultHealthEndpoint;
|
||||
}
|
||||
|
||||
public void setPrefix(String prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
@@ -165,17 +155,11 @@ public class ZookeeperDependencies {
|
||||
this.dependencies = dependencies;
|
||||
}
|
||||
|
||||
public void setDefaultHealthEndpoint(String defaultHealthEndpoint) {
|
||||
this.defaultHealthEndpoint = defaultHealthEndpoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuffer sb = new StringBuffer("ZookeeperDependencies{");
|
||||
sb.append("prefix='").append(this.prefix).append('\'');
|
||||
sb.append(", dependencies=").append(this.dependencies);
|
||||
sb.append(", defaultHealthEndpoint='").append(this.defaultHealthEndpoint)
|
||||
.append('\'');
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@@ -28,12 +28,12 @@ import org.springframework.context.annotation.Configuration;
|
||||
* Provides AutoConfiguration for Zookeeper dependency set up in properties.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties
|
||||
@ConditionalOnZookeeperEnabled
|
||||
@ConditionalOnDependenciesPassed
|
||||
@AutoConfigureAfter(ZookeeperAutoConfiguration.class)
|
||||
public class ZookeeperDependenciesAutoConfiguration {
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ import static java.util.Collections.singletonList;
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @author Spencer Gibb
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class ZookeeperDependency {
|
||||
@@ -43,11 +44,6 @@ public class ZookeeperDependency {
|
||||
*/
|
||||
private String path;
|
||||
|
||||
/**
|
||||
* Type of load balancer that should be used for this particular dependency.
|
||||
*/
|
||||
private LoadBalancerType loadBalancerType = LoadBalancerType.ROUND_ROBIN;
|
||||
|
||||
/**
|
||||
* Content type template with {@code $version} placeholder which will be filled by the
|
||||
* {@link ZookeeperDependency#version} variable.
|
||||
@@ -89,11 +85,9 @@ public class ZookeeperDependency {
|
||||
public ZookeeperDependency() {
|
||||
}
|
||||
|
||||
public ZookeeperDependency(String path, LoadBalancerType loadBalancerType,
|
||||
String contentTypeTemplate, String version,
|
||||
public ZookeeperDependency(String path, String contentTypeTemplate, String version,
|
||||
Map<String, Collection<String>> headers, boolean required, String stubs) {
|
||||
this.path = path;
|
||||
this.loadBalancerType = loadBalancerType;
|
||||
this.contentTypeTemplate = contentTypeTemplate;
|
||||
this.version = version;
|
||||
this.headers = headers;
|
||||
@@ -182,10 +176,6 @@ public class ZookeeperDependency {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
public LoadBalancerType getLoadBalancerType() {
|
||||
return this.loadBalancerType;
|
||||
}
|
||||
|
||||
public String getContentTypeTemplate() {
|
||||
return this.contentTypeTemplate;
|
||||
}
|
||||
@@ -214,10 +204,6 @@ public class ZookeeperDependency {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public void setLoadBalancerType(LoadBalancerType loadBalancerType) {
|
||||
this.loadBalancerType = loadBalancerType;
|
||||
}
|
||||
|
||||
public void setContentTypeTemplate(String contentTypeTemplate) {
|
||||
this.contentTypeTemplate = contentTypeTemplate;
|
||||
}
|
||||
@@ -246,7 +232,6 @@ public class ZookeeperDependency {
|
||||
public String toString() {
|
||||
final StringBuffer sb = new StringBuffer("ZookeeperDependency{");
|
||||
sb.append("path='").append(this.path).append('\'');
|
||||
sb.append(", loadBalancerType=").append(this.loadBalancerType);
|
||||
sb.append(", contentTypeTemplate='").append(this.contentTypeTemplate)
|
||||
.append('\'');
|
||||
sb.append(", version='").append(this.version).append('\'');
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Auto Configuration
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryAutoConfiguration,\
|
||||
org.springframework.cloud.zookeeper.discovery.dependency.DependencyFeignClientAutoConfiguration,\
|
||||
org.springframework.cloud.zookeeper.discovery.dependency.DependencyRestTemplateAutoConfiguration,\
|
||||
org.springframework.cloud.zookeeper.discovery.dependency.ZookeeperDependenciesAutoConfiguration,\
|
||||
org.springframework.cloud.zookeeper.discovery.watcher.DependencyWatcherAutoConfiguration,\
|
||||
@@ -8,8 +9,8 @@ org.springframework.cloud.zookeeper.serviceregistry.ZookeeperAutoServiceRegistra
|
||||
org.springframework.cloud.zookeeper.serviceregistry.ZookeeperServiceRegistryAutoConfiguration,\
|
||||
org.springframework.cloud.zookeeper.support.CuratorServiceDiscoveryAutoConfiguration,\
|
||||
org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryClientConfiguration, \
|
||||
org.springframework.cloud.zookeeper.discovery.LoadBalancerZookeeperAutoConfiguration, \
|
||||
org.springframework.cloud.zookeeper.discovery.reactive.ZookeeperReactiveDiscoveryClientConfiguration
|
||||
# org.springframework.cloud.zookeeper.discovery.dependency.DependencyFeignClientAutoConfiguration,\
|
||||
|
||||
# Environment Post Processors
|
||||
org.springframework.boot.env.EnvironmentPostProcessor=\
|
||||
|
||||
@@ -32,17 +32,15 @@ import org.springframework.cloud.client.discovery.composite.CompositeDiscoveryCl
|
||||
import org.springframework.cloud.zookeeper.discovery.test.CommonTestConfig;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = ZookeeperDiscoveryAutoRegistrationFalseTests.Config.class, properties = {
|
||||
@@ -85,15 +83,4 @@ public class ZookeeperDiscoveryAutoRegistrationFalseTests {
|
||||
|
||||
}
|
||||
|
||||
@Controller
|
||||
@Profile("ribbon")
|
||||
class PingController {
|
||||
|
||||
@RequestMapping("/ping")
|
||||
String ping() {
|
||||
return "pong";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ public class ZookeeperDiscoveryHealthIndicatorWithNestedStructureTests {
|
||||
}
|
||||
|
||||
@Bean
|
||||
TestLoadBalancedClient testRibbonClient(@LoadBalanced RestTemplate restTemplate,
|
||||
TestLoadBalancedClient testLoadBalancedClient(@LoadBalanced RestTemplate restTemplate,
|
||||
@Value("${spring.application.name}") String springAppName) {
|
||||
return new TestLoadBalancedClient(restTemplate, springAppName);
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
|
||||
"feign.hystrix.enabled=false",
|
||||
"spring.cloud.zookeeper.discovery.uriSpec={scheme}://{address}:{port}/contextPath",
|
||||
"spring.cloud.zookeeper.discovery.instance-ssl-port=8443" }, webEnvironment = RANDOM_PORT)
|
||||
@ActiveProfiles("ribbon")
|
||||
@ActiveProfiles("loadbalancer")
|
||||
@DirtiesContext
|
||||
public class ZookeeperDiscoverySecurePortTests {
|
||||
|
||||
@@ -73,7 +73,7 @@ public class ZookeeperDiscoverySecurePortTests {
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@Import(CommonTestConfig.class)
|
||||
@Profile("ribbon")
|
||||
@Profile("loadbalancer")
|
||||
static class Config {
|
||||
|
||||
}
|
||||
|
||||
@@ -59,10 +59,9 @@ import static org.springframework.cloud.zookeeper.discovery.test.TestLoadBalance
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = ZookeeperDiscoveryTests.Config.class, properties = {
|
||||
"feign.hystrix.enabled=false",
|
||||
"spring.cloud.zookeeper.discovery.uri-spec={scheme}://{address}:{port}/contextPath",
|
||||
"management.endpoints.web.exposure.include=*" }, webEnvironment = RANDOM_PORT)
|
||||
@ActiveProfiles("ribbon")
|
||||
@ActiveProfiles("loadbalancer")
|
||||
@DirtiesContext
|
||||
public class ZookeeperDiscoveryTests {
|
||||
|
||||
@@ -85,7 +84,7 @@ public class ZookeeperDiscoveryTests {
|
||||
Registration registration;
|
||||
|
||||
@Test
|
||||
public void should_find_the_app_by_its_name_via_Ribbon() {
|
||||
public void should_find_the_app_by_its_name_via_LoadBalancer() {
|
||||
// expect:
|
||||
then(registeredServiceStatusViaServiceName()).isEqualTo("UP");
|
||||
}
|
||||
@@ -98,12 +97,12 @@ public class ZookeeperDiscoveryTests {
|
||||
ServiceInstance instance = instances.get(0);
|
||||
// expect:
|
||||
then(registeredServiceStatus(instance)).isEqualTo("UP");
|
||||
then(instance.getInstanceId()).isEqualTo("ribbon-instance-id-123");
|
||||
then(instance.getInstanceId()).isEqualTo("loadbalancer-instance-id-123");
|
||||
then(instance.getMetadata().get("testMetadataKey"))
|
||||
.isEqualTo("testMetadataValue");
|
||||
then(instance).isInstanceOf(ZookeeperServiceInstance.class);
|
||||
ZookeeperServiceInstance zkInstance = (ZookeeperServiceInstance) instance;
|
||||
then(zkInstance.getServiceInstance().getId()).isEqualTo("ribbon-instance-id-123");
|
||||
then(zkInstance.getServiceInstance().getId()).isEqualTo("loadbalancer-instance-id-123");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -148,7 +147,7 @@ public class ZookeeperDiscoveryTests {
|
||||
.isEqualTo(this.registration.getHost());
|
||||
}
|
||||
|
||||
@FeignClient("ribbonApp")
|
||||
@FeignClient("loadBalancerApp")
|
||||
public interface IdUsingFeignClient {
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/hi")
|
||||
@@ -160,12 +159,12 @@ public class ZookeeperDiscoveryTests {
|
||||
@EnableAutoConfiguration
|
||||
@Import(CommonTestConfig.class)
|
||||
@EnableFeignClients(clients = { IdUsingFeignClient.class })
|
||||
@Profile("ribbon")
|
||||
@Profile("loadbalancer")
|
||||
@RestController
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
TestLoadBalancedClient testRibbonClient(@LoadBalanced RestTemplate restTemplate,
|
||||
TestLoadBalancedClient testLoadBalancedClient(@LoadBalanced RestTemplate restTemplate,
|
||||
@Value("${spring.application.name}") String springAppName) {
|
||||
return new TestLoadBalancedClient(restTemplate, springAppName);
|
||||
}
|
||||
@@ -178,7 +177,7 @@ public class ZookeeperDiscoveryTests {
|
||||
}
|
||||
|
||||
@Controller
|
||||
@Profile("ribbon")
|
||||
@Profile("loadbalancer")
|
||||
class PingController {
|
||||
|
||||
@RequestMapping("/ping")
|
||||
|
||||
@@ -64,8 +64,7 @@ public class ZookeeprDiscoveryNonWebAppTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_work_when_using_web_client_without_the_web_environment()
|
||||
throws Exception {
|
||||
public void should_work_when_using_web_client_without_the_web_environment() {
|
||||
SpringApplication producerApp = new SpringApplicationBuilder(HelloProducer.class)
|
||||
.web(WebApplicationType.SERVLET).build();
|
||||
SpringApplication clientApplication = new SpringApplicationBuilder(
|
||||
|
||||
@@ -45,7 +45,7 @@ import static org.assertj.core.api.BDDAssertions.then;
|
||||
public class DependencyConfig {
|
||||
|
||||
@Bean
|
||||
TestLoadBalancedClient testRibbonClient(@LoadBalanced RestTemplate restTemplate) {
|
||||
TestLoadBalancedClient testLoadBalancedClient(@LoadBalanced RestTemplate restTemplate) {
|
||||
return new TestLoadBalancedClient(restTemplate);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015-2019 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.zookeeper.discovery.dependency;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import com.jayway.awaitility.Awaitility;
|
||||
import org.apache.curator.framework.CuratorFramework;
|
||||
import org.apache.curator.test.TestingServer;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||
import org.springframework.cloud.zookeeper.ZookeeperProperties;
|
||||
import org.springframework.cloud.zookeeper.discovery.TestServiceRegistrar;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.util.SocketUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = StickyRuleTests.Config.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@ActiveProfiles("loadbalancerclient")
|
||||
public class StickyRuleTests {
|
||||
|
||||
@Autowired
|
||||
LoadBalancerClient loadBalancerClient;
|
||||
|
||||
@Autowired
|
||||
DiscoveryClient discoveryClient;
|
||||
|
||||
@Test
|
||||
@Ignore // FIXME: 3.0.0
|
||||
public void should_use_sticky_load_balancing_strategy_taken_from_Zookeeper_dependencies() {
|
||||
// given:
|
||||
System.setProperty(
|
||||
"spring.cloud.zookeeper.dependency.ribbon.loadbalancer.checkping",
|
||||
"false");
|
||||
// expect:
|
||||
thereAreTwoRegisteredServices();
|
||||
URI uri = getUriForAlias();
|
||||
Awaitility.await().until(uriMatchesTwice(uri));
|
||||
}
|
||||
|
||||
private Callable<Boolean> uriMatchesTwice(final URI uri) {
|
||||
return new Callable<Boolean>() {
|
||||
@Override
|
||||
public Boolean call() throws Exception {
|
||||
return uriMatches() && uriMatches();
|
||||
}
|
||||
|
||||
private boolean uriMatches() {
|
||||
return uri != null && uri.equals(getUriForAlias());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private boolean thereAreTwoRegisteredServices() {
|
||||
List<ServiceInstance> instances = this.discoveryClient.getInstances("someAlias");
|
||||
return instances != null && instances.size() == 2;
|
||||
}
|
||||
|
||||
private URI getUriForAlias() {
|
||||
ServiceInstance alias = this.loadBalancerClient.choose("someAlias");
|
||||
return alias != null ? alias.getUri() : null;
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@Profile("loadbalancerclient")
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
@LoadBalanced
|
||||
RestTemplate loadBalancedRestTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
|
||||
@Bean(destroyMethod = "close")
|
||||
TestingServer testingServer() throws Exception {
|
||||
return new TestingServer(SocketUtils.findAvailableTcpPort());
|
||||
}
|
||||
|
||||
@Bean
|
||||
ZookeeperProperties zookeeperProperties() throws Exception {
|
||||
ZookeeperProperties zookeeperProperties = new ZookeeperProperties();
|
||||
zookeeperProperties
|
||||
.setConnectString("localhost:" + testingServer().getPort());
|
||||
return zookeeperProperties;
|
||||
}
|
||||
|
||||
@Bean(initMethod = "start", destroyMethod = "stop")
|
||||
TestServiceRegistrar serviceOne(CuratorFramework curatorFramework) {
|
||||
return new TestServiceRegistrar(SocketUtils.findAvailableTcpPort(),
|
||||
curatorFramework);
|
||||
}
|
||||
|
||||
@Bean(initMethod = "start", destroyMethod = "stop")
|
||||
TestServiceRegistrar serviceTwo(CuratorFramework curatorFramework) {
|
||||
return new TestServiceRegistrar(SocketUtils.findAvailableTcpPort(),
|
||||
curatorFramework);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -32,14 +32,14 @@ import static org.assertj.core.api.BDDAssertions.then;
|
||||
public class ZookeeperDependenciesTests {
|
||||
|
||||
private static final ZookeeperDependency EXPECTED_DEPENDENCY = new ZookeeperDependency(
|
||||
"path", LoadBalancerType.RANDOM, "contentTypeTemplate", "version",
|
||||
"path", "contentTypeTemplate", "version",
|
||||
defaultHeader(), false, "");
|
||||
|
||||
private static final Map<String, ZookeeperDependency> DEPENDENCIES = defaultDependencies();
|
||||
|
||||
private static Map<String, Collection<String>> defaultHeader() {
|
||||
return Collections.singletonMap("header",
|
||||
(Collection<String>) Collections.singletonList("value"));
|
||||
Collections.singletonList("value"));
|
||||
}
|
||||
|
||||
private static Map<String, ZookeeperDependency> defaultDependencies() {
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.cloud.zookeeper.discovery.dependency;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
@@ -41,11 +40,12 @@ import static org.springframework.cloud.zookeeper.discovery.test.TestLoadBalance
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = ZookeeperDiscoveryWithDependenciesIntegrationTests.Config.class, properties = {
|
||||
"feign.hystrix.enabled=false", "debug=true",
|
||||
"management.endpoints.web.exposure.include=*" }, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
"debug=true",
|
||||
"management.endpoints.web.exposure.include=*"}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@ActiveProfiles("dependencies")
|
||||
public class ZookeeperDiscoveryWithDependenciesIntegrationTests {
|
||||
|
||||
@@ -69,12 +69,7 @@ public class ZookeeperDiscoveryWithDependenciesIntegrationTests {
|
||||
// given:
|
||||
final DiscoveryClient discoveryClient = this.discoveryClient;
|
||||
// expect:
|
||||
await().until(new Callable<Boolean>() {
|
||||
@Override
|
||||
public Boolean call() throws Exception {
|
||||
return !discoveryClient.getInstances("nameWithoutAlias").isEmpty();
|
||||
}
|
||||
});
|
||||
await().until(() -> !discoveryClient.getInstances("nameWithoutAlias").isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -94,12 +89,9 @@ public class ZookeeperDiscoveryWithDependenciesIntegrationTests {
|
||||
// given:
|
||||
final IdUsingFeignClient idUsingFeignClient = this.idUsingFeignClient;
|
||||
// expect:
|
||||
await().until(new Callable<Boolean>() {
|
||||
@Override
|
||||
public Boolean call() throws Exception {
|
||||
then(idUsingFeignClient.getBeans()).isNotEmpty();
|
||||
return true;
|
||||
}
|
||||
await().until(() -> {
|
||||
then(idUsingFeignClient.getBeans()).isNotEmpty();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -107,12 +99,7 @@ public class ZookeeperDiscoveryWithDependenciesIntegrationTests {
|
||||
@Test
|
||||
public void should_find_a_collaborator_via_load_balanced_rest_template_by_using_its_alias_from_dependencies() {
|
||||
// expect:
|
||||
await().until(new Callable<Boolean>() {
|
||||
@Override
|
||||
public Boolean call() throws Exception {
|
||||
return callingServiceAtBeansEndpointIsNotEmpty();
|
||||
}
|
||||
});
|
||||
await().until(() -> callingServiceAtBeansEndpointIsNotEmpty());
|
||||
}
|
||||
|
||||
@Ignore // FIXME 2.0.0
|
||||
@@ -121,24 +108,18 @@ public class ZookeeperDiscoveryWithDependenciesIntegrationTests {
|
||||
// given:
|
||||
final AliasUsingFeignClient aliasUsingFeignClient = this.aliasUsingFeignClient;
|
||||
// expect:
|
||||
await().until(new Callable<Boolean>() {
|
||||
@Override
|
||||
public Boolean call() throws Exception {
|
||||
then(aliasUsingFeignClient.getBeans()).isNotEmpty();
|
||||
return true;
|
||||
}
|
||||
await().until(() -> {
|
||||
then(aliasUsingFeignClient.getBeans()).isNotEmpty();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_have_headers_from_dependencies_attached_to_the_request_via_load_balanced_rest_template() {
|
||||
// expect:
|
||||
await().until(new Callable<Boolean>() {
|
||||
@Override
|
||||
public Boolean call() throws Exception {
|
||||
callingServiceToCheckIfHeadersArePassed();
|
||||
return true;
|
||||
}
|
||||
await().until(() -> {
|
||||
callingServiceToCheckIfHeadersArePassed();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -148,12 +129,9 @@ public class ZookeeperDiscoveryWithDependenciesIntegrationTests {
|
||||
// given:
|
||||
final AliasUsingFeignClient aliasUsingFeignClient = this.aliasUsingFeignClient;
|
||||
// expect:
|
||||
await().until(new Callable<Boolean>() {
|
||||
@Override
|
||||
public Boolean call() throws Exception {
|
||||
aliasUsingFeignClient.checkHeaders();
|
||||
return true;
|
||||
}
|
||||
await().until(() -> {
|
||||
aliasUsingFeignClient.checkHeaders();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -164,12 +142,7 @@ public class ZookeeperDiscoveryWithDependenciesIntegrationTests {
|
||||
List<ServiceInstance> instances = discoveryClient.getInstances("someAlias");
|
||||
final ServiceInstance instance = instances.get(0);
|
||||
// expect:
|
||||
await().until(new Callable<Boolean>() {
|
||||
@Override
|
||||
public Boolean call() throws Exception {
|
||||
return callingServiceViaUrlOnBeansEndpointIsNotEmpty(instance);
|
||||
}
|
||||
});
|
||||
await().until(() -> callingServiceViaUrlOnBeansEndpointIsNotEmpty(instance));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -50,7 +50,7 @@ public class ZookeeperDiscoveryWithDyingDependenciesTests {
|
||||
|
||||
// Issue: #45
|
||||
@Test
|
||||
public void should_refresh_a_dependency_in_Ribbon_when_the_dependency_has_deregistered_and_registered_in_Zookeeper()
|
||||
public void should_refresh_a_dependency_in_LoadBalancer_when_the_dependency_has_deregistered_and_registered_in_Zookeeper()
|
||||
throws Exception {
|
||||
ConfigurableApplicationContext serverContext = null;
|
||||
ConfigurableApplicationContext clientContext = null;
|
||||
|
||||
@@ -43,8 +43,7 @@ public class CommonTestConfig {
|
||||
}
|
||||
|
||||
@Bean
|
||||
ZookeeperProperties zookeeperProperties(TestingServer testingServer)
|
||||
throws Exception {
|
||||
ZookeeperProperties zookeeperProperties(TestingServer testingServer) {
|
||||
ZookeeperProperties zookeeperProperties = new ZookeeperProperties();
|
||||
zookeeperProperties.setConnectString("localhost:" + testingServer.getPort());
|
||||
return zookeeperProperties;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
spring:
|
||||
application:
|
||||
name: ribbonApp
|
||||
name: loadBalancerApp
|
||||
cloud:
|
||||
zookeeper:
|
||||
discovery:
|
||||
metadata:
|
||||
testMetadataKey: testMetadataValue
|
||||
uriSpec: "{scheme}://{address}:{port}/contextPath"
|
||||
instance-id: ribbon-instance-id-123
|
||||
instance-id: loadbalancer-instance-id-123
|
||||
management:
|
||||
security:
|
||||
enabled: false
|
||||
@@ -1,4 +1,4 @@
|
||||
testInstance.ribbon.ServerListRefreshInterval: 100
|
||||
testInstance.loadbalancer.ServerListRefreshInterval: 100
|
||||
logging:
|
||||
level:
|
||||
org.apache.zookeeper.ClientCnxn: ERROR
|
||||
Reference in New Issue
Block a user