Merge branch 'master' into 2.0.x
This commit is contained in:
@@ -688,6 +688,9 @@ turbine:
|
||||
appConfig: customers
|
||||
----
|
||||
|
||||
If you need to customize which cluster names should be used by Turbine (you don't want to store cluster names in
|
||||
`turbine.aggregator.clusterConfig` configuration) provide a bean of type `TurbineClustersProvider`.
|
||||
|
||||
The `clusterName` can be customized by a SPEL expression in `turbine.clusterNameExpression` with root an instance of `InstanceInfo`. The default value is `appName`, which means that the Eureka serviceId ends up as the cluster key (i.e. the `InstanceInfo` for customers has an `appName` of "CUSTOMERS"). A different example would be `turbine.clusterNameExpression=aSGName`, which would get the cluster name from the AWS ASG name. Another example:
|
||||
|
||||
----
|
||||
@@ -830,6 +833,7 @@ To set the `IRule` for a service name `users` you could set the following:
|
||||
----
|
||||
users:
|
||||
ribbon:
|
||||
NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
|
||||
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule
|
||||
----
|
||||
|
||||
@@ -1497,9 +1501,39 @@ The location of the backend can be specified as either a "serviceId"
|
||||
url: http://example.com/users_service
|
||||
----
|
||||
|
||||
These simple url-routes don't get executed as a `HystrixCommand` nor can you loadbalance multiple URLs with Ribbon.
|
||||
To achieve this, specify a service-route and configure a Ribbon client for the
|
||||
serviceId (this currently requires disabling Eureka support in Ribbon:
|
||||
These simple url-routes don't get executed as a `HystrixCommand` nor do they loadbalance multiple URLs with Ribbon.
|
||||
To achieve this, you can specify a `serviceId` with a static list of servers:
|
||||
|
||||
.application.yml
|
||||
[source,yaml]
|
||||
----
|
||||
zuul:
|
||||
routes:
|
||||
echo:
|
||||
path: /myusers/**
|
||||
serviceId: myusers-service
|
||||
stripPrefix: true
|
||||
|
||||
hystrix:
|
||||
command:
|
||||
myusers-service:
|
||||
execution:
|
||||
isolation:
|
||||
thread:
|
||||
timeoutInMilliseconds: ...
|
||||
|
||||
myusers-service:
|
||||
ribbon:
|
||||
NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
|
||||
ListOfServers: http://example1.com,http://example2.com
|
||||
ConnectTimeout: 1000
|
||||
ReadTimeout: 3000
|
||||
MaxTotalHttpConnections: 500
|
||||
MaxConnectionsPerHost: 100
|
||||
----
|
||||
|
||||
Another method is specifiying a service-route and configure a Ribbon client for the
|
||||
serviceId (this requires disabling Eureka support in Ribbon:
|
||||
see <<spring-cloud-ribbon-without-eureka,above for more information>>), e.g.
|
||||
|
||||
.application.yml
|
||||
@@ -1697,11 +1731,17 @@ Security. The assumption in this case is that the downstream services
|
||||
might add these headers too, and we want the values from the proxy.
|
||||
To not discard these well known security headers in case Spring Security is on the classpath you can set `zuul.ignoreSecurityHeaders` to `false`. This can be useful if you disabled the HTTP Security response headers in Spring Security and want the values provided by downstream services
|
||||
|
||||
=== The Routes Endpoint
|
||||
=== Management Endpoints
|
||||
|
||||
If you are using `@EnableZuulProxy` with tha Spring Boot Actuator you
|
||||
will enable (by default) an additional endpoint, available via HTTP as
|
||||
`/routes`. A GET to this endpoint will return a list of the mapped
|
||||
If you are using `@EnableZuulProxy` with the Spring Boot Actuator you
|
||||
will enable (by default) two additional endpoints:
|
||||
|
||||
* Routes
|
||||
* Filters
|
||||
|
||||
==== Routes Endpoint
|
||||
|
||||
A GET to the routes endpoint at `/routes` will return a list of the mapped
|
||||
routes:
|
||||
|
||||
.GET /routes
|
||||
@@ -1740,6 +1780,12 @@ NOTE: the routes should respond automatically to changes in the
|
||||
service catalog, but the POST to /routes is a way to force the change
|
||||
to happen immediately.
|
||||
|
||||
==== Filters Endpoint
|
||||
|
||||
A GET to the filters endpoint at `/filters` will return a map of Zuul
|
||||
filters by type. For each filter type in the map, you will find a list
|
||||
of all the filters of that type, along with their details.
|
||||
|
||||
=== Strangulation Patterns and Local Forwards
|
||||
|
||||
A common pattern when migrating an existing application or API is to
|
||||
|
||||
4
pom.xml
4
pom.xml
@@ -28,7 +28,7 @@
|
||||
<spring-cloud-config.version>2.0.0.BUILD-SNAPSHOT</spring-cloud-config.version>
|
||||
<spring-cloud-stream.version>Elmhurst.BUILD-SNAPSHOT</spring-cloud-stream.version>
|
||||
<!-- Has to be a stable version (not one that depends on this version of netflix): -->
|
||||
<spring-cloud-contract.version>1.1.2.RELEASE</spring-cloud-contract.version>
|
||||
<donotreplacespring-cloud-contract.version>1.1.2.RELEASE</donotreplacespring-cloud-contract.version>
|
||||
|
||||
<!-- Plugin versions -->
|
||||
<maven-compiler-plugin.version>3.6.1</maven-compiler-plugin.version>
|
||||
@@ -115,7 +115,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-contract-dependencies</artifactId>
|
||||
<version>${spring-cloud-contract.version}</version>
|
||||
<version>${donotreplacespring-cloud-contract.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package org.springframework.cloud.netflix.zuul;
|
||||
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import com.netflix.zuul.filters.FilterRegistry;
|
||||
import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
|
||||
import org.springframework.jmx.export.annotation.ManagedAttribute;
|
||||
import org.springframework.jmx.export.annotation.ManagedResource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* Endpoint for listing Zuul filters.
|
||||
*
|
||||
* @author Daryl Robbins
|
||||
* @author Gregor Zurowski
|
||||
*/
|
||||
@ManagedResource(description = "List Zuul filters")
|
||||
public class FiltersEndpoint extends AbstractEndpoint<Map<String, List<Map<String, Object>>>> {
|
||||
|
||||
private static final String ID = "filters";
|
||||
|
||||
private final FilterRegistry filterRegistry;
|
||||
|
||||
public FiltersEndpoint(FilterRegistry filterRegistry) {
|
||||
super(ID, true);
|
||||
this.filterRegistry = filterRegistry;
|
||||
}
|
||||
|
||||
@ManagedAttribute
|
||||
@Override
|
||||
public Map<String, List<Map<String, Object>>> invoke() {
|
||||
// Map of filters by type
|
||||
final Map<String, List<Map<String, Object>>> filterMap = new TreeMap<>();
|
||||
|
||||
for (ZuulFilter filter : this.filterRegistry.getAllFilters()) {
|
||||
// Ensure that we have a list to store filters of each type
|
||||
if (!filterMap.containsKey(filter.filterType())) {
|
||||
filterMap.put(filter.filterType(), new ArrayList<Map<String, Object>>());
|
||||
}
|
||||
|
||||
final Map<String, Object> filterInfo = new LinkedHashMap<>();
|
||||
filterInfo.put("class", filter.getClass().getName());
|
||||
filterInfo.put("order", filter.filterOrder());
|
||||
filterInfo.put("disabled", filter.isFilterDisabled());
|
||||
filterInfo.put("static", filter.isStaticFilter());
|
||||
|
||||
filterMap.get(filter.filterType()).add(filterInfo);
|
||||
}
|
||||
|
||||
return filterMap;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,9 +17,6 @@
|
||||
|
||||
package org.springframework.cloud.netflix.ribbon;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -27,6 +24,7 @@ import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoCon
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfigurationIntegrationTests.TestConfiguration;
|
||||
import org.springframework.cloud.netflix.test.TestUtils;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
@@ -35,6 +33,8 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@@ -48,6 +48,7 @@ public class RibbonAutoConfigurationIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void serverListIsConfigured() throws Exception {
|
||||
TestUtils.assumeTestIgnored(RibbonAutoConfigurationIntegrationTests.class);
|
||||
IClientConfig config = this.factory.getClientConfig("client");
|
||||
assertEquals(25000,
|
||||
config.getPropertyAsInteger(CommonClientConfigKey.ConnectTimeout, 3000));
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2013-2017 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
|
||||
*
|
||||
* http://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.netflix.test;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assume.assumeThat;
|
||||
|
||||
public class TestUtils {
|
||||
public static void assumeTestIgnored(Class clazz) {
|
||||
assumeTestIgnored(clazz.getSimpleName());
|
||||
}
|
||||
|
||||
public static void assumeTestIgnored(String name) {
|
||||
assumeThat("Test ignored",
|
||||
System.getenv("SPRING_CLOUD_NETFLIX_IGNORE_TESTS"),
|
||||
not(containsString(name)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package org.springframework.cloud.netflix.zuul;
|
||||
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
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.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hibernate.validator.internal.util.Contracts.assertTrue;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Tests for Filters endpoint
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = FiltersEndpointApplication.class,
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
value = { "server.contextPath: /app" })
|
||||
public class FiltersEndpointTests {
|
||||
|
||||
@Autowired
|
||||
private FiltersEndpoint endpoint;
|
||||
|
||||
@Test
|
||||
public void getFilters() {
|
||||
final Map<String, List<Map<String, Object>>> filters = endpoint.invoke();
|
||||
|
||||
boolean foundFilter = false;
|
||||
|
||||
if (filters.containsKey("sample")) {
|
||||
for (Map<String, Object> filterInfo : filters.get("sample")) {
|
||||
if (TestFilter.class.getName().equals(filterInfo.get("class"))) {
|
||||
foundFilter = true;
|
||||
|
||||
// Verify filter's attributes
|
||||
assertEquals(0, filterInfo.get("order"));
|
||||
|
||||
break; // the search is over
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue(foundFilter, "Could not find expected sample filter from filters endpoint");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@RestController
|
||||
@EnableZuulProxy
|
||||
class FiltersEndpointApplication {
|
||||
|
||||
@Bean
|
||||
public ZuulFilter sampleFilter() {
|
||||
return new TestFilter();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TestFilter extends ZuulFilter {
|
||||
@Override
|
||||
public String filterType() {
|
||||
return "sample";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldFilter() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object run() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int filterOrder() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -24,5 +24,6 @@ import com.netflix.appinfo.InstanceInfo;
|
||||
*/
|
||||
public interface CloudEurekaInstanceConfig extends EurekaInstanceConfig {
|
||||
void setNonSecurePort(int port);
|
||||
void setSecurePort(int securePort);
|
||||
InstanceInfo.InstanceStatus getInitialStatus();
|
||||
}
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
|
||||
package org.springframework.cloud.netflix.eureka;
|
||||
|
||||
import static org.springframework.cloud.commons.util.IdUtils.getDefaultInstanceId;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
@@ -62,7 +60,6 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.PropertyResolver;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.netflix.appinfo.ApplicationInfoManager;
|
||||
@@ -73,6 +70,8 @@ import com.netflix.discovery.AbstractDiscoveryClientOptionalArgs;
|
||||
import com.netflix.discovery.EurekaClient;
|
||||
import com.netflix.discovery.EurekaClientConfig;
|
||||
|
||||
import static org.springframework.cloud.commons.util.IdUtils.getDefaultInstanceId;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Spencer Gibb
|
||||
@@ -130,13 +129,21 @@ public class EurekaClientAutoConfiguration {
|
||||
public EurekaInstanceConfigBean eurekaInstanceConfigBean(InetUtils inetUtils) {
|
||||
String hostname = getProperty("eureka.instance.hostname");
|
||||
boolean preferIpAddress = Boolean.parseBoolean(getProperty("eureka.instance.prefer-ip-address"));
|
||||
boolean isSecurePortEnabled = Boolean.parseBoolean(getProperty("eureka.instance.secure-port-enabled"));
|
||||
int nonSecurePort = Integer.valueOf(env.getProperty("server.port", env.getProperty("port", "8080")));
|
||||
|
||||
int managementPort = Integer.valueOf(env.getProperty("management.port", String.valueOf(nonSecurePort)));
|
||||
String managementContextPath = env.getProperty("management.context-path", env.getProperty("server.servlet.context-path", "/"));
|
||||
EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean(inetUtils);
|
||||
instance.setNonSecurePort(nonSecurePort);
|
||||
instance.setInstanceId(getDefaultInstanceId(env));
|
||||
instance.setPreferIpAddress(preferIpAddress);
|
||||
|
||||
if(isSecurePortEnabled) {
|
||||
int securePort = Integer.valueOf(env.getProperty("server.port", env.getProperty("port", "8080")));
|
||||
instance.setSecurePort(securePort);
|
||||
}
|
||||
|
||||
if (managementPort != nonSecurePort && managementPort != 0) {
|
||||
if (StringUtils.hasText(hostname)) {
|
||||
instance.setHostname(hostname);
|
||||
@@ -152,6 +159,7 @@ public class EurekaClientAutoConfiguration {
|
||||
if (StringUtils.hasText(healthCheckUrlPath)) {
|
||||
instance.setHealthCheckUrlPath(healthCheckUrlPath);
|
||||
}
|
||||
|
||||
String scheme = instance.getSecurePortEnabled() ? "https" : "http";
|
||||
try {
|
||||
URL base = new URL(scheme, instance.getHostname(), managementPort, managementContextPath);
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.springframework.core.Ordered;
|
||||
* @author Spencer Gibb
|
||||
* @author Jon Schneider
|
||||
* @author Jakub Narloch
|
||||
* @author raiyan
|
||||
*/
|
||||
public class EurekaAutoServiceRegistration implements AutoServiceRegistration, SmartLifecycle, Ordered {
|
||||
|
||||
@@ -61,9 +62,15 @@ public class EurekaAutoServiceRegistration implements AutoServiceRegistration, S
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
// only set the port if the nonSecurePort is 0 and this.port != 0
|
||||
if (this.port.get() != 0 && this.registration.getNonSecurePort() == 0) {
|
||||
this.registration.setNonSecurePort(this.port.get());
|
||||
// only set the port if the nonSecurePort or securePort is 0 and this.port != 0
|
||||
if (this.port.get() != 0) {
|
||||
if (this.registration.getNonSecurePort() == 0) {
|
||||
this.registration.setNonSecurePort(this.port.get());
|
||||
}
|
||||
|
||||
if (this.registration.getSecurePort() == 0 && this.registration.isSecure()) {
|
||||
this.registration.setSecurePort(this.port.get());
|
||||
}
|
||||
}
|
||||
|
||||
// only initialize if nonSecurePort is greater than 0 and it isn't already running
|
||||
|
||||
@@ -193,6 +193,14 @@ public class EurekaRegistration implements Registration, Closeable {
|
||||
return this.instanceConfig.getNonSecurePort();
|
||||
}
|
||||
|
||||
public void setSecurePort(int port) {
|
||||
this.instanceConfig.setSecurePort(port);
|
||||
}
|
||||
|
||||
public int getSecurePort() {
|
||||
return this.instanceConfig.getSecurePort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
this.eurekaClient.shutdown();
|
||||
|
||||
@@ -98,6 +98,23 @@ public class EurekaClientAutoConfigurationTests {
|
||||
this.context.getBeanDefinition("eurekaClient").getFactoryMethodName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void securePortPeriods() {
|
||||
testSecurePort("server.port");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void securePortUnderscores() {
|
||||
testSecurePort("SERVER_PORT");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void securePort() {
|
||||
testSecurePort("PORT");
|
||||
assertEquals("eurekaClient",
|
||||
this.context.getBeanDefinition("eurekaClient").getFactoryMethodName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void managementPort() {
|
||||
TestPropertyValues.of("server.port=8989",
|
||||
@@ -378,6 +395,13 @@ public class EurekaClientAutoConfigurationTests {
|
||||
assertEquals(8888, getInstanceConfig().getNonSecurePort());
|
||||
}
|
||||
|
||||
private void testSecurePort(String propName) {
|
||||
EnvironmentTestUtils.addEnvironment(this.context, "eureka.instance.securePortEnabled=true");
|
||||
addEnvironment(this.context, propName + ":8443");
|
||||
setupContext();
|
||||
assertEquals(8443, getInstanceConfig().getSecurePort());
|
||||
}
|
||||
|
||||
private EurekaInstanceConfigBean getInstanceConfig() {
|
||||
return this.context.getBean(EurekaInstanceConfigBean.class);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<description>Spring Cloud Netflix Hystrix Contract</description>
|
||||
<properties>
|
||||
<main.basedir>${basedir}/..</main.basedir>
|
||||
<spring-cloud-contract.version>1.1.2.RELEASE</spring-cloud-contract.version>
|
||||
<donotreplacespring-cloud-contract.version>1.1.2.RELEASE</donotreplacespring-cloud-contract.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
@@ -30,7 +30,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-contract-verifier</artifactId>
|
||||
<version>${spring-cloud-contract.version}</version>
|
||||
<version>${donotreplacespring-cloud-contract.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
<plugin>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
|
||||
<version>${spring-cloud-contract.version}</version>
|
||||
<version>${donotreplacespring-cloud-contract.version}</version>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<baseClassMappings>
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2013-2017 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
|
||||
*
|
||||
* http://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.netflix.turbine;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Provides clusters names for Turbine based on configuration value.
|
||||
*
|
||||
* @author Anastasiia Smirnova
|
||||
*/
|
||||
public class ConfigurationBasedTurbineClustersProvider implements TurbineClustersProvider {
|
||||
|
||||
private static final Log log = LogFactory.getLog(ConfigurationBasedTurbineClustersProvider.class);
|
||||
private final TurbineAggregatorProperties properties;
|
||||
|
||||
public ConfigurationBasedTurbineClustersProvider(TurbineAggregatorProperties turbineAggregatorProperties) {
|
||||
this.properties = turbineAggregatorProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getClusterNames() {
|
||||
List<String> clusterNames = properties.getClusterConfig();
|
||||
log.trace("Using clusters names: " + clusterNames);
|
||||
return clusterNames;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2013-2017 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
|
||||
*
|
||||
* http://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.netflix.turbine;
|
||||
|
||||
import com.netflix.discovery.EurekaClient;
|
||||
import com.netflix.discovery.shared.Application;
|
||||
import com.netflix.discovery.shared.Applications;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Provides clusters names for Turbine based on applications names registered in Eureka.
|
||||
*
|
||||
* @author Anastasiia Smirnova
|
||||
*/
|
||||
public class EurekaBasedTurbineClustersProvider implements TurbineClustersProvider {
|
||||
|
||||
private static final Log log = LogFactory.getLog(EurekaBasedTurbineClustersProvider.class);
|
||||
private final EurekaClient eurekaClient;
|
||||
|
||||
public EurekaBasedTurbineClustersProvider(EurekaClient eurekaClient) {
|
||||
this.eurekaClient = eurekaClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getClusterNames() {
|
||||
Applications applications = eurekaClient.getApplications();
|
||||
List<Application> registeredApplications = applications.getRegisteredApplications();
|
||||
List<String> appNames = new ArrayList<>(registeredApplications.size());
|
||||
for (Application application : registeredApplications) {
|
||||
appNames.add(application.getName());
|
||||
}
|
||||
log.trace("Using clusters names: " + appNames);
|
||||
return appNames;
|
||||
}
|
||||
}
|
||||
@@ -16,12 +16,8 @@
|
||||
|
||||
package org.springframework.cloud.netflix.turbine;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import com.netflix.config.DynamicPropertyFactory;
|
||||
import com.netflix.config.DynamicStringProperty;
|
||||
import com.netflix.turbine.data.AggDataFromCluster;
|
||||
import com.netflix.turbine.discovery.Instance;
|
||||
import com.netflix.turbine.handler.PerformanceCriteria;
|
||||
@@ -43,8 +39,11 @@ public class SpringAggregatorFactory implements ClusterMonitorFactory<AggDataFro
|
||||
|
||||
private static final Log log = LogFactory.getLog(SpringAggregatorFactory.class);
|
||||
|
||||
private static final DynamicStringProperty aggClusters = DynamicPropertyFactory
|
||||
.getInstance().getStringProperty("turbine.aggregator.clusterConfig", null);
|
||||
private final TurbineClustersProvider clustersProvider;
|
||||
|
||||
public SpringAggregatorFactory(TurbineClustersProvider clustersProvider) {
|
||||
this.clustersProvider = clustersProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link com.netflix.turbine.monitor.cluster.ClusterMonitor}<
|
||||
@@ -73,7 +72,7 @@ public class SpringAggregatorFactory implements ClusterMonitorFactory<AggDataFro
|
||||
|
||||
@Override
|
||||
public void initClusterMonitors() {
|
||||
for (String clusterName : getClusterNames()) {
|
||||
for (String clusterName : clustersProvider.getClusterNames()) {
|
||||
ClusterMonitor<AggDataFromCluster> clusterMonitor = (ClusterMonitor<AggDataFromCluster>) findOrRegisterAggregateMonitor(clusterName);
|
||||
clusterMonitor.registerListenertoClusterMonitor(this.StaticListener);
|
||||
try {
|
||||
@@ -87,27 +86,12 @@ public class SpringAggregatorFactory implements ClusterMonitorFactory<AggDataFro
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> getClusterNames() {
|
||||
List<String> clusters = new ArrayList<String>();
|
||||
String clusterNames = aggClusters.get();
|
||||
if (clusterNames == null || clusterNames.trim().length() == 0) {
|
||||
clusters.add("default");
|
||||
}
|
||||
else {
|
||||
String[] parts = aggClusters.get().split(",");
|
||||
for (String s : parts) {
|
||||
clusters.add(s);
|
||||
}
|
||||
}
|
||||
return clusters;
|
||||
}
|
||||
|
||||
/**
|
||||
* shutdown all configured cluster monitors
|
||||
*/
|
||||
@Override
|
||||
public void shutdownClusterMonitors() {
|
||||
for (String clusterName : getClusterNames()) {
|
||||
for (String clusterName : clustersProvider.getClusterNames()) {
|
||||
ClusterMonitor<AggDataFromCluster> clusterMonitor = (ClusterMonitor<AggDataFromCluster>) AggregateClusterMonitor
|
||||
.findOrRegisterAggregateMonitor(clusterName);
|
||||
clusterMonitor.stopMonitor();
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2013-2017 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
|
||||
*
|
||||
* http://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.netflix.turbine;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author Anastasiia Smirnova
|
||||
*/
|
||||
@ConfigurationProperties("turbine.aggregator")
|
||||
public class TurbineAggregatorProperties {
|
||||
|
||||
private static final String DEFAULT = "default";
|
||||
/**
|
||||
* The list of cluster names.
|
||||
*/
|
||||
private List<String> clusterConfig = Collections.singletonList(DEFAULT);
|
||||
|
||||
public List<String> getClusterConfig() {
|
||||
return clusterConfig;
|
||||
}
|
||||
|
||||
public void setClusterConfig(List<String> clusterConfig) {
|
||||
this.clusterConfig = clusterConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
TurbineAggregatorProperties that = (TurbineAggregatorProperties) o;
|
||||
return Objects.equals(clusterConfig, that.clusterConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(clusterConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TurbineAggregatorProperties{" + "clusterConfig='" + clusterConfig + '\''
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2013-2017 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
|
||||
*
|
||||
* http://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.netflix.turbine;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Interface that gives possibility to customize which clusters names Turbine will use.
|
||||
*
|
||||
* @author Anastasiia Smirnova
|
||||
*/
|
||||
public interface TurbineClustersProvider {
|
||||
|
||||
List<String> getClusterNames();
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.cloud.netflix.turbine;
|
||||
|
||||
import com.netflix.turbine.monitor.cluster.ClusterMonitorFactory;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||
@@ -43,18 +44,40 @@ public class TurbineHttpConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "turbineStreamServlet")
|
||||
public ServletRegistrationBean turbineStreamServlet() {
|
||||
return new ServletRegistrationBean(new TurbineStreamServlet(), "/turbine.stream");
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public TurbineProperties turbineProperties() {
|
||||
return new TurbineProperties();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TurbineLifecycle turbineLifecycle(InstanceDiscovery instanceDiscovery) {
|
||||
return new TurbineLifecycle(instanceDiscovery);
|
||||
@ConditionalOnMissingBean
|
||||
public TurbineAggregatorProperties turbineAggregatorProperties() {
|
||||
return new TurbineAggregatorProperties();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public TurbineLifecycle turbineLifecycle(InstanceDiscovery instanceDiscovery,
|
||||
ClusterMonitorFactory<?> factory) {
|
||||
return new TurbineLifecycle(instanceDiscovery, factory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ClusterMonitorFactory clusterMonitorFactory(TurbineClustersProvider clustersProvider) {
|
||||
return new SpringAggregatorFactory(clustersProvider);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public TurbineClustersProvider clustersProvider(TurbineAggregatorProperties turbineAggregatorProperties) {
|
||||
return new ConfigurationBasedTurbineClustersProvider(turbineAggregatorProperties);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.cloud.netflix.turbine;
|
||||
|
||||
import com.netflix.turbine.monitor.cluster.ClusterMonitorFactory;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
@@ -29,11 +30,13 @@ import com.netflix.turbine.plugins.PluginsFactory;
|
||||
public class TurbineLifecycle implements SmartLifecycle, Ordered {
|
||||
|
||||
private final InstanceDiscovery instanceDiscovery;
|
||||
private final ClusterMonitorFactory<?> factory;
|
||||
|
||||
private boolean running;
|
||||
private volatile boolean running;
|
||||
|
||||
public TurbineLifecycle(InstanceDiscovery instanceDiscovery) {
|
||||
public TurbineLifecycle(InstanceDiscovery instanceDiscovery, ClusterMonitorFactory<?> factory) {
|
||||
this.instanceDiscovery = instanceDiscovery;
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -48,7 +51,7 @@ public class TurbineLifecycle implements SmartLifecycle, Ordered {
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
PluginsFactory.setClusterMonitorFactory(new SpringAggregatorFactory());
|
||||
PluginsFactory.setClusterMonitorFactory(factory);
|
||||
PluginsFactory.setInstanceDiscovery(instanceDiscovery);
|
||||
TurbineInit.init();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.springframework.cloud.netflix.turbine;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class ConfigurationBasedTurbineClustersProviderTest {
|
||||
|
||||
@Test
|
||||
public void shouldReturnDefaultClusterIfConfigurationIsEmpty() throws Exception {
|
||||
TurbineAggregatorProperties properties = new TurbineAggregatorProperties();
|
||||
TurbineClustersProvider provider = new ConfigurationBasedTurbineClustersProvider(
|
||||
properties);
|
||||
List<String> clusterNames = provider.getClusterNames();
|
||||
|
||||
assertThat(clusterNames).containsOnly("default");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnConfiguredClusters() throws Exception {
|
||||
TurbineAggregatorProperties properties = new TurbineAggregatorProperties();
|
||||
properties.setClusterConfig(Arrays.asList("cluster1", "cluster2", "cluster3"));
|
||||
TurbineClustersProvider provider = new ConfigurationBasedTurbineClustersProvider(
|
||||
properties);
|
||||
List<String> clusterNames = provider.getClusterNames();
|
||||
|
||||
assertThat(clusterNames).containsOnly("cluster1", "cluster2", "cluster3");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package org.springframework.cloud.netflix.turbine;
|
||||
|
||||
import com.netflix.discovery.EurekaClient;
|
||||
import com.netflix.discovery.shared.Application;
|
||||
import com.netflix.discovery.shared.Applications;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class EurekaBasedTurbineClustersProviderTest {
|
||||
|
||||
EurekaClient eurekaClient = mock(EurekaClient.class);
|
||||
TurbineClustersProvider provider = new EurekaBasedTurbineClustersProvider(eurekaClient);
|
||||
|
||||
@Test
|
||||
public void shouldProvideAllClustersNames() throws Exception {
|
||||
Applications applications = registeredApplications(asList(application("service1"),
|
||||
application("service2"), application("service3")));
|
||||
when(eurekaClient.getApplications()).thenReturn(applications);
|
||||
|
||||
List<String> clusterNames = provider.getClusterNames();
|
||||
|
||||
assertThat(clusterNames).containsOnly("service1", "service2", "service3");
|
||||
}
|
||||
|
||||
private Applications registeredApplications(List<Application> registered) {
|
||||
Applications applications = mock(Applications.class);
|
||||
when(applications.getRegisteredApplications()).thenReturn(registered);
|
||||
return applications;
|
||||
}
|
||||
|
||||
private Application application(String name) {
|
||||
Application application = mock(Application.class);
|
||||
when(application.getName()).thenReturn(name);
|
||||
return application;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package org.springframework.cloud.netflix.turbine;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.boot.test.util.EnvironmentTestUtils.addEnvironment;
|
||||
|
||||
public class TurbineAggregatorPropertiesTest {
|
||||
|
||||
private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
|
||||
@After
|
||||
public void clear() {
|
||||
if (this.context != null) {
|
||||
this.context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHaveDefaultConfiguration() throws Exception {
|
||||
setupContext();
|
||||
|
||||
TurbineAggregatorProperties actual = getProperties();
|
||||
assertThat(actual.getClusterConfig()).containsOnly("default");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldLoadCustomProperties() {
|
||||
addEnvironment(this.context,
|
||||
"turbine.aggregator.clusterConfig=cluster1, cluster2, cluster3");
|
||||
setupContext();
|
||||
|
||||
TurbineAggregatorProperties actual = getProperties();
|
||||
assertThat(actual.getClusterConfig()).containsOnly("cluster1", "cluster2",
|
||||
"cluster3");
|
||||
}
|
||||
|
||||
private void setupContext() {
|
||||
this.context.register(TestConfiguration.class);
|
||||
this.context.refresh();
|
||||
}
|
||||
|
||||
private TurbineAggregatorProperties getProperties() {
|
||||
return this.context.getBean(TurbineAggregatorProperties.class);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(TurbineAggregatorProperties.class)
|
||||
static class TestConfiguration {
|
||||
}
|
||||
}
|
||||
@@ -57,6 +57,8 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import com.netflix.zuul.filters.FilterRegistry;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Dave Syer
|
||||
@@ -158,17 +160,24 @@ public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration {
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(Health.class)
|
||||
protected static class RoutesEndpointConfiguration {
|
||||
protected static class EndpointConfiguration {
|
||||
|
||||
@Autowired(required = false)
|
||||
private TraceRepository traces;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
public RoutesEndpoint zuulEndpoint(RouteLocator routeLocator) {
|
||||
public RoutesEndpoint routesEndpoint(RouteLocator routeLocator) {
|
||||
return new RoutesEndpoint(routeLocator);
|
||||
}
|
||||
|
||||
@ConditionalOnEnabledEndpoint
|
||||
@Bean
|
||||
public FiltersEndpoint filtersEndpoint() {
|
||||
FilterRegistry filterRegistry = FilterRegistry.instance();
|
||||
return new FiltersEndpoint(filterRegistry);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ProxyRequestHelper proxyRequestHelper(ZuulProperties zuulProperties) {
|
||||
TraceProxyRequestHelper helper = new TraceProxyRequestHelper();
|
||||
|
||||
@@ -31,6 +31,9 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.netflix.zuul.RoutesEndpoint;
|
||||
import org.springframework.cloud.netflix.zuul.RoutesRefreshedEvent;
|
||||
import org.springframework.cloud.netflix.zuul.RoutesMvcEndpoint;
|
||||
import org.springframework.cloud.netflix.zuul.filters.Route;
|
||||
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
|
||||
@@ -25,6 +25,9 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
|
||||
import org.springframework.cloud.netflix.zuul.RoutesEndpoint;
|
||||
import org.springframework.cloud.netflix.zuul.RoutesRefreshedEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
|
||||
@@ -27,6 +27,7 @@ import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.netflix.zuul.RoutesEndpoint;
|
||||
import org.springframework.cloud.netflix.zuul.filters.Route;
|
||||
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user