Commit 4f6b2bce by Spencer Gibb

Record load balancer stats in RibbonHttpRequest.

fixes gh-731
parent 472b9d17
...@@ -67,7 +67,7 @@ public class RibbonAutoConfiguration { ...@@ -67,7 +67,7 @@ public class RibbonAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean(LoadBalancerClient.class) @ConditionalOnMissingBean(LoadBalancerClient.class)
public LoadBalancerClient loadBalancerClient() { public RibbonLoadBalancerClient loadBalancerClient() {
return new RibbonLoadBalancerClient(springClientFactory()); return new RibbonLoadBalancerClient(springClientFactory());
} }
...@@ -80,7 +80,7 @@ public class RibbonAutoConfiguration { ...@@ -80,7 +80,7 @@ public class RibbonAutoConfiguration {
private SpringClientFactory springClientFactory; private SpringClientFactory springClientFactory;
@Autowired @Autowired
private LoadBalancerClient loadBalancerClient; private RibbonLoadBalancerClient loadBalancerClient;
@Bean @Bean
public RestTemplateCustomizer restTemplateCustomizer() { public RestTemplateCustomizer restTemplateCustomizer() {
......
...@@ -19,8 +19,7 @@ package org.springframework.cloud.netflix.ribbon; ...@@ -19,8 +19,7 @@ package org.springframework.cloud.netflix.ribbon;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.RibbonServer;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest; import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.ClientHttpRequestFactory;
...@@ -36,9 +35,9 @@ public class RibbonClientHttpRequestFactory implements ClientHttpRequestFactory ...@@ -36,9 +35,9 @@ public class RibbonClientHttpRequestFactory implements ClientHttpRequestFactory
private final SpringClientFactory clientFactory; 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.clientFactory = clientFactory;
this.loadBalancer = loadBalancer; this.loadBalancer = loadBalancer;
} }
...@@ -51,17 +50,19 @@ public class RibbonClientHttpRequestFactory implements ClientHttpRequestFactory ...@@ -51,17 +50,19 @@ public class RibbonClientHttpRequestFactory implements ClientHttpRequestFactory
if (serviceId == null) { if (serviceId == null) {
throw new IOException("Invalid hostname in the URI [" + originalUri.toASCIIString() + "]"); throw new IOException("Invalid hostname in the URI [" + originalUri.toASCIIString() + "]");
} }
ServiceInstance instance = loadBalancer.choose(serviceId); RibbonServer instance = loadBalancer.chooseRibbonServer(serviceId);
if (instance == null) { if (instance == null) {
throw new IllegalStateException("No instances available for "+serviceId); throw new IllegalStateException("No instances available for "+serviceId);
} }
URI uri = loadBalancer.reconstructURI(instance, originalUri); URI uri = this.loadBalancer.reconstructURI(instance, originalUri);
//@formatter:off //@formatter:off
IClientConfig clientConfig = clientFactory.getClientConfig(instance.getServiceId()); IClientConfig clientConfig = this.clientFactory.getClientConfig(instance.getServiceId());
RestClient client = clientFactory.getClient(instance.getServiceId(), RestClient.class); RestClient client = this.clientFactory.getClient(instance.getServiceId(), RestClient.class);
HttpRequest.Verb verb = HttpRequest.Verb.valueOf(httpMethod.name()); HttpRequest.Verb verb = HttpRequest.Verb.valueOf(httpMethod.name());
RibbonLoadBalancerContext context = this.clientFactory.getLoadBalancerContext(serviceId);
RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, instance.getServer());
//@formatter:on //@formatter:on
return new RibbonHttpRequest(uri, verb, client, clientConfig); return new RibbonHttpRequest(uri, verb, client, clientConfig, statsRecorder);
} }
} }
...@@ -34,6 +34,7 @@ import java.util.List; ...@@ -34,6 +34,7 @@ import java.util.List;
/** /**
* @author Spencer Gibb * @author Spencer Gibb
*/ */
@SuppressWarnings("deprecation")
public class RibbonHttpRequest extends AbstractClientHttpRequest { public class RibbonHttpRequest extends AbstractClientHttpRequest {
private HttpRequest.Builder builder; private HttpRequest.Builder builder;
...@@ -41,14 +42,16 @@ public class RibbonHttpRequest extends AbstractClientHttpRequest { ...@@ -41,14 +42,16 @@ public class RibbonHttpRequest extends AbstractClientHttpRequest {
private HttpRequest.Verb verb; private HttpRequest.Verb verb;
private RestClient client; private RestClient client;
private IClientConfig config; private IClientConfig config;
private RibbonStatsRecorder statsRecorder;
private ByteArrayOutputStream outputStream = null; private ByteArrayOutputStream outputStream = null;
public RibbonHttpRequest(URI uri, HttpRequest.Verb verb, RestClient client, public RibbonHttpRequest(URI uri, HttpRequest.Verb verb, RestClient client,
IClientConfig config) { IClientConfig config, RibbonStatsRecorder statsRecorder) {
this.uri = uri; this.uri = uri;
this.verb = verb; this.verb = verb;
this.client = client; this.client = client;
this.config = config; this.config = config;
this.statsRecorder = statsRecorder;
this.builder = HttpRequest.newBuilder().uri(uri).verb(verb); this.builder = HttpRequest.newBuilder().uri(uri).verb(verb);
} }
...@@ -71,7 +74,6 @@ public class RibbonHttpRequest extends AbstractClientHttpRequest { ...@@ -71,7 +74,6 @@ public class RibbonHttpRequest extends AbstractClientHttpRequest {
} }
@Override @Override
@SuppressWarnings("deprecation")
protected ClientHttpResponse executeInternal(HttpHeaders headers) protected ClientHttpResponse executeInternal(HttpHeaders headers)
throws IOException { throws IOException {
try { try {
...@@ -82,18 +84,12 @@ public class RibbonHttpRequest extends AbstractClientHttpRequest { ...@@ -82,18 +84,12 @@ public class RibbonHttpRequest extends AbstractClientHttpRequest {
} }
HttpRequest request = builder.build(); HttpRequest request = builder.build();
HttpResponse response = client.execute(request, config); HttpResponse response = client.execute(request, config);
statsRecorder.recordStats(response);
return new RibbonHttpResponse(response); return new RibbonHttpResponse(response);
} catch (Exception e) { } catch (Exception e) {
statsRecorder.recordStats(e);
throw new IOException(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<ClientHttpResponse>() {
@Override
public ClientHttpResponse apply(ServiceInstance instance) throws Exception {
}
});*/
} }
private void addHeaders(HttpHeaders headers) { private void addHeaders(HttpHeaders headers) {
......
...@@ -19,7 +19,6 @@ package org.springframework.cloud.netflix.ribbon; ...@@ -19,7 +19,6 @@ package org.springframework.cloud.netflix.ribbon;
import java.net.URI; import java.net.URI;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.springframework.cloud.client.DefaultServiceInstance; import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.ServiceInstance;
...@@ -33,8 +32,6 @@ import com.netflix.client.config.CommonClientConfigKey; ...@@ -33,8 +32,6 @@ import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.IClientConfig; import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server; import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerStats;
import com.netflix.servo.monitor.Stopwatch;
/** /**
* @author Spencer Gibb * @author Spencer Gibb
...@@ -65,6 +62,10 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient { ...@@ -65,6 +62,10 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient {
@Override @Override
public ServiceInstance choose(String serviceId) { public ServiceInstance choose(String serviceId) {
return this.chooseRibbonServer(serviceId);
}
RibbonServer chooseRibbonServer(String serviceId) {
Server server = getServer(serviceId); Server server = getServer(serviceId);
if (server == null) { if (server == null) {
return null; return null;
...@@ -76,23 +77,21 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient { ...@@ -76,23 +77,21 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient {
@Override @Override
public <T> T execute(String serviceId, LoadBalancerRequest<T> request) { public <T> T execute(String serviceId, LoadBalancerRequest<T> request) {
ILoadBalancer loadBalancer = getLoadBalancer(serviceId); ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
RibbonLoadBalancerContext context = this.clientFactory
.getLoadBalancerContext(serviceId);
Server server = getServer(loadBalancer); Server server = getServer(loadBalancer);
RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server, RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server,
serviceId), serverIntrospector(serviceId).getMetadata(server)); serviceId), serverIntrospector(serviceId).getMetadata(server));
ServerStats serverStats = context.getServerStats(server); RibbonLoadBalancerContext context = this.clientFactory
context.noteOpenConnection(serverStats); .getLoadBalancerContext(serviceId);
Stopwatch tracer = context.getExecuteTracer().start(); RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);
try { try {
T returnVal = request.apply(ribbonServer); T returnVal = request.apply(ribbonServer);
recordStats(context, tracer, serverStats, returnVal, null); statsRecorder.recordStats(returnVal);
return returnVal; return returnVal;
} }
catch (Exception ex) { catch (Exception ex) {
recordStats(context, tracer, serverStats, null, ex); statsRecorder.recordStats(ex);
ReflectionUtils.rethrowRuntimeException(ex); ReflectionUtils.rethrowRuntimeException(ex);
} }
return null; return null;
...@@ -116,13 +115,6 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient { ...@@ -116,13 +115,6 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient {
return serverIntrospector(serviceId).isSecure(server); 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) { protected Server getServer(String serviceId) {
return getServer(getLoadBalancer(serviceId)); return getServer(getLoadBalancer(serviceId));
} }
......
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 */);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment