Removes modules in maintenance mode.
fixes gh-3415
This commit is contained in:
32
docs/pom.xml
32
docs/pom.xml
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>2.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>3.0.0.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-docs</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
@@ -15,14 +15,10 @@
|
||||
<docs.main>spring-cloud-netflix</docs.main>
|
||||
<docs.whitelisted.branches>1.2.x,1.3.x,1.4.x</docs.whitelisted.branches>
|
||||
<configprops.inclusionPattern>
|
||||
.*.eureka.*|.*.hystrix.*|.*.ribbon.*|.*.sidecar.*|.*.turbine.*|.*.zuul.*|.*.archaius.*
|
||||
.*.eureka.*
|
||||
</configprops.inclusionPattern>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-archaius</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
||||
@@ -31,30 +27,6 @@
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-turbine-stream</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
36
pom.xml
36
pom.xml
@@ -3,7 +3,7 @@
|
||||
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>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>2.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>3.0.0.BUILD-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>Spring Cloud Netflix</name>
|
||||
<description>Spring Cloud Netflix</description>
|
||||
@@ -79,12 +79,6 @@
|
||||
</build>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-hystrix-contract</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-dependencies</artifactId>
|
||||
@@ -131,19 +125,6 @@
|
||||
<artifactId>javax.inject</artifactId>
|
||||
<version>1</version>
|
||||
</dependency>
|
||||
<!-- archaius dependencies that are now runtime -->
|
||||
<dependency>
|
||||
<groupId>commons-configuration</groupId>
|
||||
<artifactId>commons-configuration</artifactId>
|
||||
<version>1.8</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<!-- end archaius deps -->
|
||||
<!-- Eureka core dep that is now optional -->
|
||||
<dependency>
|
||||
<groupId>com.sun.jersey</groupId>
|
||||
@@ -188,22 +169,11 @@
|
||||
</reporting>
|
||||
<modules>
|
||||
<module>spring-cloud-netflix-dependencies</module>
|
||||
<module>spring-cloud-netflix-archaius</module>
|
||||
<!-- Not part of the reactor build: build and deploy separately -->
|
||||
<!--module>spring-cloud-netflix-hystrix-contract</module-->
|
||||
<module>spring-cloud-netflix-core</module>
|
||||
<module>spring-cloud-netflix-concurrency-limits</module>
|
||||
<module>spring-cloud-netflix-hystrix-dashboard</module>
|
||||
<module>spring-cloud-netflix-hystrix-stream</module>
|
||||
<module>spring-cloud-netflix-eureka-client</module>
|
||||
<module>spring-cloud-netflix-eureka-server</module>
|
||||
<module>spring-cloud-netflix-turbine</module>
|
||||
<module>spring-cloud-netflix-turbine-stream</module>
|
||||
<module>spring-cloud-netflix-sidecar</module>
|
||||
<module>spring-cloud-netflix-zuul</module>
|
||||
<module>spring-cloud-netflix-ribbon</module>
|
||||
<module>spring-cloud-starter-netflix</module>
|
||||
<module>spring-cloud-netflix-hystrix</module>
|
||||
<module>spring-cloud-starter-netflix-eureka-client</module>
|
||||
<module>spring-cloud-starter-netflix-eureka-server</module>
|
||||
<module>docs</module>
|
||||
</modules>
|
||||
<profiles>
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
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>
|
||||
<parent>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>2.2.2.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
|
||||
<artifactId>spring-cloud-netflix-archaius</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring Cloud Netflix Archaius</name>
|
||||
<description>Spring Cloud Netflix Archaius</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-context</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.archaius</groupId>
|
||||
<artifactId>archaius-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-configuration</groupId>
|
||||
<artifactId>commons-configuration</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -1,241 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.archaius;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
|
||||
import com.netflix.config.AggregatedConfiguration;
|
||||
import com.netflix.config.ConcurrentCompositeConfiguration;
|
||||
import com.netflix.config.ConfigurationManager;
|
||||
import com.netflix.config.DeploymentContext;
|
||||
import com.netflix.config.DynamicProperty;
|
||||
import com.netflix.config.DynamicPropertyFactory;
|
||||
import com.netflix.config.DynamicURLConfiguration;
|
||||
import org.apache.commons.configuration.AbstractConfiguration;
|
||||
import org.apache.commons.configuration.ConfigurationBuilder;
|
||||
import org.apache.commons.configuration.EnvironmentConfiguration;
|
||||
import org.apache.commons.configuration.SystemConfiguration;
|
||||
import org.apache.commons.configuration.event.ConfigurationEvent;
|
||||
import org.apache.commons.configuration.event.ConfigurationListener;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static com.netflix.config.ConfigurationManager.APPLICATION_PROPERTIES;
|
||||
import static com.netflix.config.ConfigurationManager.DISABLE_DEFAULT_ENV_CONFIG;
|
||||
import static com.netflix.config.ConfigurationManager.DISABLE_DEFAULT_SYS_CONFIG;
|
||||
import static com.netflix.config.ConfigurationManager.ENV_CONFIG_NAME;
|
||||
import static com.netflix.config.ConfigurationManager.SYS_CONFIG_NAME;
|
||||
import static com.netflix.config.ConfigurationManager.URL_CONFIG_NAME;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Liang Yong
|
||||
*/
|
||||
@Lazy(false)
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass({ ConcurrentCompositeConfiguration.class,
|
||||
ConfigurationBuilder.class })
|
||||
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
|
||||
public class ArchaiusAutoConfiguration {
|
||||
|
||||
private static final Log log = LogFactory.getLog(ArchaiusAutoConfiguration.class);
|
||||
|
||||
private static final AtomicBoolean initialized = new AtomicBoolean(false);
|
||||
|
||||
@Autowired
|
||||
private ConfigurableEnvironment env;
|
||||
|
||||
@Autowired(required = false)
|
||||
private List<AbstractConfiguration> externalConfigurations = new ArrayList<>();
|
||||
|
||||
private static DynamicURLConfiguration defaultURLConfig;
|
||||
|
||||
@PreDestroy
|
||||
public void close() {
|
||||
if (defaultURLConfig != null) {
|
||||
defaultURLConfig.stopLoading();
|
||||
}
|
||||
setStatic(ConfigurationManager.class, "instance", null);
|
||||
setStatic(ConfigurationManager.class, "customConfigurationInstalled", false);
|
||||
setStatic(DynamicPropertyFactory.class, "config", null);
|
||||
setStatic(DynamicPropertyFactory.class, "initializedWithDefaultConfig", false);
|
||||
setStatic(DynamicProperty.class, "dynamicPropertySupportImpl", null);
|
||||
initialized.compareAndSet(true, false);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public static ConfigurableEnvironmentConfiguration configurableEnvironmentConfiguration(
|
||||
ConfigurableEnvironment env, ApplicationContext context) {
|
||||
Map<String, AbstractConfiguration> abstractConfigurationMap = context
|
||||
.getBeansOfType(AbstractConfiguration.class);
|
||||
List<AbstractConfiguration> externalConfigurations = new ArrayList<>(
|
||||
abstractConfigurationMap.values());
|
||||
ConfigurableEnvironmentConfiguration envConfig = new ConfigurableEnvironmentConfiguration(
|
||||
env);
|
||||
configureArchaius(envConfig, env, externalConfigurations);
|
||||
return envConfig;
|
||||
}
|
||||
|
||||
protected static void configureArchaius(
|
||||
ConfigurableEnvironmentConfiguration envConfig, ConfigurableEnvironment env,
|
||||
List<AbstractConfiguration> externalConfigurations) {
|
||||
if (initialized.compareAndSet(false, true)) {
|
||||
String appName = env.getProperty("spring.application.name");
|
||||
if (appName == null) {
|
||||
appName = "application";
|
||||
log.warn("No spring.application.name found, defaulting to 'application'");
|
||||
}
|
||||
System.setProperty(DeploymentContext.ContextKey.appId.getKey(), appName);
|
||||
|
||||
ConcurrentCompositeConfiguration config = new ConcurrentCompositeConfiguration();
|
||||
|
||||
// support to add other Configurations (Jdbc, DynamoDb, Zookeeper, jclouds,
|
||||
// etc...)
|
||||
if (externalConfigurations != null) {
|
||||
for (AbstractConfiguration externalConfig : externalConfigurations) {
|
||||
config.addConfiguration(externalConfig);
|
||||
}
|
||||
}
|
||||
config.addConfiguration(envConfig,
|
||||
ConfigurableEnvironmentConfiguration.class.getSimpleName());
|
||||
|
||||
defaultURLConfig = new DynamicURLConfiguration();
|
||||
try {
|
||||
config.addConfiguration(defaultURLConfig, URL_CONFIG_NAME);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
log.error("Cannot create config from " + defaultURLConfig, ex);
|
||||
}
|
||||
|
||||
// TODO: sys/env above urls?
|
||||
if (!Boolean.getBoolean(DISABLE_DEFAULT_SYS_CONFIG)) {
|
||||
SystemConfiguration sysConfig = new SystemConfiguration();
|
||||
config.addConfiguration(sysConfig, SYS_CONFIG_NAME);
|
||||
}
|
||||
if (!Boolean.getBoolean(DISABLE_DEFAULT_ENV_CONFIG)) {
|
||||
EnvironmentConfiguration environmentConfiguration = new EnvironmentConfiguration();
|
||||
config.addConfiguration(environmentConfiguration, ENV_CONFIG_NAME);
|
||||
}
|
||||
|
||||
ConcurrentCompositeConfiguration appOverrideConfig = new ConcurrentCompositeConfiguration();
|
||||
config.addConfiguration(appOverrideConfig, APPLICATION_PROPERTIES);
|
||||
config.setContainerConfigurationIndex(
|
||||
config.getIndexOfConfiguration(appOverrideConfig));
|
||||
|
||||
addArchaiusConfiguration(config);
|
||||
}
|
||||
else {
|
||||
// TODO: reinstall ConfigurationManager
|
||||
log.warn(
|
||||
"Netflix ConfigurationManager has already been installed, unable to re-install");
|
||||
}
|
||||
}
|
||||
|
||||
private static void addArchaiusConfiguration(
|
||||
ConcurrentCompositeConfiguration config) {
|
||||
if (ConfigurationManager.isConfigurationInstalled()) {
|
||||
AbstractConfiguration installedConfiguration = ConfigurationManager
|
||||
.getConfigInstance();
|
||||
if (installedConfiguration instanceof ConcurrentCompositeConfiguration) {
|
||||
ConcurrentCompositeConfiguration configInstance = (ConcurrentCompositeConfiguration) installedConfiguration;
|
||||
configInstance.addConfiguration(config);
|
||||
}
|
||||
else {
|
||||
installedConfiguration.append(config);
|
||||
if (!(installedConfiguration instanceof AggregatedConfiguration)) {
|
||||
log.warn(
|
||||
"Appending a configuration to an existing non-aggregated installed configuration will have no effect");
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
ConfigurationManager.install(config);
|
||||
}
|
||||
}
|
||||
|
||||
private static void setStatic(Class<?> type, String name, Object value) {
|
||||
// Hack a private static field
|
||||
Field field = ReflectionUtils.findField(type, name);
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
ReflectionUtils.setField(field, null, value);
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(Health.class)
|
||||
protected static class ArchaiusEndpointConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnAvailableEndpoint
|
||||
protected ArchaiusEndpoint archaiusEndpoint() {
|
||||
return new ArchaiusEndpoint();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnProperty(value = "archaius.propagate.environmentChangedEvent",
|
||||
matchIfMissing = true)
|
||||
@ConditionalOnClass(EnvironmentChangeEvent.class)
|
||||
protected static class PropagateEventsConfiguration
|
||||
implements ApplicationListener<EnvironmentChangeEvent> {
|
||||
|
||||
@Autowired
|
||||
private Environment env;
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(EnvironmentChangeEvent event) {
|
||||
AbstractConfiguration manager = ConfigurationManager.getConfigInstance();
|
||||
for (String key : event.getKeys()) {
|
||||
for (ConfigurationListener listener : manager
|
||||
.getConfigurationListeners()) {
|
||||
Object source = event.getSource();
|
||||
// TODO: Handle add vs set vs delete?
|
||||
int type = AbstractConfiguration.EVENT_SET_PROPERTY;
|
||||
String value = this.env.getProperty(key);
|
||||
boolean beforeUpdate = false;
|
||||
listener.configurationChanged(new ConfigurationEvent(source, type,
|
||||
key, value, beforeUpdate));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.archaius;
|
||||
|
||||
import com.netflix.config.ConfigurationManager;
|
||||
import org.apache.commons.configuration.AbstractConfiguration;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public final class ArchaiusDelegatingProxyUtils {
|
||||
|
||||
private ArchaiusDelegatingProxyUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code ApplicationContext} class name.
|
||||
*/
|
||||
public static String APPLICATION_CONTEXT = ApplicationContext.class.getName();
|
||||
|
||||
public static <T> T getNamedInstance(Class<T> type, String name) {
|
||||
ApplicationContext context = (ApplicationContext) ConfigurationManager
|
||||
.getConfigInstance().getProperty(APPLICATION_CONTEXT);
|
||||
return context != null && context.containsBean(name) ? context.getBean(name, type)
|
||||
: null;
|
||||
}
|
||||
|
||||
public static <T> T getInstanceWithPrefix(Class<T> type, String prefix) {
|
||||
String name = prefix + type.getSimpleName();
|
||||
return getNamedInstance(type, name);
|
||||
}
|
||||
|
||||
public static void addApplicationContext(ConfigurableApplicationContext context) {
|
||||
AbstractConfiguration config = ConfigurationManager.getConfigInstance();
|
||||
config.clearProperty(APPLICATION_CONTEXT);
|
||||
config.setProperty(APPLICATION_CONTEXT, context);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.archaius;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.netflix.config.ConcurrentCompositeConfiguration;
|
||||
import com.netflix.config.ConfigurationManager;
|
||||
import org.apache.commons.configuration.AbstractConfiguration;
|
||||
import org.apache.commons.configuration.Configuration;
|
||||
import org.apache.commons.configuration.EnvironmentConfiguration;
|
||||
import org.apache.commons.configuration.SystemConfiguration;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
|
||||
/**
|
||||
* An actuator endpoint that returns Archaius configuration.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Endpoint(id = "archaius")
|
||||
public class ArchaiusEndpoint {
|
||||
|
||||
@ReadOperation
|
||||
public Map<String, Object> invoke() {
|
||||
Map<String, Object> map = new LinkedHashMap<>();
|
||||
AbstractConfiguration config = ConfigurationManager.getConfigInstance();
|
||||
if (config instanceof ConcurrentCompositeConfiguration) {
|
||||
ConcurrentCompositeConfiguration composite = (ConcurrentCompositeConfiguration) config;
|
||||
for (Configuration item : composite.getConfigurations()) {
|
||||
append(map, item);
|
||||
}
|
||||
}
|
||||
else {
|
||||
append(map, config);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private void append(Map<String, Object> map, Configuration config) {
|
||||
if (config instanceof ConfigurableEnvironmentConfiguration) {
|
||||
return;
|
||||
}
|
||||
if (config instanceof SystemConfiguration) {
|
||||
return;
|
||||
}
|
||||
if (config instanceof EnvironmentConfiguration) {
|
||||
return;
|
||||
}
|
||||
for (Iterator<String> iter = config.getKeys(); iter.hasNext();) {
|
||||
String key = iter.next();
|
||||
map.put(key, config.getProperty(key));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.archaius;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.configuration.AbstractConfiguration;
|
||||
|
||||
import org.springframework.core.env.CompositePropertySource;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.EnumerablePropertySource;
|
||||
import org.springframework.core.env.MutablePropertySources;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
|
||||
/**
|
||||
* EnvironmentConfiguration wrapper class providing further configuration possibilities.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class ConfigurableEnvironmentConfiguration extends AbstractConfiguration {
|
||||
|
||||
private final ConfigurableEnvironment environment;
|
||||
|
||||
public ConfigurableEnvironmentConfiguration(ConfigurableEnvironment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addPropertyDirect(String key, Object value) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return !getKeys().hasNext(); // TODO: find a better way to do this
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(String key) {
|
||||
return this.environment.containsProperty(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getProperty(String key) {
|
||||
return this.environment.getProperty(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<String> getKeys() {
|
||||
List<String> result = new ArrayList<>();
|
||||
for (Map.Entry<String, PropertySource<?>> entry : getPropertySources()
|
||||
.entrySet()) {
|
||||
PropertySource<?> source = entry.getValue();
|
||||
if (source instanceof EnumerablePropertySource) {
|
||||
EnumerablePropertySource<?> enumerable = (EnumerablePropertySource<?>) source;
|
||||
for (String name : enumerable.getPropertyNames()) {
|
||||
result.add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.iterator();
|
||||
}
|
||||
|
||||
private Map<String, PropertySource<?>> getPropertySources() {
|
||||
Map<String, PropertySource<?>> map = new LinkedHashMap<>();
|
||||
MutablePropertySources sources = (this.environment != null
|
||||
? this.environment.getPropertySources()
|
||||
: new StandardEnvironment().getPropertySources());
|
||||
for (PropertySource<?> source : sources) {
|
||||
extract("", map, source);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private void extract(String root, Map<String, PropertySource<?>> map,
|
||||
PropertySource<?> source) {
|
||||
if (source instanceof CompositePropertySource) {
|
||||
for (PropertySource<?> nest : ((CompositePropertySource) source)
|
||||
.getPropertySources()) {
|
||||
extract(source.getName() + ":", map, nest);
|
||||
}
|
||||
}
|
||||
else {
|
||||
map.put(root + source.getName(), source);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"properties": [
|
||||
{
|
||||
"defaultValue": "true",
|
||||
"name": "archaius.propagate.environmentChangedEvent",
|
||||
"description": "Propagates EnvironmentChanged events to Archaius ConfigurationManager.",
|
||||
"type": "java.lang.Boolean"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration
|
||||
@@ -1,108 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.archaius;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import com.netflix.config.ConfigurationManager;
|
||||
import com.netflix.config.DynamicPropertyFactory;
|
||||
import com.netflix.config.DynamicStringProperty;
|
||||
import org.apache.commons.configuration.AbstractConfiguration;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.test.util.TestPropertyValues;
|
||||
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class ArchaiusAutoConfigurationTests {
|
||||
|
||||
private AnnotationConfigApplicationContext context;
|
||||
|
||||
private Object propertyValue;
|
||||
|
||||
@After
|
||||
public void close() {
|
||||
if (this.context != null) {
|
||||
this.context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configurationCreated() {
|
||||
this.context = new AnnotationConfigApplicationContext(
|
||||
ArchaiusAutoConfiguration.class);
|
||||
AbstractConfiguration config = this.context
|
||||
.getBean(ConfigurableEnvironmentConfiguration.class);
|
||||
assertThat(config.getString("java.io.tmpdir")).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void environmentChangeEventPropagated() {
|
||||
this.context = new AnnotationConfigApplicationContext(
|
||||
ArchaiusAutoConfiguration.class);
|
||||
ConfigurationManager.getConfigInstance().addConfigurationListener(event -> {
|
||||
if (event.getPropertyName().equals("my.prop")) {
|
||||
ArchaiusAutoConfigurationTests.this.propertyValue = event
|
||||
.getPropertyValue();
|
||||
}
|
||||
});
|
||||
TestPropertyValues.of("my.prop=my.newval").applyTo(this.context);
|
||||
this.context.publishEvent(
|
||||
new EnvironmentChangeEvent(Collections.singleton("my.prop")));
|
||||
assertThat(this.propertyValue).isEqualTo("my.newval");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configurationWithoutExternalConfigurations() throws Exception {
|
||||
this.context = new AnnotationConfigApplicationContext(
|
||||
ArchaiusAutoConfiguration.class);
|
||||
DynamicStringProperty dbProperty = DynamicPropertyFactory.getInstance()
|
||||
.getStringProperty("db.property", null);
|
||||
DynamicStringProperty staticProperty = DynamicPropertyFactory.getInstance()
|
||||
.getStringProperty("archaius.file.property", null);
|
||||
|
||||
assertThat(dbProperty.getValue()).isNull();
|
||||
assertThat(staticProperty.getValue()).isNotNull();
|
||||
assertThat(staticProperty.getValue()).isEqualTo("Static config file property");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configurationWithInjectedConfiguration() throws Exception {
|
||||
this.context = new AnnotationConfigApplicationContext(
|
||||
ArchaiusAutoConfiguration.class, TestArchaiusExternalConfiguration.class);
|
||||
DynamicStringProperty dbProperty = DynamicPropertyFactory.getInstance()
|
||||
.getStringProperty("db.property", null);
|
||||
DynamicStringProperty secondDbProperty = DynamicPropertyFactory.getInstance()
|
||||
.getStringProperty("db.second.property", null);
|
||||
DynamicStringProperty staticProperty = DynamicPropertyFactory.getInstance()
|
||||
.getStringProperty("archaius.file.property", null);
|
||||
|
||||
assertThat(dbProperty.getValue()).isNotNull();
|
||||
assertThat(secondDbProperty.getValue()).isNotNull();
|
||||
assertThat(staticProperty.getValue()).isNotNull();
|
||||
assertThat(dbProperty.getValue()).isEqualTo("this is a db property");
|
||||
assertThat(secondDbProperty.getValue()).isEqualTo("this is another db property");
|
||||
assertThat(staticProperty.getValue()).isEqualTo("Static config file property");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.archaius;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.netflix.config.ConcurrentCompositeConfiguration;
|
||||
import com.netflix.config.ConfigurationManager;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class ArchaiusEndpointTests {
|
||||
|
||||
private ArchaiusEndpoint endpoint = new ArchaiusEndpoint();
|
||||
|
||||
@Test
|
||||
public void detectsPropertiesWhenSet() {
|
||||
ConfigurationManager.getConfigInstance().setProperty("foo", "bar");
|
||||
assertThat(this.endpoint.invoke().containsKey("foo")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotIncludeSpringEnvironment() {
|
||||
ConcurrentCompositeConfiguration composite = new ConcurrentCompositeConfiguration(
|
||||
ConfigurationManager.getConfigInstance());
|
||||
ConfigurableEnvironmentConfiguration config = new ConfigurableEnvironmentConfiguration(
|
||||
new StandardEnvironment());
|
||||
assertThat(config.containsKey("user.dir")).isTrue();
|
||||
composite.addConfiguration(config);
|
||||
ConfigurationManager.getConfigInstance().setProperty("foo", "bar");
|
||||
Map<String, Object> map = this.endpoint.invoke();
|
||||
assertThat(map.containsKey("foo")).isTrue();
|
||||
assertThat(map.containsKey("user.dir")).isFalse();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.archaius;
|
||||
|
||||
import com.netflix.config.DynamicProperty;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Liang Yong
|
||||
*/
|
||||
|
||||
@SpringBootTest(classes = LazyLoadConfigurationTests.TestConfig.class,
|
||||
properties = { "client.ribbon.listOfServers=foo.com,bar.com",
|
||||
"spring.main.lazy-initialization=true" })
|
||||
public class LazyLoadConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void enableLazyInitialization() {
|
||||
DynamicProperty instance = DynamicProperty
|
||||
.getInstance("client.ribbon.listOfServers");
|
||||
assertThat(instance.getString()).isEqualTo("foo.com,bar.com");
|
||||
}
|
||||
|
||||
@SpringBootConfiguration
|
||||
@EnableAutoConfiguration
|
||||
static class TestConfig {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.archaius;
|
||||
|
||||
import com.netflix.config.ConcurrentMapConfiguration;
|
||||
import org.apache.commons.configuration.AbstractConfiguration;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author Alexandru-George Burghelea
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class TestArchaiusExternalConfiguration {
|
||||
|
||||
@Bean
|
||||
@Qualifier("dynamicConfiguration")
|
||||
public AbstractConfiguration createDynamicConfiguration() {
|
||||
ConcurrentMapConfiguration config = new ConcurrentMapConfiguration();
|
||||
config.addProperty("db.property", "this is a db property");
|
||||
config.addProperty("db.second.property", "this is another db property");
|
||||
return config;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
archaius.file.property=Static config file property
|
||||
db.second.property=It should be overridden
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>2.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>3.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-concurrency-limits</artifactId>
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
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>
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>2.2.2.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring Cloud Netflix Core - Deprecated</name>
|
||||
<description>Deprecated Spring Cloud Netflix Core - will be removed. Please use spring-cloud-netflix-hystrix instead.</description>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-hystrix</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>java8plus</id>
|
||||
<activation>
|
||||
<jdk>[1.8,2.0)</jdk>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<compilerArgs>
|
||||
<arg>-parameters</arg>
|
||||
</compilerArgs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-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.netflix.core;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @since 2.1.0
|
||||
* @deprecated Module spring-cloud-netflix-core is deprecated as of 2.1.0, use
|
||||
* spring-cloud-netflix-hystrix instead.
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Deprecated
|
||||
public class CoreAutoConfiguration {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(CoreAutoConfiguration.class);
|
||||
|
||||
public CoreAutoConfiguration() {
|
||||
LOG.warn(
|
||||
"This module is deprecated. It will be removed in the next major release. "
|
||||
+ "Please use spring-cloud-netflix-hystrix instead.");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.netflix.core.CoreAutoConfiguration
|
||||
@@ -5,24 +5,17 @@
|
||||
<parent>
|
||||
<artifactId>spring-cloud-dependencies-parent</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>2.2.1.RELEASE</version>
|
||||
<version>2.2.2.BUILD-SNAPSHOT</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-dependencies</artifactId>
|
||||
<version>2.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>3.0.0.BUILD-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>spring-cloud-netflix-dependencies</name>
|
||||
<description>Spring Cloud Netflix Dependencies</description>
|
||||
<properties>
|
||||
<archaius.version>0.7.6</archaius.version>
|
||||
<concurrency-limits.version>0.1.12</concurrency-limits.version>
|
||||
<eureka.version>1.9.13</eureka.version>
|
||||
<eventbus.version>0.3.0</eventbus.version>
|
||||
<hystrix.version>1.5.18</hystrix.version>
|
||||
<ribbon.version>2.3.0</ribbon.version>
|
||||
<servo.version>0.12.21</servo.version>
|
||||
<zuul.version>1.3.1</zuul.version>
|
||||
<turbine.version>1.0.0</turbine.version>
|
||||
</properties>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
@@ -31,16 +24,6 @@
|
||||
<artifactId>spring-cloud-netflix-eureka-client</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-archaius</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-archaius</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
||||
@@ -51,111 +34,16 @@
|
||||
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-hystrix-contract</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-turbine-stream</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-hystrix</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-eureka-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-hystrix-dashboard</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-hystrix-stream</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-sidecar</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-turbine</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-turbine-stream</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-zuul</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-ribbon</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<!--<dependency>
|
||||
<groupId>com.netflix.netflix-commons</groupId>
|
||||
<artifactId>netflix-commons-util</artifactId>
|
||||
<version>0.3.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.archaius</groupId>
|
||||
<artifactId>archaius-core</artifactId>
|
||||
<version>${archaius.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<groupId>commons-logging</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependency>-->
|
||||
<dependency>
|
||||
<groupId>com.netflix.concurrency-limits</groupId>
|
||||
<artifactId>concurrency-limits-core</artifactId>
|
||||
@@ -166,17 +54,6 @@
|
||||
<artifactId>concurrency-limits-servlet</artifactId>
|
||||
<version>${concurrency-limits.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.servo</groupId>
|
||||
<artifactId>servo-core</artifactId>
|
||||
<version>${servo.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.eureka</groupId>
|
||||
<artifactId>eureka-client</artifactId>
|
||||
@@ -243,137 +120,6 @@
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.hystrix</groupId>
|
||||
<artifactId>hystrix-core</artifactId>
|
||||
<version>${hystrix.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.hystrix</groupId>
|
||||
<artifactId>hystrix-serialization</artifactId>
|
||||
<version>${hystrix.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.hystrix</groupId>
|
||||
<artifactId>hystrix-metrics-event-stream</artifactId>
|
||||
<version>${hystrix.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.hystrix</groupId>
|
||||
<artifactId>hystrix-javanica</artifactId>
|
||||
<version>${hystrix.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjrt</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon</artifactId>
|
||||
<version>${ribbon.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-core</artifactId>
|
||||
<version>${ribbon.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-httpclient</artifactId>
|
||||
<version>${ribbon.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-eureka</artifactId>
|
||||
<version>${ribbon.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-loadbalancer</artifactId>
|
||||
<version>${ribbon.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.zuul</groupId>
|
||||
<artifactId>zuul-core</artifactId>
|
||||
<version>${zuul.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>groovy-all</artifactId>
|
||||
<groupId>org.codehaus.groovy</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<groupId>org.mockito</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.netflix-commons</groupId>
|
||||
<artifactId>netflix-eventbus</artifactId>
|
||||
<version>${eventbus.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<profiles>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>2.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>3.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-eureka-client</artifactId>
|
||||
@@ -32,15 +32,6 @@
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-hystrix</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-ribbon</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-config-client</artifactId>
|
||||
@@ -82,41 +73,11 @@
|
||||
<artifactId>jersey-apache-client4</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.archaius</groupId>
|
||||
<artifactId>archaius-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<!--<dependency>
|
||||
<groupId>com.netflix.netflix-commons</groupId>
|
||||
<artifactId>netflix-eventbus</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-loadbalancer</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-eureka</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-httpclient</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependency>-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure-processor</artifactId>
|
||||
|
||||
@@ -26,7 +26,7 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerProperties;
|
||||
import org.springframework.cloud.netflix.ribbon.eureka.ZoneUtils;
|
||||
import org.springframework.cloud.netflix.eureka.support.ZoneUtils;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.ribbon.eureka;
|
||||
package org.springframework.cloud.netflix.eureka.support;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.ribbon.eureka;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import com.netflix.discovery.EurekaClient;
|
||||
import com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.AllNestedConditions;
|
||||
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.client.ConditionalOnDiscoveryEnabled;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* Conditional that requires both Ribbon and Eureka to be enabled.
|
||||
* @author Ihor Kryvenko
|
||||
* @author Spencer Gibb
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Conditional(ConditionalOnRibbonAndEurekaEnabled.OnRibbonAndEurekaEnabledCondition.class)
|
||||
public @interface ConditionalOnRibbonAndEurekaEnabled {
|
||||
|
||||
class OnRibbonAndEurekaEnabledCondition extends AllNestedConditions {
|
||||
|
||||
OnRibbonAndEurekaEnabledCondition() {
|
||||
super(ConfigurationPhase.REGISTER_BEAN);
|
||||
}
|
||||
|
||||
@ConditionalOnClass(DiscoveryEnabledNIWSServerList.class)
|
||||
@ConditionalOnBean(SpringClientFactory.class)
|
||||
@ConditionalOnProperty(value = "ribbon.eureka.enabled", matchIfMissing = true)
|
||||
static class Defaults {
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnBean(EurekaClient.class)
|
||||
static class EurekaBeans {
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
|
||||
@ConditionalOnDiscoveryEnabled
|
||||
static class OnEurekaClientEnabled {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.ribbon.eureka;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.netflix.appinfo.InstanceInfo;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
import com.netflix.niws.loadbalancer.DiscoveryEnabledServer;
|
||||
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonProperties;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class DomainExtractingServerList implements ServerList<DiscoveryEnabledServer> {
|
||||
|
||||
private ServerList<DiscoveryEnabledServer> list;
|
||||
|
||||
private final RibbonProperties ribbon;
|
||||
|
||||
private boolean approximateZoneFromHostname;
|
||||
|
||||
public DomainExtractingServerList(ServerList<DiscoveryEnabledServer> list,
|
||||
IClientConfig clientConfig, boolean approximateZoneFromHostname) {
|
||||
this.list = list;
|
||||
this.ribbon = RibbonProperties.from(clientConfig);
|
||||
this.approximateZoneFromHostname = approximateZoneFromHostname;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DiscoveryEnabledServer> getInitialListOfServers() {
|
||||
List<DiscoveryEnabledServer> servers = setZones(
|
||||
this.list.getInitialListOfServers());
|
||||
return servers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DiscoveryEnabledServer> getUpdatedListOfServers() {
|
||||
List<DiscoveryEnabledServer> servers = setZones(
|
||||
this.list.getUpdatedListOfServers());
|
||||
return servers;
|
||||
}
|
||||
|
||||
private List<DiscoveryEnabledServer> setZones(List<DiscoveryEnabledServer> servers) {
|
||||
List<DiscoveryEnabledServer> result = new ArrayList<>();
|
||||
boolean isSecure = this.ribbon.isSecure(true);
|
||||
boolean shouldUseIpAddr = this.ribbon.isUseIPAddrForServer();
|
||||
for (DiscoveryEnabledServer server : servers) {
|
||||
result.add(new DomainExtractingServer(server, isSecure, shouldUseIpAddr,
|
||||
this.approximateZoneFromHostname));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class DomainExtractingServer extends DiscoveryEnabledServer {
|
||||
|
||||
private String id;
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
DomainExtractingServer(DiscoveryEnabledServer server, boolean useSecurePort,
|
||||
boolean useIpAddr, boolean approximateZoneFromHostname) {
|
||||
// host and port are set in super()
|
||||
super(server.getInstanceInfo(), useSecurePort, useIpAddr);
|
||||
if (server.getInstanceInfo().getMetadata().containsKey("zone")) {
|
||||
setZone(server.getInstanceInfo().getMetadata().get("zone"));
|
||||
}
|
||||
else if (approximateZoneFromHostname) {
|
||||
setZone(ZoneUtils.extractApproximateZone(server.getHost()));
|
||||
}
|
||||
else {
|
||||
setZone(server.getZone());
|
||||
}
|
||||
setId(extractId(server));
|
||||
setAlive(server.isAlive());
|
||||
setReadyToServe(server.isReadyToServe());
|
||||
}
|
||||
|
||||
private String extractId(Server server) {
|
||||
if (server instanceof DiscoveryEnabledServer) {
|
||||
DiscoveryEnabledServer enabled = (DiscoveryEnabledServer) server;
|
||||
InstanceInfo instance = enabled.getInstanceInfo();
|
||||
if (instance.getMetadata().containsKey("instanceId")) {
|
||||
return instance.getHostName() + ":"
|
||||
+ instance.getMetadata().get("instanceId");
|
||||
}
|
||||
}
|
||||
return super.getId();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.ribbon.eureka;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.inject.Provider;
|
||||
|
||||
import com.netflix.appinfo.EurekaInstanceConfig;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.config.ConfigurationManager;
|
||||
import com.netflix.config.DeploymentContext.ContextKey;
|
||||
import com.netflix.discovery.EurekaClient;
|
||||
import com.netflix.discovery.EurekaClientConfig;
|
||||
import com.netflix.loadbalancer.IPing;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
import com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList;
|
||||
import com.netflix.niws.loadbalancer.NIWSDiscoveryPing;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.cloud.netflix.ribbon.PropertiesFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClientName;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonUtils;
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Preprocessor that configures defaults for eureka-discovered ribbon clients. Such as:
|
||||
* <code>@zone</code>, NIWSServerListClassName, DeploymentContextBasedVipAddresses,
|
||||
* NFLoadBalancerRuleClassName, NIWSServerListFilterClassName and more
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Dave Syer
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class EurekaRibbonClientConfiguration {
|
||||
|
||||
private static final Log log = LogFactory
|
||||
.getLog(EurekaRibbonClientConfiguration.class);
|
||||
|
||||
@Value("${ribbon.eureka.approximateZoneFromHostname:false}")
|
||||
private boolean approximateZoneFromHostname = false;
|
||||
|
||||
@RibbonClientName
|
||||
private String serviceId = "client";
|
||||
|
||||
@Autowired(required = false)
|
||||
private EurekaClientConfig clientConfig;
|
||||
|
||||
@Autowired(required = false)
|
||||
private EurekaInstanceConfig eurekaConfig;
|
||||
|
||||
@Autowired
|
||||
private PropertiesFactory propertiesFactory;
|
||||
|
||||
public EurekaRibbonClientConfiguration() {
|
||||
}
|
||||
|
||||
public EurekaRibbonClientConfiguration(EurekaClientConfig clientConfig,
|
||||
String serviceId, EurekaInstanceConfig eurekaConfig,
|
||||
boolean approximateZoneFromHostname) {
|
||||
this.clientConfig = clientConfig;
|
||||
this.serviceId = serviceId;
|
||||
this.eurekaConfig = eurekaConfig;
|
||||
this.approximateZoneFromHostname = approximateZoneFromHostname;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public IPing ribbonPing(IClientConfig config) {
|
||||
if (this.propertiesFactory.isSet(IPing.class, serviceId)) {
|
||||
return this.propertiesFactory.get(IPing.class, config, serviceId);
|
||||
}
|
||||
NIWSDiscoveryPing ping = new NIWSDiscoveryPing();
|
||||
ping.initWithNiwsConfig(config);
|
||||
return ping;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ServerList<?> ribbonServerList(IClientConfig config,
|
||||
Provider<EurekaClient> eurekaClientProvider) {
|
||||
if (this.propertiesFactory.isSet(ServerList.class, serviceId)) {
|
||||
return this.propertiesFactory.get(ServerList.class, config, serviceId);
|
||||
}
|
||||
DiscoveryEnabledNIWSServerList discoveryServerList = new DiscoveryEnabledNIWSServerList(
|
||||
config, eurekaClientProvider);
|
||||
DomainExtractingServerList serverList = new DomainExtractingServerList(
|
||||
discoveryServerList, config, this.approximateZoneFromHostname);
|
||||
return serverList;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ServerIntrospector serverIntrospector() {
|
||||
return new EurekaServerIntrospector();
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void preprocess() {
|
||||
String zone = ConfigurationManager.getDeploymentContext()
|
||||
.getValue(ContextKey.zone);
|
||||
if (this.clientConfig != null && StringUtils.isEmpty(zone)) {
|
||||
if (this.approximateZoneFromHostname && this.eurekaConfig != null) {
|
||||
String approxZone = ZoneUtils
|
||||
.extractApproximateZone(this.eurekaConfig.getHostName(false));
|
||||
log.debug("Setting Zone To " + approxZone);
|
||||
ConfigurationManager.getDeploymentContext().setValue(ContextKey.zone,
|
||||
approxZone);
|
||||
}
|
||||
else {
|
||||
String availabilityZone = this.eurekaConfig == null ? null
|
||||
: this.eurekaConfig.getMetadataMap().get("zone");
|
||||
if (availabilityZone == null) {
|
||||
String[] zones = this.clientConfig
|
||||
.getAvailabilityZones(this.clientConfig.getRegion());
|
||||
// Pick the first one from the regions we want to connect to
|
||||
availabilityZone = zones != null && zones.length > 0 ? zones[0]
|
||||
: null;
|
||||
}
|
||||
if (availabilityZone != null) {
|
||||
// You can set this with archaius.deployment.* (maybe requires
|
||||
// custom deployment context)?
|
||||
ConfigurationManager.getDeploymentContext().setValue(ContextKey.zone,
|
||||
availabilityZone);
|
||||
}
|
||||
}
|
||||
}
|
||||
RibbonUtils.initializeRibbonDefaults(serviceId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.ribbon.eureka;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.netflix.appinfo.InstanceInfo;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.niws.loadbalancer.DiscoveryEnabledServer;
|
||||
|
||||
import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class EurekaServerIntrospector extends DefaultServerIntrospector {
|
||||
|
||||
@Override
|
||||
public boolean isSecure(Server server) {
|
||||
if (server instanceof DiscoveryEnabledServer) {
|
||||
DiscoveryEnabledServer discoveryServer = (DiscoveryEnabledServer) server;
|
||||
return discoveryServer.getInstanceInfo()
|
||||
.isPortEnabled(InstanceInfo.PortType.SECURE);
|
||||
}
|
||||
return super.isSecure(server);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getMetadata(Server server) {
|
||||
if (server instanceof DiscoveryEnabledServer) {
|
||||
DiscoveryEnabledServer discoveryServer = (DiscoveryEnabledServer) server;
|
||||
return discoveryServer.getInstanceInfo().getMetadata();
|
||||
}
|
||||
return super.getMetadata(server);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.ribbon.eureka;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClients;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Spring configuration for configuring Ribbon defaults to be Eureka based if Eureka
|
||||
* client is enabled.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Biju Kunjummen
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties
|
||||
@ConditionalOnRibbonAndEurekaEnabled
|
||||
@AutoConfigureAfter(RibbonAutoConfiguration.class)
|
||||
@RibbonClients(defaultConfiguration = EurekaRibbonClientConfiguration.class)
|
||||
public class RibbonEurekaAutoConfiguration {
|
||||
|
||||
}
|
||||
@@ -2,7 +2,6 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.netflix.eureka.config.EurekaClientConfigServerAutoConfiguration,\
|
||||
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceAutoConfiguration,\
|
||||
org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration,\
|
||||
org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration,\
|
||||
org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration,\
|
||||
org.springframework.cloud.netflix.eureka.reactive.EurekaReactiveDiscoveryClientConfiguration,\
|
||||
org.springframework.cloud.netflix.eureka.loadbalancer.LoadBalancerEurekaAutoConfiguration
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 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.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.ribbon.eureka;
|
||||
package org.springframework.cloud.netflix.eureka.support;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.ribbon.eureka;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.netflix.appinfo.InstanceInfo;
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import com.netflix.client.config.DefaultClientConfigImpl;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
import com.netflix.niws.loadbalancer.DiscoveryEnabledServer;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class DomainExtractingServerListTests {
|
||||
|
||||
static final String IP_ADDR = "10.0.0.2";
|
||||
|
||||
static final int PORT = 8080;
|
||||
|
||||
static final String ZONE = "myzone.mydomain.com";
|
||||
|
||||
static final String HOST_NAME = "myHostName." + ZONE;
|
||||
|
||||
static final String INSTANCE_ID = "myInstanceId";
|
||||
|
||||
private Map<String, String> metadata = Collections
|
||||
.<String, String>singletonMap("instanceId", INSTANCE_ID);
|
||||
|
||||
@Test
|
||||
public void testDomainExtractingServer() {
|
||||
DomainExtractingServerList serverList = getDomainExtractingServerList(
|
||||
new DefaultClientConfigImpl(), true);
|
||||
List<DiscoveryEnabledServer> servers = serverList.getInitialListOfServers();
|
||||
assertThat(servers).as("servers was null").isNotNull();
|
||||
assertThat(servers.size()).as("servers was not size 1").isEqualTo(1);
|
||||
DomainExtractingServer des = assertDomainExtractingServer(servers, ZONE);
|
||||
assertThat(des.getHostPort()).as("hostPort was wrong")
|
||||
.isEqualTo(HOST_NAME + ":" + PORT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testZoneInMetaData() {
|
||||
this.metadata = new HashMap<>();
|
||||
this.metadata.put("zone", "us-west-1");
|
||||
this.metadata.put("instanceId", INSTANCE_ID);
|
||||
DomainExtractingServerList serverList = getDomainExtractingServerList(
|
||||
new DefaultClientConfigImpl(), false);
|
||||
List<DiscoveryEnabledServer> servers = serverList.getInitialListOfServers();
|
||||
assertThat(servers).as("servers was null").isNotNull();
|
||||
assertThat(servers.size()).as("servers was not size 1").isEqualTo(1);
|
||||
DomainExtractingServer des = assertDomainExtractingServer(servers, "us-west-1");
|
||||
assertThat(des.getZone()).as("Zone was wrong").isEqualTo("us-west-1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDomainExtractingServerDontApproximateZone() {
|
||||
DomainExtractingServerList serverList = getDomainExtractingServerList(
|
||||
new DefaultClientConfigImpl(), false);
|
||||
List<DiscoveryEnabledServer> servers = serverList.getInitialListOfServers();
|
||||
assertThat(servers).as("servers was null").isNotNull();
|
||||
assertThat(servers.size()).as("servers was not size 1").isEqualTo(1);
|
||||
DomainExtractingServer des = assertDomainExtractingServer(servers, null);
|
||||
assertThat(des.getHostPort()).as("hostPort was wrong")
|
||||
.isEqualTo(HOST_NAME + ":" + PORT);
|
||||
}
|
||||
|
||||
protected DomainExtractingServer assertDomainExtractingServer(
|
||||
List<DiscoveryEnabledServer> servers, String zone) {
|
||||
Server actualServer = servers.get(0);
|
||||
assertThat(actualServer instanceof DomainExtractingServer)
|
||||
.as("server was not a DomainExtractingServer").isTrue();
|
||||
DomainExtractingServer des = DomainExtractingServer.class.cast(actualServer);
|
||||
assertThat(des.getZone()).as("zone was wrong").isEqualTo(zone);
|
||||
assertThat(des.getId()).as("instanceId was wrong")
|
||||
.isEqualTo(HOST_NAME + ":" + INSTANCE_ID);
|
||||
return des;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDomainExtractingServerUseIpAddress() {
|
||||
DefaultClientConfigImpl config = new DefaultClientConfigImpl();
|
||||
config.setProperty(CommonClientConfigKey.UseIPAddrForServer, true);
|
||||
DomainExtractingServerList serverList = getDomainExtractingServerList(config,
|
||||
true);
|
||||
List<DiscoveryEnabledServer> servers = serverList.getInitialListOfServers();
|
||||
assertThat(servers).as("servers was null").isNotNull();
|
||||
assertThat(servers.size()).as("servers was not size 1").isEqualTo(1);
|
||||
DomainExtractingServer des = assertDomainExtractingServer(servers, ZONE);
|
||||
assertThat(des.getHostPort()).as("hostPort was wrong")
|
||||
.isEqualTo(IP_ADDR + ":" + PORT);
|
||||
}
|
||||
|
||||
protected DomainExtractingServerList getDomainExtractingServerList(
|
||||
DefaultClientConfigImpl config, boolean approximateZoneFromHostname) {
|
||||
DiscoveryEnabledServer server = mock(DiscoveryEnabledServer.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
ServerList<DiscoveryEnabledServer> originalServerList = mock(ServerList.class);
|
||||
InstanceInfo instanceInfo = mock(InstanceInfo.class);
|
||||
given(server.getInstanceInfo()).willReturn(instanceInfo);
|
||||
given(server.getHost()).willReturn(HOST_NAME);
|
||||
given(instanceInfo.getMetadata()).willReturn(this.metadata);
|
||||
given(instanceInfo.getHostName()).willReturn(HOST_NAME);
|
||||
given(instanceInfo.getIPAddr()).willReturn(IP_ADDR);
|
||||
given(instanceInfo.getPort()).willReturn(PORT);
|
||||
given(originalServerList.getInitialListOfServers())
|
||||
.willReturn(Arrays.asList(server));
|
||||
return new DomainExtractingServerList(originalServerList, config,
|
||||
approximateZoneFromHostname);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017-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.netflix.ribbon.eureka;
|
||||
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ZoneAvoidanceRule;
|
||||
import com.netflix.loadbalancer.ZoneAwareLoadBalancer;
|
||||
import com.netflix.niws.loadbalancer.NIWSDiscoveryPing;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.commons.util.UtilAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.eureka.EurekaRibbonClientPreprocessorIntegrationTests.TestConfiguration;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Ribbon Eureka client should be disabled if Eureka client is not enabled.
|
||||
*
|
||||
* @author Biju Kunjummen
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = TestConfiguration.class,
|
||||
properties = "eureka.client.enabled=false")
|
||||
@DirtiesContext
|
||||
public class EurekaDisabledRibbonClientIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
private SpringClientFactory factory;
|
||||
|
||||
@Test
|
||||
public void serverListShouldNotBeEurekaBased() throws Exception {
|
||||
assertThat(getLoadBalancer().getServerListImpl())
|
||||
.isNotInstanceOf(DomainExtractingServerList.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ruleDefaultsToZoneAvoidance() throws Exception {
|
||||
ZoneAvoidanceRule.class.cast(getLoadBalancer().getRule());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pingShouldNotBeEurekaBased() throws Exception {
|
||||
assertThat(getLoadBalancer().getPing()).isNotInstanceOf(NIWSDiscoveryPing.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serverIntrospectorShouldNotBeEurekaBased() throws Exception {
|
||||
assertThat(this.factory.getInstance("foo", ServerIntrospector.class))
|
||||
.isNotInstanceOf(EurekaServerIntrospector.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private ZoneAwareLoadBalancer<Server> getLoadBalancer() {
|
||||
return (ZoneAwareLoadBalancer<Server>) this.factory.getLoadBalancer("foo");
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@RibbonClient("foo")
|
||||
@Import({ UtilAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class,
|
||||
ArchaiusAutoConfiguration.class, RibbonAutoConfiguration.class,
|
||||
EurekaDiscoveryClientConfiguration.class, EurekaClientAutoConfiguration.class,
|
||||
RibbonEurekaAutoConfiguration.class })
|
||||
protected static class TestConfiguration {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.ribbon.eureka;
|
||||
|
||||
import com.netflix.config.ConfigurationManager;
|
||||
import com.netflix.config.DeploymentContext.ContextKey;
|
||||
import com.netflix.config.DynamicStringProperty;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.ZoneAwareLoadBalancer;
|
||||
import com.netflix.niws.loadbalancer.DiscoveryEnabledServer;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.cloud.commons.util.InetUtils;
|
||||
import org.springframework.cloud.commons.util.InetUtilsProperties;
|
||||
import org.springframework.cloud.netflix.eureka.EurekaClientConfigBean;
|
||||
import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.cloud.netflix.ribbon.RibbonUtils.VALUE_NOT_SET;
|
||||
import static org.springframework.cloud.netflix.ribbon.RibbonUtils.getProperty;
|
||||
import static org.springframework.cloud.netflix.ribbon.RibbonUtils.getRibbonKey;
|
||||
import static org.springframework.cloud.netflix.ribbon.RibbonUtils.setRibbonProperty;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class EurekaRibbonClientConfigurationTests {
|
||||
|
||||
@After
|
||||
@Before
|
||||
public void close() {
|
||||
ConfigurationManager.getDeploymentContext().setValue(ContextKey.zone, "");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void basicConfigurationCreatedForLoadBalancer() {
|
||||
EurekaClientConfigBean client = new EurekaClientConfigBean();
|
||||
EurekaInstanceConfigBean configBean = getEurekaInstanceConfigBean();
|
||||
client.getAvailabilityZones().put(client.getRegion(), "foo");
|
||||
SpringClientFactory clientFactory = new SpringClientFactory();
|
||||
EurekaRibbonClientConfiguration clientPreprocessor = new EurekaRibbonClientConfiguration(
|
||||
client, "service", configBean, false);
|
||||
clientPreprocessor.preprocess();
|
||||
ILoadBalancer balancer = clientFactory.getLoadBalancer("service");
|
||||
assertThat(balancer).isNotNull();
|
||||
@SuppressWarnings("unchecked")
|
||||
ZoneAwareLoadBalancer<DiscoveryEnabledServer> aware = (ZoneAwareLoadBalancer<DiscoveryEnabledServer>) balancer;
|
||||
assertThat(aware.getServerListImpl() instanceof DomainExtractingServerList)
|
||||
.isTrue();
|
||||
assertThat(ConfigurationManager.getDeploymentContext().getValue(ContextKey.zone))
|
||||
.isEqualTo("foo");
|
||||
}
|
||||
|
||||
private EurekaInstanceConfigBean getEurekaInstanceConfigBean() {
|
||||
return new EurekaInstanceConfigBean(new InetUtils(new InetUtilsProperties()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetProp() {
|
||||
EurekaClientConfigBean client = new EurekaClientConfigBean();
|
||||
EurekaInstanceConfigBean configBean = getEurekaInstanceConfigBean();
|
||||
EurekaRibbonClientConfiguration preprocessor = new EurekaRibbonClientConfiguration(
|
||||
client, "myService", configBean, false);
|
||||
String serviceId = "myService";
|
||||
String suffix = "mySuffix";
|
||||
String value = "myValue";
|
||||
DynamicStringProperty property = getProperty(getRibbonKey(serviceId, suffix));
|
||||
assertThat(property.get()).as("property doesn't have default value")
|
||||
.isEqualTo(VALUE_NOT_SET);
|
||||
setRibbonProperty(serviceId, suffix, value);
|
||||
assertThat(property.get()).as("property has wrong value").isEqualTo(value);
|
||||
setRibbonProperty(serviceId, suffix, value);
|
||||
assertThat(property.get()).as("property has wrong value").isEqualTo(value);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExplicitZone() {
|
||||
EurekaClientConfigBean client = new EurekaClientConfigBean();
|
||||
EurekaInstanceConfigBean configBean = getEurekaInstanceConfigBean();
|
||||
configBean.getMetadataMap().put("zone", "myZone");
|
||||
EurekaRibbonClientConfiguration preprocessor = new EurekaRibbonClientConfiguration(
|
||||
client, "myService", configBean, false);
|
||||
preprocessor.preprocess();
|
||||
assertThat(ConfigurationManager.getDeploymentContext().getValue(ContextKey.zone))
|
||||
.isEqualTo("myZone");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultZone() {
|
||||
EurekaClientConfigBean client = new EurekaClientConfigBean();
|
||||
EurekaInstanceConfigBean configBean = getEurekaInstanceConfigBean();
|
||||
EurekaRibbonClientConfiguration preprocessor = new EurekaRibbonClientConfiguration(
|
||||
client, "myService", configBean, false);
|
||||
preprocessor.preprocess();
|
||||
assertThat(ConfigurationManager.getDeploymentContext().getValue(ContextKey.zone))
|
||||
.isEqualTo("defaultZone");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApproximateZone() {
|
||||
EurekaClientConfigBean client = new EurekaClientConfigBean();
|
||||
EurekaInstanceConfigBean configBean = getEurekaInstanceConfigBean();
|
||||
configBean.setHostname("this.is.a.test.com");
|
||||
EurekaRibbonClientConfiguration preprocessor = new EurekaRibbonClientConfiguration(
|
||||
client, "myService", configBean, true);
|
||||
preprocessor.preprocess();
|
||||
assertThat(ConfigurationManager.getDeploymentContext().getValue(ContextKey.zone))
|
||||
.isEqualTo("is.a.test.com");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.ribbon.eureka;
|
||||
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ZoneAvoidanceRule;
|
||||
import com.netflix.loadbalancer.ZoneAwareLoadBalancer;
|
||||
import com.netflix.niws.loadbalancer.NIWSDiscoveryPing;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.commons.util.UtilAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.eureka.EurekaRibbonClientPreprocessorIntegrationTests.TestConfiguration;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = TestConfiguration.class)
|
||||
@DirtiesContext
|
||||
public class EurekaRibbonClientPreprocessorIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
private SpringClientFactory factory;
|
||||
|
||||
@Test
|
||||
public void serverListDefaultsToDomainExtracting() throws Exception {
|
||||
DomainExtractingServerList.class.cast(getLoadBalancer().getServerListImpl());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ruleDefaultsToZoneAvoidance() throws Exception {
|
||||
ZoneAvoidanceRule.class.cast(getLoadBalancer().getRule());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pingDefaultsToDiscoveryPing() throws Exception {
|
||||
NIWSDiscoveryPing.class.cast(getLoadBalancer().getPing());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serverIntrospectorDefaultsToEureka() throws Exception {
|
||||
EurekaServerIntrospector.class
|
||||
.cast(this.factory.getInstance("foo", ServerIntrospector.class));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private ZoneAwareLoadBalancer<Server> getLoadBalancer() {
|
||||
return (ZoneAwareLoadBalancer<Server>) this.factory.getLoadBalancer("foo");
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@RibbonClient("foo")
|
||||
@Import({ UtilAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class,
|
||||
ArchaiusAutoConfiguration.class, RibbonAutoConfiguration.class,
|
||||
EurekaDiscoveryClientConfiguration.class, EurekaClientAutoConfiguration.class,
|
||||
RibbonEurekaAutoConfiguration.class })
|
||||
protected static class TestConfiguration {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.ribbon.eureka;
|
||||
|
||||
import com.netflix.discovery.EurekaClient;
|
||||
import com.netflix.loadbalancer.ConfigurationBasedServerList;
|
||||
import com.netflix.loadbalancer.DummyPing;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ZoneAwareLoadBalancer;
|
||||
import com.netflix.niws.loadbalancer.NIWSDiscoveryPing;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.commons.util.UtilAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClients;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(
|
||||
classes = EurekaRibbonClientPropertyOverrideIntegrationTests.TestConfiguration.class)
|
||||
@DirtiesContext
|
||||
public class EurekaRibbonClientPropertyOverrideIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
private SpringClientFactory factory;
|
||||
|
||||
@Test
|
||||
public void pingOverridesToDummy() throws Exception {
|
||||
DummyPing.class.cast(getLoadBalancer("foo3").getPing());
|
||||
NIWSDiscoveryPing.class.cast(getLoadBalancer("bar").getPing());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serverListOverridesToTest() throws Exception {
|
||||
ConfigurationBasedServerList.class
|
||||
.cast(getLoadBalancer("foo3").getServerListImpl());
|
||||
DomainExtractingServerList.class.cast(getLoadBalancer("bar").getServerListImpl());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private ZoneAwareLoadBalancer<Server> getLoadBalancer(String name) {
|
||||
return (ZoneAwareLoadBalancer<Server>) this.factory.getLoadBalancer(name);
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@RibbonClients
|
||||
@ImportAutoConfiguration({ UtilAutoConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class, ArchaiusAutoConfiguration.class,
|
||||
RibbonAutoConfiguration.class, RibbonEurekaAutoConfiguration.class })
|
||||
protected static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
public EurekaClient eurekaClient() {
|
||||
return mock(EurekaClient.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.ribbon.eureka;
|
||||
|
||||
import com.netflix.discovery.EurekaClient;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ZoneAvoidanceRule;
|
||||
import com.netflix.loadbalancer.ZoneAwareLoadBalancer;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.commons.util.InetUtils;
|
||||
import org.springframework.cloud.commons.util.InetUtilsProperties;
|
||||
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.ZonePreferenceServerListFilter;
|
||||
import org.springframework.cloud.netflix.ribbon.eureka.RibbonClientPreprocessorIntegrationTests.TestConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = TestConfiguration.class)
|
||||
@DirtiesContext
|
||||
public class RibbonClientPreprocessorIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
private SpringClientFactory factory;
|
||||
|
||||
@Test
|
||||
public void serverListIsWrapped() throws Exception {
|
||||
@SuppressWarnings("unchecked")
|
||||
ZoneAwareLoadBalancer<Server> loadBalancer = (ZoneAwareLoadBalancer<Server>) this.factory
|
||||
.getLoadBalancer("foo");
|
||||
DomainExtractingServerList.class.cast(loadBalancer.getServerListImpl());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ruleDefaultsToAvoidance() throws Exception {
|
||||
@SuppressWarnings("unchecked")
|
||||
ZoneAwareLoadBalancer<Server> loadBalancer = (ZoneAwareLoadBalancer<Server>) this.factory
|
||||
.getLoadBalancer("foo");
|
||||
ZoneAvoidanceRule.class.cast(loadBalancer.getRule());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serverListFilterOverride() throws Exception {
|
||||
@SuppressWarnings("unchecked")
|
||||
ZoneAwareLoadBalancer<Server> loadBalancer = (ZoneAwareLoadBalancer<Server>) this.factory
|
||||
.getLoadBalancer("foo");
|
||||
assertThat(ZonePreferenceServerListFilter.class.cast(loadBalancer.getFilter())
|
||||
.getZone()).isEqualTo("myTestZone");
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@RibbonClient("foo")
|
||||
@ImportAutoConfiguration({ PropertyPlaceholderAutoConfiguration.class,
|
||||
ArchaiusAutoConfiguration.class, RibbonAutoConfiguration.class })
|
||||
protected static class PlainConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@RibbonClient(name = "foo", configuration = FooConfiguration.class)
|
||||
@ImportAutoConfiguration({ PropertyPlaceholderAutoConfiguration.class,
|
||||
ArchaiusAutoConfiguration.class, RibbonAutoConfiguration.class,
|
||||
RibbonEurekaAutoConfiguration.class })
|
||||
protected static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
EurekaClient eurekaClient() {
|
||||
return Mockito.mock(EurekaClient.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
protected static class FooConfiguration {
|
||||
|
||||
@Bean
|
||||
public ZonePreferenceServerListFilter serverListFilter() {
|
||||
ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter();
|
||||
filter.setZone("myTestZone");
|
||||
return filter;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EurekaInstanceConfigBean getEurekaInstanceConfigBean() {
|
||||
EurekaInstanceConfigBean bean = new EurekaInstanceConfigBean(
|
||||
new InetUtils(new InetUtilsProperties()));
|
||||
return bean;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016-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.netflix.ribbon.eureka;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(
|
||||
classes = RibbonEurekaAutoConfigurationTests.EurekaClientDisabledApp.class,
|
||||
properties = { "eureka.client.enabled=false",
|
||||
"spring.application.name=eurekadisabledtest" },
|
||||
webEnvironment = RANDOM_PORT)
|
||||
@DirtiesContext
|
||||
public class RibbonEurekaAutoConfigurationTests {
|
||||
|
||||
@Autowired
|
||||
TestLoadbalancerClient testLoadbalancerClient;
|
||||
|
||||
@Test
|
||||
public void contextLoads() {
|
||||
assertThat(testLoadbalancerClient.instanceFound).isFalse();
|
||||
}
|
||||
|
||||
@SpringBootConfiguration
|
||||
@EnableAutoConfiguration
|
||||
public static class EurekaClientDisabledApp {
|
||||
|
||||
@Bean
|
||||
public TestLoadbalancerClient testLoadbalanceClient(
|
||||
LoadBalancerClient loadBalancerClient) {
|
||||
return new TestLoadbalancerClient(loadBalancerClient);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CommandLineRunner commandLineRunner(
|
||||
final TestLoadbalancerClient testLoadbalancerClient) {
|
||||
return args -> testLoadbalancerClient.doStuff();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class TestLoadbalancerClient {
|
||||
|
||||
Log log = LogFactory.getLog(this.getClass());
|
||||
|
||||
private LoadBalancerClient loadBalancerClient;
|
||||
|
||||
private boolean instanceFound = false;
|
||||
|
||||
TestLoadbalancerClient(LoadBalancerClient loadBalancerClient) {
|
||||
this.loadBalancerClient = loadBalancerClient;
|
||||
}
|
||||
|
||||
public void doStuff() {
|
||||
ServiceInstance serviceInstance = loadBalancerClient
|
||||
.choose("https://host/doStuff");
|
||||
if (serviceInstance != null) {
|
||||
log.info(
|
||||
"There is a service instance, because Eureka discovery is enabled and the service is registered");
|
||||
instanceFound = true;
|
||||
}
|
||||
else {
|
||||
log.warn(
|
||||
"No instance found, because Eureka is disabled or there is no service matching.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>2.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>3.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-eureka-server</artifactId>
|
||||
@@ -41,10 +41,6 @@
|
||||
<artifactId>spring-cloud-context</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-hystrix</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-eureka-client</artifactId>
|
||||
@@ -75,17 +71,6 @@
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.archaius</groupId>
|
||||
<artifactId>archaius-core</artifactId>
|
||||
</dependency>
|
||||
<!-- archaius dependencies that are now runtime -->
|
||||
<dependency>
|
||||
<groupId>commons-configuration</groupId>
|
||||
<artifactId>commons-configuration</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!-- end archaius deps -->
|
||||
<dependency>
|
||||
<groupId>javax.inject</groupId>
|
||||
<artifactId>javax.inject</artifactId>
|
||||
|
||||
@@ -30,7 +30,6 @@ import com.netflix.appinfo.AmazonInfo;
|
||||
import com.netflix.appinfo.ApplicationInfoManager;
|
||||
import com.netflix.appinfo.DataCenterInfo;
|
||||
import com.netflix.appinfo.InstanceInfo;
|
||||
import com.netflix.config.ConfigurationManager;
|
||||
import com.netflix.discovery.shared.Application;
|
||||
import com.netflix.discovery.shared.Pair;
|
||||
import com.netflix.eureka.EurekaServerContext;
|
||||
@@ -118,10 +117,8 @@ public class EurekaController {
|
||||
private void populateHeader(Map<String, Object> model) {
|
||||
model.put("currentTime", StatusResource.getCurrentTimeAsString());
|
||||
model.put("upTime", StatusInfo.getUpTime());
|
||||
model.put("environment",
|
||||
ConfigurationManager.getDeploymentContext().getDeploymentEnvironment());
|
||||
model.put("datacenter",
|
||||
ConfigurationManager.getDeploymentContext().getDeploymentDatacenter());
|
||||
model.put("environment", "N/A"); // FIXME:
|
||||
model.put("datacenter", "N/A"); // FIXME:
|
||||
PeerAwareInstanceRegistry registry = getRegistry();
|
||||
model.put("registry", registry);
|
||||
model.put("isBelowRenewThresold", registry.isBelowRenewThresold() == 1);
|
||||
|
||||
@@ -21,7 +21,6 @@ import javax.servlet.ServletContext;
|
||||
import com.netflix.appinfo.ApplicationInfoManager;
|
||||
import com.netflix.appinfo.DataCenterInfo;
|
||||
import com.netflix.appinfo.InstanceInfo;
|
||||
import com.netflix.config.ConfigurationManager;
|
||||
import com.netflix.discovery.EurekaClientConfig;
|
||||
import com.netflix.discovery.converters.JsonXStream;
|
||||
import com.netflix.discovery.converters.XmlXStream;
|
||||
@@ -44,18 +43,6 @@ public class EurekaServerBootstrap {
|
||||
|
||||
private static final Log log = LogFactory.getLog(EurekaServerBootstrap.class);
|
||||
|
||||
private static final String TEST = "test";
|
||||
|
||||
private static final String ARCHAIUS_DEPLOYMENT_ENVIRONMENT = "archaius.deployment.environment";
|
||||
|
||||
private static final String EUREKA_ENVIRONMENT = "eureka.environment";
|
||||
|
||||
private static final String DEFAULT = "default";
|
||||
|
||||
private static final String ARCHAIUS_DEPLOYMENT_DATACENTER = "archaius.deployment.datacenter";
|
||||
|
||||
private static final String EUREKA_DATACENTER = "eureka.datacenter";
|
||||
|
||||
protected EurekaServerConfig eurekaServerConfig;
|
||||
|
||||
protected ApplicationInfoManager applicationInfoManager;
|
||||
@@ -109,30 +96,6 @@ public class EurekaServerBootstrap {
|
||||
protected void initEurekaEnvironment() throws Exception {
|
||||
log.info("Setting the eureka configuration..");
|
||||
|
||||
String dataCenter = ConfigurationManager.getConfigInstance()
|
||||
.getString(EUREKA_DATACENTER);
|
||||
if (dataCenter == null) {
|
||||
log.info(
|
||||
"Eureka data center value eureka.datacenter is not set, defaulting to default");
|
||||
ConfigurationManager.getConfigInstance()
|
||||
.setProperty(ARCHAIUS_DEPLOYMENT_DATACENTER, DEFAULT);
|
||||
}
|
||||
else {
|
||||
ConfigurationManager.getConfigInstance()
|
||||
.setProperty(ARCHAIUS_DEPLOYMENT_DATACENTER, dataCenter);
|
||||
}
|
||||
String environment = ConfigurationManager.getConfigInstance()
|
||||
.getString(EUREKA_ENVIRONMENT);
|
||||
if (environment == null) {
|
||||
ConfigurationManager.getConfigInstance()
|
||||
.setProperty(ARCHAIUS_DEPLOYMENT_ENVIRONMENT, TEST);
|
||||
log.info(
|
||||
"Eureka environment value eureka.environment is not set, defaulting to test");
|
||||
}
|
||||
else {
|
||||
ConfigurationManager.getConfigInstance()
|
||||
.setProperty(ARCHAIUS_DEPLOYMENT_ENVIRONMENT, environment);
|
||||
}
|
||||
}
|
||||
|
||||
protected void initEurekaServerContext() throws Exception {
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
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>
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-build</artifactId>
|
||||
<version>2.2.2.BUILD-SNAPSHOT</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-hystrix-contract</artifactId>
|
||||
<version>2.2.2.BUILD-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>spring-cloud-netflix-hystrix-contract</name>
|
||||
<description>Spring Cloud Netflix Hystrix Contract</description>
|
||||
<properties>
|
||||
<donotreplacespring-cloud-contract.version>2.1.3.RELEASE</donotreplacespring-cloud-contract.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<version>3.11.0</version> <!-- Spring boot 2.1 has a binary incompatible version with boot 2.0 -->
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-contract-verifier</artifactId>
|
||||
<version>${donotreplacespring-cloud-contract.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>spring</id>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>https://repo.spring.io/libs-snapshot-local</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
<releases>
|
||||
<enabled>false</enabled>
|
||||
</releases>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>https://repo.spring.io/libs-milestone-local</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spring-releases</id>
|
||||
<name>Spring Releases</name>
|
||||
<url>https://repo.spring.io/release</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>https://repo.spring.io/libs-snapshot-local</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
<releases>
|
||||
<enabled>false</enabled>
|
||||
</releases>
|
||||
</pluginRepository>
|
||||
<pluginRepository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>https://repo.spring.io/libs-milestone-local</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
<pluginRepository>
|
||||
<id>spring-releases</id>
|
||||
<name>Spring Releases</name>
|
||||
<url>https://repo.spring.io/libs-release-local</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016-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.netflix.hystrix.contract;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Daniel Lavoie
|
||||
*
|
||||
*/
|
||||
public final class HystrixContractUtils {
|
||||
|
||||
private HystrixContractUtils() {
|
||||
throw new AssertionError("Must not instantiate utility class.");
|
||||
}
|
||||
|
||||
|
||||
public static String simpleBody() {
|
||||
try {
|
||||
return StreamUtils.copyToString(new DefaultResourceLoader()
|
||||
.getResource("classpath:/stubs/simpleBody.json").getInputStream(),
|
||||
StandardCharsets.UTF_8);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new IllegalStateException("Cannot read stub", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkEvent(String event) {
|
||||
assertThat(event).isNotNull();
|
||||
assertThat(event).isEqualTo("message");
|
||||
}
|
||||
|
||||
public static void checkOrigin(Map<String, Object> origin) {
|
||||
assertThat(origin.get("host")).isNotNull();
|
||||
assertThat(origin.get("port")).isNotNull();
|
||||
assertThat(origin.get("serviceId")).isEqualTo("application");
|
||||
// TODO: boot 2 changed application context id generation
|
||||
assertThat(origin.get("id")).asString().startsWith("application");
|
||||
}
|
||||
|
||||
public static void checkData(Map<String, Object> data, String group, String name) {
|
||||
if (!data.get("type").equals("HystrixCommand")) {
|
||||
assertThat(data.get("type")).isEqualTo("HystrixThreadPool");
|
||||
return;
|
||||
}
|
||||
assertThat(data.get("type")).isEqualTo("HystrixCommand");
|
||||
if (!data.get("name").equals(name)) {
|
||||
return;
|
||||
}
|
||||
assertThat(data.get("name")).asString().isEqualTo(name);
|
||||
assertThat(data.get("group")).isNotNull();
|
||||
assertThat(data.get("group")).isEqualTo(group);
|
||||
assertThat(data.get("errorCount")).isEqualTo(0);
|
||||
assertThat(data.get("errorPercentage")).isEqualTo(0);
|
||||
assertThat(data.get("requestCount")).isInstanceOf(java.lang.Integer.class);
|
||||
assertThat(data.get("currentConcurrentExecutionCount"))
|
||||
.isInstanceOf(java.lang.Integer.class);
|
||||
assertThat(data.get("rollingCountFailure")).isEqualTo(0);
|
||||
assertThat(data.get("rollingCountSuccess")).isInstanceOf(java.lang.Integer.class);
|
||||
assertThat(data.get("rollingCountShortCircuited")).isEqualTo(0);
|
||||
assertThat(data.get("rollingCountFallbackSuccess")).isEqualTo(0);
|
||||
assertThat(data.get("isCircuitBreakerOpen")).isEqualTo(false);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
{
|
||||
"origin":{
|
||||
"host":"192.168.1.192",
|
||||
"port":0,
|
||||
"serviceId":"application",
|
||||
"id":"application:0"
|
||||
},
|
||||
"event" : "message",
|
||||
"data":{
|
||||
"type":"HystrixCommand",
|
||||
"name":"application.hello",
|
||||
"group":"Application",
|
||||
"currentTime":1494840901153,
|
||||
"isCircuitBreakerOpen":false,
|
||||
"errorPercentage":0,
|
||||
"errorCount":0,
|
||||
"requestCount":1,
|
||||
"rollingCountCollapsedRequests":0,
|
||||
"rollingCountExceptionsThrown":0,
|
||||
"rollingCountFailure":0,
|
||||
"rollingCountFallbackFailure":0,
|
||||
"rollingCountFallbackRejection":0,
|
||||
"rollingCountFallbackSuccess":0,
|
||||
"rollingCountResponsesFromCache":0,
|
||||
"rollingCountSemaphoreRejected":0,
|
||||
"rollingCountShortCircuited":0,
|
||||
"rollingCountSuccess":0,
|
||||
"rollingCountThreadPoolRejected":0,
|
||||
"rollingCountTimeout":0,
|
||||
"currentConcurrentExecutionCount":0,
|
||||
"latencyExecute_mean":0,
|
||||
"latencyExecute":{
|
||||
"0":0,
|
||||
"25":0,
|
||||
"50":0,
|
||||
"75":0,
|
||||
"90":0,
|
||||
"95":0,
|
||||
"99":0,
|
||||
"99.5":0,
|
||||
"100":0
|
||||
},
|
||||
"latencyTotal_mean":0,
|
||||
"latencyTotal":{
|
||||
"0":0,
|
||||
"25":0,
|
||||
"50":0,
|
||||
"75":0,
|
||||
"90":0,
|
||||
"95":0,
|
||||
"99":0,
|
||||
"99.5":0,
|
||||
"100":0
|
||||
},
|
||||
"propertyValue_circuitBreakerRequestVolumeThreshold":20,
|
||||
"propertyValue_circuitBreakerSleepWindowInMilliseconds":5000,
|
||||
"propertyValue_circuitBreakerErrorThresholdPercentage":50,
|
||||
"propertyValue_circuitBreakerForceOpen":false,
|
||||
"propertyValue_circuitBreakerForceClosed":false,
|
||||
"propertyValue_circuitBreakerEnabled":true,
|
||||
"propertyValue_executionIsolationStrategy":"THREAD",
|
||||
"propertyValue_executionIsolationThreadTimeoutInMilliseconds":1000,
|
||||
"propertyValue_executionIsolationThreadInterruptOnTimeout":true,
|
||||
"propertyValue_executionIsolationThreadPoolKeyOverride":null,
|
||||
"propertyValue_executionIsolationSemaphoreMaxConcurrentRequests":10,
|
||||
"propertyValue_fallbackIsolationSemaphoreMaxConcurrentRequests":10,
|
||||
"propertyValue_metricsRollingStatisticalWindowInMilliseconds":10000,
|
||||
"propertyValue_requestCacheEnabled":true,
|
||||
"propertyValue_requestLogEnabled":true,
|
||||
"reportingHosts":1
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
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>
|
||||
<artifactId>spring-cloud-netflix-hystrix-dashboard</artifactId>
|
||||
<name>Spring Cloud Netflix Hystrix</name>
|
||||
<url>https://projects.spring.io/spring-cloud/</url>
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>2.2.2.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-freemarker</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-commons</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-hystrix</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.hystrix</groupId>
|
||||
<artifactId>hystrix-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.hystrix</groupId>
|
||||
<artifactId>hystrix-metrics-event-stream</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>jquery</artifactId>
|
||||
<version>2.1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>d3js</artifactId>
|
||||
<version>3.4.11</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2015 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.netflix.hystrix.dashboard;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Import(HystrixDashboardConfiguration.class)
|
||||
public @interface EnableHystrixDashboard {
|
||||
|
||||
}
|
||||
@@ -1,297 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.hystrix.dashboard;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.apache.http.impl.conn.PoolingClientConnectionManager;
|
||||
import org.apache.http.params.HttpConnectionParams;
|
||||
import org.apache.http.params.HttpParams;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
||||
import org.springframework.cloud.client.actuator.HasFeatures;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.ui.freemarker.SpringTemplateLoader;
|
||||
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Roy Clarkson
|
||||
* @author Fahim Farook
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties(HystrixDashboardProperties.class)
|
||||
public class HystrixDashboardConfiguration {
|
||||
|
||||
private static final String DEFAULT_TEMPLATE_LOADER_PATH = "classpath:/templates/";
|
||||
|
||||
private static final String DEFAULT_CHARSET = "UTF-8";
|
||||
|
||||
@Autowired
|
||||
private HystrixDashboardProperties dashboardProperties;
|
||||
|
||||
@Bean
|
||||
public HasFeatures hystrixDashboardFeature() {
|
||||
return HasFeatures.namedFeature("Hystrix Dashboard",
|
||||
HystrixDashboardConfiguration.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Spring Boot's {@link FreeMarkerAutoConfiguration} to prefer using a
|
||||
* {@link SpringTemplateLoader} instead of the file system. This corrects an issue
|
||||
* where Spring Boot may use an empty 'templates' file resource to resolve templates
|
||||
* instead of the packaged Hystrix classpath templates.
|
||||
* @return FreeMarker configuration
|
||||
*/
|
||||
@Bean
|
||||
public FreeMarkerConfigurer freeMarkerConfigurer() {
|
||||
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
|
||||
configurer.setTemplateLoaderPaths(DEFAULT_TEMPLATE_LOADER_PATH);
|
||||
configurer.setDefaultEncoding(DEFAULT_CHARSET);
|
||||
configurer.setPreferFileSystemAccess(false);
|
||||
return configurer;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ServletRegistrationBean proxyStreamServlet() {
|
||||
final ProxyStreamServlet proxyStreamServlet = new ProxyStreamServlet();
|
||||
proxyStreamServlet.setEnableIgnoreConnectionCloseHeader(
|
||||
this.dashboardProperties.isEnableIgnoreConnectionCloseHeader());
|
||||
final ServletRegistrationBean registration = new ServletRegistrationBean(
|
||||
proxyStreamServlet, "/proxy.stream");
|
||||
registration.setInitParameters(this.dashboardProperties.getInitParameters());
|
||||
return registration;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HystrixDashboardController hsytrixDashboardController() {
|
||||
return new HystrixDashboardController();
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxy an EventStream request (data.stream via proxy.stream) since EventStream does
|
||||
* not yet support CORS (https://bugs.webkit.org/show_bug.cgi?id=61862) so that a UI
|
||||
* can request a stream from a different server.
|
||||
*/
|
||||
public static class ProxyStreamServlet extends HttpServlet {
|
||||
|
||||
private static final Log log = LogFactory.getLog(ProxyStreamServlet.class);
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String CONNECTION_CLOSE_VALUE = "close";
|
||||
|
||||
private boolean enableIgnoreConnectionCloseHeader = false;
|
||||
|
||||
public void setEnableIgnoreConnectionCloseHeader(
|
||||
boolean enableIgnoreConnectionCloseHeader) {
|
||||
this.enableIgnoreConnectionCloseHeader = enableIgnoreConnectionCloseHeader;
|
||||
}
|
||||
|
||||
public ProxyStreamServlet() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest
|
||||
* request, javax.servlet.http.HttpServletResponse response)
|
||||
*/
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
String origin = request.getParameter("origin");
|
||||
if (origin == null) {
|
||||
response.setStatus(500);
|
||||
response.getWriter().println(
|
||||
"Required parameter 'origin' missing. Example: 107.20.175.135:7001");
|
||||
return;
|
||||
}
|
||||
origin = origin.trim();
|
||||
|
||||
HttpGet httpget = null;
|
||||
InputStream is = null;
|
||||
boolean hasFirstParameter = false;
|
||||
StringBuilder url = new StringBuilder();
|
||||
if (!origin.startsWith("http")) {
|
||||
url.append("http://");
|
||||
}
|
||||
url.append(origin);
|
||||
if (origin.contains("?")) {
|
||||
hasFirstParameter = true;
|
||||
}
|
||||
Map<String, String[]> params = request.getParameterMap();
|
||||
for (String key : params.keySet()) {
|
||||
if (!key.equals("origin")) {
|
||||
String[] values = params.get(key);
|
||||
String value = values[0].trim();
|
||||
if (hasFirstParameter) {
|
||||
url.append("&");
|
||||
}
|
||||
else {
|
||||
url.append("?");
|
||||
hasFirstParameter = true;
|
||||
}
|
||||
url.append(key).append("=").append(value);
|
||||
}
|
||||
}
|
||||
String proxyUrl = url.toString();
|
||||
log.info("\n\nProxy opening connection to: " + proxyUrl + "\n\n");
|
||||
try {
|
||||
httpget = new HttpGet(proxyUrl);
|
||||
HttpClient client = ProxyConnectionManager.httpClient;
|
||||
HttpResponse httpResponse = client.execute(httpget);
|
||||
int statusCode = httpResponse.getStatusLine().getStatusCode();
|
||||
if (statusCode == HttpStatus.SC_OK) {
|
||||
// writeTo swallows exceptions and never quits even if outputstream is
|
||||
// throwing IOExceptions (such as broken pipe) ... since the
|
||||
// inputstream is infinite
|
||||
// httpResponse.getEntity().writeTo(new
|
||||
// OutputStreamWrapper(response.getOutputStream()));
|
||||
// so I copy it manually ...
|
||||
is = httpResponse.getEntity().getContent();
|
||||
|
||||
// set headers
|
||||
copyHeadersToServletResponse(httpResponse.getAllHeaders(), response);
|
||||
|
||||
// copy data from source to response
|
||||
OutputStream os = response.getOutputStream();
|
||||
int b = -1;
|
||||
while ((b = is.read()) != -1) {
|
||||
try {
|
||||
os.write(b);
|
||||
if (b == 10 /** flush buffer on line feed */
|
||||
) {
|
||||
os.flush();
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
if (ex.getClass().getSimpleName()
|
||||
.equalsIgnoreCase("ClientAbortException")) {
|
||||
// don't throw an exception as this means the user closed
|
||||
// the connection
|
||||
log.debug(
|
||||
"Connection closed by client. Will stop proxying ...");
|
||||
// break out of the while loop
|
||||
break;
|
||||
}
|
||||
else {
|
||||
// received unknown error while writing so throw an
|
||||
// exception
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.warn("Failed opening connection to " + proxyUrl + " : "
|
||||
+ statusCode + " : " + httpResponse.getStatusLine());
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
log.error("Error proxying request: " + url, ex);
|
||||
}
|
||||
finally {
|
||||
if (httpget != null) {
|
||||
try {
|
||||
httpget.abort();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
log.error("failed aborting proxy connection.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
// httpget.abort() MUST be called first otherwise is.close() hangs
|
||||
// (because data is still streaming?)
|
||||
if (is != null) {
|
||||
// this should already be closed by httpget.abort() above
|
||||
try {
|
||||
is.close();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// ignore errors on close
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void copyHeadersToServletResponse(Header[] headers,
|
||||
HttpServletResponse response) {
|
||||
for (Header header : headers) {
|
||||
// Some versions of Cloud Foundry (HAProxy) are
|
||||
// incorrectly setting a "Connection: close" header
|
||||
// causing the Hystrix dashboard to close the connection
|
||||
// to the stream
|
||||
// https://github.com/cloudfoundry/gorouter/issues/71
|
||||
if (this.enableIgnoreConnectionCloseHeader
|
||||
&& HttpHeaders.CONNECTION.equalsIgnoreCase(header.getName())
|
||||
&& CONNECTION_CLOSE_VALUE.equalsIgnoreCase(header.getValue())) {
|
||||
log.warn("Ignoring 'Connection: close' header from stream response");
|
||||
}
|
||||
else if (!HttpHeaders.TRANSFER_ENCODING
|
||||
.equalsIgnoreCase(header.getName())) {
|
||||
response.addHeader(header.getName(), header.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private static class ProxyConnectionManager {
|
||||
|
||||
private final static PoolingClientConnectionManager threadSafeConnectionManager = new PoolingClientConnectionManager();
|
||||
|
||||
private final static HttpClient httpClient = new DefaultHttpClient(
|
||||
threadSafeConnectionManager);
|
||||
|
||||
static {
|
||||
log.debug("Initialize ProxyConnectionManager");
|
||||
/* common settings */
|
||||
HttpParams httpParams = httpClient.getParams();
|
||||
HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
|
||||
HttpConnectionParams.setSoTimeout(httpParams, 10000);
|
||||
|
||||
/* number of connections to allow */
|
||||
threadSafeConnectionManager.setDefaultMaxPerRoute(400);
|
||||
threadSafeConnectionManager.setMaxTotal(400);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.hystrix.dashboard;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Controller
|
||||
public class HystrixDashboardController {
|
||||
|
||||
@RequestMapping("/hystrix")
|
||||
public String home(Model model, WebRequest request) {
|
||||
model.addAttribute("basePath", extractPath(request));
|
||||
return "hystrix/index";
|
||||
}
|
||||
|
||||
@RequestMapping("/hystrix/{path}")
|
||||
public String monitor(@PathVariable String path, Model model, WebRequest request) {
|
||||
model.addAttribute("basePath", extractPath(request));
|
||||
model.addAttribute("contextPath", request.getContextPath());
|
||||
return "hystrix/" + path;
|
||||
}
|
||||
|
||||
private String extractPath(WebRequest request) {
|
||||
String path = request.getContextPath() + request.getAttribute(
|
||||
"org.springframework."
|
||||
+ "web.servlet.HandlerMapping.pathWithinHandlerMapping",
|
||||
RequestAttributes.SCOPE_REQUEST);
|
||||
return path;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.hystrix.dashboard;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* @author Roy Clarkson
|
||||
* @author Fahim Farook
|
||||
*/
|
||||
@ConfigurationProperties("hystrix.dashboard")
|
||||
public class HystrixDashboardProperties {
|
||||
|
||||
/**
|
||||
* Directs the Hystrix dashboard to ignore 'Connection:close' headers if present in
|
||||
* the Hystrix response stream.
|
||||
*/
|
||||
private boolean enableIgnoreConnectionCloseHeader = false;
|
||||
|
||||
/**
|
||||
* Initialization parameters for {@link ProxyStreamServlet}. ProxyStreamServlet itself
|
||||
* is not dependent on any initialization parameters, but could be used for adding web
|
||||
* container specific configurations. i.e. wl-dispatch-policy for WebLogic.
|
||||
*/
|
||||
private Map<String, String> initParameters = new HashMap<>();
|
||||
|
||||
public boolean isEnableIgnoreConnectionCloseHeader() {
|
||||
return enableIgnoreConnectionCloseHeader;
|
||||
}
|
||||
|
||||
public void setEnableIgnoreConnectionCloseHeader(
|
||||
boolean enableIgnoreConnectionCloseHeader) {
|
||||
this.enableIgnoreConnectionCloseHeader = enableIgnoreConnectionCloseHeader;
|
||||
}
|
||||
|
||||
public Map<String, String> getInitParameters() {
|
||||
return this.initParameters;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
.dependencies .spacer {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
padding-top:4px;
|
||||
clear:both;
|
||||
}
|
||||
|
||||
|
||||
.dependencies .last {
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
.dependencies span.loading {
|
||||
display: block;
|
||||
padding-top: 6%;
|
||||
padding-bottom: 6%;
|
||||
color: gray;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dependencies span.loading.failed {
|
||||
color: red;
|
||||
}
|
||||
|
||||
|
||||
.dependencies div.monitor {
|
||||
float: left;
|
||||
margin-right:5px;
|
||||
margin-top:5px;
|
||||
}
|
||||
|
||||
.dependencies div.monitor p.name {
|
||||
font-weight:bold;
|
||||
font-size: 10pt;
|
||||
text-align: right;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.dependencies div.monitor_data {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* override the HREF when we have specified it as a tooltip to not act like a link */
|
||||
.dependencies div.monitor_data a.tooltip {
|
||||
text-decoration: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.dependencies div.monitor_data div.counters {
|
||||
text-align: right;
|
||||
padding-bottom: 10px;
|
||||
font-size: 10pt;
|
||||
clear: both;
|
||||
|
||||
}
|
||||
|
||||
.dependencies div.monitor_data div.counters div.cell {
|
||||
display: inline;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.dependencies .borderRight {
|
||||
border-right: 1px solid grey;
|
||||
padding-right: 6px;
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.dependencies div.cell .line {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dependencies div.monitor_data a,
|
||||
.dependencies span.rate_value {
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
|
||||
.dependencies span.smaller {
|
||||
font-size: 8pt;
|
||||
color: grey;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.dependencies div.tableRow {
|
||||
width:100%;
|
||||
white-space: nowrap;
|
||||
font-size: 8pt;
|
||||
margin: 0 auto;
|
||||
clear:both;
|
||||
padding-left:26%;
|
||||
}
|
||||
|
||||
.dependencies div.tableRow .cell {
|
||||
float:left;
|
||||
}
|
||||
|
||||
.dependencies div.tableRow .header {
|
||||
width:18%;
|
||||
text-align:right;
|
||||
padding-right:2%;
|
||||
}
|
||||
|
||||
.dependencies div.tableRow .data {
|
||||
width:17%;
|
||||
font-weight: bold;
|
||||
text-align:right;
|
||||
}
|
||||
|
||||
|
||||
.dependencies div.monitor {
|
||||
width: 245px; /* we want a fixed width instead of percentage as I want the boxes to be a set size and then fill in as many as can fit in each row ... this allows 3 columns on an iPad */
|
||||
height: 155px;
|
||||
}
|
||||
|
||||
.dependencies .success {
|
||||
color: green;
|
||||
}
|
||||
.dependencies .shortCircuited {
|
||||
color: blue;
|
||||
}
|
||||
.dependencies .timeout {
|
||||
color: #FF9900; /* shade of orange */
|
||||
}
|
||||
.dependencies .failure {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.badRequest {
|
||||
color: #00CC99;
|
||||
}
|
||||
|
||||
.dependencies .rejected {
|
||||
color: purple;
|
||||
}
|
||||
|
||||
.dependencies .exceptionsThrown {
|
||||
color: brown;
|
||||
}
|
||||
|
||||
.dependencies div.monitor_data a.rate {
|
||||
color: black;
|
||||
font-size: 11pt;
|
||||
}
|
||||
|
||||
.dependencies div.rate {
|
||||
padding-top: 1px;
|
||||
clear:both;
|
||||
text-align:right;
|
||||
}
|
||||
|
||||
.dependencies .errorPercentage {
|
||||
color: grey;
|
||||
}
|
||||
|
||||
.dependencies div.cell .errorPercentage {
|
||||
padding-left:5px;
|
||||
font-size: 12pt !important;
|
||||
}
|
||||
|
||||
|
||||
.dependencies div.monitor div.chart {
|
||||
}
|
||||
|
||||
.dependencies div.monitor div.chart svg {
|
||||
}
|
||||
|
||||
.dependencies div.monitor div.chart svg text {
|
||||
fill: white;
|
||||
}
|
||||
|
||||
|
||||
.dependencies div.circuitStatus {
|
||||
width:100%;
|
||||
white-space: nowrap;
|
||||
font-size: 9pt;
|
||||
margin: 0 auto;
|
||||
clear:both;
|
||||
text-align:right;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.dependencies #hidden {
|
||||
width:1px;
|
||||
height:1px;
|
||||
background: lightgrey;
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* sparkline */
|
||||
.dependencies path {
|
||||
stroke: steelblue;
|
||||
stroke-width: 1;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,542 +0,0 @@
|
||||
|
||||
(function(window) {
|
||||
|
||||
// cache the templates we use on this page as global variables (asynchronously)
|
||||
jQuery.get(getRelativePath("components/hystrixCommand/templates/hystrixCircuit.html"), function(data) {
|
||||
hystrixTemplateCircuit = data;
|
||||
});
|
||||
jQuery.get(getRelativePath("components/hystrixCommand/templates/hystrixCircuitContainer.html"), function(data) {
|
||||
hystrixTemplateCircuitContainer = data;
|
||||
});
|
||||
|
||||
function getRelativePath(path) {
|
||||
var p = location.pathname.slice(0, location.pathname.lastIndexOf("/")+1);
|
||||
return p + path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Object containing functions for displaying and updating the UI with streaming data.
|
||||
*
|
||||
* Publish this externally as "HystrixCommandMonitor"
|
||||
*/
|
||||
window.HystrixCommandMonitor = function(containerId, args) {
|
||||
|
||||
var self = this; // keep scope under control
|
||||
self.args = args;
|
||||
if(self.args == undefined) {
|
||||
self.args = {};
|
||||
}
|
||||
|
||||
this.containerId = containerId;
|
||||
|
||||
/**
|
||||
* Initialization on construction
|
||||
*/
|
||||
// intialize various variables we use for visualization
|
||||
var maxXaxisForCircle="40%";
|
||||
var maxYaxisForCircle="40%";
|
||||
var maxRadiusForCircle="125";
|
||||
|
||||
// CIRCUIT_BREAKER circle visualization settings
|
||||
self.circuitCircleRadius = d3.scale.pow().exponent(0.5).domain([0, 400]).range(["5", maxRadiusForCircle]); // requests per second per host
|
||||
self.circuitCircleYaxis = d3.scale.linear().domain([0, 400]).range(["30%", maxXaxisForCircle]);
|
||||
self.circuitCircleXaxis = d3.scale.linear().domain([0, 400]).range(["30%", maxYaxisForCircle]);
|
||||
self.circuitColorRange = d3.scale.linear().domain([10, 25, 40, 50]).range(["green", "#FFCC00", "#FF9900", "red"]);
|
||||
self.circuitErrorPercentageColorRange = d3.scale.linear().domain([0, 10, 35, 50]).range(["grey", "black", "#FF9900", "red"]);
|
||||
|
||||
/**
|
||||
* We want to keep sorting in the background since data values are always changing, so this will re-sort every X milliseconds
|
||||
* to maintain whatever sort the user (or default) has chosen.
|
||||
*
|
||||
* In other words, sorting only for adds/deletes is not sufficient as all but alphabetical sort are dynamically changing.
|
||||
*/
|
||||
setInterval(function() {
|
||||
// sort since we have added a new one
|
||||
self.sortSameAsLast();
|
||||
}, 10000);
|
||||
|
||||
|
||||
/**
|
||||
* END of Initialization on construction
|
||||
*/
|
||||
|
||||
/**
|
||||
* Event listener to handle new messages from EventSource as streamed from the server.
|
||||
*/
|
||||
/* public */ self.eventSourceMessageListener = function(e) {
|
||||
var data = JSON.parse(e.data);
|
||||
if(data) {
|
||||
// check for reportingHosts (if not there, set it to 1 for singleHost vs cluster)
|
||||
if(!data.reportingHosts) {
|
||||
data.reportingHosts = 1;
|
||||
}
|
||||
|
||||
if(data && data.type == 'HystrixCommand') {
|
||||
if (data.deleteData == 'true') {
|
||||
deleteCircuit(data.escapedName);
|
||||
} else {
|
||||
displayCircuit(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Pre process the data before displying in the UI.
|
||||
* e.g Get Averages from sums, do rate calculation etc.
|
||||
*/
|
||||
function preProcessData(data) {
|
||||
// set defaults for values that may be missing from older streams
|
||||
setIfMissing(data, "rollingCountBadRequests", 0);
|
||||
// assert all the values we need
|
||||
validateData(data);
|
||||
// escape string used in jQuery & d3 selectors
|
||||
data.escapedName = data.name.replace(/([ !"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g,'\\$1');
|
||||
// do math
|
||||
convertAllAvg(data);
|
||||
calcRatePerSecond(data);
|
||||
}
|
||||
|
||||
function setIfMissing(data, key, defaultValue) {
|
||||
if(data[key] == undefined) {
|
||||
data[key] = defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Since the stream of data can be aggregated from multiple hosts in a tiered manner
|
||||
* the aggregation just sums everything together and provides us the denominator (reportingHosts)
|
||||
* so we must divide by it to get an average per instance value.
|
||||
*
|
||||
* We want to do this on any numerical values where we want per instance rather than cluster-wide sum.
|
||||
*/
|
||||
function convertAllAvg(data) {
|
||||
convertAvg(data, "errorPercentage", true);
|
||||
convertAvg(data, "latencyExecute_mean", false);
|
||||
convertAvg(data, "latencyTotal_mean", false);
|
||||
}
|
||||
|
||||
function convertAvg(data, key, decimal) {
|
||||
if (decimal) {
|
||||
data[key] = getInstanceAverage(data[key], data["reportingHosts"], decimal);
|
||||
} else {
|
||||
data[key] = getInstanceAverage(data[key], data["reportingHosts"], decimal);
|
||||
}
|
||||
}
|
||||
|
||||
function getInstanceAverage(value, reportingHosts, decimal) {
|
||||
if (decimal) {
|
||||
return roundNumber(value/reportingHosts);
|
||||
} else {
|
||||
return Math.floor(value/reportingHosts);
|
||||
}
|
||||
}
|
||||
|
||||
function calcRatePerSecond(data) {
|
||||
var numberSeconds = data["propertyValue_metricsRollingStatisticalWindowInMilliseconds"] / 1000;
|
||||
|
||||
var totalRequests = data["requestCount"];
|
||||
if (totalRequests < 0) {
|
||||
totalRequests = 0;
|
||||
}
|
||||
data["ratePerSecond"] = roundNumber(totalRequests / numberSeconds);
|
||||
data["ratePerSecondPerHost"] = roundNumber(totalRequests / numberSeconds / data["reportingHosts"]) ;
|
||||
}
|
||||
|
||||
function validateData(data) {
|
||||
assertNotNull(data,"reportingHosts");
|
||||
assertNotNull(data,"type");
|
||||
assertNotNull(data,"name");
|
||||
assertNotNull(data,"group");
|
||||
// assertNotNull(data,"currentTime");
|
||||
assertNotNull(data,"isCircuitBreakerOpen");
|
||||
assertNotNull(data,"errorPercentage");
|
||||
assertNotNull(data,"errorCount");
|
||||
assertNotNull(data,"requestCount");
|
||||
assertNotNull(data,"rollingCountCollapsedRequests");
|
||||
assertNotNull(data,"rollingCountExceptionsThrown");
|
||||
assertNotNull(data,"rollingCountFailure");
|
||||
assertNotNull(data,"rollingCountFallbackFailure");
|
||||
assertNotNull(data,"rollingCountFallbackRejection");
|
||||
assertNotNull(data,"rollingCountFallbackSuccess");
|
||||
assertNotNull(data,"rollingCountResponsesFromCache");
|
||||
assertNotNull(data,"rollingCountSemaphoreRejected");
|
||||
assertNotNull(data,"rollingCountShortCircuited");
|
||||
assertNotNull(data,"rollingCountSuccess");
|
||||
assertNotNull(data,"rollingCountThreadPoolRejected");
|
||||
assertNotNull(data,"rollingCountTimeout");
|
||||
assertNotNull(data,"rollingCountBadRequests");
|
||||
assertNotNull(data,"currentConcurrentExecutionCount");
|
||||
assertNotNull(data,"latencyExecute_mean");
|
||||
assertNotNull(data,"latencyExecute");
|
||||
assertNotNull(data,"latencyTotal_mean");
|
||||
assertNotNull(data,"latencyTotal");
|
||||
assertNotNull(data,"propertyValue_circuitBreakerRequestVolumeThreshold");
|
||||
assertNotNull(data,"propertyValue_circuitBreakerSleepWindowInMilliseconds");
|
||||
assertNotNull(data,"propertyValue_circuitBreakerErrorThresholdPercentage");
|
||||
assertNotNull(data,"propertyValue_circuitBreakerForceOpen");
|
||||
assertNotNull(data,"propertyValue_circuitBreakerForceClosed");
|
||||
assertNotNull(data,"propertyValue_executionIsolationStrategy");
|
||||
assertNotNull(data,"propertyValue_executionIsolationThreadTimeoutInMilliseconds");
|
||||
assertNotNull(data,"propertyValue_executionIsolationThreadInterruptOnTimeout");
|
||||
// assertNotNull(data,"propertyValue_executionIsolationThreadPoolKeyOverride");
|
||||
assertNotNull(data,"propertyValue_executionIsolationSemaphoreMaxConcurrentRequests");
|
||||
assertNotNull(data,"propertyValue_fallbackIsolationSemaphoreMaxConcurrentRequests");
|
||||
assertNotNull(data,"propertyValue_requestCacheEnabled");
|
||||
assertNotNull(data,"propertyValue_requestLogEnabled");
|
||||
assertNotNull(data,"propertyValue_metricsRollingStatisticalWindowInMilliseconds");
|
||||
}
|
||||
|
||||
function assertNotNull(data, key) {
|
||||
if(data[key] == undefined) {
|
||||
throw new Error("Key Missing: " + key + " for " + data.name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to display the CIRCUIT data
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
/* private */ function displayCircuit(data) {
|
||||
|
||||
try {
|
||||
preProcessData(data);
|
||||
} catch (err) {
|
||||
log("Failed preProcessData: " + err.message);
|
||||
return;
|
||||
}
|
||||
|
||||
// add the 'addCommas' function to the 'data' object so the HTML templates can use it
|
||||
data.addCommas = addCommas;
|
||||
// add the 'roundNumber' function to the 'data' object so the HTML templates can use it
|
||||
data.roundNumber = roundNumber;
|
||||
// add the 'getInstanceAverage' function to the 'data' object so the HTML templates can use it
|
||||
data.getInstanceAverage = getInstanceAverage;
|
||||
|
||||
var addNew = false;
|
||||
// check if we need to create the container
|
||||
if(!$('#CIRCUIT_' + data.escapedName).length) {
|
||||
// args for display
|
||||
if(self.args.includeDetailIcon != undefined && self.args.includeDetailIcon) {
|
||||
data.includeDetailIcon = true;
|
||||
}else {
|
||||
data.includeDetailIcon = false;
|
||||
}
|
||||
|
||||
// it doesn't exist so add it
|
||||
var html = tmpl(hystrixTemplateCircuitContainer, data);
|
||||
// remove the loading thing first
|
||||
$('#' + containerId + ' span.loading').remove();
|
||||
// now create the new data and add it
|
||||
$('#' + containerId + '').append(html);
|
||||
|
||||
// add the default sparkline graph
|
||||
d3.selectAll('#graph_CIRCUIT_' + data.escapedName + ' svg').append("svg:path");
|
||||
|
||||
// remember this is new so we can trigger a sort after setting data
|
||||
addNew = true;
|
||||
}
|
||||
|
||||
|
||||
// now update/insert the data
|
||||
$('#CIRCUIT_' + data.escapedName + ' div.monitor_data').html(tmpl(hystrixTemplateCircuit, data));
|
||||
|
||||
var ratePerSecond = data.ratePerSecond;
|
||||
var ratePerSecondPerHost = data.ratePerSecondPerHost;
|
||||
var ratePerSecondPerHostDisplay = ratePerSecondPerHost;
|
||||
var errorThenVolume = (data.errorPercentage * 100000000) + ratePerSecond;
|
||||
|
||||
// set the rates on the div element so it's available for sorting
|
||||
$('#CIRCUIT_' + data.escapedName).attr('rate_value', ratePerSecond);
|
||||
$('#CIRCUIT_' + data.escapedName).attr('error_then_volume', errorThenVolume);
|
||||
|
||||
// update errorPercentage color on page
|
||||
$('#CIRCUIT_' + data.escapedName + ' a.errorPercentage').css('color', self.circuitErrorPercentageColorRange(data.errorPercentage));
|
||||
|
||||
updateCircle('circuit', '#CIRCUIT_' + data.escapedName + ' circle', ratePerSecondPerHostDisplay, data.errorPercentage);
|
||||
|
||||
if(data.graphValues) {
|
||||
// we have a set of values to initialize with
|
||||
updateSparkline('circuit', '#CIRCUIT_' + data.escapedName + ' path', data.graphValues);
|
||||
} else {
|
||||
updateSparkline('circuit', '#CIRCUIT_' + data.escapedName + ' path', ratePerSecond);
|
||||
}
|
||||
|
||||
if(addNew) {
|
||||
// sort since we added a new circuit
|
||||
self.sortSameAsLast();
|
||||
}
|
||||
}
|
||||
|
||||
/* round a number to X digits: num => the number to round, dec => the number of decimals */
|
||||
/* private */ function roundNumber(num) {
|
||||
var dec=1;
|
||||
var result = Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);
|
||||
var resultAsString = result.toString();
|
||||
if(resultAsString.indexOf('.') == -1) {
|
||||
resultAsString = resultAsString + '.0';
|
||||
}
|
||||
return resultAsString;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/* private */ function updateCircle(variablePrefix, cssTarget, rate, errorPercentage) {
|
||||
var newXaxisForCircle = self[variablePrefix + 'CircleXaxis'](rate);
|
||||
if(parseInt(newXaxisForCircle) > parseInt(maxXaxisForCircle)) {
|
||||
newXaxisForCircle = maxXaxisForCircle;
|
||||
}
|
||||
var newYaxisForCircle = self[variablePrefix + 'CircleYaxis'](rate);
|
||||
if(parseInt(newYaxisForCircle) > parseInt(maxYaxisForCircle)) {
|
||||
newYaxisForCircle = maxYaxisForCircle;
|
||||
}
|
||||
var newRadiusForCircle = self[variablePrefix + 'CircleRadius'](rate);
|
||||
if(parseInt(newRadiusForCircle) > parseInt(maxRadiusForCircle)) {
|
||||
newRadiusForCircle = maxRadiusForCircle;
|
||||
}
|
||||
|
||||
d3.selectAll(cssTarget)
|
||||
.transition()
|
||||
.duration(400)
|
||||
.attr("cy", newYaxisForCircle)
|
||||
.attr("cx", newXaxisForCircle)
|
||||
.attr("r", newRadiusForCircle)
|
||||
.style("fill", self[variablePrefix + 'ColorRange'](errorPercentage));
|
||||
}
|
||||
|
||||
/* private */ function updateSparkline(variablePrefix, cssTarget, newDataPoint) {
|
||||
var currentTimeMilliseconds = new Date().getTime();
|
||||
var data = self[variablePrefix + cssTarget + '_data'];
|
||||
if(typeof data == 'undefined') {
|
||||
// else it's new
|
||||
if(typeof newDataPoint == 'object') {
|
||||
// we received an array of values, so initialize with it
|
||||
data = newDataPoint;
|
||||
} else {
|
||||
// v: VALUE, t: TIME_IN_MILLISECONDS
|
||||
data = [{"v":parseFloat(newDataPoint),"t":currentTimeMilliseconds}];
|
||||
}
|
||||
self[variablePrefix + cssTarget + '_data'] = data;
|
||||
} else {
|
||||
if(typeof newDataPoint == 'object') {
|
||||
/* if an array is passed in we'll replace the cached one */
|
||||
data = newDataPoint;
|
||||
} else {
|
||||
// else we just add to the existing one
|
||||
data.push({"v":parseFloat(newDataPoint),"t":currentTimeMilliseconds});
|
||||
}
|
||||
}
|
||||
|
||||
while(data.length > 200) { // 400 should be plenty for the 2 minutes we have the scale set to below even with a very low update latency
|
||||
// remove data so we don't keep increasing forever
|
||||
data.shift();
|
||||
}
|
||||
|
||||
if(data.length == 1 && data[0].v == 0) {
|
||||
//console.log("we have a single 0 so skipping");
|
||||
// don't show if we have a single 0
|
||||
return;
|
||||
}
|
||||
|
||||
if(data.length > 1 && data[0].v == 0 && data[1].v != 0) {
|
||||
//console.log("we have a leading 0 so removing it");
|
||||
// get rid of a leading 0 if the following number is not a 0
|
||||
data.shift();
|
||||
}
|
||||
|
||||
var xScale = d3.time.scale().domain([new Date(currentTimeMilliseconds-(60*1000*2)), new Date(currentTimeMilliseconds)]).range([0, 140]);
|
||||
|
||||
var yMin = d3.min(data, function(d) { return d.v; });
|
||||
var yMax = d3.max(data, function(d) { return d.v; });
|
||||
var yScale = d3.scale.linear().domain([yMin, yMax]).nice().range([60, 0]); // y goes DOWN, so 60 is the "lowest"
|
||||
|
||||
sparkline = d3.svg.line()
|
||||
// assign the X function to plot our line as we wish
|
||||
.x(function(d,i) {
|
||||
// return the X coordinate where we want to plot this datapoint based on the time
|
||||
return xScale(new Date(d.t));
|
||||
})
|
||||
.y(function(d) {
|
||||
return yScale(d.v);
|
||||
})
|
||||
.interpolate("basis");
|
||||
|
||||
d3.selectAll(cssTarget).attr("d", sparkline(data));
|
||||
}
|
||||
|
||||
/* private */ function deleteCircuit(circuitName) {
|
||||
$('#CIRCUIT_' + circuitName).remove();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// public methods for sorting
|
||||
HystrixCommandMonitor.prototype.sortByVolume = function() {
|
||||
var direction = "desc";
|
||||
if(this.sortedBy == 'rate_desc') {
|
||||
direction = 'asc';
|
||||
}
|
||||
this.sortByVolumeInDirection(direction);
|
||||
};
|
||||
|
||||
HystrixCommandMonitor.prototype.sortByVolumeInDirection = function(direction) {
|
||||
this.sortedBy = 'rate_' + direction;
|
||||
$('#' + this.containerId + ' div.monitor').tsort({order: direction, attr: 'rate_value'});
|
||||
};
|
||||
|
||||
HystrixCommandMonitor.prototype.sortAlphabetically = function() {
|
||||
var direction = "asc";
|
||||
if(this.sortedBy == 'alph_asc') {
|
||||
direction = 'desc';
|
||||
}
|
||||
this.sortAlphabeticalInDirection(direction);
|
||||
};
|
||||
|
||||
HystrixCommandMonitor.prototype.sortAlphabeticalInDirection = function(direction) {
|
||||
this.sortedBy = 'alph_' + direction;
|
||||
$('#' + this.containerId + ' div.monitor').tsort("p.name", {order: direction});
|
||||
};
|
||||
|
||||
|
||||
HystrixCommandMonitor.prototype.sortByError = function() {
|
||||
var direction = "desc";
|
||||
if(this.sortedBy == 'error_desc') {
|
||||
direction = 'asc';
|
||||
}
|
||||
this.sortByErrorInDirection(direction);
|
||||
};
|
||||
|
||||
HystrixCommandMonitor.prototype.sortByErrorInDirection = function(direction) {
|
||||
this.sortedBy = 'error_' + direction;
|
||||
$('#' + this.containerId + ' div.monitor').tsort(".errorPercentage .value", {order: direction});
|
||||
};
|
||||
|
||||
HystrixCommandMonitor.prototype.sortByErrorThenVolume = function() {
|
||||
var direction = "desc";
|
||||
if(this.sortedBy == 'error_then_volume_desc') {
|
||||
direction = 'asc';
|
||||
}
|
||||
this.sortByErrorThenVolumeInDirection(direction);
|
||||
};
|
||||
|
||||
HystrixCommandMonitor.prototype.sortByErrorThenVolumeInDirection = function(direction) {
|
||||
this.sortedBy = 'error_then_volume_' + direction;
|
||||
$('#' + this.containerId + ' div.monitor').tsort({order: direction, attr: 'error_then_volume'});
|
||||
};
|
||||
|
||||
HystrixCommandMonitor.prototype.sortByLatency90 = function() {
|
||||
var direction = "desc";
|
||||
if(this.sortedBy == 'lat90_desc') {
|
||||
direction = 'asc';
|
||||
}
|
||||
this.sortedBy = 'lat90_' + direction;
|
||||
this.sortByMetricInDirection(direction, ".latency90 .value");
|
||||
};
|
||||
|
||||
HystrixCommandMonitor.prototype.sortByLatency99 = function() {
|
||||
var direction = "desc";
|
||||
if(this.sortedBy == 'lat99_desc') {
|
||||
direction = 'asc';
|
||||
}
|
||||
this.sortedBy = 'lat99_' + direction;
|
||||
this.sortByMetricInDirection(direction, ".latency99 .value");
|
||||
};
|
||||
|
||||
HystrixCommandMonitor.prototype.sortByLatency995 = function() {
|
||||
var direction = "desc";
|
||||
if(this.sortedBy == 'lat995_desc') {
|
||||
direction = 'asc';
|
||||
}
|
||||
this.sortedBy = 'lat995_' + direction;
|
||||
this.sortByMetricInDirection(direction, ".latency995 .value");
|
||||
};
|
||||
|
||||
HystrixCommandMonitor.prototype.sortByLatencyMean = function() {
|
||||
var direction = "desc";
|
||||
if(this.sortedBy == 'latMean_desc') {
|
||||
direction = 'asc';
|
||||
}
|
||||
this.sortedBy = 'latMean_' + direction;
|
||||
this.sortByMetricInDirection(direction, ".latencyMean .value");
|
||||
};
|
||||
|
||||
HystrixCommandMonitor.prototype.sortByLatencyMedian = function() {
|
||||
var direction = "desc";
|
||||
if(this.sortedBy == 'latMedian_desc') {
|
||||
direction = 'asc';
|
||||
}
|
||||
this.sortedBy = 'latMedian_' + direction;
|
||||
this.sortByMetricInDirection(direction, ".latencyMedian .value");
|
||||
};
|
||||
|
||||
HystrixCommandMonitor.prototype.sortByMetricInDirection = function(direction, metric) {
|
||||
$('#' + this.containerId + ' div.monitor').tsort(metric, {order: direction});
|
||||
};
|
||||
|
||||
// this method is for when new divs are added to cause the elements to be sorted to whatever the user last chose
|
||||
HystrixCommandMonitor.prototype.sortSameAsLast = function() {
|
||||
if(this.sortedBy == 'alph_asc') {
|
||||
this.sortAlphabeticalInDirection('asc');
|
||||
} else if(this.sortedBy == 'alph_desc') {
|
||||
this.sortAlphabeticalInDirection('desc');
|
||||
} else if(this.sortedBy == 'rate_asc') {
|
||||
this.sortByVolumeInDirection('asc');
|
||||
} else if(this.sortedBy == 'rate_desc') {
|
||||
this.sortByVolumeInDirection('desc');
|
||||
} else if(this.sortedBy == 'error_asc') {
|
||||
this.sortByErrorInDirection('asc');
|
||||
} else if(this.sortedBy == 'error_desc') {
|
||||
this.sortByErrorInDirection('desc');
|
||||
} else if(this.sortedBy == 'error_then_volume_asc') {
|
||||
this.sortByErrorThenVolumeInDirection('asc');
|
||||
} else if(this.sortedBy == 'error_then_volume_desc') {
|
||||
this.sortByErrorThenVolumeInDirection('desc');
|
||||
} else if(this.sortedBy == 'lat90_asc') {
|
||||
this.sortByMetricInDirection('asc', '.latency90 .value');
|
||||
} else if(this.sortedBy == 'lat90_desc') {
|
||||
this.sortByMetricInDirection('desc', '.latency90 .value');
|
||||
} else if(this.sortedBy == 'lat99_asc') {
|
||||
this.sortByMetricInDirection('asc', '.latency99 .value');
|
||||
} else if(this.sortedBy == 'lat99_desc') {
|
||||
this.sortByMetricInDirection('desc', '.latency99 .value');
|
||||
} else if(this.sortedBy == 'lat995_asc') {
|
||||
this.sortByMetricInDirection('asc', '.latency995 .value');
|
||||
} else if(this.sortedBy == 'lat995_desc') {
|
||||
this.sortByMetricInDirection('desc', '.latency995 .value');
|
||||
} else if(this.sortedBy == 'latMean_asc') {
|
||||
this.sortByMetricInDirection('asc', '.latencyMean .value');
|
||||
} else if(this.sortedBy == 'latMean_desc') {
|
||||
this.sortByMetricInDirection('desc', '.latencyMean .value');
|
||||
} else if(this.sortedBy == 'latMedian_asc') {
|
||||
this.sortByMetricInDirection('asc', '.latencyMedian .value');
|
||||
} else if(this.sortedBy == 'latMedian_desc') {
|
||||
this.sortByMetricInDirection('desc', '.latencyMedian .value');
|
||||
}
|
||||
};
|
||||
|
||||
// default sort type and direction
|
||||
this.sortedBy = 'alph_asc';
|
||||
|
||||
|
||||
// a temporary home for the logger until we become more sophisticated
|
||||
function log(message) {
|
||||
console.log(message);
|
||||
};
|
||||
|
||||
function addCommas(nStr){
|
||||
nStr += '';
|
||||
if(nStr.length <=3) {
|
||||
return nStr; //shortcut if we don't need commas
|
||||
}
|
||||
x = nStr.split('.');
|
||||
x1 = x[0];
|
||||
x2 = x.length > 1 ? '.' + x[1] : '';
|
||||
var rgx = /(\d+)(\d{3})/;
|
||||
while (rgx.test(x1)) {
|
||||
x1 = x1.replace(rgx, '$1' + ',' + '$2');
|
||||
}
|
||||
return x1 + x2;
|
||||
}
|
||||
})(window);
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 863 B |
Binary file not shown.
|
Before Width: | Height: | Size: 13 KiB |
@@ -1,77 +0,0 @@
|
||||
<div class="counters">
|
||||
|
||||
<div class="cell line">
|
||||
<a href="javascript://" title="Error Percentage [Timed-out + Threadpool Rejected + Failure / Total]" class="tooltip errorPercentage"><span class="value"><%= errorPercentage %></span> %</a>
|
||||
</div>
|
||||
|
||||
<div class="cell borderRight">
|
||||
<% if(propertyValue_executionIsolationStrategy == 'THREAD') { %>
|
||||
<a href="javascript://" title="Timed-out Request Count" class="line tooltip timeout"><%= addCommas(rollingCountTimeout) %></a>
|
||||
<a href="javascript://" title="Threadpool Rejected Request Count" class="line tooltip rejected"><%= addCommas(rollingCountThreadPoolRejected) %></a>
|
||||
<% } %>
|
||||
<% if(propertyValue_executionIsolationStrategy == 'SEMAPHORE') { %>
|
||||
<a href="javascript://" title="Semaphore Rejected Request Count" class="line tooltip rejected"><%= addCommas(rollingCountSemaphoreRejected) %></a>
|
||||
<% } %>
|
||||
<a href="javascript://" title="Failure Request Count" class="line tooltip failure"><%= addCommas(rollingCountFailure) %></a>
|
||||
</div>
|
||||
<div class="cell borderRight">
|
||||
<a href="javascript://" title="Successful Request Count" class="line tooltip success"><%= addCommas(rollingCountSuccess) %></a>
|
||||
<a href="javascript://" title="Short-circuited Request Count" class="line tooltip shortCircuited"><%= addCommas(rollingCountShortCircuited) %></a>
|
||||
<a href="javascript://" title="Bad Request Count" class="line tooltip badRequest"><%= addCommas(rollingCountBadRequests) %></a>
|
||||
<br>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rate">
|
||||
<a href="javascript://" title="Total Request Rate per Second per Reporting Host" class="tooltip rate"><span class="smaller">Host: </span><span class="ratePerSecondPerHost"><%= addCommas(roundNumber(ratePerSecondPerHost)) %></span>/s</a>
|
||||
</div>
|
||||
<div class="rate">
|
||||
<a href="javascript://" title="Total Request Rate per Second for Cluster" class="tooltip rate"><span class="smaller">Cluster: </span><span class="ratePerSecond"><%= addCommas(roundNumber(ratePerSecond)) %></span>/s</a>
|
||||
</div>
|
||||
|
||||
<div class="circuitStatus">
|
||||
<% if(propertyValue_circuitBreakerForceClosed) { %>
|
||||
<span class="smaller">[ <font color="orange">Forced Closed</font> ]</span>
|
||||
<% } %>
|
||||
<% if(propertyValue_circuitBreakerForceOpen) { %>
|
||||
Circuit <font color="red">Forced Open</font>
|
||||
<% } else { %>
|
||||
<% if(isCircuitBreakerOpen == reportingHosts) { %>
|
||||
Circuit <font color="red">Open</font>
|
||||
<% } else if(isCircuitBreakerOpen == 0) { %>
|
||||
Circuit <font color="green">Closed</font>
|
||||
<% } else {
|
||||
/* We have some circuits that are open */
|
||||
%>
|
||||
Circuit <font color="orange"><%= isCircuitBreakerOpen.toString().replace("true", "Open").replace("false", "Closed") %>)</font>
|
||||
<% } %>
|
||||
<% } %>
|
||||
</div>
|
||||
|
||||
<div class="spacer"></div>
|
||||
|
||||
<div class="tableRow">
|
||||
<% if(typeof reportingHosts != 'undefined') { %>
|
||||
<div class="cell header">Hosts</div>
|
||||
<div class="cell data"><%= reportingHosts %></div>
|
||||
<% } else { %>
|
||||
<div class="cell header">Host</div>
|
||||
<div class="cell data">Single</div>
|
||||
<% } %>
|
||||
<div class="cell header">90th</div>
|
||||
<div class="cell data latency90"><span class="value"><%= getInstanceAverage(latencyExecute['90'], reportingHosts, false) %></span>ms</div>
|
||||
</div>
|
||||
<div class="tableRow">
|
||||
<div class="cell header">Median</div>
|
||||
<div class="cell data latencyMedian"><span class="value"><%= getInstanceAverage(latencyExecute['50'], reportingHosts, false) %></span>ms</div>
|
||||
<div class="cell header">99th</div>
|
||||
<div class="cell data latency99"><span class="value"><%= getInstanceAverage(latencyExecute['99'], reportingHosts, false) %></span>ms</div>
|
||||
</div>
|
||||
<div class="tableRow">
|
||||
<div class="cell header">Mean</div>
|
||||
<div class="cell data latencyMean"><span class="value"><%= latencyExecute_mean %></span>ms</div>
|
||||
<div class="cell header">99.5th</div>
|
||||
<div class="cell data latency995"><span class="value"><%= getInstanceAverage(latencyExecute['99.5'], reportingHosts, false) %></span>ms</div>
|
||||
</div>
|
||||
|
||||
<!-- <a href="">History</a> <a href="">EPIC</a> <a href="">Actions</a> -->
|
||||
@@ -1,40 +0,0 @@
|
||||
<div class="monitor" id="CIRCUIT_<%= name %>" style="position:relative;">
|
||||
<%
|
||||
var displayName = name;
|
||||
var toolTip = "";
|
||||
if(displayName.length > 32) {
|
||||
displayName = displayName.substring(0,4) + "..." + displayName.substring(displayName.length-20, displayName.length);
|
||||
toolTip = "title=\"" + name + "\"";
|
||||
}
|
||||
%>
|
||||
|
||||
<div id="chart_CIRCUIT_<%= name %>" class="chart" style="position:absolute;top:0px;left:0; float:left; width:100%; height:100%;"></div>
|
||||
<div style="position:absolute;top:0;width:100%;height:15px;opacity:0.8; background:white;">
|
||||
<% if(includeDetailIcon) { %>
|
||||
<p class="name" <%= toolTip %> style="padding-right:16px">
|
||||
<%= displayName %>
|
||||
<a href="../dependencies/command.jsp?name=<%= name %>"><img src="components/hystrixCommand/magnifying-glass-icon-20.png" height="14px" width="14px" border="0" style="position: absolute; right:0px;"></a>
|
||||
</p>
|
||||
<% } else { %>
|
||||
<p class="name" <%= toolTip %>><%= displayName %></p>
|
||||
<% } %>
|
||||
</div>
|
||||
<div style="position:absolute;top:15px;; opacity:0.8; background:white; width:100%; height:95%;">
|
||||
<div class="monitor_data"></div>
|
||||
</div>
|
||||
<div id="graph_CIRCUIT_<%= name %>" class="graph" style="position:absolute;top:25px;left:0; float:left; width:140px; height:62px;"></div>
|
||||
|
||||
<script>
|
||||
var y = 200;
|
||||
/* escape with two backslashes */
|
||||
var vis = d3.select("#chart_CIRCUIT_<%= name.replace(/([ !"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g,'\\\\$1') %>").append("svg:svg").attr("width", "100%").attr("height", "100%");
|
||||
/* add a circle -- we don't use the data point, we set it manually, so just passing in [1] */
|
||||
var circle = vis.selectAll("circle").data([1]).enter().append("svg:circle");
|
||||
/* setup the initial styling and sizing of the circle */
|
||||
circle.style("fill", "green").attr("cx", "30%").attr("cy", "30%").attr("r", 5);
|
||||
|
||||
/* add the line graph - it will be populated by javascript, no default to show here */
|
||||
/* escape with two backslashes */
|
||||
var graph = d3.select("#graph_CIRCUIT_<%= name.replace(/([ !"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g,'\\\\$1') %>").append("svg:svg").attr("width", "100%").attr("height", "100%");
|
||||
</script>
|
||||
</div>
|
||||
@@ -1,6 +0,0 @@
|
||||
<div class="tableRow">
|
||||
<div class="cell header">Median</div>
|
||||
<div class="cell data"><span class="value"><%= sla_medianLastMinute %></span>ms</div>
|
||||
<div class="cell header">99th</div>
|
||||
<div class="cell data"><span class="value"><%= sla_percentile99LastMinute %></span>ms</div>
|
||||
</div>
|
||||
@@ -1,141 +0,0 @@
|
||||
.dependencyThreadPools .spacer {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
padding-top:4px;
|
||||
clear:both;
|
||||
}
|
||||
|
||||
|
||||
.dependencyThreadPools .last {
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
.dependencyThreadPools span.loading {
|
||||
display: block;
|
||||
padding-top: 6%;
|
||||
padding-bottom: 6%;
|
||||
color: gray;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dependencyThreadPools span.loading.failed {
|
||||
color: red;
|
||||
}
|
||||
|
||||
|
||||
.dependencyThreadPools div.monitor {
|
||||
float: left;
|
||||
margin-right:5px; /* these are tweaked to look good on desktop and iPad portrait, and fit things densely */
|
||||
margin-top:5px;
|
||||
}
|
||||
|
||||
.dependencyThreadPools div.monitor p.name {
|
||||
font-weight:bold;
|
||||
font-size: 10pt;
|
||||
text-align: right;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.dependencyThreadPools div.monitor_data {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.dependencyThreadPools span.smaller {
|
||||
font-size: 8pt;
|
||||
color: grey;
|
||||
}
|
||||
|
||||
|
||||
.dependencyThreadPools div.tableRow {
|
||||
width:100%;
|
||||
white-space: nowrap;
|
||||
font-size: 8pt;
|
||||
margin: 0 auto;
|
||||
clear:both;
|
||||
}
|
||||
|
||||
.dependencyThreadPools div.tableRow .cell {
|
||||
float:left;
|
||||
}
|
||||
|
||||
.dependencyThreadPools div.tableRow .header {
|
||||
text-align:right;
|
||||
padding-right:5px;
|
||||
}
|
||||
|
||||
.dependencyThreadPools div.tableRow .header.left {
|
||||
width:85px;
|
||||
}
|
||||
|
||||
.dependencyThreadPools div.tableRow .header.right {
|
||||
width:75px;
|
||||
}
|
||||
|
||||
.dependencyThreadPools div.tableRow .data {
|
||||
font-weight: bold;
|
||||
text-align:right;
|
||||
}
|
||||
|
||||
.dependencyThreadPools div.tableRow .data.left {
|
||||
width:30px;
|
||||
}
|
||||
|
||||
.dependencyThreadPools div.tableRow .data.right {
|
||||
width:45px;
|
||||
}
|
||||
|
||||
.dependencyThreadPools div.monitor {
|
||||
width: 245px; /* we want a fixed width instead of percentage as I want the boxes to be a set size and then fill in as many as can fit in each row ... this allows 3 columns on an iPad */
|
||||
height: 110px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* override the HREF when we have specified it as a tooltip to not act like a link */
|
||||
.dependencyThreadPools div.monitor_data a.tooltip {
|
||||
text-decoration: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.dependencyThreadPools div.monitor_data a.rate {
|
||||
font-weight:bold;
|
||||
color: black;
|
||||
font-size: 11pt;
|
||||
}
|
||||
|
||||
.dependencyThreadPools div.rate {
|
||||
padding-top: 1px;
|
||||
clear:both;
|
||||
text-align:right;
|
||||
}
|
||||
|
||||
.dependencyThreadPools span.rate_value {
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.dependencyThreadPools div.monitor div.chart {
|
||||
}
|
||||
|
||||
.dependencyThreadPools div.monitor div.chart svg {
|
||||
}
|
||||
|
||||
.dependencyThreadPools div.monitor div.chart svg text {
|
||||
fill: white;
|
||||
}
|
||||
|
||||
.dependencyThreadPools #hidden {
|
||||
width:1px;
|
||||
height:1px;
|
||||
background: lightgrey;
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,343 +0,0 @@
|
||||
|
||||
(function(window) {
|
||||
|
||||
// cache the templates we use on this page as global variables (asynchronously)
|
||||
jQuery.get(getRelativePath("components/hystrixThreadPool/templates/hystrixThreadPool.html"), function(data) {
|
||||
htmlTemplate = data;
|
||||
});
|
||||
jQuery.get(getRelativePath("components/hystrixThreadPool/templates/hystrixThreadPoolContainer.html"), function(data) {
|
||||
htmlTemplateContainer = data;
|
||||
});
|
||||
|
||||
function getRelativePath(path) {
|
||||
var p = location.pathname.slice(0, location.pathname.lastIndexOf("/")+1);
|
||||
return p + path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Object containing functions for displaying and updating the UI with streaming data.
|
||||
*
|
||||
* Publish this externally as "HystrixThreadPoolMonitor"
|
||||
*/
|
||||
window.HystrixThreadPoolMonitor = function(containerId) {
|
||||
|
||||
var self = this; // keep scope under control
|
||||
|
||||
this.containerId = containerId;
|
||||
|
||||
/**
|
||||
* Initialization on construction
|
||||
*/
|
||||
// intialize various variables we use for visualization
|
||||
var maxXaxisForCircle="40%";
|
||||
var maxYaxisForCircle="40%";
|
||||
var maxRadiusForCircle="125";
|
||||
var maxDomain = 2000;
|
||||
|
||||
self.circleRadius = d3.scale.pow().exponent(0.5).domain([0, maxDomain]).range(["5", maxRadiusForCircle]); // requests per second per host
|
||||
self.circleYaxis = d3.scale.linear().domain([0, maxDomain]).range(["30%", maxXaxisForCircle]);
|
||||
self.circleXaxis = d3.scale.linear().domain([0, maxDomain]).range(["30%", maxYaxisForCircle]);
|
||||
self.colorRange = d3.scale.linear().domain([10, 25, 40, 50]).range(["green", "#FFCC00", "#FF9900", "red"]);
|
||||
self.errorPercentageColorRange = d3.scale.linear().domain([0, 10, 35, 50]).range(["grey", "black", "#FF9900", "red"]);
|
||||
|
||||
/**
|
||||
* We want to keep sorting in the background since data values are always changing, so this will re-sort every X milliseconds
|
||||
* to maintain whatever sort the user (or default) has chosen.
|
||||
*
|
||||
* In other words, sorting only for adds/deletes is not sufficient as all but alphabetical sort are dynamically changing.
|
||||
*/
|
||||
setInterval(function() {
|
||||
// sort since we have added a new one
|
||||
self.sortSameAsLast();
|
||||
}, 1000)
|
||||
|
||||
/**
|
||||
* END of Initialization on construction
|
||||
*/
|
||||
|
||||
/**
|
||||
* Event listener to handle new messages from EventSource as streamed from the server.
|
||||
*/
|
||||
/* public */ self.eventSourceMessageListener = function(e) {
|
||||
var data = JSON.parse(e.data);
|
||||
if(data) {
|
||||
// check for reportingHosts (if not there, set it to 1 for singleHost vs cluster)
|
||||
if(!data.reportingHosts) {
|
||||
data.reportingHosts = 1;
|
||||
}
|
||||
|
||||
if(data && data.type == 'HystrixThreadPool') {
|
||||
if (data.deleteData == 'true') {
|
||||
deleteThreadPool(data.escapedName);
|
||||
} else {
|
||||
displayThreadPool(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre process the data before displying in the UI.
|
||||
* e.g Get Averages from sums, do rate calculation etc.
|
||||
*/
|
||||
function preProcessData(data) {
|
||||
validateData(data);
|
||||
// escape string used in jQuery & d3 selectors
|
||||
data.escapedName = data.name.replace(/([ !"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g,'\\$1');
|
||||
// do math
|
||||
converAllAvg(data);
|
||||
calcRatePerSecond(data);
|
||||
}
|
||||
|
||||
function converAllAvg(data) {
|
||||
convertAvg(data, "propertyValue_queueSizeRejectionThreshold", false);
|
||||
}
|
||||
|
||||
function convertAvg(data, key, decimal) {
|
||||
if (decimal) {
|
||||
data[key] = roundNumber(data[key]/data["reportingHosts"]);
|
||||
} else {
|
||||
data[key] = Math.floor(data[key]/data["reportingHosts"]);
|
||||
}
|
||||
}
|
||||
|
||||
function calcRatePerSecond(data) {
|
||||
var numberSeconds = data["propertyValue_metricsRollingStatisticalWindowInMilliseconds"] / 1000;
|
||||
|
||||
var totalThreadsExecuted = data["rollingCountThreadsExecuted"];
|
||||
if (totalThreadsExecuted < 0) {
|
||||
totalThreadsExecuted = 0;
|
||||
}
|
||||
data["ratePerSecond"] = roundNumber(totalThreadsExecuted / numberSeconds);
|
||||
data["ratePerSecondPerHost"] = roundNumber(totalThreadsExecuted / numberSeconds / data["reportingHosts"]);
|
||||
}
|
||||
|
||||
function validateData(data) {
|
||||
|
||||
assertNotNull(data,"type");
|
||||
assertNotNull(data,"name");
|
||||
// assertNotNull(data,"currentTime");
|
||||
assertNotNull(data,"currentActiveCount");
|
||||
assertNotNull(data,"currentCompletedTaskCount");
|
||||
assertNotNull(data,"currentCorePoolSize");
|
||||
assertNotNull(data,"currentLargestPoolSize");
|
||||
assertNotNull(data,"currentMaximumPoolSize");
|
||||
assertNotNull(data,"currentPoolSize");
|
||||
assertNotNull(data,"currentQueueSize");
|
||||
assertNotNull(data,"currentTaskCount");
|
||||
assertNotNull(data,"rollingCountThreadsExecuted");
|
||||
assertNotNull(data,"rollingMaxActiveThreads");
|
||||
assertNotNull(data,"reportingHosts");
|
||||
|
||||
assertNotNull(data,"propertyValue_queueSizeRejectionThreshold");
|
||||
assertNotNull(data,"propertyValue_metricsRollingStatisticalWindowInMilliseconds");
|
||||
}
|
||||
|
||||
function assertNotNull(data, key) {
|
||||
if(data[key] == undefined) {
|
||||
if (key == "dependencyOwner") {
|
||||
data["dependencyOwner"] = data.name;
|
||||
} else {
|
||||
throw new Error("Key Missing: " + key + " for " + data.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to display the THREAD_POOL data
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
/* private */ function displayThreadPool(data) {
|
||||
|
||||
try {
|
||||
preProcessData(data);
|
||||
} catch (err) {
|
||||
log("Failed preProcessData: " + err.message);
|
||||
return;
|
||||
}
|
||||
|
||||
// add the 'addCommas' function to the 'data' object so the HTML templates can use it
|
||||
data.addCommas = addCommas;
|
||||
// add the 'roundNumber' function to the 'data' object so the HTML templates can use it
|
||||
data.roundNumber = roundNumber;
|
||||
|
||||
var addNew = false;
|
||||
// check if we need to create the container
|
||||
if(!$('#THREAD_POOL_' + data.escapedName).length) {
|
||||
// it doesn't exist so add it
|
||||
var html = tmpl(htmlTemplateContainer, data);
|
||||
// remove the loading thing first
|
||||
$('#' + containerId + ' span.loading').remove();
|
||||
// get the current last column and remove the 'last' class from it
|
||||
$('#' + containerId + ' div.last').removeClass('last');
|
||||
// now create the new data and add it
|
||||
$('#' + containerId + '').append(html);
|
||||
// add the 'last' class to the column we just added
|
||||
$('#' + containerId + ' div.monitor').last().addClass('last');
|
||||
|
||||
// add the default sparkline graph
|
||||
d3.selectAll('#graph_THREAD_POOL_' + data.escapedName + ' svg').append("svg:path");
|
||||
|
||||
// remember this is new so we can trigger a sort after setting data
|
||||
addNew = true;
|
||||
}
|
||||
|
||||
// set the rate on the div element so it's available for sorting
|
||||
$('#THREAD_POOL_' + data.escapedName).attr('rate_value', data.ratePerSecondPerHost);
|
||||
|
||||
// now update/insert the data
|
||||
$('#THREAD_POOL_' + data.escapedName + ' div.monitor_data').html(tmpl(htmlTemplate, data));
|
||||
|
||||
// set variables for circle visualization
|
||||
var rate = data.ratePerSecondPerHost;
|
||||
// we will treat each item in queue as 1% of an error visualization
|
||||
// ie. 5 threads in queue per instance == 5% error percentage
|
||||
var errorPercentage = data.currentQueueSize / data.reportingHosts;
|
||||
|
||||
updateCircle('#THREAD_POOL_' + data.escapedName + ' circle', rate, errorPercentage);
|
||||
|
||||
if(addNew) {
|
||||
// sort since we added a new circuit
|
||||
self.sortSameAsLast();
|
||||
}
|
||||
}
|
||||
|
||||
/* round a number to X digits: num => the number to round, dec => the number of decimals */
|
||||
/* private */ function roundNumber(num) {
|
||||
var dec=1; // we are hardcoding to support only 1 decimal so that our padding logic at the end is simple
|
||||
var result = Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);
|
||||
var resultAsString = result.toString();
|
||||
if(resultAsString.indexOf('.') == -1) {
|
||||
resultAsString = resultAsString + '.';
|
||||
for(var i=0; i<dec; i++) {
|
||||
resultAsString = resultAsString + '0';
|
||||
}
|
||||
}
|
||||
return resultAsString;
|
||||
};
|
||||
|
||||
|
||||
/* private */ function updateCircle(cssTarget, rate, errorPercentage) {
|
||||
var newXaxisForCircle = self.circleXaxis(rate);
|
||||
if(parseInt(newXaxisForCircle) > parseInt(maxXaxisForCircle)) {
|
||||
newXaxisForCircle = maxXaxisForCircle;
|
||||
}
|
||||
var newYaxisForCircle = self.circleYaxis(rate);
|
||||
if(parseInt(newYaxisForCircle) > parseInt(maxYaxisForCircle)) {
|
||||
newYaxisForCircle = maxYaxisForCircle;
|
||||
}
|
||||
var newRadiusForCircle = self.circleRadius(rate);
|
||||
if(parseInt(newRadiusForCircle) > parseInt(maxRadiusForCircle)) {
|
||||
newRadiusForCircle = maxRadiusForCircle;
|
||||
}
|
||||
|
||||
d3.selectAll(cssTarget)
|
||||
.transition()
|
||||
.duration(400)
|
||||
.attr("cy", newYaxisForCircle)
|
||||
.attr("cx", newXaxisForCircle)
|
||||
.attr("r", newRadiusForCircle)
|
||||
.style("fill", self.colorRange(errorPercentage));
|
||||
}
|
||||
|
||||
/* private */ function deleteThreadPool(poolName) {
|
||||
$('#THREAD_POOL_' + poolName).remove();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// public methods for sorting
|
||||
HystrixThreadPoolMonitor.prototype.sortByVolume = function() {
|
||||
var direction = "desc";
|
||||
if(this.sortedBy == 'rate_desc') {
|
||||
direction = 'asc';
|
||||
}
|
||||
this.sortByVolumeInDirection(direction);
|
||||
}
|
||||
|
||||
HystrixThreadPoolMonitor.prototype.sortByVolumeInDirection = function(direction) {
|
||||
this.sortedBy = 'rate_' + direction;
|
||||
$('#' + this.containerId + ' div.monitor').tsort({order: direction, attr: 'rate_value'});
|
||||
}
|
||||
|
||||
HystrixThreadPoolMonitor.prototype.sortAlphabetically = function() {
|
||||
var direction = "asc";
|
||||
if(this.sortedBy == 'alph_asc') {
|
||||
direction = 'desc';
|
||||
}
|
||||
this.sortAlphabeticalInDirection(direction);
|
||||
}
|
||||
|
||||
HystrixThreadPoolMonitor.prototype.sortAlphabeticalInDirection = function(direction) {
|
||||
this.sortedBy = 'alph_' + direction;
|
||||
$('#' + this.containerId + ' div.monitor').tsort("p.name", {order: direction});
|
||||
}
|
||||
|
||||
HystrixThreadPoolMonitor.prototype.sortByMetricInDirection = function(direction, metric) {
|
||||
$('#' + this.containerId + ' div.monitor').tsort(metric, {order: direction});
|
||||
}
|
||||
|
||||
// this method is for when new divs are added to cause the elements to be sorted to whatever the user last chose
|
||||
HystrixThreadPoolMonitor.prototype.sortSameAsLast = function() {
|
||||
if(this.sortedBy == 'alph_asc') {
|
||||
this.sortAlphabeticalInDirection('asc');
|
||||
} else if(this.sortedBy == 'alph_desc') {
|
||||
this.sortAlphabeticalInDirection('desc');
|
||||
} else if(this.sortedBy == 'rate_asc') {
|
||||
this.sortByVolumeInDirection('asc');
|
||||
} else if(this.sortedBy == 'rate_desc') {
|
||||
this.sortByVolumeInDirection('desc');
|
||||
} else if(this.sortedBy == 'error_asc') {
|
||||
this.sortByErrorInDirection('asc');
|
||||
} else if(this.sortedBy == 'error_desc') {
|
||||
this.sortByErrorInDirection('desc');
|
||||
} else if(this.sortedBy == 'lat90_asc') {
|
||||
this.sortByMetricInDirection('asc', 'p90');
|
||||
} else if(this.sortedBy == 'lat90_desc') {
|
||||
this.sortByMetricInDirection('desc', 'p90');
|
||||
} else if(this.sortedBy == 'lat99_asc') {
|
||||
this.sortByMetricInDirection('asc', 'p99');
|
||||
} else if(this.sortedBy == 'lat99_desc') {
|
||||
this.sortByMetricInDirection('desc', 'p99');
|
||||
} else if(this.sortedBy == 'lat995_asc') {
|
||||
this.sortByMetricInDirection('asc', 'p995');
|
||||
} else if(this.sortedBy == 'lat995_desc') {
|
||||
this.sortByMetricInDirection('desc', 'p995');
|
||||
} else if(this.sortedBy == 'latMean_asc') {
|
||||
this.sortByMetricInDirection('asc', 'pMean');
|
||||
} else if(this.sortedBy == 'latMean_desc') {
|
||||
this.sortByMetricInDirection('desc', 'pMean');
|
||||
} else if(this.sortedBy == 'latMedian_asc') {
|
||||
this.sortByMetricInDirection('asc', 'pMedian');
|
||||
} else if(this.sortedBy == 'latMedian_desc') {
|
||||
this.sortByMetricInDirection('desc', 'pMedian');
|
||||
}
|
||||
}
|
||||
|
||||
// default sort type and direction
|
||||
this.sortedBy = 'alph_asc';
|
||||
|
||||
|
||||
// a temporary home for the logger until we become more sophisticated
|
||||
function log(message) {
|
||||
console.log(message);
|
||||
};
|
||||
|
||||
function addCommas(nStr){
|
||||
nStr += '';
|
||||
if(nStr.length <=3) {
|
||||
return nStr; //shortcut if we don't need commas
|
||||
}
|
||||
x = nStr.split('.');
|
||||
x1 = x[0];
|
||||
x2 = x.length > 1 ? '.' + x[1] : '';
|
||||
var rgx = /(\d+)(\d{3})/;
|
||||
while (rgx.test(x1)) {
|
||||
x1 = x1.replace(rgx, '$1' + ',' + '$2');
|
||||
}
|
||||
return x1 + x2;
|
||||
}
|
||||
})(window)
|
||||
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
|
||||
<div class="spacer"></div>
|
||||
|
||||
<div class="rate">
|
||||
<a href="javascript://" title="Total Execution Rate per Second per Reporting Host" class="tooltip rate"><span class="smaller">Host: </span><span class="ratePerSecondPerHost"><%= addCommas(ratePerSecondPerHost) %></span>/s</a>
|
||||
</div>
|
||||
<div class="rate">
|
||||
<a href="javascript://" title="Total Execution Rate per Second for Cluster" class="tooltip rate"><span class="smaller">Cluster: </span><span class="ratePerSecond"><%= addCommas(ratePerSecond) %></span>/s</a>
|
||||
</div>
|
||||
|
||||
<div class="spacer"></div>
|
||||
|
||||
<div class="tableRow">
|
||||
<div class="cell header left">Active</div>
|
||||
<div class="cell data left"><%= currentActiveCount%></div>
|
||||
|
||||
<div class="cell header right">Max Active</div>
|
||||
<div class="cell data right"><%= addCommas(rollingMaxActiveThreads)%></div>
|
||||
</div>
|
||||
|
||||
<div class="tableRow">
|
||||
<div class="cell header left">Queued</div>
|
||||
<div class="cell data left"><%= currentQueueSize %></div>
|
||||
<div class="cell header right">Executions</div>
|
||||
<div class="cell data right"><%= addCommas(rollingCountThreadsExecuted)%></div>
|
||||
</div>
|
||||
<div class="tableRow">
|
||||
<div class="cell header left">Pool Size</div>
|
||||
<div class="cell data left"><%= currentPoolSize %></div>
|
||||
<div class="cell header right">Queue Size</div>
|
||||
<div class="cell data right"><%= propertyValue_queueSizeRejectionThreshold %></div>
|
||||
</div>
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
<div class="monitor" id="THREAD_POOL_<%= name %>" style="position:relative;">
|
||||
|
||||
<%
|
||||
var displayName = name;
|
||||
var toolTip = "";
|
||||
if(displayName.length > 32) {
|
||||
displayName = displayName.substring(0,4) + "..." + displayName.substring(displayName.length-20, displayName.length);
|
||||
toolTip = "title=\"" + name + "\"";
|
||||
}
|
||||
%>
|
||||
|
||||
<div id="chart_THREAD_POOL_<%= name %>" class="chart" style="position:absolute;top:0px;left:0; float:left; width:100%; height:100%;"></div>
|
||||
<div style="position:absolute;top:0;width:100%;height:15px;opacity:0.8; background:white;"><p class="name" <%= toolTip %>><%= displayName %></p></div>
|
||||
<div style="position:absolute;top:15px;; opacity:0.8; background:white; width:100%; height:95%;">
|
||||
<div class="monitor_data"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
<% if(typeof errorPercentage != 'undefined') { %>
|
||||
var errorPercentageData = [<%= errorPercentage %>];
|
||||
<% } else { %>
|
||||
var errorPercentageData = [99];
|
||||
<% } %>
|
||||
var y = 200;
|
||||
/* escape with two backslashes */
|
||||
var vis = d3.select("#chart_THREAD_POOL_<%= name.replace(/([ !"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g,'\\\\$1') %>").append("svg:svg").attr("width", "100%").attr("height", "100%");
|
||||
/* add a circle -- we don't use the data point, we set it manually, so just passing in [1] */
|
||||
var circle = vis.selectAll("circle").data([1]).enter().append("svg:circle");
|
||||
/* setup the initial styling and sizing of the circle */
|
||||
circle.style("fill", "green").attr("cx", "30%").attr("cy", "20%").attr("r", 5);
|
||||
</script>
|
||||
|
||||
</div>
|
||||
@@ -1,71 +0,0 @@
|
||||
@IMPORT url("resets.css");
|
||||
|
||||
body {
|
||||
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
|
||||
}
|
||||
|
||||
img, object, embed {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
img {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
|
||||
#header {
|
||||
background: #FFFFFF url(../images/hystrix-logo-tagline-tiny.png) no-repeat scroll 99% 0%;
|
||||
height: 65px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#header h2 {
|
||||
float:left;
|
||||
color: black;
|
||||
position:relative;
|
||||
padding-left: 20px;
|
||||
top: 26px;
|
||||
font-size: 20px;
|
||||
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
|
||||
}
|
||||
|
||||
#header .header_nav {
|
||||
position:absolute;
|
||||
top:48px;
|
||||
right:15px;
|
||||
}
|
||||
|
||||
#header .header_links {
|
||||
float:left;
|
||||
color: lightgray;
|
||||
font-size: 18px;
|
||||
top: 3px;
|
||||
padding-left: 10px;
|
||||
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
|
||||
}
|
||||
|
||||
#header .header_links a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
#header .header_clusters {
|
||||
float:left;
|
||||
position:relative;
|
||||
padding-left: 10px;
|
||||
top: -1px;
|
||||
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
|
||||
}
|
||||
|
||||
|
||||
@media screen and (min-width: 1500px) {
|
||||
|
||||
#header .header_nav {
|
||||
top:13px;
|
||||
right:130px;
|
||||
}
|
||||
|
||||
#header {
|
||||
background: #FFFFFF url(../images/hystrix-logo-tagline-tiny.png) no-repeat scroll 99% 50%;
|
||||
height: 65px;
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
.container {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.row {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
padding-top:4px;
|
||||
clear:both;
|
||||
}
|
||||
|
||||
|
||||
.last {
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
.menubar {
|
||||
overflow: hidden;
|
||||
border-bottom: 1px solid black;
|
||||
}
|
||||
|
||||
.menubar div {
|
||||
padding-bottom:5px;
|
||||
|
||||
margin: 0 auto;
|
||||
overflow: hidden;
|
||||
|
||||
font-size: 80%;
|
||||
font-family:'Bookman Old Style',Bookman,'URW Bookman L','Palatino Linotype',serif;
|
||||
|
||||
float:left;
|
||||
}
|
||||
|
||||
.menubar .title {
|
||||
float: left;
|
||||
padding-right: 20px;
|
||||
|
||||
font-size: 110%;
|
||||
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
|
||||
font-weight: bold;
|
||||
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.menubar .menu_actions {
|
||||
float: left;
|
||||
position:relative;
|
||||
top: 4px;
|
||||
}
|
||||
|
||||
.menubar .menu_legend {
|
||||
float: right;
|
||||
position:relative;
|
||||
top: 4px;
|
||||
|
||||
}
|
||||
|
||||
h3.sectionHeader {
|
||||
color: black;
|
||||
font-size: 110%;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
padding-left: 8px;
|
||||
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
|
||||
background: lightgrey;
|
||||
}
|
||||
|
||||
.success {
|
||||
color: green;
|
||||
}
|
||||
.shortCircuited {
|
||||
color: blue;
|
||||
}
|
||||
.timeout {
|
||||
color: #FF9900; /* shade of orange */
|
||||
}
|
||||
.failure {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.rejected {
|
||||
color: purple;
|
||||
}
|
||||
|
||||
.exceptionsThrown {
|
||||
color: brown;
|
||||
}
|
||||
|
||||
.badRequest {
|
||||
color: #00CC99;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1100px) {
|
||||
.container {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
html5doctor.com Reset Stylesheet
|
||||
v1.6.1
|
||||
Last Updated: 2010-09-17
|
||||
Author: Richard Clark - http://richclarkdesign.com
|
||||
Twitter: @rich_clark
|
||||
*/
|
||||
|
||||
html, body, div, span, object, iframe,
|
||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||
abbr, address, cite, code,
|
||||
del, dfn, em, img, ins, kbd, q, samp,
|
||||
small, strong, sub, sup, var,
|
||||
b, i,
|
||||
dl, dt, dd, ol, ul, li,
|
||||
fieldset, form, label, legend,
|
||||
table, caption, tbody, tfoot, thead, tr, th, td,
|
||||
article, aside, canvas, details, figcaption, figure,
|
||||
footer, header, hgroup, menu, nav, section, summary,
|
||||
time, mark, audio, video {
|
||||
margin:0;
|
||||
padding:0;
|
||||
border:0;
|
||||
outline:0;
|
||||
font-size:100%;
|
||||
vertical-align:baseline;
|
||||
background:transparent;
|
||||
}
|
||||
|
||||
body {
|
||||
line-height:1;
|
||||
}
|
||||
|
||||
article,aside,details,figcaption,figure,
|
||||
footer,header,hgroup,menu,nav,section {
|
||||
display:block;
|
||||
}
|
||||
|
||||
nav ul {
|
||||
list-style:none;
|
||||
}
|
||||
|
||||
blockquote, q {
|
||||
quotes:none;
|
||||
}
|
||||
|
||||
blockquote:before, blockquote:after,
|
||||
q:before, q:after {
|
||||
content:'';
|
||||
content:none;
|
||||
}
|
||||
|
||||
a {
|
||||
margin:0;
|
||||
padding:0;
|
||||
font-size:100%;
|
||||
vertical-align:baseline;
|
||||
background:transparent;
|
||||
}
|
||||
|
||||
/* change colours to suit your needs */
|
||||
ins {
|
||||
background-color:#ff9;
|
||||
color:#000;
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
/* change colours to suit your needs */
|
||||
mark {
|
||||
background-color:#ff9;
|
||||
color:#000;
|
||||
font-style:italic;
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
del {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
abbr[title], dfn[title] {
|
||||
border-bottom:1px dotted;
|
||||
cursor:help;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse:collapse;
|
||||
border-spacing:0;
|
||||
}
|
||||
|
||||
/* change border colour to suit your needs */
|
||||
hr {
|
||||
display:block;
|
||||
height:1px;
|
||||
border:0;
|
||||
border-top:1px solid #cccccc;
|
||||
margin:1em 0;
|
||||
padding:0;
|
||||
}
|
||||
|
||||
input, select {
|
||||
vertical-align:middle;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/* SimpleGrid - a fork of CSSGrid by Crowd Favorite (https://github.com/crowdfavorite/css-grid)
|
||||
* https://simplegrid.info
|
||||
* by Conor Muirhead (http://conor.cc) of Early LLC (https://earlymade.com)
|
||||
* License: https://creativecommons.org/licenses/MIT/ */
|
||||
|
||||
/* Containers */
|
||||
body { font-size: 1.125em; }
|
||||
.grid{ width:1206px; }
|
||||
|
||||
/* 6-Col Grid Sizes */
|
||||
.slot-0,.slot-1,.slot-2,.slot-3,.slot-4,.slot-5{ width:176px; } /* Sixths */
|
||||
.slot-0-1,.slot-1-2,.slot-2-3,.slot-3-4,.slot-4-5{ width:382px; } /* Thirds */
|
||||
.slot-0-1-2-3,.slot-1-2-3-4,.slot-2-3-4-5{ width:794px; } /* Two-Thirds */
|
||||
.slot-0-1-2-3-4,.slot-1-2-3-4-5{ width:1000px; } /* Five-Sixths */
|
||||
|
||||
/* 4-Col Grid Sizes */
|
||||
.slot-6,.slot-7,.slot-8,.slot-9{ width:279px; } /* Quarters */
|
||||
.slot-6-7-8,.slot-7-8-9{ width:897px; } /* Three-Quarters */
|
||||
|
||||
/* 6-Col/4-Col Shared Grid Sizes */
|
||||
.slot-0-1-2,.slot-1-2-3,.slot-2-3-4,.slot-3-4-5, .slot-6-7,.slot-7-8,.slot-8-9{ width:588px; } /* Halves */
|
||||
@@ -1,33 +0,0 @@
|
||||
/* SimpleGrid - a fork of CSSGrid by Crowd Favorite (https://github.com/crowdfavorite/css-grid)
|
||||
* https://simplegrid.info
|
||||
* by Conor Muirhead (http://conor.cc) of Early LLC (https://earlymade.com)
|
||||
* License: https://creativecommons.org/licenses/MIT/ */
|
||||
|
||||
/* Containers */
|
||||
body { font-size: 0.875em; padding: 0; }
|
||||
.grid{ margin:0 auto; padding: 0 10px; width:700px; }
|
||||
.row{ clear:left; }
|
||||
|
||||
/* Slots Setup */
|
||||
.slot-0,.slot-1,.slot-2,.slot-3,.slot-4,.slot-5,.slot-0-1,.slot-0-1-2,.slot-0-1-2-3,.slot-0-1-2-3-4,.slot-0-1-2-3-4-5,.slot-1-2,.slot-1-2-3,.slot-1-2-3-4,.slot-1-2-3-4-5,.slot-2-3,.slot-2-3-4,.slot-2-3-4-5,.slot-3-4,.slot-3-4-5,.slot-4-5,.slot-6,.slot-7,.slot-8,.slot-9,.slot-6-7,.slot-6-7-8,.slot-6-7-8-9,.slot-7-8,.slot-7-8-9,.slot-8-9{ display:inline; float:left; margin-left:20px; }
|
||||
|
||||
/* 6-Col Grid Sizes */
|
||||
.slot-0,.slot-1,.slot-2,.slot-3,.slot-4,.slot-5{ width:100px; } /* Sixths */
|
||||
.slot-0-1,.slot-1-2,.slot-2-3,.slot-3-4,.slot-4-5{ width:220px; } /* Thirds */
|
||||
.slot-0-1-2-3,.slot-1-2-3-4,.slot-2-3-4-5{ width:460px; } /* Two-Thirds */
|
||||
.slot-0-1-2-3-4,.slot-1-2-3-4-5{ width:580px; } /* Five-Sixths */
|
||||
|
||||
/* 4-Col Grid Sizes */
|
||||
.slot-6,.slot-7,.slot-8,.slot-9{ width:160px; } /* Quarters */
|
||||
.slot-6-7-8,.slot-7-8-9{ width:520px; } /* Three-Quarters */
|
||||
|
||||
/* 6-Col/4-Col Shared Grid Sizes */
|
||||
.slot-0-1-2,.slot-1-2-3,.slot-2-3-4,.slot-3-4-5, .slot-6-7,.slot-7-8,.slot-8-9{ width:340px; } /* Halves */
|
||||
.slot-0-1-2-3-4-5, .slot-6-7-8-9{ width: 100%; } /* Full-Width */
|
||||
|
||||
/* Zeroing Out Leftmost Slot Margins */
|
||||
.slot-0,.slot-0-1,.slot-0-1-2,.slot-0-1-2-3,.slot-0-1-2-3-4,.slot-0-1-2-3-4-5,.slot-6,.slot-6-7,.slot-6-7-8,.slot-6-7-8-9,.slot-1 .slot-1,.slot-1-2 .slot-1,.slot-1-2 .slot-1-2,.slot-1-2-3 .slot-1,.slot-1-2-3 .slot-1-2,.slot-1-2-3 .slot-1-2-3,.slot-1-2-3-4 .slot-1,.slot-1-2-3-4 .slot-1-2,.slot-1-2-3-4 .slot-1-2-3,.slot-1-2-3-4 .slot-1-2-3-4,.slot-1-2-3-4-5 .slot-1,.slot-1-2-3-4-5 .slot-1-2,.slot-1-2-3-4-5 .slot-1-2-3,.slot-1-2-3-4-5 .slot-1-2-3-4,.slot-1-2-3-4-5 .slot-1-2-3-4-5,.slot-2 .slot-2,.slot-2-3 .slot-2,.slot-2-3 .slot-2-3,.slot-2-3-4 .slot-2,.slot-2-3-4 .slot-2-3,.slot-2-3-4 .slot-2-3-4,.slot-2-3-4-5 .slot-2,.slot-2-3-4-5 .slot-2-3,.slot-2-3-4-5 .slot-2-3-4,.slot-2-3-4-5 .slot-2-3-4-5,.slot-3 .slot-3,.slot-3-4 .slot-3,.slot-3-4 .slot-3-4,.slot-3-4-5 .slot-3,.slot-3-4-5 .slot-3-4,.slot-3-4-5 .slot-3-4-5,.slot-4 .slot-4,.slot-4-5 .slot-4,.slot-4-5 .slot-4-5,.slot-5 .slot-5,.slot-7 .slot-7,.slot-7-8 .slot-7,.slot-7-8 .slot-7-8,.slot-7-8-9 .slot-7,.slot-7-8-9 .slot-7-8,.slot-7-8-9 .slot-7-8-9,.slot-8 .slot-8,.slot-8-9 .slot-8,.slot-8-9 .slot-8-9{ margin-left:0 !important; } /* Important is to avoid repeating this in larger screen css files */
|
||||
|
||||
/* Row Clearfix */
|
||||
.row:after{ visibility:hidden; display:block; font-size:0; content:" "; clear:both; height:0; }
|
||||
.row{ zoom:1; }
|
||||
@@ -1,24 +0,0 @@
|
||||
/* SimpleGrid - a fork of CSSGrid by Crowd Favorite (https://github.com/crowdfavorite/css-grid)
|
||||
* https://simplegrid.info
|
||||
* by Conor Muirhead (http://conor.cc) of Early LLC (https://earlymade.com)
|
||||
* License: https://creativecommons.org/licenses/MIT/ */
|
||||
|
||||
/* Containers */
|
||||
body { font-size: 100%; }
|
||||
.grid{ width:966px; }
|
||||
|
||||
/* Slots Setup */
|
||||
.slot-0,.slot-1,.slot-2,.slot-3,.slot-4,.slot-5,.slot-0-1,.slot-0-1-2,.slot-0-1-2-3,.slot-0-1-2-3-4,.slot-0-1-2-3-4-5,.slot-1-2,.slot-1-2-3,.slot-1-2-3-4,.slot-1-2-3-4-5,.slot-2-3,.slot-2-3-4,.slot-2-3-4-5,.slot-3-4,.slot-3-4-5,.slot-4-5,.slot-6,.slot-7,.slot-8,.slot-9,.slot-6-7,.slot-6-7-8,.slot-6-7-8-9,.slot-7-8,.slot-7-8-9,.slot-8-9{ display:inline; float:left; margin-left:30px; }
|
||||
|
||||
/* 6-Col Grid Sizes */
|
||||
.slot-0,.slot-1,.slot-2,.slot-3,.slot-4,.slot-5{ width:136px; } /* Sixths */
|
||||
.slot-0-1,.slot-1-2,.slot-2-3,.slot-3-4,.slot-4-5{ width:302px; } /* Thirds */
|
||||
.slot-0-1-2-3,.slot-1-2-3-4,.slot-2-3-4-5{ width:634px; } /* Two-Thirds */
|
||||
.slot-0-1-2-3-4,.slot-1-2-3-4-5{ width:800px; } /* Five-Sixths */
|
||||
|
||||
/* 4-Col Grid Sizes */
|
||||
.slot-6,.slot-7,.slot-8,.slot-9{ width:219px; } /* Quarters */
|
||||
.slot-6-7-8,.slot-7-8-9{ width:717px; } /* Three-Quarters */
|
||||
|
||||
/* 6-Col/4-Col Shared Grid Sizes */
|
||||
.slot-0-1-2,.slot-1-2-3,.slot-2-3-4,.slot-3-4-5, .slot-6-7,.slot-7-8,.slot-8-9{ width:468px; } /* Halves */
|
||||
@@ -1,19 +0,0 @@
|
||||
Copyright (c) 2011 Crowd Favorite, Ltd.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -1 +0,0 @@
|
||||
https://simplegrid.info/
|
||||
@@ -1,27 +0,0 @@
|
||||
/* Extension of SimpleGrid by benjchristensen to allow percentage based sizing on very large displays
|
||||
*
|
||||
* SimpleGrid - a fork of CSSGrid by Crowd Favorite (https://github.com/crowdfavorite/css-grid)
|
||||
* https://simplegrid.info
|
||||
* by Conor Muirhead (http://conor.cc) of Early LLC (https://earlymade.com)
|
||||
* License: https://creativecommons.org/licenses/MIT/ */
|
||||
|
||||
/* Containers */
|
||||
body { font-size: 1.125em; }
|
||||
.grid{ width:100%; }
|
||||
|
||||
/* Slots Setup */
|
||||
.slot-0,.slot-1,.slot-2,.slot-3,.slot-4,.slot-5,.slot-0-1,.slot-0-1-2,.slot-0-1-2-3,.slot-0-1-2-3-4,.slot-0-1-2-3-4-5,.slot-1-2,.slot-1-2-3,.slot-1-2-3-4,.slot-1-2-3-4-5,.slot-2-3,.slot-2-3-4,.slot-2-3-4-5,.slot-3-4,.slot-3-4-5,.slot-4-5,.slot-6,.slot-7,.slot-8,.slot-9,.slot-6-7,.slot-6-7-8,.slot-6-7-8-9,.slot-7-8,.slot-7-8-9,.slot-8-9{ display:inline; float:left; margin-left:0px; }
|
||||
|
||||
|
||||
/* 6-Col Grid Sizes */
|
||||
.slot-0,.slot-1,.slot-2,.slot-3,.slot-4,.slot-5{ width:16.6%; } /* Sixths */
|
||||
.slot-0-1,.slot-1-2,.slot-2-3,.slot-3-4,.slot-4-5{ width:33.3%; } /* Thirds */
|
||||
.slot-0-1-2-3,.slot-1-2-3-4,.slot-2-3-4-5{ width:66.6%; } /* Two-Thirds */
|
||||
.slot-0-1-2-3-4,.slot-1-2-3-4-5{ width:83.3%; } /* Five-Sixths */
|
||||
|
||||
/* 4-Col Grid Sizes */
|
||||
.slot-6,.slot-7,.slot-8,.slot-9{ width:25%; } /* Quarters */
|
||||
.slot-6-7-8,.slot-7-8-9{ width:75%; } /* Three-Quarters */
|
||||
|
||||
/* 6-Col/4-Col Shared Grid Sizes */
|
||||
.slot-0-1-2,.slot-1-2-3,.slot-2-3-4,.slot-3-4-5, .slot-6-7,.slot-7-8,.slot-8-9{ width:50%; } /* Halves */
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 13 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 55 KiB |
@@ -1,12 +0,0 @@
|
||||
/*
|
||||
* jQuery TinySort - A plugin to sort child nodes by (sub) contents or attributes.
|
||||
*
|
||||
* Version: 1.0.5
|
||||
*
|
||||
* Copyright (c) 2008-2011 Ron Valstar http://ronvalstar.nl/
|
||||
*
|
||||
* Dual licensed under the MIT and GPL licenses:
|
||||
* https://www.opensource.org/licenses/mit-license.php
|
||||
* https://www.gnu.org/licenses/gpl.html
|
||||
*/
|
||||
(function(b){b.tinysort={id:"TinySort",version:"1.0.5",copyright:"Copyright (c) 2008-2011 Ron Valstar",uri:"http://tinysort.sjeiti.com/",defaults:{order:"asc",attr:"",place:"start",returns:false,useVal:false}};b.fn.extend({tinysort:function(h,j){if(h&&typeof(h)!="string"){j=h;h=null}var e=b.extend({},b.tinysort.defaults,j);var p={};this.each(function(t){var v=(!h||h=="")?b(this):b(this).find(h);var u=e.order=="rand"?""+Math.random():(e.attr==""?(e.useVal?v.val():v.text()):v.attr(e.attr));var s=b(this).parent();if(!p[s]){p[s]={s:[],n:[]}}if(v.length>0){p[s].s.push({s:u,e:b(this),n:t})}else{p[s].n.push({e:b(this),n:t})}});for(var g in p){var d=p[g];d.s.sort(function k(t,s){var i=t.s.toLowerCase?t.s.toLowerCase():t.s;var u=s.s.toLowerCase?s.s.toLowerCase():s.s;if(c(t.s)&&c(s.s)){i=parseFloat(t.s);u=parseFloat(s.s)}return(e.order=="asc"?1:-1)*(i<u?-1:(i>u?1:0))})}var m=[];for(var g in p){var d=p[g];var n=[];var f=b(this).length;switch(e.place){case"first":b.each(d.s,function(s,t){f=Math.min(f,t.n)});break;case"org":b.each(d.s,function(s,t){n.push(t.n)});break;case"end":f=d.n.length;break;default:f=0}var q=[0,0];for(var l=0;l<b(this).length;l++){var o=l>=f&&l<f+d.s.length;if(a(n,l)){o=true}var r=(o?d.s:d.n)[q[o?0:1]].e;r.parent().append(r);if(o||!e.returns){m.push(r.get(0))}q[o?0:1]++}}return this.pushStack(m)}});function c(e){var d=/^\s*?[\+-]?(\d*\.?\d*?)\s*?$/.exec(e);return d&&d.length>0?d[1]:false}function a(e,f){var d=false;b.each(e,function(h,g){if(!d){d=g==f}});return d}b.fn.TinySort=b.fn.Tinysort=b.fn.tsort=b.fn.tinysort})(jQuery);
|
||||
@@ -1,43 +0,0 @@
|
||||
|
||||
//Simple JavaScript Templating
|
||||
//John Resig - https://johnresig.com/ - MIT Licensed
|
||||
// https://johnresig.com/blog/javascript-micro-templating/
|
||||
(function(window, undefined) {
|
||||
var cache = {};
|
||||
|
||||
window.tmpl = function tmpl(str, data) {
|
||||
try {
|
||||
// Figure out if we're getting a template, or if we need to
|
||||
// load the template - and be sure to cache the result.
|
||||
var fn = !/\W/.test(str) ?
|
||||
cache[str] = cache[str] ||
|
||||
tmpl(document.getElementById(str).innerHTML) :
|
||||
|
||||
// Generate a reusable function that will serve as a template
|
||||
// generator (and which will be cached).
|
||||
new Function("obj",
|
||||
"var p=[],print=function(){p.push.apply(p,arguments);};" +
|
||||
|
||||
// Introduce the data as local variables using with(){}
|
||||
"with(obj){p.push('" +
|
||||
|
||||
// Convert the template into pure JavaScript
|
||||
str
|
||||
.replace(/[\r\t\n]/g, " ")
|
||||
.split("<%").join("\t")
|
||||
.replace(/((^|%>)[^\t]*)'/g, "$1\r")
|
||||
.replace(/\t=(.*?)%>/g, "',$1,'")
|
||||
.split("\t").join("');")
|
||||
.split("%>").join("p.push('")
|
||||
.split("\r").join("\\'")
|
||||
+ "');}return p.join('');");
|
||||
|
||||
//console.log(fn);
|
||||
|
||||
// Provide some basic currying to the user
|
||||
return data ? fn(data) : fn;
|
||||
}catch(e) {
|
||||
console.log(e);
|
||||
}
|
||||
};
|
||||
})(window);
|
||||
@@ -1,58 +0,0 @@
|
||||
<#import "/spring.ftl" as spring />
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<base href="${basePath}">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<title>Hystrix Dashboard</title>
|
||||
|
||||
<!-- Javascript to monitor and display -->
|
||||
<script src="<@spring.url '/webjars/jquery/2.1.1/jquery.min.js'/>" type="text/javascript"></script>
|
||||
|
||||
<script>
|
||||
function sendToMonitor() {
|
||||
|
||||
if($('#stream').val().length > 0) {
|
||||
var url = "<@spring.url '/hystrix'/>/monitor?stream=" + encodeURIComponent($('#stream').val()) + "";
|
||||
if($('#delay').val().length > 0) {
|
||||
url += "&delay=" + $('#delay').val();
|
||||
}
|
||||
if($('#title').val().length > 0) {
|
||||
url += "&title=" + encodeURIComponent($('#title').val());
|
||||
}
|
||||
location.href= url;
|
||||
} else {
|
||||
$('#message').html("The 'stream' value is required.");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div style="width:800px;margin:0 auto;">
|
||||
|
||||
<center>
|
||||
<img width="264" height="233" src="<@spring.url '/hystrix'/>/images/hystrix-logo.png">
|
||||
<br>
|
||||
<br>
|
||||
|
||||
<h2>Hystrix Dashboard</h2>
|
||||
<input id="stream" type="textfield" size="120" placeholder="https://hostname:port/turbine/turbine.stream"></input>
|
||||
<br><br>
|
||||
<i>Cluster via Turbine (default cluster):</i> https://turbine-hostname:port/turbine.stream
|
||||
<br>
|
||||
<i>Cluster via Turbine (custom cluster):</i> https://turbine-hostname:port/turbine.stream?cluster=[clusterName]
|
||||
<br>
|
||||
<i>Single Hystrix App:</i> https://hystrix-app:port/actuator/hystrix.stream
|
||||
<br><br>
|
||||
Delay: <input id="delay" type="textfield" size="10" placeholder="2000"></input>ms
|
||||
|
||||
Title: <input id="title" type="textfield" size="60" placeholder="Example Hystrix App"></input><br>
|
||||
<br>
|
||||
<button onclick="sendToMonitor()">Monitor Stream</button>
|
||||
<br><br>
|
||||
<div id="message" style="color:red"></div>
|
||||
|
||||
</center>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,202 +0,0 @@
|
||||
<#import "/spring.ftl" as spring />
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<base href="${basePath}">
|
||||
<meta charset="utf-8" />
|
||||
<title>Hystrix Monitor</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<!-- Setup base for everything -->
|
||||
<link rel="stylesheet" type="text/css" href="css/global.css" />
|
||||
|
||||
<!-- Our custom CSS -->
|
||||
<link rel="stylesheet" type="text/css" href="css/monitor.css" />
|
||||
|
||||
<!-- d3 -->
|
||||
<script type="text/javascript" src="<@spring.url '/webjars/d3js/3.4.11/d3.min.js'/>" ></script>
|
||||
|
||||
<!-- Javascript to monitor and display -->
|
||||
<script type="text/javascript" src="<@spring.url '/webjars/jquery/2.1.1/jquery.min.js'/>" ></script>
|
||||
<script type="text/javascript" src="js/jquery.tinysort.min.js"></script>
|
||||
<script type="text/javascript" src="js/tmpl.js"></script>
|
||||
|
||||
<!-- HystrixCommand -->
|
||||
<script type="text/javascript" src="components/hystrixCommand/hystrixCommand.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="components/hystrixCommand/hystrixCommand.css" />
|
||||
|
||||
<!-- HystrixThreadPool -->
|
||||
<script type="text/javascript" src="components/hystrixThreadPool/hystrixThreadPool.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="components/hystrixThreadPool/hystrixThreadPool.css" />
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="header">
|
||||
<h2><span id="title_name"></span></h2>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="menubar">
|
||||
<div class="title">
|
||||
Circuit
|
||||
</div>
|
||||
<div class="menu_actions">
|
||||
Sort:
|
||||
<a href="javascript://" onclick="hystrixMonitor.sortByErrorThenVolume();">Error then Volume</a> |
|
||||
<a href="javascript://" onclick="hystrixMonitor.sortAlphabetically();">Alphabetical</a> |
|
||||
<a href="javascript://" onclick="hystrixMonitor.sortByVolume();">Volume</a> |
|
||||
<a href="javascript://" onclick="hystrixMonitor.sortByError();">Error</a> |
|
||||
<a href="javascript://" onclick="hystrixMonitor.sortByLatencyMean();">Mean</a> |
|
||||
<a href="javascript://" onclick="hystrixMonitor.sortByLatencyMedian();">Median</a> |
|
||||
<a href="javascript://" onclick="hystrixMonitor.sortByLatency90();">90</a> |
|
||||
<a href="javascript://" onclick="hystrixMonitor.sortByLatency99();">99</a> |
|
||||
<a href="javascript://" onclick="hystrixMonitor.sortByLatency995();">99.5</a>
|
||||
</div>
|
||||
<div class="menu_legend">
|
||||
<span class="success">Success</span> | <span class="shortCircuited">Short-Circuited</span> | <span class="badRequest"> Bad Request</span> | <span class="timeout">Timeout</span> | <span class="rejected">Rejected</span> | <span class="failure">Failure</span> | <span class="errorPercentage">Error %</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="dependencies" class="row dependencies"><span class="loading">Loading ...</span></div>
|
||||
|
||||
<div class="spacer"></div>
|
||||
|
||||
<div class="row">
|
||||
<div class="menubar">
|
||||
<div class="title">
|
||||
Thread Pools
|
||||
</div>
|
||||
<div class="menu_actions">
|
||||
Sort: <a href="javascript://" onclick="dependencyThreadPoolMonitor.sortAlphabetically();">Alphabetical</a> |
|
||||
<a href="javascript://" onclick="dependencyThreadPoolMonitor.sortByVolume();">Volume</a> |
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="dependencyThreadPools" class="row dependencyThreadPools"><span class="loading">Loading ...</span></div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
/**
|
||||
* Queue up the monitor to start once the page has finished loading.
|
||||
*
|
||||
* This is an inline script and expects to execute once on page load.
|
||||
*/
|
||||
|
||||
// commands
|
||||
var hystrixMonitor = new HystrixCommandMonitor('dependencies', {includeDetailIcon:false});
|
||||
|
||||
var stream = getUrlVars()["stream"];
|
||||
|
||||
console.log("Stream: " + stream)
|
||||
|
||||
if(stream != undefined) {
|
||||
if(getUrlVars()["delay"] != undefined) {
|
||||
stream = stream + "&delay=" + getUrlVars()["delay"];
|
||||
}
|
||||
|
||||
var commandStream = "${contextPath}/proxy.stream?origin=" + stream;
|
||||
var poolStream = "${contextPath}/proxy.stream?origin=" + stream;
|
||||
|
||||
if(getUrlVars()["title"] != undefined) {
|
||||
$('#title_name').text("Hystrix Stream: " + decodeURIComponent(getUrlVars()["title"]))
|
||||
} else {
|
||||
$('#title_name').text("Hystrix Stream: " + decodeURIComponent(stream))
|
||||
}
|
||||
}
|
||||
console.log("Command Stream: " + commandStream)
|
||||
|
||||
$(window).load(function() { // within load with a setTimeout to prevent the infinite spinner
|
||||
setTimeout(function() {
|
||||
if(commandStream == undefined) {
|
||||
console.log("commandStream is undefined")
|
||||
$("#dependencies .loading").html("The 'stream' argument was not provided.");
|
||||
$("#dependencies .loading").addClass("failed");
|
||||
} else {
|
||||
// sort by error+volume by default
|
||||
hystrixMonitor.sortByErrorThenVolume();
|
||||
|
||||
// start the EventSource which will open a streaming connection to the server
|
||||
var source = new EventSource(commandStream);
|
||||
|
||||
// add the listener that will process incoming events
|
||||
source.addEventListener('message', hystrixMonitor.eventSourceMessageListener, false);
|
||||
|
||||
// source.addEventListener('open', function(e) {
|
||||
// console.console.log(">>> opened connection, phase: " + e.eventPhase);
|
||||
// // Connection was opened.
|
||||
// }, false);
|
||||
|
||||
source.addEventListener('error', function(e) {
|
||||
$("#dependencies .loading").html("Unable to connect to Command Metric Stream.");
|
||||
$("#dependencies .loading").addClass("failed");
|
||||
if (e.eventPhase == EventSource.CLOSED) {
|
||||
// Connection was closed.
|
||||
console.log("Connection was closed on error: " + JSON.stringify(e));
|
||||
} else {
|
||||
console.log("Error occurred while streaming: " + JSON.stringify(e));
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
},0);
|
||||
});
|
||||
|
||||
// thread pool
|
||||
var dependencyThreadPoolMonitor = new HystrixThreadPoolMonitor('dependencyThreadPools');
|
||||
|
||||
$(window).load(function() { // within load with a setTimeout to prevent the infinite spinner
|
||||
setTimeout(function() {
|
||||
if(poolStream == undefined) {
|
||||
console.log("poolStream is undefined")
|
||||
$("#dependencyThreadPools .loading").html("The 'stream' argument was not provided.");
|
||||
$("#dependencyThreadPools .loading").addClass("failed");
|
||||
} else {
|
||||
dependencyThreadPoolMonitor.sortByVolume();
|
||||
|
||||
// start the EventSource which will open a streaming connection to the server
|
||||
var source = new EventSource(poolStream);
|
||||
|
||||
// add the listener that will process incoming events
|
||||
source.addEventListener('message', dependencyThreadPoolMonitor.eventSourceMessageListener, false);
|
||||
|
||||
// source.addEventListener('open', function(e) {
|
||||
// console.console.log(">>> opened connection, phase: " + e.eventPhase);
|
||||
// // Connection was opened.
|
||||
// }, false);
|
||||
|
||||
source.addEventListener('error', function(e) {
|
||||
$("#dependencies .loading").html("Unable to connect to Command Metric Stream.");
|
||||
$("#dependencies .loading").addClass("failed");
|
||||
if (e.eventPhase == EventSource.CLOSED) {
|
||||
// Connection was closed.
|
||||
console.log("Connection was closed on error: " + e);
|
||||
} else {
|
||||
console.log("Error occurred while streaming: " + e);
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
},0);
|
||||
});
|
||||
|
||||
//Read a page's GET URL variables and return them as an associative array.
|
||||
// from: https://jquery-howto.blogspot.com/2009/09/get-url-parameters-values-with-jquery.html
|
||||
function getUrlVars()
|
||||
{
|
||||
var vars = [], hash;
|
||||
var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
|
||||
for(var i = 0; i < hashes.length; i++)
|
||||
{
|
||||
hash = hashes[i].split('=');
|
||||
vars.push(hash[0]);
|
||||
vars[hash[0]] = hash[1];
|
||||
}
|
||||
return vars;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.hystrix.dashboard;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.message.BasicHeader;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Roy Clarkson
|
||||
* @author Fahim Farook
|
||||
* @author Biju Kunjummen
|
||||
*/
|
||||
public class HystrixDashboardConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void normal() {
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
Header[] headers = new Header[1];
|
||||
headers[0] = new BasicHeader("Content-Type", "text/proxy.stream");
|
||||
HystrixDashboardConfiguration.ProxyStreamServlet proxyStreamServlet = new HystrixDashboardConfiguration.ProxyStreamServlet();
|
||||
ReflectionTestUtils.invokeMethod(proxyStreamServlet,
|
||||
"copyHeadersToServletResponse", headers, response);
|
||||
assertThat(response.getHeaderNames().size()).isEqualTo(1);
|
||||
assertThat(response.getHeader("Content-Type")).isEqualTo("text/proxy.stream");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void connectionClose() {
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
Header[] headers = new Header[2];
|
||||
headers[0] = new BasicHeader("Content-Type", "text/proxy.stream");
|
||||
headers[1] = new BasicHeader("Connection", "close");
|
||||
HystrixDashboardConfiguration.ProxyStreamServlet proxyStreamServlet = new HystrixDashboardConfiguration.ProxyStreamServlet();
|
||||
ReflectionTestUtils.invokeMethod(proxyStreamServlet,
|
||||
"copyHeadersToServletResponse", headers, response);
|
||||
assertThat(response.getHeaderNames().size()).isEqualTo(2);
|
||||
assertThat(response.getHeader("Content-Type")).isEqualTo("text/proxy.stream");
|
||||
assertThat(response.getHeader("Connection")).isEqualTo("close");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ignoreConnectionClose() {
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
Header[] headers = new Header[2];
|
||||
headers[0] = new BasicHeader("Content-Type", "text/proxy.stream");
|
||||
headers[1] = new BasicHeader("Connection", "close");
|
||||
HystrixDashboardConfiguration.ProxyStreamServlet proxyStreamServlet = new HystrixDashboardConfiguration.ProxyStreamServlet();
|
||||
proxyStreamServlet.setEnableIgnoreConnectionCloseHeader(true);
|
||||
ReflectionTestUtils.invokeMethod(proxyStreamServlet,
|
||||
"copyHeadersToServletResponse", headers, response);
|
||||
assertThat(response.getHeaderNames().size()).isEqualTo(1);
|
||||
assertThat(response.getHeader("Content-Type")).isEqualTo("text/proxy.stream");
|
||||
assertThat(response.getHeader("Connection")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doNotIgnoreConnectionClose() {
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
Header[] headers = new Header[2];
|
||||
headers[0] = new BasicHeader("Content-Type", "text/proxy.stream");
|
||||
headers[1] = new BasicHeader("Connection", "close");
|
||||
HystrixDashboardConfiguration.ProxyStreamServlet proxyStreamServlet = new HystrixDashboardConfiguration.ProxyStreamServlet();
|
||||
proxyStreamServlet.setEnableIgnoreConnectionCloseHeader(false);
|
||||
ReflectionTestUtils.invokeMethod(proxyStreamServlet,
|
||||
"copyHeadersToServletResponse", headers, response);
|
||||
assertThat(response.getHeaderNames().size()).isEqualTo(2);
|
||||
assertThat(response.getHeader("Content-Type")).isEqualTo("text/proxy.stream");
|
||||
assertThat(response.getHeader("Connection")).isEqualTo("close");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void initParameters() {
|
||||
new ApplicationContextRunner()
|
||||
.withUserConfiguration(HystrixDashboardConfiguration.class)
|
||||
.withPropertyValues(
|
||||
"hystrix.dashboard.init-parameters.wl-dispatch-polixy=work-manager-hystrix")
|
||||
.run(context -> {
|
||||
final ServletRegistrationBean registration = context
|
||||
.getBean(ServletRegistrationBean.class);
|
||||
assertThat(registration).isNotNull();
|
||||
|
||||
final Map<String, String> initParameters = registration
|
||||
.getInitParameters();
|
||||
assertThat(initParameters).isNotNull();
|
||||
assertThat(initParameters.get("wl-dispatch-polixy"))
|
||||
.isEqualTo("work-manager-hystrix");
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.hystrix.dashboard;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.cloud.netflix.hystrix.dashboard.HystrixDashboardContextTests.Application;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = Application.class, webEnvironment = WebEnvironment.RANDOM_PORT,
|
||||
properties = { "spring.application.name=hystrix-dashboard",
|
||||
"server.servlet.context-path=/context" })
|
||||
public class HystrixDashboardContextTests {
|
||||
|
||||
public static final String JQUERY_PATH = "/context/webjars/jquery/2.1.1/jquery.min.js";
|
||||
|
||||
@LocalServerPort
|
||||
private int port = 0;
|
||||
|
||||
@Test
|
||||
public void homePage() {
|
||||
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
|
||||
"http://localhost:" + this.port + "/context/hystrix", String.class);
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
String body = entity.getBody();
|
||||
assertThat(body.contains("base href=\"/context/hystrix\""))
|
||||
.as("wrong base path rendered in template").isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void correctJavascriptLink() {
|
||||
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
|
||||
"http://localhost:" + this.port + "/context/hystrix", String.class);
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
String body = entity.getBody();
|
||||
assertThat(body.contains("src=\"" + JQUERY_PATH + "\""))
|
||||
.as("wrong jquery path rendered in template").isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cssAvailable() {
|
||||
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
|
||||
"http://localhost:" + this.port + "/context/hystrix/css/global.css",
|
||||
String.class);
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void webjarsAvailable() {
|
||||
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
|
||||
"http://localhost:" + this.port + JQUERY_PATH, String.class);
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void monitorPage() {
|
||||
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
|
||||
"http://localhost:" + this.port + "/context/hystrix/monitor",
|
||||
String.class);
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
String body = entity.getBody();
|
||||
assertThat(body.contains("base href=\"/context/hystrix/monitor\""))
|
||||
.as("wrong base path rendered in template").isTrue();
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableAutoConfiguration
|
||||
@EnableHystrixDashboard
|
||||
protected static class Application {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.hystrix.dashboard;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.cloud.netflix.hystrix.dashboard.HystrixDashboardHomePageTests.Application;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = Application.class, webEnvironment = WebEnvironment.RANDOM_PORT,
|
||||
value = { "server.port=0", "spring.application.name=hystrix-dashboard" })
|
||||
public class HystrixDashboardHomePageTests {
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private int port = 0;
|
||||
|
||||
@Test
|
||||
public void homePage() {
|
||||
ResponseEntity<String> entity = new TestRestTemplate()
|
||||
.getForEntity("http://localhost:" + this.port, String.class);
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
entity.getBody().contains("<base href=\"/\">");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cssAvailable() {
|
||||
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
|
||||
"http://localhost:" + this.port + "/hystrix/css/global.css",
|
||||
String.class);
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void monitorPage() {
|
||||
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
|
||||
"http://localhost:" + this.port + "/hystrix/monitor", String.class);
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableAutoConfiguration
|
||||
@EnableHystrixDashboard
|
||||
@Controller
|
||||
protected static class Application {
|
||||
|
||||
@RequestMapping("/")
|
||||
public String home() {
|
||||
return "forward:/hystrix";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.hystrix.dashboard;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.cloud.netflix.hystrix.dashboard.HystrixDashboardTests.Application;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = Application.class, webEnvironment = WebEnvironment.RANDOM_PORT,
|
||||
value = { "spring.application.name=hystrix-dashboard" })
|
||||
public class HystrixDashboardTests {
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private int port = 0;
|
||||
|
||||
@Test
|
||||
public void homePage() {
|
||||
ResponseEntity<String> entity = new TestRestTemplate()
|
||||
.getForEntity("http://localhost:" + this.port + "/hystrix", String.class);
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
String body = entity.getBody();
|
||||
assertThat(body.contains("<base href=\"/hystrix\">")).isTrue();
|
||||
assertThat(body.contains("\"/webjars")).isTrue();
|
||||
assertThat(body.contains("= \"/hystrix/monitor")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cssAvailable() {
|
||||
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
|
||||
"http://localhost:" + this.port + "/hystrix/css/global.css",
|
||||
String.class);
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void monitorPage() {
|
||||
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
|
||||
"http://localhost:" + this.port + "/hystrix/monitor", String.class);
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
String body = entity.getBody();
|
||||
assertThat(body.contains("<base href=\"/hystrix/monitor\">")).isTrue();
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableAutoConfiguration
|
||||
@EnableHystrixDashboard
|
||||
protected static class Application {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
The presence of this templates directory tests the Spring Boot FreeMarker configuration
|
||||
@@ -1,171 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
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>
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>2.2.2.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-hystrix-stream</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring Cloud Netflix Hystrix Stream</name>
|
||||
<description>Spring Cloud Netflix Hystrix Stream</description>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-commons</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-hystrix</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.hystrix</groupId>
|
||||
<artifactId>hystrix-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.hystrix</groupId>
|
||||
<artifactId>hystrix-metrics-event-stream</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.hystrix</groupId>
|
||||
<artifactId>hystrix-javanica</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream-test-support</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-hystrix-contract</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-contract-verifier</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
|
||||
<version>${donotreplacespring-cloud-contract.version}</version>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<baseClassMappings>
|
||||
<baseClassMapping>
|
||||
<contractPackageRegex>.*</contractPackageRegex>
|
||||
<baseClassFQN>org.springframework.cloud.netflix.hystrix.stream.StreamSourceTestBase</baseClassFQN>
|
||||
</baseClassMapping>
|
||||
</baseClassMappings>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-hystrix-contract</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>add-source</id>
|
||||
<phase>generate-test-sources</phase>
|
||||
<goals>
|
||||
<goal>add-test-source</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<sources>
|
||||
<source>${project.build.directory}/generated-test-sources/contracts/</source>
|
||||
</sources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<!--This plugin's configuration is used to store Eclipse m2e settings
|
||||
only. It has no influence on the Maven build itself. -->
|
||||
<plugin>
|
||||
<groupId>org.eclipse.m2e</groupId>
|
||||
<artifactId>lifecycle-mapping</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<configuration>
|
||||
<lifecycleMappingMetadata>
|
||||
<pluginExecutions>
|
||||
<pluginExecution>
|
||||
<pluginExecutionFilter>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
|
||||
<versionRange>[1.0.0.RELEASE,)</versionRange>
|
||||
<goals>
|
||||
<goal>convert</goal>
|
||||
<goal>generateTests</goal>
|
||||
</goals>
|
||||
</pluginExecutionFilter>
|
||||
<action>
|
||||
<ignore></ignore>
|
||||
</action>
|
||||
</pluginExecution>
|
||||
</pluginExecutions>
|
||||
</lifecycleMappingMetadata>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
</project>
|
||||
@@ -1,116 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.hystrix.stream;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import com.netflix.hystrix.HystrixCircuitBreaker;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.actuator.HasFeatures;
|
||||
import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryProperties;
|
||||
import org.springframework.cloud.client.serviceregistry.Registration;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.annotation.Output;
|
||||
import org.springframework.cloud.stream.config.BindingProperties;
|
||||
import org.springframework.cloud.stream.config.BindingServiceConfiguration;
|
||||
import org.springframework.cloud.stream.config.BindingServiceProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
/**
|
||||
* Autoconfiguration for a Spring Cloud Hystrix on Spring Cloud Stream. Enabled by default
|
||||
* if spring-cloud-stream is on the classpath, and can be switched off with
|
||||
* <code>hystrix.stream.queue.enabled</code>. There are some high level configuration
|
||||
* options in {@link HystrixStreamProperties}. The binding name for Spring Cloud Stream is
|
||||
* {@link HystrixStreamClient#OUTPUT} so you can configure stream other properties through
|
||||
* that.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass({ HystrixCircuitBreaker.class, EnableBinding.class })
|
||||
@ConditionalOnProperty(value = "hystrix.stream.queue.enabled", matchIfMissing = true)
|
||||
@EnableConfigurationProperties
|
||||
@EnableScheduling
|
||||
@EnableBinding(HystrixStreamClient.class)
|
||||
@AutoConfigureBefore(BindingServiceConfiguration.class) // Needed for bindings done in
|
||||
// auto config
|
||||
public class HystrixStreamAutoConfiguration {
|
||||
|
||||
@Autowired
|
||||
private BindingServiceProperties bindings;
|
||||
|
||||
@Autowired
|
||||
private HystrixStreamProperties properties;
|
||||
|
||||
@Autowired
|
||||
@Output(HystrixStreamClient.OUTPUT)
|
||||
private MessageChannel outboundChannel;
|
||||
|
||||
@Autowired(required = false)
|
||||
private Registration registration;
|
||||
|
||||
@Bean
|
||||
public HasFeatures hystrixStreamQueueFeature() {
|
||||
return HasFeatures.namedFeature("Hystrix Stream (Queue)",
|
||||
HystrixStreamAutoConfiguration.class);
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
BindingProperties outputBinding = this.bindings.getBindings()
|
||||
.get(HystrixStreamClient.OUTPUT);
|
||||
if (outputBinding == null) {
|
||||
this.bindings.getBindings().put(HystrixStreamClient.OUTPUT,
|
||||
new BindingProperties());
|
||||
}
|
||||
BindingProperties output = this.bindings.getBindings()
|
||||
.get(HystrixStreamClient.OUTPUT);
|
||||
if (output.getDestination() == null) {
|
||||
output.setDestination(this.properties.getDestination());
|
||||
}
|
||||
if (output.getContentType() == null) {
|
||||
output.setContentType(this.properties.getContentType());
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HystrixStreamProperties hystrixStreamProperties() {
|
||||
return new HystrixStreamProperties();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HystrixStreamTask hystrixStreamTask(
|
||||
SimpleDiscoveryProperties simpleDiscoveryProperties) {
|
||||
ServiceInstance serviceInstance = this.registration;
|
||||
if (serviceInstance == null) {
|
||||
serviceInstance = simpleDiscoveryProperties.getLocal();
|
||||
}
|
||||
return new HystrixStreamTask(this.outboundChannel, serviceInstance,
|
||||
this.properties);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.hystrix.stream;
|
||||
|
||||
import org.springframework.cloud.stream.annotation.Output;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
|
||||
/**
|
||||
* Store Hystrix Stream Client output information.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public interface HystrixStreamClient {
|
||||
|
||||
/**
|
||||
* Hystrix Stream Output channel name.
|
||||
*/
|
||||
String OUTPUT = "hystrixStreamOutput";
|
||||
|
||||
/**
|
||||
* Provides Hystrix Stream Output setup.
|
||||
* @return corresponding {@link MessageChannel} instance
|
||||
*/
|
||||
@Output(OUTPUT)
|
||||
MessageChannel hystrixStreamOutput();
|
||||
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.hystrix.stream;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.netflix.hystrix.HystrixConstants;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@ConfigurationProperties("hystrix.stream.queue")
|
||||
public class HystrixStreamProperties {
|
||||
|
||||
/** Flag to indicate that Hystrix Stream is enabled. Default is true. */
|
||||
private boolean enabled = true;
|
||||
|
||||
/** Flag to indicate to prefix metric names with serviceId. Default is true. */
|
||||
private boolean prefixMetricName = true;
|
||||
|
||||
/** Flag to indicate to send the id field in the metrics. Default is true */
|
||||
private boolean sendId = true;
|
||||
|
||||
/**
|
||||
* The destination of the stream. Destination as defined by Spring Cloud Stream.
|
||||
* Defaults to springCloudHystrixStream
|
||||
*/
|
||||
private String destination = HystrixConstants.HYSTRIX_STREAM_DESTINATION;
|
||||
|
||||
/** The content type of the messages. Defaults to application/json */
|
||||
private String contentType = "application/json";
|
||||
|
||||
/** How often (in ms) to send messages to the stream. Defaults to 500. */
|
||||
private long sendRate = 500;
|
||||
|
||||
/**
|
||||
* How often to put messages in the queue. This queue drains to the stream. Defaults
|
||||
* to 500.
|
||||
*/
|
||||
private long gatherRate = 500;
|
||||
|
||||
/**
|
||||
* The size of the metrics queue. This queue drains to the stream. Defaults to 1000.
|
||||
*/
|
||||
private int size = 1000;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public boolean isPrefixMetricName() {
|
||||
return prefixMetricName;
|
||||
}
|
||||
|
||||
public void setPrefixMetricName(boolean prefixMetricName) {
|
||||
this.prefixMetricName = prefixMetricName;
|
||||
}
|
||||
|
||||
public boolean isSendId() {
|
||||
return sendId;
|
||||
}
|
||||
|
||||
public void setSendId(boolean sendId) {
|
||||
this.sendId = sendId;
|
||||
}
|
||||
|
||||
public String getDestination() {
|
||||
return destination;
|
||||
}
|
||||
|
||||
public void setDestination(String destination) {
|
||||
this.destination = destination;
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
public void setContentType(String contentType) {
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
public long getSendRate() {
|
||||
return sendRate;
|
||||
}
|
||||
|
||||
public void setSendRate(long sendRate) {
|
||||
this.sendRate = sendRate;
|
||||
}
|
||||
|
||||
public long getGatherRate() {
|
||||
return gatherRate;
|
||||
}
|
||||
|
||||
public void setGatherRate(long gatherRate) {
|
||||
this.gatherRate = gatherRate;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public void setSize(int size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,394 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.hystrix.stream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.netflix.hystrix.HystrixCircuitBreaker;
|
||||
import com.netflix.hystrix.HystrixCommandKey;
|
||||
import com.netflix.hystrix.HystrixCommandMetrics;
|
||||
import com.netflix.hystrix.HystrixCommandProperties;
|
||||
import com.netflix.hystrix.HystrixThreadPoolKey;
|
||||
import com.netflix.hystrix.HystrixThreadPoolMetrics;
|
||||
import com.netflix.hystrix.util.HystrixRollingNumberEvent;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @see com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsPoller (nested
|
||||
* private class MetricsPoller)
|
||||
*/
|
||||
public class HystrixStreamTask implements ApplicationContextAware {
|
||||
|
||||
private static Log log = LogFactory.getLog(HystrixStreamTask.class);
|
||||
|
||||
private MessageChannel outboundChannel;
|
||||
|
||||
private ServiceInstance registration;
|
||||
|
||||
private HystrixStreamProperties properties;
|
||||
|
||||
private ApplicationContext context;
|
||||
|
||||
// Visible for testing
|
||||
final LinkedBlockingQueue<String> jsonMetrics;
|
||||
|
||||
private final JsonFactory jsonFactory = new JsonFactory();
|
||||
|
||||
public HystrixStreamTask(MessageChannel outboundChannel, ServiceInstance registration,
|
||||
HystrixStreamProperties properties) {
|
||||
Assert.notNull(outboundChannel, "outboundChannel may not be null");
|
||||
Assert.notNull(registration, "registration may not be null");
|
||||
Assert.notNull(properties, "properties may not be null");
|
||||
this.outboundChannel = outboundChannel;
|
||||
this.registration = registration;
|
||||
this.properties = properties;
|
||||
this.jsonMetrics = new LinkedBlockingQueue<>(properties.getSize());
|
||||
}
|
||||
|
||||
/* for testing */ ServiceInstance getRegistration() {
|
||||
return registration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext)
|
||||
throws BeansException {
|
||||
this.context = applicationContext;
|
||||
}
|
||||
|
||||
// TODO: use integration to split this up?
|
||||
@Scheduled(
|
||||
fixedRateString = "${hystrix.stream.queue.sendRate:${hystrix.stream.queue.send-rate:500}}")
|
||||
public void sendMetrics() {
|
||||
ArrayList<String> metrics = new ArrayList<>();
|
||||
this.jsonMetrics.drainTo(metrics);
|
||||
|
||||
if (!metrics.isEmpty()) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("sending stream metrics size: " + metrics.size());
|
||||
}
|
||||
for (String json : metrics) {
|
||||
// TODO: batch all metrics to one message
|
||||
try {
|
||||
// TODO: remove the explicit content type when s-c-stream can handle
|
||||
// that for us
|
||||
this.outboundChannel.send(MessageBuilder.withPayload(json)
|
||||
.setHeader(MessageHeaders.CONTENT_TYPE,
|
||||
this.properties.getContentType())
|
||||
.build());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("failed sending stream metrics: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Scheduled(
|
||||
fixedRateString = "${hystrix.stream.queue.gatherRate:${hystrix.stream.queue.gather-rate:500}}")
|
||||
public void gatherMetrics() {
|
||||
try {
|
||||
// command metrics
|
||||
Collection<HystrixCommandMetrics> instances = HystrixCommandMetrics
|
||||
.getInstances();
|
||||
if (!instances.isEmpty()) {
|
||||
log.trace("gathering metrics size: " + instances.size());
|
||||
}
|
||||
|
||||
for (HystrixCommandMetrics commandMetrics : instances) {
|
||||
HystrixCommandKey key = commandMetrics.getCommandKey();
|
||||
HystrixCircuitBreaker circuitBreaker = HystrixCircuitBreaker.Factory
|
||||
.getInstance(key);
|
||||
|
||||
StringWriter jsonString = new StringWriter();
|
||||
JsonGenerator json = this.jsonFactory.createGenerator(jsonString);
|
||||
|
||||
json.writeStartObject();
|
||||
|
||||
addServiceData(json, registration);
|
||||
json.writeStringField("event", "message");
|
||||
json.writeObjectFieldStart("data");
|
||||
json.writeStringField("type", "HystrixCommand");
|
||||
String name = key.name();
|
||||
|
||||
if (this.properties.isPrefixMetricName() && registration != null) {
|
||||
name = registration.getServiceId() + "." + name;
|
||||
}
|
||||
|
||||
json.writeStringField("name", name);
|
||||
json.writeStringField("group", commandMetrics.getCommandGroup().name());
|
||||
json.writeNumberField("currentTime", System.currentTimeMillis());
|
||||
|
||||
// circuit breaker
|
||||
if (circuitBreaker == null) {
|
||||
// circuit breaker is disabled and thus never open
|
||||
json.writeBooleanField("isCircuitBreakerOpen", false);
|
||||
}
|
||||
else {
|
||||
json.writeBooleanField("isCircuitBreakerOpen",
|
||||
circuitBreaker.isOpen());
|
||||
}
|
||||
HystrixCommandMetrics.HealthCounts healthCounts = commandMetrics
|
||||
.getHealthCounts();
|
||||
json.writeNumberField("errorPercentage",
|
||||
healthCounts.getErrorPercentage());
|
||||
json.writeNumberField("errorCount", healthCounts.getErrorCount());
|
||||
json.writeNumberField("requestCount", healthCounts.getTotalRequests());
|
||||
|
||||
// rolling counters
|
||||
json.writeNumberField("rollingCountCollapsedRequests", commandMetrics
|
||||
.getRollingCount(HystrixRollingNumberEvent.COLLAPSED));
|
||||
json.writeNumberField("rollingCountExceptionsThrown", commandMetrics
|
||||
.getRollingCount(HystrixRollingNumberEvent.EXCEPTION_THROWN));
|
||||
json.writeNumberField("rollingCountFailure", commandMetrics
|
||||
.getRollingCount(HystrixRollingNumberEvent.FAILURE));
|
||||
json.writeNumberField("rollingCountFallbackFailure", commandMetrics
|
||||
.getRollingCount(HystrixRollingNumberEvent.FALLBACK_FAILURE));
|
||||
json.writeNumberField("rollingCountFallbackRejection", commandMetrics
|
||||
.getRollingCount(HystrixRollingNumberEvent.FALLBACK_REJECTION));
|
||||
json.writeNumberField("rollingCountFallbackSuccess", commandMetrics
|
||||
.getRollingCount(HystrixRollingNumberEvent.FALLBACK_SUCCESS));
|
||||
json.writeNumberField("rollingCountResponsesFromCache", commandMetrics
|
||||
.getRollingCount(HystrixRollingNumberEvent.RESPONSE_FROM_CACHE));
|
||||
json.writeNumberField("rollingCountSemaphoreRejected", commandMetrics
|
||||
.getRollingCount(HystrixRollingNumberEvent.SEMAPHORE_REJECTED));
|
||||
json.writeNumberField("rollingCountShortCircuited", commandMetrics
|
||||
.getRollingCount(HystrixRollingNumberEvent.SHORT_CIRCUITED));
|
||||
json.writeNumberField("rollingCountSuccess", commandMetrics
|
||||
.getRollingCount(HystrixRollingNumberEvent.SUCCESS));
|
||||
json.writeNumberField("rollingCountThreadPoolRejected", commandMetrics
|
||||
.getRollingCount(HystrixRollingNumberEvent.THREAD_POOL_REJECTED));
|
||||
json.writeNumberField("rollingCountTimeout", commandMetrics
|
||||
.getRollingCount(HystrixRollingNumberEvent.TIMEOUT));
|
||||
|
||||
json.writeNumberField("currentConcurrentExecutionCount",
|
||||
commandMetrics.getCurrentConcurrentExecutionCount());
|
||||
|
||||
// latency percentiles
|
||||
json.writeNumberField("latencyExecute_mean",
|
||||
commandMetrics.getExecutionTimeMean());
|
||||
json.writeObjectFieldStart("latencyExecute");
|
||||
json.writeNumberField("0", commandMetrics.getExecutionTimePercentile(0));
|
||||
json.writeNumberField("25",
|
||||
commandMetrics.getExecutionTimePercentile(25));
|
||||
json.writeNumberField("50",
|
||||
commandMetrics.getExecutionTimePercentile(50));
|
||||
json.writeNumberField("75",
|
||||
commandMetrics.getExecutionTimePercentile(75));
|
||||
json.writeNumberField("90",
|
||||
commandMetrics.getExecutionTimePercentile(90));
|
||||
json.writeNumberField("95",
|
||||
commandMetrics.getExecutionTimePercentile(95));
|
||||
json.writeNumberField("99",
|
||||
commandMetrics.getExecutionTimePercentile(99));
|
||||
json.writeNumberField("99.5",
|
||||
commandMetrics.getExecutionTimePercentile(99.5));
|
||||
json.writeNumberField("100",
|
||||
commandMetrics.getExecutionTimePercentile(100));
|
||||
json.writeEndObject();
|
||||
//
|
||||
json.writeNumberField("latencyTotal_mean",
|
||||
commandMetrics.getTotalTimeMean());
|
||||
json.writeObjectFieldStart("latencyTotal");
|
||||
json.writeNumberField("0", commandMetrics.getTotalTimePercentile(0));
|
||||
json.writeNumberField("25", commandMetrics.getTotalTimePercentile(25));
|
||||
json.writeNumberField("50", commandMetrics.getTotalTimePercentile(50));
|
||||
json.writeNumberField("75", commandMetrics.getTotalTimePercentile(75));
|
||||
json.writeNumberField("90", commandMetrics.getTotalTimePercentile(90));
|
||||
json.writeNumberField("95", commandMetrics.getTotalTimePercentile(95));
|
||||
json.writeNumberField("99", commandMetrics.getTotalTimePercentile(99));
|
||||
json.writeNumberField("99.5",
|
||||
commandMetrics.getTotalTimePercentile(99.5));
|
||||
json.writeNumberField("100", commandMetrics.getTotalTimePercentile(100));
|
||||
json.writeEndObject();
|
||||
|
||||
// property values for reporting what is actually seen by the command
|
||||
// rather than what was set somewhere
|
||||
HystrixCommandProperties commandProperties = commandMetrics
|
||||
.getProperties();
|
||||
|
||||
json.writeNumberField(
|
||||
"propertyValue_circuitBreakerRequestVolumeThreshold",
|
||||
commandProperties.circuitBreakerRequestVolumeThreshold().get());
|
||||
json.writeNumberField(
|
||||
"propertyValue_circuitBreakerSleepWindowInMilliseconds",
|
||||
commandProperties.circuitBreakerSleepWindowInMilliseconds()
|
||||
.get());
|
||||
json.writeNumberField(
|
||||
"propertyValue_circuitBreakerErrorThresholdPercentage",
|
||||
commandProperties.circuitBreakerErrorThresholdPercentage().get());
|
||||
json.writeBooleanField("propertyValue_circuitBreakerForceOpen",
|
||||
commandProperties.circuitBreakerForceOpen().get());
|
||||
json.writeBooleanField("propertyValue_circuitBreakerForceClosed",
|
||||
commandProperties.circuitBreakerForceClosed().get());
|
||||
json.writeBooleanField("propertyValue_circuitBreakerEnabled",
|
||||
commandProperties.circuitBreakerEnabled().get());
|
||||
|
||||
json.writeStringField("propertyValue_executionIsolationStrategy",
|
||||
commandProperties.executionIsolationStrategy().get().name());
|
||||
json.writeNumberField(
|
||||
"propertyValue_executionIsolationThreadTimeoutInMilliseconds",
|
||||
commandProperties.executionIsolationThreadTimeoutInMilliseconds()
|
||||
.get());
|
||||
json.writeBooleanField(
|
||||
"propertyValue_executionIsolationThreadInterruptOnTimeout",
|
||||
commandProperties.executionIsolationThreadInterruptOnTimeout()
|
||||
.get());
|
||||
json.writeStringField(
|
||||
"propertyValue_executionIsolationThreadPoolKeyOverride",
|
||||
commandProperties.executionIsolationThreadPoolKeyOverride()
|
||||
.get());
|
||||
json.writeNumberField(
|
||||
"propertyValue_executionIsolationSemaphoreMaxConcurrentRequests",
|
||||
commandProperties
|
||||
.executionIsolationSemaphoreMaxConcurrentRequests()
|
||||
.get());
|
||||
json.writeNumberField(
|
||||
"propertyValue_fallbackIsolationSemaphoreMaxConcurrentRequests",
|
||||
commandProperties
|
||||
.fallbackIsolationSemaphoreMaxConcurrentRequests().get());
|
||||
|
||||
// TODO
|
||||
/*
|
||||
* The following are commented out as these rarely change and are verbose
|
||||
* for streaming for something people don't change. We could perhaps allow
|
||||
* a property or request argument to include these.
|
||||
*/
|
||||
|
||||
// json.put("propertyValue_metricsRollingPercentileEnabled",
|
||||
// commandProperties.metricsRollingPercentileEnabled().get());
|
||||
// json.put("propertyValue_metricsRollingPercentileBucketSize",
|
||||
// commandProperties.metricsRollingPercentileBucketSize().get());
|
||||
// json.put("propertyValue_metricsRollingPercentileWindow",
|
||||
// commandProperties.metricsRollingPercentileWindowInMilliseconds().get());
|
||||
// json.put("propertyValue_metricsRollingPercentileWindowBuckets",
|
||||
// commandProperties.metricsRollingPercentileWindowBuckets().get());
|
||||
// json.put("propertyValue_metricsRollingStatisticalWindowBuckets",
|
||||
// commandProperties.metricsRollingStatisticalWindowBuckets().get());
|
||||
json.writeNumberField(
|
||||
"propertyValue_metricsRollingStatisticalWindowInMilliseconds",
|
||||
commandProperties.metricsRollingStatisticalWindowInMilliseconds()
|
||||
.get());
|
||||
|
||||
json.writeBooleanField("propertyValue_requestCacheEnabled",
|
||||
commandProperties.requestCacheEnabled().get());
|
||||
json.writeBooleanField("propertyValue_requestLogEnabled",
|
||||
commandProperties.requestLogEnabled().get());
|
||||
|
||||
json.writeNumberField("reportingHosts", 1); // this will get summed across
|
||||
// all instances in a cluster
|
||||
|
||||
json.writeEndObject(); // end data attribute
|
||||
json.writeEndObject();
|
||||
json.close();
|
||||
|
||||
// output
|
||||
this.jsonMetrics.add(jsonString.getBuffer().toString());
|
||||
}
|
||||
|
||||
// thread pool metrics
|
||||
for (HystrixThreadPoolMetrics threadPoolMetrics : HystrixThreadPoolMetrics
|
||||
.getInstances()) {
|
||||
HystrixThreadPoolKey key = threadPoolMetrics.getThreadPoolKey();
|
||||
|
||||
StringWriter jsonString = new StringWriter();
|
||||
JsonGenerator json = this.jsonFactory.createGenerator(jsonString);
|
||||
json.writeStartObject();
|
||||
|
||||
addServiceData(json, this.registration);
|
||||
json.writeObjectFieldStart("data");
|
||||
|
||||
json.writeStringField("type", "HystrixThreadPool");
|
||||
json.writeStringField("name", key.name());
|
||||
json.writeNumberField("currentTime", System.currentTimeMillis());
|
||||
|
||||
json.writeNumberField("currentActiveCount",
|
||||
threadPoolMetrics.getCurrentActiveCount().intValue());
|
||||
json.writeNumberField("currentCompletedTaskCount",
|
||||
threadPoolMetrics.getCurrentCompletedTaskCount().longValue());
|
||||
json.writeNumberField("currentCorePoolSize",
|
||||
threadPoolMetrics.getCurrentCorePoolSize().intValue());
|
||||
json.writeNumberField("currentLargestPoolSize",
|
||||
threadPoolMetrics.getCurrentLargestPoolSize().intValue());
|
||||
json.writeNumberField("currentMaximumPoolSize",
|
||||
threadPoolMetrics.getCurrentMaximumPoolSize().intValue());
|
||||
json.writeNumberField("currentPoolSize",
|
||||
threadPoolMetrics.getCurrentPoolSize().intValue());
|
||||
json.writeNumberField("currentQueueSize",
|
||||
threadPoolMetrics.getCurrentQueueSize().intValue());
|
||||
json.writeNumberField("currentTaskCount",
|
||||
threadPoolMetrics.getCurrentTaskCount().longValue());
|
||||
json.writeNumberField("rollingCountThreadsExecuted",
|
||||
threadPoolMetrics.getRollingCountThreadsExecuted());
|
||||
json.writeNumberField("rollingMaxActiveThreads",
|
||||
threadPoolMetrics.getRollingMaxActiveThreads());
|
||||
|
||||
json.writeNumberField("propertyValue_queueSizeRejectionThreshold",
|
||||
threadPoolMetrics.getProperties().queueSizeRejectionThreshold()
|
||||
.get());
|
||||
json.writeNumberField(
|
||||
"propertyValue_metricsRollingStatisticalWindowInMilliseconds",
|
||||
threadPoolMetrics.getProperties()
|
||||
.metricsRollingStatisticalWindowInMilliseconds().get());
|
||||
|
||||
json.writeNumberField("reportingHosts", 1); // this will get summed across
|
||||
// all instances in a cluster
|
||||
|
||||
json.writeEndObject(); // end of data object
|
||||
json.writeEndObject();
|
||||
json.close();
|
||||
// output to stream
|
||||
this.jsonMetrics.add(jsonString.getBuffer().toString());
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
log.error("Error adding metrics to queue", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void addServiceData(JsonGenerator json, ServiceInstance localService)
|
||||
throws IOException {
|
||||
json.writeObjectFieldStart("origin");
|
||||
json.writeStringField("host", localService.getHost());
|
||||
json.writeNumberField("port", localService.getPort());
|
||||
json.writeStringField("serviceId", localService.getServiceId());
|
||||
if (this.properties.isSendId()) {
|
||||
json.writeStringField("id", this.context.getId());
|
||||
}
|
||||
json.writeEndObject();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.netflix.hystrix.stream.HystrixStreamAutoConfiguration
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.hystrix.stream;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryProperties;
|
||||
import org.springframework.cloud.client.serviceregistry.Registration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest({ "eureka.client.enabled=false" })
|
||||
@DirtiesContext
|
||||
public class HystrixStreamAutoConfigurationNoRegistrationTests {
|
||||
|
||||
@Autowired
|
||||
HystrixStreamTask task;
|
||||
|
||||
@Autowired(required = false)
|
||||
Registration registration;
|
||||
|
||||
@Autowired
|
||||
SimpleDiscoveryProperties simpleDiscoveryProperties;
|
||||
|
||||
@Test
|
||||
public void withoutRegistrationWorks() throws Exception {
|
||||
assertThat(this.registration).isNull();
|
||||
assertThat(this.simpleDiscoveryProperties).isNotNull();
|
||||
assertThat(task.getRegistration())
|
||||
.isEqualTo(this.simpleDiscoveryProperties.getLocal());
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootConfiguration
|
||||
protected static class Config {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.hystrix.stream;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.cloud.client.serviceregistry.Registration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@DirtiesContext
|
||||
public class HystrixStreamAutoConfigurationTests {
|
||||
|
||||
@Autowired
|
||||
HystrixStreamTask task;
|
||||
|
||||
@Autowired
|
||||
Registration registration;
|
||||
|
||||
@Test
|
||||
public void withRegistrationWorks() throws Exception {
|
||||
assertThat(task.getRegistration()).isEqualTo(this.registration);
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootConfiguration
|
||||
protected static class Config {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016-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.netflix.hystrix.stream;
|
||||
|
||||
import com.netflix.hystrix.HystrixCommandGroupKey;
|
||||
import com.netflix.hystrix.HystrixCommandKey;
|
||||
import com.netflix.hystrix.HystrixCommandMetrics;
|
||||
import com.netflix.hystrix.HystrixCommandProperties;
|
||||
import com.netflix.hystrix.strategy.properties.HystrixPropertiesCommandDefault;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.client.serviceregistry.Registration;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.then;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class HystrixStreamTaskTests {
|
||||
|
||||
@Mock
|
||||
MessageChannel outboundChannel;
|
||||
|
||||
@Mock
|
||||
DiscoveryClient discoveryClient;
|
||||
|
||||
@Mock
|
||||
ApplicationContext context;
|
||||
|
||||
@Spy
|
||||
HystrixStreamProperties properties;
|
||||
|
||||
@Mock
|
||||
Registration registration;
|
||||
|
||||
@InjectMocks
|
||||
HystrixStreamTask hystrixStreamTask;
|
||||
|
||||
@Test
|
||||
public void should_not_send_metrics_when_they_are_empty() throws Exception {
|
||||
this.hystrixStreamTask.sendMetrics();
|
||||
|
||||
verifyNoInteractions(this.outboundChannel);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_send_metrics_when_they_are_not_empty() throws Exception {
|
||||
this.hystrixStreamTask.jsonMetrics.put("someJson");
|
||||
|
||||
this.hystrixStreamTask.sendMetrics();
|
||||
|
||||
then(this.outboundChannel).should().send(any(Message.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_gather_json_metrics() throws Exception {
|
||||
HystrixCommandKey hystrixCommandKey = HystrixCommandKey.Factory
|
||||
.asKey("commandKey");
|
||||
HystrixCommandMetrics.getInstance(hystrixCommandKey,
|
||||
HystrixCommandGroupKey.Factory.asKey("commandGroupKey"),
|
||||
new HystrixPropertiesCommandDefault(hystrixCommandKey,
|
||||
HystrixCommandProperties.defaultSetter()));
|
||||
|
||||
this.hystrixStreamTask.setApplicationContext(this.context);
|
||||
this.hystrixStreamTask.gatherMetrics();
|
||||
|
||||
assertThat(this.hystrixStreamTask.jsonMetrics.isEmpty()).isFalse();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.hystrix.stream;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
|
||||
import org.springframework.cloud.client.serviceregistry.Registration;
|
||||
import org.springframework.cloud.stream.test.binder.MessageCollector;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Daniel Lavoie
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { "debug=true",
|
||||
"spring.jmx.enabled=true", "spring.application.name=mytestapp" })
|
||||
@DirtiesContext
|
||||
public class HystrixStreamTests {
|
||||
|
||||
@Autowired
|
||||
private HystrixStreamTask task;
|
||||
|
||||
@Autowired
|
||||
private Application application;
|
||||
|
||||
@Autowired(required = false)
|
||||
private Registration registration;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper mapper;
|
||||
|
||||
@Autowired
|
||||
private MessageCollector collector;
|
||||
|
||||
@Autowired
|
||||
@Qualifier(HystrixStreamClient.OUTPUT)
|
||||
private MessageChannel output;
|
||||
|
||||
@Test
|
||||
public void contextLoads() throws Exception {
|
||||
this.application.hello();
|
||||
// It is important that local service instance resolves for metrics
|
||||
// origin details to be populated
|
||||
assertThat(this.registration).isNotNull();
|
||||
assertThat(this.registration.getServiceId()).isEqualTo("mytestapp");
|
||||
this.task.gatherMetrics();
|
||||
Message<?> message = this.collector.forChannel(output).take();
|
||||
JsonNode tree = mapper.readTree((String) message.getPayload());
|
||||
assertThat(tree.hasNonNull("origin")).isTrue();
|
||||
assertThat(tree.hasNonNull("data")).isTrue();
|
||||
assertThat(tree.hasNonNull("event")).isTrue();
|
||||
assertThat(tree.findValue("event").asText()).isEqualTo("message");
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@EnableCircuitBreaker
|
||||
@RestController
|
||||
@SpringBootConfiguration
|
||||
public static class Application {
|
||||
|
||||
@HystrixCommand
|
||||
@RequestMapping("/")
|
||||
public String hello() {
|
||||
return "Hello World";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,242 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.hystrix.stream;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
|
||||
import org.springframework.cloud.contract.verifier.messaging.MessageVerifier;
|
||||
import org.springframework.cloud.contract.verifier.messaging.boot.AutoConfigureMessageVerifier;
|
||||
import org.springframework.cloud.contract.verifier.messaging.stream.StreamStubMessages;
|
||||
import org.springframework.cloud.netflix.hystrix.contract.HystrixContractUtils;
|
||||
import org.springframework.cloud.netflix.hystrix.stream.StreamSourceTestBase.TestApplication;
|
||||
import org.springframework.cloud.stream.config.BindingProperties;
|
||||
import org.springframework.cloud.stream.config.BindingServiceProperties;
|
||||
import org.springframework.cloud.stream.test.binder.MessageCollector;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.converter.DefaultContentTypeResolver;
|
||||
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* Base class for sensor autogenerated tests (used by Spring Cloud Contract).
|
||||
*
|
||||
* This bootstraps the Spring Boot application code.
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = TestApplication.class,
|
||||
properties = { "spring.application.name=application" })
|
||||
@AutoConfigureMessageVerifier
|
||||
public abstract class StreamSourceTestBase {
|
||||
|
||||
@Autowired
|
||||
TestApplication application;
|
||||
|
||||
public void createMetricsData() throws Exception {
|
||||
application.hello();
|
||||
}
|
||||
|
||||
public void assertOrigin(Object input) {
|
||||
System.err.println(input);
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> origin = (Map<String, Object>) input;
|
||||
HystrixContractUtils.checkOrigin(origin);
|
||||
}
|
||||
|
||||
public void assertData(Object input) {
|
||||
// System.err.println(input);
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> data = (Map<String, Object>) input;
|
||||
HystrixContractUtils.checkData(data, TestApplication.class.getSimpleName(),
|
||||
"application.hello");
|
||||
}
|
||||
|
||||
public void assertEvent(Object input) {
|
||||
HystrixContractUtils.checkEvent((String) input);
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@EnableCircuitBreaker
|
||||
@RestController
|
||||
public static class TestApplication {
|
||||
|
||||
@HystrixCommand
|
||||
@RequestMapping("/")
|
||||
public String hello() {
|
||||
return "Hello World";
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(TestApplication.class, args);
|
||||
}
|
||||
|
||||
// TODO: remove this as soon as contract 2.0.0 is available
|
||||
@Bean
|
||||
MessageVerifier<Message<?>> contractVerifierMessageExchange(
|
||||
ApplicationContext applicationContext) {
|
||||
return new PatchedStubMessages(applicationContext);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class PatchedStubMessages implements MessageVerifier<Message<?>> {
|
||||
|
||||
private static final Logger log = LoggerFactory
|
||||
.getLogger(StreamStubMessages.class);
|
||||
|
||||
private final ApplicationContext context;
|
||||
|
||||
private final MessageCollector messageCollector;
|
||||
|
||||
private final ContractVerifierStreamMessageBuilder builder = new ContractVerifierStreamMessageBuilder();
|
||||
|
||||
PatchedStubMessages(ApplicationContext context) {
|
||||
this.context = context;
|
||||
this.messageCollector = context.getBean(MessageCollector.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void send(T payload, Map<String, Object> headers, String destination) {
|
||||
send(this.builder.create(payload, headers), destination);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Message<?> message, String destination) {
|
||||
try {
|
||||
MessageChannel messageChannel = this.context
|
||||
.getBean(resolvedDestination(destination), MessageChannel.class);
|
||||
messageChannel.send(message);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error(
|
||||
"Exception occurred while trying to send a message [" + message
|
||||
+ "] " + "to a channel with name [" + destination + "]",
|
||||
e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message<?> receive(String destination, long timeout, TimeUnit timeUnit) {
|
||||
try {
|
||||
MessageChannel messageChannel = this.context
|
||||
.getBean(resolvedDestination(destination), MessageChannel.class);
|
||||
Message<?> message = this.messageCollector.forChannel(messageChannel)
|
||||
.poll(timeout, timeUnit);
|
||||
if (message == null) {
|
||||
return message;
|
||||
}
|
||||
return MessageBuilder.createMessage(message.getPayload(),
|
||||
message.getHeaders());
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("Exception occurred while trying to read a message from "
|
||||
+ " a channel with name [" + destination + "]", e);
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private String resolvedDestination(String destination) {
|
||||
try {
|
||||
BindingServiceProperties channelBindingServiceProperties = this.context
|
||||
.getBean(BindingServiceProperties.class);
|
||||
for (Map.Entry<String, BindingProperties> entry : channelBindingServiceProperties
|
||||
.getBindings().entrySet()) {
|
||||
if (destination.equals(entry.getValue().getDestination())) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Found a channel named [{}] with destination [{}]",
|
||||
entry.getKey(), destination);
|
||||
}
|
||||
return entry.getKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error(
|
||||
"Exception took place while trying to resolve the destination. Will assume the name ["
|
||||
+ destination + "]",
|
||||
e);
|
||||
}
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("No destination named [" + destination
|
||||
+ "] was found. Assuming that the destination equals the channel name",
|
||||
destination);
|
||||
}
|
||||
return destination;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message<?> receive(String destination) {
|
||||
return receive(destination, 5, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private MappingJackson2MessageConverter converter() {
|
||||
ObjectMapper mapper = null;
|
||||
try {
|
||||
mapper = this.context.getBean(ObjectMapper.class);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException e) {
|
||||
|
||||
}
|
||||
MappingJackson2MessageConverter converter = createJacksonConverter();
|
||||
if (mapper != null) {
|
||||
converter.setObjectMapper(mapper);
|
||||
}
|
||||
return converter;
|
||||
}
|
||||
|
||||
protected MappingJackson2MessageConverter createJacksonConverter() {
|
||||
DefaultContentTypeResolver resolver = new DefaultContentTypeResolver();
|
||||
resolver.setDefaultMimeType(MimeTypeUtils.APPLICATION_JSON);
|
||||
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
|
||||
converter.setContentTypeResolver(resolver);
|
||||
return converter;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class ContractVerifierStreamMessageBuilder {
|
||||
|
||||
public <T> Message<?> create(T payload, Map<String, Object> headers) {
|
||||
return MessageBuilder.createMessage(payload, new MessageHeaders(headers));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
server:
|
||||
port: 17642
|
||||
|
||||
logging:
|
||||
level:
|
||||
org.springframework.netflix.hystrix.stream: TRACE
|
||||
@@ -1,29 +0,0 @@
|
||||
package contracts
|
||||
|
||||
import org.springframework.cloud.netflix.hystrix.contract.HystrixContractUtils
|
||||
|
||||
org.springframework.cloud.contract.spec.Contract.make {
|
||||
// Human readable description
|
||||
description 'Should produce valid metrics data'
|
||||
// Label by means of which the output message can be triggered
|
||||
label 'metrics'
|
||||
// input to the contract
|
||||
input {
|
||||
// the contract will be triggered by a method
|
||||
triggeredBy('createMetricsData()')
|
||||
}
|
||||
// output message of the contract
|
||||
outputMessage {
|
||||
// destination to which the output message will be sent
|
||||
sentTo 'hystrixStreamOutput'
|
||||
headers {
|
||||
header('contentType': 'application/json')
|
||||
}
|
||||
body(HystrixContractUtils.simpleBody())
|
||||
testMatchers {
|
||||
jsonPath('$.origin', byCommand('assertOrigin($it)'))
|
||||
jsonPath('$.event', byCommand('assertEvent($it)'))
|
||||
jsonPath('$.data', byCommand('assertData($it)'))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
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>
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>2.2.2.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-hystrix</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring Cloud Netflix Hystrix</name>
|
||||
<description>Spring Cloud Netflix Hystrix</description>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-reactor-netty</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-codec-http</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.reactivex</groupId>
|
||||
<artifactId>rxjava-reactive-streams</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.retry</groupId>
|
||||
<artifactId>spring-retry</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-commons</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-context</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-loadbalancer</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.hystrix</groupId>
|
||||
<artifactId>hystrix-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.hystrix</groupId>
|
||||
<artifactId>hystrix-serialization</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.hystrix</groupId>
|
||||
<artifactId>hystrix-metrics-event-stream</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.hystrix</groupId>
|
||||
<artifactId>hystrix-javanica</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-httpclient</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.reactivex</groupId>
|
||||
<artifactId>rxjava</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.jersey.contribs</groupId>
|
||||
<artifactId>jersey-apache-client4</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-ribbon</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-test-support</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>java8plus</id>
|
||||
<activation>
|
||||
<jdk>[1.8,2.0)</jdk>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<compilerArgs>
|
||||
<arg>-parameters</arg>
|
||||
</compilerArgs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2018 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.netflix.hystrix;
|
||||
|
||||
import com.netflix.hystrix.HystrixCommandGroupKey;
|
||||
import com.netflix.hystrix.HystrixCommandKey;
|
||||
import com.netflix.hystrix.HystrixCommandProperties;
|
||||
|
||||
import org.springframework.cloud.client.circuitbreaker.ConfigBuilder;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public abstract class AbstractHystrixConfigBuilder<CONFB>
|
||||
implements ConfigBuilder<CONFB> {
|
||||
|
||||
private final String commandName;
|
||||
|
||||
protected String groupName;
|
||||
|
||||
protected HystrixCommandProperties.Setter commandProperties;
|
||||
|
||||
public AbstractHystrixConfigBuilder(String id) {
|
||||
this.commandName = id;
|
||||
}
|
||||
|
||||
public AbstractHystrixConfigBuilder groupName(String groupName) {
|
||||
this.groupName = groupName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AbstractHystrixConfigBuilder commandProperties(
|
||||
HystrixCommandProperties.Setter commandProperties) {
|
||||
this.commandProperties = commandProperties;
|
||||
return this;
|
||||
}
|
||||
|
||||
protected HystrixCommandGroupKey getGroupKey() {
|
||||
String groupNameToUse;
|
||||
if (StringUtils.hasText(this.groupName)) {
|
||||
groupNameToUse = this.groupName;
|
||||
}
|
||||
else {
|
||||
groupNameToUse = commandName + "group";
|
||||
}
|
||||
return HystrixCommandGroupKey.Factory.asKey(groupNameToUse);
|
||||
}
|
||||
|
||||
protected HystrixCommandKey getCommandKey() {
|
||||
return HystrixCommandKey.Factory.asKey(this.commandName);
|
||||
}
|
||||
|
||||
protected HystrixCommandProperties.Setter getCommandPropertiesSetter() {
|
||||
return this.commandProperties != null ? this.commandProperties
|
||||
: HystrixCommandProperties.Setter();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2015 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.netflix.hystrix;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
|
||||
|
||||
/**
|
||||
* Convenience annotation for clients to enable Hystrix circuit breakers (specifically).
|
||||
* Use this (optionally) in case you want discovery and know for sure that it is Hystrix
|
||||
* you want. All it does is turn on circuit breakers and let the autoconfiguration find
|
||||
* the Hystrix classes if they are available (i.e. you need Hystrix on the classpath as
|
||||
* well).
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
@EnableCircuitBreaker
|
||||
public @interface EnableHystrix {
|
||||
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.hystrix;
|
||||
|
||||
import com.netflix.hystrix.Hystrix;
|
||||
import com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect;
|
||||
import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
|
||||
import com.netflix.hystrix.metric.consumer.HystrixDashboardStream;
|
||||
import com.netflix.hystrix.serial.SerialHystrixDashboardData;
|
||||
import io.micrometer.core.instrument.binder.hystrix.HystrixMetricsBinder;
|
||||
import org.reactivestreams.Publisher;
|
||||
import rx.Observable;
|
||||
import rx.RxReactiveStreams;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
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.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.client.actuator.HasFeatures;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.reactive.DispatcherHandler;
|
||||
|
||||
import static org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type.REACTIVE;
|
||||
import static org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type.SERVLET;
|
||||
|
||||
/**
|
||||
* Auto configuration for Hystrix.
|
||||
*
|
||||
* @author Christian Dupuis
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass({ Hystrix.class, HealthIndicator.class,
|
||||
HealthContributorAutoConfiguration.class })
|
||||
@AutoConfigureAfter({ HealthContributorAutoConfiguration.class })
|
||||
public class HystrixAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnEnabledHealthIndicator("hystrix")
|
||||
public HystrixHealthIndicator hystrixHealthIndicator() {
|
||||
return new HystrixHealthIndicator();
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnProperty(value = "management.metrics.binders.hystrix.enabled",
|
||||
matchIfMissing = true)
|
||||
@ConditionalOnClass({ HystrixMetricsBinder.class })
|
||||
protected static class HystrixMetricsConfiguration {
|
||||
|
||||
@Bean
|
||||
public HystrixMetricsBinder hystrixMetricsBinder() {
|
||||
return new HystrixMetricsBinder();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* See original
|
||||
* {@link org.springframework.boot.actuate.autoconfigure.jolokia.JolokiaEndpointAutoConfiguration}.
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnWebApplication(type = SERVLET)
|
||||
@ConditionalOnBean(HystrixCommandAspect.class) // only install the stream if enabled
|
||||
@ConditionalOnClass({ HystrixMetricsStreamServlet.class })
|
||||
@EnableConfigurationProperties(HystrixProperties.class)
|
||||
protected static class HystrixServletAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnAvailableEndpoint
|
||||
public HystrixStreamEndpoint hystrixStreamEndpoint(HystrixProperties properties) {
|
||||
return new HystrixStreamEndpoint(properties.getConfig());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HasFeatures hystrixStreamFeature() {
|
||||
return HasFeatures.namedFeature("Hystrix Stream Servlet",
|
||||
HystrixMetricsStreamServlet.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnWebApplication(type = REACTIVE)
|
||||
@ConditionalOnBean(HystrixCommandAspect.class) // only install the stream if enabled
|
||||
@ConditionalOnClass({ DispatcherHandler.class })
|
||||
@EnableConfigurationProperties(HystrixProperties.class)
|
||||
protected static class HystrixWebfluxManagementContextConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnAvailableEndpoint
|
||||
public HystrixWebfluxEndpoint hystrixWebfluxController() {
|
||||
Observable<String> serializedDashboardData = HystrixDashboardStream
|
||||
.getInstance().observe()
|
||||
.concatMap(dashboardData -> Observable.from(SerialHystrixDashboardData
|
||||
.toMultipleJsonStrings(dashboardData)));
|
||||
Publisher<String> publisher = RxReactiveStreams
|
||||
.toPublisher(serializedDashboardData);
|
||||
return new HystrixWebfluxEndpoint(publisher);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HasFeatures hystrixStreamFeature() {
|
||||
return HasFeatures.namedFeature("Hystrix Stream Webflux",
|
||||
HystrixWebfluxEndpoint.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2018 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.netflix.hystrix;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.netflix.hystrix.HystrixCommand;
|
||||
|
||||
import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
|
||||
|
||||
/**
|
||||
* Hystrix implementation of {@link CircuitBreaker}.
|
||||
*
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class HystrixCircuitBreaker implements CircuitBreaker {
|
||||
|
||||
private HystrixCommand.Setter setter;
|
||||
|
||||
public HystrixCircuitBreaker(HystrixCommand.Setter setter) {
|
||||
this.setter = setter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T run(Supplier<T> toRun, Function<Throwable, T> fallback) {
|
||||
|
||||
HystrixCommand<T> command = new HystrixCommand<T>(setter) {
|
||||
@Override
|
||||
protected T run() throws Exception {
|
||||
return toRun.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T getFallback() {
|
||||
return fallback.apply(getExecutionException());
|
||||
}
|
||||
};
|
||||
return command.execute();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.hystrix;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.netflix.hystrix.Hystrix;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
|
||||
import org.springframework.cloud.client.circuitbreaker.Customizer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
* @author Eric Bussieres
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass({ Hystrix.class })
|
||||
@ConditionalOnProperty(name = "spring.cloud.circuitbreaker.hystrix.enabled",
|
||||
matchIfMissing = true)
|
||||
public class HystrixCircuitBreakerAutoConfiguration {
|
||||
|
||||
@Autowired(required = false)
|
||||
private List<Customizer<HystrixCircuitBreakerFactory>> customizers = new ArrayList<>();
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(CircuitBreakerFactory.class)
|
||||
public CircuitBreakerFactory hystrixCircuitBreakerFactory() {
|
||||
HystrixCircuitBreakerFactory factory = new HystrixCircuitBreakerFactory();
|
||||
customizers.forEach(customizer -> customizer.customize(factory));
|
||||
return factory;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.hystrix;
|
||||
|
||||
import com.netflix.hystrix.Hystrix;
|
||||
import com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect;
|
||||
import org.apache.catalina.core.ApplicationContext;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.cloud.client.actuator.HasFeatures;
|
||||
import org.springframework.cloud.client.actuator.NamedFeature;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Christian Dupuis
|
||||
* @author Venil Noronha
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class HystrixCircuitBreakerConfiguration {
|
||||
|
||||
@Bean
|
||||
public HystrixCommandAspect hystrixCommandAspect() {
|
||||
return new HystrixCommandAspect();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HystrixShutdownHook hystrixShutdownHook() {
|
||||
return new HystrixShutdownHook();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HasFeatures hystrixFeature() {
|
||||
return HasFeatures
|
||||
.namedFeatures(new NamedFeature("Hystrix", HystrixCommandAspect.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link DisposableBean} that makes sure that Hystrix internal state is cleared when
|
||||
* {@link ApplicationContext} shuts down.
|
||||
*/
|
||||
private class HystrixShutdownHook implements DisposableBean {
|
||||
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
// Just call Hystrix to reset thread pool etc.
|
||||
Hystrix.reset();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2018 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.netflix.hystrix;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.netflix.hystrix.HystrixCommand;
|
||||
import com.netflix.hystrix.HystrixCommandGroupKey;
|
||||
import com.netflix.hystrix.HystrixCommandKey;
|
||||
|
||||
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Builds Hystrix circuit breakers.
|
||||
*
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class HystrixCircuitBreakerFactory extends
|
||||
CircuitBreakerFactory<HystrixCommand.Setter, HystrixCircuitBreakerFactory.HystrixConfigBuilder> {
|
||||
|
||||
private Function<String, HystrixCommand.Setter> defaultConfiguration = id -> HystrixCommand.Setter
|
||||
.withGroupKey(
|
||||
HystrixCommandGroupKey.Factory.asKey(getClass().getSimpleName()))
|
||||
.andCommandKey(HystrixCommandKey.Factory.asKey(id));
|
||||
|
||||
public void configureDefault(
|
||||
Function<String, HystrixCommand.Setter> defaultConfiguration) {
|
||||
this.defaultConfiguration = defaultConfiguration;
|
||||
}
|
||||
|
||||
public HystrixConfigBuilder configBuilder(String id) {
|
||||
return new HystrixConfigBuilder(id);
|
||||
}
|
||||
|
||||
public HystrixCircuitBreaker create(String id) {
|
||||
Assert.hasText(id, "A CircuitBreaker must have an id.");
|
||||
HystrixCommand.Setter setter = getConfigurations().computeIfAbsent(id,
|
||||
defaultConfiguration);
|
||||
return new HystrixCircuitBreaker(setter);
|
||||
}
|
||||
|
||||
public static class HystrixConfigBuilder
|
||||
extends AbstractHystrixConfigBuilder<HystrixCommand.Setter> {
|
||||
|
||||
public HystrixConfigBuilder(String id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HystrixCommand.Setter build() {
|
||||
return HystrixCommand.Setter.withGroupKey(getGroupKey())
|
||||
.andCommandKey(getCommandKey())
|
||||
.andCommandPropertiesDefaults(getCommandPropertiesSetter());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,231 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.netflix.hystrix;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.netflix.hystrix.HystrixCommandGroupKey;
|
||||
import com.netflix.hystrix.HystrixCommandKey;
|
||||
import com.netflix.hystrix.HystrixCommandProperties;
|
||||
import com.netflix.hystrix.HystrixObservableCommand;
|
||||
import com.netflix.hystrix.HystrixObservableCommand.Setter;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import rx.Observable;
|
||||
import rx.RxReactiveStreams;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Utility class to wrap a {@see Publisher} in a {@see HystrixObservableCommand}. Good for
|
||||
* use in a Spring WebFlux application. Allows more flexibility than the @HystrixCommand
|
||||
* annotation.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public final class HystrixCommands {
|
||||
|
||||
private HystrixCommands() {
|
||||
throw new AssertionError("Must not instantiate utility class.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param publisher A {@link Publisher} to pass to the new
|
||||
* {@link PublisherHystrixCommand}
|
||||
* @param <T> type of the {@link Publisher}s to be built by the returned builder
|
||||
* @return a builder for Hystrix-command-specific {@link Publisher}s
|
||||
*/
|
||||
public static <T> PublisherBuilder<T> from(Publisher<T> publisher) {
|
||||
return new PublisherBuilder<>(publisher);
|
||||
}
|
||||
|
||||
/**
|
||||
* A builder class for building Hystrix-command-specific {@link Publisher}s.
|
||||
*
|
||||
* @param <T> type of the {@link Publisher}s to be built
|
||||
*/
|
||||
public static class PublisherBuilder<T> {
|
||||
|
||||
private final Publisher<T> publisher;
|
||||
|
||||
private String commandName;
|
||||
|
||||
private String groupName;
|
||||
|
||||
private Function<Throwable, Publisher<T>> fallback;
|
||||
|
||||
private Setter setter;
|
||||
|
||||
private HystrixCommandProperties.Setter commandProperties;
|
||||
|
||||
private boolean eager = false;
|
||||
|
||||
private Function<HystrixObservableCommand<T>, Observable<T>> toObservable;
|
||||
|
||||
public PublisherBuilder(Publisher<T> publisher) {
|
||||
this.publisher = publisher;
|
||||
}
|
||||
|
||||
public PublisherBuilder<T> commandName(String commandName) {
|
||||
this.commandName = commandName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PublisherBuilder<T> groupName(String groupName) {
|
||||
this.groupName = groupName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PublisherBuilder<T> fallback(Publisher<T> fallback) {
|
||||
this.fallback = throwable -> fallback;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PublisherBuilder<T> fallback(Function<Throwable, Publisher<T>> fallback) {
|
||||
this.fallback = fallback;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PublisherBuilder<T> setter(Setter setter) {
|
||||
this.setter = setter;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PublisherBuilder<T> commandProperties(
|
||||
HystrixCommandProperties.Setter commandProperties) {
|
||||
this.commandProperties = commandProperties;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PublisherBuilder<T> commandProperties(
|
||||
Function<HystrixCommandProperties.Setter, HystrixCommandProperties.Setter> commandProperties) {
|
||||
if (commandProperties == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"commandProperties must not both be null");
|
||||
}
|
||||
return this.commandProperties(
|
||||
commandProperties.apply(HystrixCommandProperties.Setter()));
|
||||
}
|
||||
|
||||
public PublisherBuilder<T> eager() {
|
||||
this.eager = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PublisherBuilder<T> toObservable(
|
||||
Function<HystrixObservableCommand<T>, Observable<T>> toObservable) {
|
||||
this.toObservable = toObservable;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Publisher<T> build() {
|
||||
if (!StringUtils.hasText(commandName) && setter == null) {
|
||||
throw new IllegalStateException(
|
||||
"commandName and setter can not both be empty");
|
||||
}
|
||||
Setter setterToUse = getSetter();
|
||||
|
||||
PublisherHystrixCommand<T> command = new PublisherHystrixCommand<>(
|
||||
setterToUse, this.publisher, this.fallback);
|
||||
|
||||
Observable<T> observable = getObservableFunction().apply(command);
|
||||
|
||||
return RxReactiveStreams.toPublisher(observable);
|
||||
}
|
||||
|
||||
public Function<HystrixObservableCommand<T>, Observable<T>> getObservableFunction() {
|
||||
Function<HystrixObservableCommand<T>, Observable<T>> observableFunc;
|
||||
|
||||
if (this.toObservable != null) {
|
||||
observableFunc = this.toObservable;
|
||||
}
|
||||
else if (this.eager) {
|
||||
observableFunc = cmd -> cmd.observe();
|
||||
}
|
||||
else { // apply a default onBackpressureBuffer if not eager
|
||||
observableFunc = cmd -> cmd.toObservable().onBackpressureBuffer();
|
||||
}
|
||||
return observableFunc;
|
||||
}
|
||||
|
||||
public Setter getSetter() {
|
||||
Setter setterToUse;
|
||||
if (this.setter != null) {
|
||||
setterToUse = this.setter;
|
||||
}
|
||||
else {
|
||||
String groupNameToUse;
|
||||
if (StringUtils.hasText(this.groupName)) {
|
||||
groupNameToUse = this.groupName;
|
||||
}
|
||||
else {
|
||||
groupNameToUse = commandName + "group";
|
||||
}
|
||||
|
||||
HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory
|
||||
.asKey(groupNameToUse);
|
||||
HystrixCommandKey commandKey = HystrixCommandKey.Factory
|
||||
.asKey(this.commandName);
|
||||
HystrixCommandProperties.Setter commandProperties = this.commandProperties != null
|
||||
? this.commandProperties : HystrixCommandProperties.Setter();
|
||||
setterToUse = Setter.withGroupKey(groupKey).andCommandKey(commandKey)
|
||||
.andCommandPropertiesDefaults(commandProperties);
|
||||
}
|
||||
return setterToUse;
|
||||
}
|
||||
|
||||
public Flux<T> toFlux() {
|
||||
return Flux.from(build());
|
||||
}
|
||||
|
||||
public Mono<T> toMono() {
|
||||
return Mono.from(build());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class PublisherHystrixCommand<T> extends HystrixObservableCommand<T> {
|
||||
|
||||
private Publisher<T> publisher;
|
||||
|
||||
private Function<Throwable, Publisher<T>> fallback;
|
||||
|
||||
protected PublisherHystrixCommand(Setter setter, Publisher<T> publisher,
|
||||
Function<Throwable, Publisher<T>> fallback) {
|
||||
super(setter);
|
||||
this.publisher = publisher;
|
||||
this.fallback = fallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Observable<T> construct() {
|
||||
return RxReactiveStreams.toObservable(publisher);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Observable<T> resumeWithFallback() {
|
||||
if (this.fallback != null) {
|
||||
Publisher<T> fallbackPublisher = fallback.apply(getExecutionException());
|
||||
return RxReactiveStreams.toObservable(fallbackPublisher);
|
||||
}
|
||||
return super.resumeWithFallback();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user