Removes modules in maintenance mode.

fixes gh-3415
This commit is contained in:
Spencer Gibb
2020-01-14 20:32:13 -05:00
parent 3455489f47
commit 48e1618cdd
429 changed files with 24 additions and 47506 deletions

View File

@@ -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
View File

@@ -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>

View File

@@ -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>

View File

@@ -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));
}
}
}
}
}

View File

@@ -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);
}
}

View File

@@ -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));
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -1,10 +0,0 @@
{
"properties": [
{
"defaultValue": "true",
"name": "archaius.propagate.environmentChangedEvent",
"description": "Propagates EnvironmentChanged events to Archaius ConfigurationManager.",
"type": "java.lang.Boolean"
}
]
}

View File

@@ -1,2 +0,0 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration

View File

@@ -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");
}
}

View File

@@ -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();
}
}

View File

@@ -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 {
}
}

View File

@@ -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;
}
}

View File

@@ -1,2 +0,0 @@
archaius.file.property=Static config file property
db.second.property=It should be overridden

View File

@@ -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>

View File

@@ -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>

View File

@@ -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.");
}
}

View File

@@ -1,2 +0,0 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.core.CoreAutoConfiguration

View File

@@ -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>

View File

@@ -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>

View File

@@ -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;

View File

@@ -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;

View File

@@ -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 {
}
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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 {
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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 {
}
}

View File

@@ -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");
}
}

View File

@@ -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 {
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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.");
}
}
}
}

View File

@@ -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>

View File

@@ -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);

View File

@@ -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 {

View File

@@ -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>

View File

@@ -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);
}
}

View File

@@ -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
}
}

View File

@@ -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>

View File

@@ -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 {
}

View File

@@ -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);
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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> -->

View File

@@ -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>

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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>

View File

@@ -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>

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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 */

View File

@@ -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; }

View File

@@ -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 */

View File

@@ -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.

View File

@@ -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: 55 KiB

View File

@@ -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);

View File

@@ -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);

View File

@@ -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
&nbsp;&nbsp;&nbsp;&nbsp;
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>

View File

@@ -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>

View File

@@ -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");
});
}
}

View File

@@ -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 {
}
}

View File

@@ -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";
}
}
}

View File

@@ -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 {
}
}

View File

@@ -1 +0,0 @@
The presence of this templates directory tests the Spring Boot FreeMarker configuration

View File

@@ -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>

View File

@@ -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);
}
}

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -1,2 +0,0 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.hystrix.stream.HystrixStreamAutoConfiguration

View File

@@ -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 {
}
}

View File

@@ -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 {
}
}

View File

@@ -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();
}
}

View File

@@ -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";
}
}
}

View File

@@ -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));
}
}
}

View File

@@ -1,6 +0,0 @@
server:
port: 17642
logging:
level:
org.springframework.netflix.hystrix.stream: TRACE

View File

@@ -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)'))
}
}
}

View File

@@ -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>

View File

@@ -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();
}
}

View File

@@ -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 {
}

View File

@@ -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);
}
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}
}

View File

@@ -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());
}
}
}

View File

@@ -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