diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java index f67bdfff..b36824de 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java @@ -67,7 +67,7 @@ public class RibbonAutoConfiguration { @Bean @ConditionalOnMissingBean(LoadBalancerClient.class) - public LoadBalancerClient loadBalancerClient() { + public RibbonLoadBalancerClient loadBalancerClient() { return new RibbonLoadBalancerClient(springClientFactory()); } @@ -80,7 +80,7 @@ public class RibbonAutoConfiguration { private SpringClientFactory springClientFactory; @Autowired - private LoadBalancerClient loadBalancerClient; + private RibbonLoadBalancerClient loadBalancerClient; @Bean public RestTemplateCustomizer restTemplateCustomizer() { diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientHttpRequestFactory.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientHttpRequestFactory.java index c1e95a81..9cd24325 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientHttpRequestFactory.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientHttpRequestFactory.java @@ -19,8 +19,7 @@ package org.springframework.cloud.netflix.ribbon; import java.io.IOException; import java.net.URI; -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; +import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.RibbonServer; import org.springframework.http.HttpMethod; import org.springframework.http.client.ClientHttpRequest; import org.springframework.http.client.ClientHttpRequestFactory; @@ -36,9 +35,9 @@ public class RibbonClientHttpRequestFactory implements ClientHttpRequestFactory private final SpringClientFactory clientFactory; - private LoadBalancerClient loadBalancer; + private RibbonLoadBalancerClient loadBalancer; - public RibbonClientHttpRequestFactory(SpringClientFactory clientFactory, LoadBalancerClient loadBalancer) { + public RibbonClientHttpRequestFactory(SpringClientFactory clientFactory, RibbonLoadBalancerClient loadBalancer) { this.clientFactory = clientFactory; this.loadBalancer = loadBalancer; } @@ -51,17 +50,19 @@ public class RibbonClientHttpRequestFactory implements ClientHttpRequestFactory if (serviceId == null) { throw new IOException("Invalid hostname in the URI [" + originalUri.toASCIIString() + "]"); } - ServiceInstance instance = loadBalancer.choose(serviceId); + RibbonServer instance = loadBalancer.chooseRibbonServer(serviceId); if (instance == null) { throw new IllegalStateException("No instances available for "+serviceId); } - URI uri = loadBalancer.reconstructURI(instance, originalUri); + URI uri = this.loadBalancer.reconstructURI(instance, originalUri); //@formatter:off - IClientConfig clientConfig = clientFactory.getClientConfig(instance.getServiceId()); - RestClient client = clientFactory.getClient(instance.getServiceId(), RestClient.class); + IClientConfig clientConfig = this.clientFactory.getClientConfig(instance.getServiceId()); + RestClient client = this.clientFactory.getClient(instance.getServiceId(), RestClient.class); HttpRequest.Verb verb = HttpRequest.Verb.valueOf(httpMethod.name()); + RibbonLoadBalancerContext context = this.clientFactory.getLoadBalancerContext(serviceId); + RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, instance.getServer()); //@formatter:on - return new RibbonHttpRequest(uri, verb, client, clientConfig); + return new RibbonHttpRequest(uri, verb, client, clientConfig, statsRecorder); } } diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonHttpRequest.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonHttpRequest.java index f5e05edb..121d4b49 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonHttpRequest.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonHttpRequest.java @@ -34,6 +34,7 @@ import java.util.List; /** * @author Spencer Gibb */ +@SuppressWarnings("deprecation") public class RibbonHttpRequest extends AbstractClientHttpRequest { private HttpRequest.Builder builder; @@ -41,14 +42,16 @@ public class RibbonHttpRequest extends AbstractClientHttpRequest { private HttpRequest.Verb verb; private RestClient client; private IClientConfig config; + private RibbonStatsRecorder statsRecorder; private ByteArrayOutputStream outputStream = null; public RibbonHttpRequest(URI uri, HttpRequest.Verb verb, RestClient client, - IClientConfig config) { + IClientConfig config, RibbonStatsRecorder statsRecorder) { this.uri = uri; this.verb = verb; this.client = client; this.config = config; + this.statsRecorder = statsRecorder; this.builder = HttpRequest.newBuilder().uri(uri).verb(verb); } @@ -71,7 +74,6 @@ public class RibbonHttpRequest extends AbstractClientHttpRequest { } @Override - @SuppressWarnings("deprecation") protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException { try { @@ -82,18 +84,12 @@ public class RibbonHttpRequest extends AbstractClientHttpRequest { } HttpRequest request = builder.build(); HttpResponse response = client.execute(request, config); + statsRecorder.recordStats(response); return new RibbonHttpResponse(response); } catch (Exception e) { + statsRecorder.recordStats(e); throw new IOException(e); } - - //TODO: fix stats, now that execute is not called - // use execute here so stats are collected - /*return loadBalancer.execute(this.config.getClientName(), new LoadBalancerRequest() { -@Override -public ClientHttpResponse apply(ServiceInstance instance) throws Exception { -} -});*/ } private void addHeaders(HttpHeaders headers) { diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClient.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClient.java index ff13f0ce..18542527 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClient.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClient.java @@ -19,7 +19,6 @@ package org.springframework.cloud.netflix.ribbon; import java.net.URI; import java.util.Collections; import java.util.Map; -import java.util.concurrent.TimeUnit; import org.springframework.cloud.client.DefaultServiceInstance; import org.springframework.cloud.client.ServiceInstance; @@ -33,8 +32,6 @@ import com.netflix.client.config.CommonClientConfigKey; import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.Server; -import com.netflix.loadbalancer.ServerStats; -import com.netflix.servo.monitor.Stopwatch; /** * @author Spencer Gibb @@ -65,6 +62,10 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient { @Override public ServiceInstance choose(String serviceId) { + return this.chooseRibbonServer(serviceId); + } + + RibbonServer chooseRibbonServer(String serviceId) { Server server = getServer(serviceId); if (server == null) { return null; @@ -76,23 +77,21 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient { @Override public T execute(String serviceId, LoadBalancerRequest request) { ILoadBalancer loadBalancer = getLoadBalancer(serviceId); - RibbonLoadBalancerContext context = this.clientFactory - .getLoadBalancerContext(serviceId); Server server = getServer(loadBalancer); RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server, serviceId), serverIntrospector(serviceId).getMetadata(server)); - ServerStats serverStats = context.getServerStats(server); - context.noteOpenConnection(serverStats); - Stopwatch tracer = context.getExecuteTracer().start(); + RibbonLoadBalancerContext context = this.clientFactory + .getLoadBalancerContext(serviceId); + RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server); try { T returnVal = request.apply(ribbonServer); - recordStats(context, tracer, serverStats, returnVal, null); + statsRecorder.recordStats(returnVal); return returnVal; } catch (Exception ex) { - recordStats(context, tracer, serverStats, null, ex); + statsRecorder.recordStats(ex); ReflectionUtils.rethrowRuntimeException(ex); } return null; @@ -116,13 +115,6 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient { return serverIntrospector(serviceId).isSecure(server); } - private void recordStats(RibbonLoadBalancerContext context, Stopwatch tracer, - ServerStats serverStats, Object entity, Throwable exception) { - tracer.stop(); - long duration = tracer.getDuration(TimeUnit.MILLISECONDS); - context.noteRequestCompletion(serverStats, entity, exception, duration, null/* errorHandler */); - } - protected Server getServer(String serviceId) { return getServer(getLoadBalancer(serviceId)); } diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonStatsRecorder.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonStatsRecorder.java new file mode 100644 index 00000000..5f2c22ca --- /dev/null +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonStatsRecorder.java @@ -0,0 +1,38 @@ +package org.springframework.cloud.netflix.ribbon; + +import java.util.concurrent.TimeUnit; + +import com.netflix.loadbalancer.Server; +import com.netflix.loadbalancer.ServerStats; +import com.netflix.servo.monitor.Stopwatch; + +/** + * @author Spencer Gibb + */ +public class RibbonStatsRecorder { + + private RibbonLoadBalancerContext context; + private final ServerStats serverStats; + private final Stopwatch tracer; + + public RibbonStatsRecorder(RibbonLoadBalancerContext context, Server server) { + this.context = context; + serverStats = context.getServerStats(server); + context.noteOpenConnection(serverStats); + tracer = context.getExecuteTracer().start(); + } + + public void recordStats(Object entity) { + this.recordStats(entity, null); + } + + public void recordStats(Throwable t) { + this.recordStats(null, t); + } + + protected void recordStats(Object entity, Throwable exception) { + this.tracer.stop(); + long duration = this.tracer.getDuration(TimeUnit.MILLISECONDS); + this.context.noteRequestCompletion(serverStats, entity, exception, duration, null/* errorHandler */); + } +}