diff --git a/docs/src/main/asciidoc/spring-cloud-netflix.adoc b/docs/src/main/asciidoc/spring-cloud-netflix.adoc index 8318570e3..406ef0e12 100644 --- a/docs/src/main/asciidoc/spring-cloud-netflix.adoc +++ b/docs/src/main/asciidoc/spring-cloud-netflix.adoc @@ -453,6 +453,38 @@ In fact, the `eureka.instance.hostname` is not needed if you are running on a ma You can add multiple peers to a system, and, as long as they are all connected to each other by at least one edge, they synchronize the registrations amongst themselves. If the peers are physically separated (inside a data center or between multiple data centers), then the system can, in principle, survive "`split-brain`" type failures. +You can add multiple peers to a system, and as long as they are all +directly connected to each other, they will synchronize +the registrations amongst themselves. + +.application.yml (Three Peer Aware Eureka Servers) +---- +eureka: + client: + serviceUrl: + defaultZone: http://peer1/eureka/,http://peer2/eureka/,http://peer3/eureka/ + +--- +spring: + profiles: peer1 +eureka: + instance: + hostname: peer1 + +--- +spring: + profiles: peer2 +eureka: + instance: + hostname: peer2 + +--- +spring: + profiles: peer3 +eureka: + instance: + hostname: peer3 +---- [[spring-cloud-eureka-server-prefer-ip-address]] === When to Prefer IP Address diff --git a/spring-cloud-netflix-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-netflix-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json new file mode 100644 index 000000000..5f29c879e --- /dev/null +++ b/spring-cloud-netflix-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -0,0 +1,64 @@ +{ + "properties": [ + { + "defaultValue": "true", + "name": "archaius.propagate.environmentChangedEvent", + "description": "Propagates EnvironmentChanged events to Archaius ConfigurationManager.", + "type": "java.lang.Boolean" + }, + { + "defaultValue": true, + "name": "eureka.client.healthcheck.enabled", + "description": "Enables the Eureka health check handler.", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "true", + "name": "management.metrics.binders.hystrix.enabled", + "description": "Enables creation of OK Http Client factory beans.", + "type": "java.lang.Boolean" + }, + { + "defaultValue": false, + "name": "hystrix.shareSecurityContext", + "description": "Enables auto-configuration of the Hystrix concurrency strategy plugin hook who will transfer the `SecurityContext` from your main thread to the one used by the Hystrix command.", + "type": "java.lang.Boolean" + }, + { + "defaultValue": false, + "name": "ribbon.restclient.enabled", + "description": "Enables the use of the deprecated Ribbon RestClient.", + "type": "java.lang.Boolean" + }, + { + "defaultValue": false, + "name": "ribbon.http.client.enabled", + "description": "Deprecated property to enable Ribbon RestClient.", + "type": "java.lang.Boolean" + }, + { + "defaultValue": true, + "name": "ribbon.eureka.enabled", + "description": "Enables the use of Eureka with Ribbon.", + "type": "java.lang.Boolean" + }, + { + "defaultValue": false, + "name": "ribbon.okhttp.enabled", + "description": "Enables the use of the OK HTTP Client with Ribbon.", + "type": "java.lang.Boolean" + }, + { + "defaultValue": true, + "name": "turbine.stream.enabled", + "description": "Enables Autoconfiguration for a Spring Cloud Turbine using Spring Cloud Stream.", + "type": "java.lang.Boolean" + }, + { + "defaultValue": false, + "name": "zuul.ribbon.eager-load.enabled", + "description": "Enables eager loading of Ribbon clients on startup.", + "type": "java.lang.Boolean" + } + ] +} \ No newline at end of file diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/ribbon/FeignRibbonClientTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/ribbon/FeignRibbonClientTests.java new file mode 100644 index 000000000..aadd8a0ce --- /dev/null +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/ribbon/FeignRibbonClientTests.java @@ -0,0 +1,140 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.netflix.feign.ribbon; + +import com.netflix.client.config.CommonClientConfigKey; +import com.netflix.client.config.DefaultClientConfigImpl; +import com.netflix.client.config.IClientConfig; +import com.netflix.loadbalancer.AbstractLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import com.netflix.loadbalancer.LoadBalancerStats; +import com.netflix.loadbalancer.Server; +import com.netflix.loadbalancer.ServerStats; +import feign.Client; +import feign.Request; +import feign.Request.Options; +import feign.RequestTemplate; +import org.hamcrest.CustomMatcher; +import org.junit.Before; +import org.junit.Test; +import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector; +import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryPolicyFactory; +import org.springframework.cloud.netflix.ribbon.ServerIntrospector; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.argThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * @author Dave Syer + * @author Spencer Gibb + */ +public class FeignRibbonClientTests { + + private AbstractLoadBalancer loadBalancer = mock(AbstractLoadBalancer.class); + private Client delegate = mock(Client.class); + private RibbonLoadBalancedRetryPolicyFactory retryPolicyFactory = mock(RibbonLoadBalancedRetryPolicyFactory.class); + + private SpringClientFactory factory = new SpringClientFactory() { + @Override + public IClientConfig getClientConfig(String name) { + DefaultClientConfigImpl config = new DefaultClientConfigImpl(); + config.set(CommonClientConfigKey.ConnectTimeout, 1000); + config.set(CommonClientConfigKey.ReadTimeout, 500); + return config; + } + + @Override + public C getInstance(String name, Class type) { + if (type.isAssignableFrom(ServerIntrospector.class)) { + @SuppressWarnings("unchecked") + C instance = (C) new DefaultServerIntrospector(); + return instance; + } + return null; + } + + @Override + public ILoadBalancer getLoadBalancer(String name) { + return FeignRibbonClientTests.this.loadBalancer; + } + }; + + // Even though we don't maintain FeignRibbonClient, keep these tests + // around to make sure the expected behaviour doesn't break + private Client client = new LoadBalancerFeignClient(this.delegate, new CachingSpringLoadBalancerFactory(this.factory, + retryPolicyFactory), this.factory); + + @Before + public void init() { + when(this.loadBalancer.chooseServer(any())).thenReturn( + new Server("foo.com", 8000)); + //to fix NPE + LoadBalancerStats stats = mock(LoadBalancerStats.class); + when(this.loadBalancer.getLoadBalancerStats()).thenReturn(stats); + when(stats.getSingleServerStat(any(Server.class))).thenReturn(mock(ServerStats.class)); + } + + @Test + public void remoteRequestIsSent() throws Exception { + Request request = new RequestTemplate().method("GET").append("http://foo/") + .request(); + this.client.execute(request, new Options()); + RequestMatcher matcher = new RequestMatcher("http://foo.com:8000/"); + verify(this.delegate).execute(argThat(matcher), + any(Options.class)); + } + + @Test + public void remoteRequestIsSentAtRoot() throws Exception { + Request request = new RequestTemplate().method("GET").append("http://foo") + .request(); + this.client.execute(request, new Options()); + RequestMatcher matcher = new RequestMatcher("http://foo.com:8000/"); + verify(this.delegate).execute(argThat(matcher), + any(Options.class)); + } + + @Test + public void remoteRequestIsSecure() throws Exception { + Request request = new RequestTemplate().method("GET").append("https://foo/") + .request(); + this.client.execute(request, new Options()); + RequestMatcher matcher = new RequestMatcher("https://foo.com:8000/"); + verify(this.delegate).execute(argThat(matcher), + any(Options.class)); + } + + private final static class RequestMatcher extends CustomMatcher { + private String url; + + private RequestMatcher(String url) { + super("request has URI: " + url); + this.url = url; + } + + @Override + public boolean matches(Object item) { + Request request = (Request) item; + return request.url().equals(this.url); + } + } + +} diff --git a/spring-cloud-starter-netflix/spring-cloud-starter-netflix-ribbon/pom.xml b/spring-cloud-starter-netflix/spring-cloud-starter-netflix-ribbon/pom.xml index 02a338918..2e9910668 100644 --- a/spring-cloud-starter-netflix/spring-cloud-starter-netflix-ribbon/pom.xml +++ b/spring-cloud-starter-netflix/spring-cloud-starter-netflix-ribbon/pom.xml @@ -30,6 +30,16 @@ com.netflix.ribbon ribbon + + + io.netty + netty-codec-http + + + io.netty + netty-transport-native-epoll + + com.netflix.ribbon