Added an interface to provide your own load balancing strategy for Zipkin (#743)

Added an interface to provide your own load balancing strategy for Zipkin. The load balancing strategy allows usage of LoadBalancerClient (impl of which can be Ribbon)

fixes #734
This commit is contained in:
Marcin Grzejszczak
2017-10-19 14:00:53 +02:00
committed by GitHub
parent 9b42dff6c5
commit 07ea07e2b6
8 changed files with 258 additions and 174 deletions

View File

@@ -0,0 +1,31 @@
package org.springframework.cloud.sleuth.zipkin;
import java.net.URI;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
class LoadBalancerClientZipkinLoadBalancer implements ZipkinLoadBalancer {
private final LoadBalancerClient loadBalancerClient;
private final ZipkinProperties zipkinProperties;
LoadBalancerClientZipkinLoadBalancer(LoadBalancerClient loadBalancerClient,
ZipkinProperties zipkinProperties) {
this.loadBalancerClient = loadBalancerClient;
this.zipkinProperties = zipkinProperties;
}
@Override
public URI instance() {
if (this.loadBalancerClient != null) {
URI uri = URI.create(this.zipkinProperties.getBaseUrl());
String host = uri.getHost();
ServiceInstance instance = this.loadBalancerClient.choose(host);
if (instance != null) {
return URI.create(String.format("http://%s:%s", instance.getHost(), instance.getPort()));
}
}
return URI.create(this.zipkinProperties.getBaseUrl());
}
}

View File

@@ -31,8 +31,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClas
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.cloud.context.config.annotation.RefreshScope;
@@ -92,43 +92,41 @@ public class ZipkinAutoConfiguration {
}
@Configuration
@ConditionalOnClass(DiscoveryClient.class)
@ConditionalOnClass(LoadBalancerClient.class)
static class DiscoveryClientZipkinUrlExtractorConfiguration {
@Autowired(required = false) DiscoveryClient discoveryClient;
@Autowired(required = false) LoadBalancerClient client;
@Bean
ZipkinUrlExtractor zipkinUrlExtractor() {
final DiscoveryClient discoveryClient = this.discoveryClient;
return new ZipkinUrlExtractor() {
@Override
public URI zipkinUrl(ZipkinProperties zipkinProperties) {
if (discoveryClient != null) {
URI uri = URI.create(zipkinProperties.getBaseUrl());
String host = uri.getHost();
List<ServiceInstance> instances = discoveryClient.getInstances(host);
if (!instances.isEmpty()) {
return instances.get(0).getUri();
}
}
@ConditionalOnMissingBean
ZipkinLoadBalancer loadBalancerClientZipkinLoadBalancer(ZipkinProperties zipkinProperties) {
return new LoadBalancerClientZipkinLoadBalancer(this.client, zipkinProperties);
}
}
@Configuration
@ConditionalOnMissingClass("org.springframework.cloud.client.loadbalancer.LoadBalancerClient")
static class DefaultZipkinUrlExtractorConfiguration {
@Bean
@ConditionalOnMissingBean
ZipkinLoadBalancer loadBalancerClientZipkinLoadBalancer(final ZipkinProperties zipkinProperties) {
return new ZipkinLoadBalancer() {
@Override public URI instance() {
return URI.create(zipkinProperties.getBaseUrl());
}
};
}
}
@Configuration
@ConditionalOnMissingClass("org.springframework.cloud.client.discovery.DiscoveryClient")
static class DefaultZipkinUrlExtractorConfiguration {
@Bean
ZipkinUrlExtractor zipkinUrlExtractor() {
return new ZipkinUrlExtractor() {
@Override
public URI zipkinUrl(ZipkinProperties zipkinProperties) {
return URI.create(zipkinProperties.getBaseUrl());
}
};
}
@Bean
ZipkinUrlExtractor zipkinUrlExtractor(final ZipkinLoadBalancer zipkinLoadBalancer) {
return new ZipkinUrlExtractor() {
@Override
public URI zipkinUrl(ZipkinProperties zipkinProperties) {
return zipkinLoadBalancer.instance();
}
};
}
@Bean

View File

@@ -0,0 +1,19 @@
package org.springframework.cloud.sleuth.zipkin;
import java.net.URI;
/**
* Load balancing strategy for picking a Zipkin instance
*
* @author Marcin Grzejszczak
* @since 1.3.0
*/
public interface ZipkinLoadBalancer {
/**
* Returns a concrete {@link URI} of a Zipkin instance.
*
* @return {@link URI} of the picked instance
*/
URI instance();
}

View File

@@ -9,11 +9,8 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.assertj.core.api.BDDAssertions;
import org.awaitility.Awaitility;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
@@ -21,12 +18,13 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerRequest;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.SpanReporter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.SocketUtils;
import zipkin.junit.ZipkinRule;
@@ -52,67 +50,59 @@ public class ZipkinDiscoveryClientTests {
@Configuration
@EnableAutoConfiguration
static class Config {
@Bean
DiscoveryClient client() {
return new ZipkinDiscoveryClient();
}
}
}
class ZipkinDiscoveryClient implements DiscoveryClient {
@Override
public String description() {
return "";
}
@Override
public ServiceInstance getLocalServiceInstance() {
return null;
}
@Override
public List<ServiceInstance> getInstances(String s) {
if ("zipkin".equals(s)) {
return Collections.singletonList(new ServiceInstance() {
@Override
public String getServiceId() {
return "zipkin";
}
@Override
public String getHost() {
return "localhost";
}
@Override
public int getPort() {
return URI.create(ZIPKIN_RULE.httpUrl()).getPort();
}
@Override
public boolean isSecure() {
return false;
}
@Override
public URI getUri() {
return URI.create(ZIPKIN_RULE.httpUrl());
}
@Override
public Map<String, String> getMetadata() {
@Bean LoadBalancerClient loadBalancerClient() {
return new LoadBalancerClient() {
@Override public <T> T execute(String serviceId,
LoadBalancerRequest<T> request) throws IOException {
return null;
}
});
@Override public <T> T execute(String serviceId,
ServiceInstance serviceInstance, LoadBalancerRequest<T> request)
throws IOException {
return null;
}
@Override public URI reconstructURI(ServiceInstance instance,
URI original) {
return null;
}
@Override public ServiceInstance choose(String serviceId) {
return new ServiceInstance() {
@Override
public String getServiceId() {
return "zipkin";
}
@Override
public String getHost() {
return "localhost";
}
@Override
public int getPort() {
return URI.create(ZIPKIN_RULE.httpUrl()).getPort();
}
@Override
public boolean isSecure() {
return false;
}
@Override
public URI getUri() {
return URI.create(ZIPKIN_RULE.httpUrl());
}
@Override
public Map<String, String> getMetadata() {
return null;
}
};
}
};
}
return Collections.emptyList();
}
@Override
public List<String> getServices() {
return Collections.singletonList("zipkin");
}
}

View File

@@ -0,0 +1,19 @@
package org.springframework.cloud.sleuth.zipkin2;
import java.net.URI;
/**
* Load balancing strategy for picking a Zipkin instance
*
* @author Marcin Grzejszczak
* @since 1.3.0
*/
public interface ZipkinLoadBalancer {
/**
* Returns a concrete {@link URI} of a Zipkin instance.
*
* @return {@link URI} of the picked instance
*/
URI instance();
}

View File

@@ -0,0 +1,33 @@
package org.springframework.cloud.sleuth.zipkin2.sender;
import java.net.URI;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.sleuth.zipkin2.ZipkinLoadBalancer;
import org.springframework.cloud.sleuth.zipkin2.ZipkinProperties;
class LoadBalancerClientZipkinLoadBalancer implements ZipkinLoadBalancer {
private final LoadBalancerClient loadBalancerClient;
private final ZipkinProperties zipkinProperties;
LoadBalancerClientZipkinLoadBalancer(LoadBalancerClient loadBalancerClient,
ZipkinProperties zipkinProperties) {
this.loadBalancerClient = loadBalancerClient;
this.zipkinProperties = zipkinProperties;
}
@Override
public URI instance() {
if (this.loadBalancerClient != null) {
URI uri = URI.create(this.zipkinProperties.getBaseUrl());
String host = uri.getHost();
ServiceInstance instance = this.loadBalancerClient.choose(host);
if (instance != null) {
return URI.create(String.format("http://%s:%s", instance.getHost(), instance.getPort()));
}
}
return URI.create(this.zipkinProperties.getBaseUrl());
}
}

View File

@@ -2,15 +2,15 @@ package org.springframework.cloud.sleuth.zipkin2.sender;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.ConditionalOnMissingClass;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.sleuth.zipkin2.ZipkinLoadBalancer;
import org.springframework.cloud.sleuth.zipkin2.ZipkinProperties;
import org.springframework.cloud.sleuth.zipkin2.ZipkinRestTemplateCustomizer;
import org.springframework.context.annotation.Bean;
@@ -39,13 +39,15 @@ class ZipkinRestTemplateSenderConfiguration {
}
@Configuration
@ConditionalOnMissingClass("org.springframework.cloud.client.discovery.DiscoveryClient")
@ConditionalOnMissingClass("org.springframework.cloud.client.loadbalancer.LoadBalancerClient")
static class DefaultZipkinUrlExtractorConfiguration {
@Autowired(required = false) LoadBalancerClient client;
@Bean
ZipkinUrlExtractor zipkinUrlExtractor() {
return new ZipkinUrlExtractor() {
@Override
public URI zipkinUrl(ZipkinProperties zipkinProperties) {
@ConditionalOnMissingBean
ZipkinLoadBalancer noOpLoadBalancer(final ZipkinProperties zipkinProperties) {
return new ZipkinLoadBalancer() {
@Override public URI instance() {
return URI.create(zipkinProperties.getBaseUrl());
}
};
@@ -53,30 +55,27 @@ class ZipkinRestTemplateSenderConfiguration {
}
@Configuration
@ConditionalOnClass(DiscoveryClient.class)
@ConditionalOnClass(LoadBalancerClient.class)
static class DiscoveryClientZipkinUrlExtractorConfiguration {
@Autowired(required = false) DiscoveryClient discoveryClient;
@Autowired(required = false) LoadBalancerClient client;
@Bean
ZipkinUrlExtractor zipkinUrlExtractor() {
final DiscoveryClient discoveryClient = this.discoveryClient;
return new ZipkinUrlExtractor() {
@Override
public URI zipkinUrl(ZipkinProperties zipkinProperties) {
if (discoveryClient != null) {
URI uri = URI.create(zipkinProperties.getBaseUrl());
String host = uri.getHost();
List<ServiceInstance> instances = discoveryClient.getInstances(host);
if (!instances.isEmpty()) {
return instances.get(0).getUri();
}
}
return URI.create(zipkinProperties.getBaseUrl());
}
};
@ConditionalOnMissingBean
ZipkinLoadBalancer loadBalancerClientZipkinLoadBalancer(ZipkinProperties zipkinProperties) {
return new LoadBalancerClientZipkinLoadBalancer(this.client, zipkinProperties);
}
}
@Bean
ZipkinUrlExtractor zipkinUrlExtractor(final ZipkinLoadBalancer zipkinLoadBalancer) {
return new ZipkinUrlExtractor() {
@Override
public URI zipkinUrl(ZipkinProperties zipkinProperties) {
return zipkinLoadBalancer.instance();
}
};
}
}
/**

View File

@@ -3,6 +3,7 @@ package org.springframework.cloud.sleuth.zipkin2;
import static org.assertj.core.api.BDDAssertions.then;
import static org.springframework.cloud.sleuth.zipkin2.ZipkinDiscoveryClientTests.ZIPKIN_RULE;
import java.io.IOException;
import java.net.URI;
import java.util.Collections;
import java.util.List;
@@ -17,6 +18,8 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerRequest;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.SpanReporter;
import org.springframework.context.annotation.Bean;
@@ -49,67 +52,59 @@ public class ZipkinDiscoveryClientTests {
@Configuration
@EnableAutoConfiguration
static class Config {
@Bean
DiscoveryClient client() {
return new ZipkinDiscoveryClient();
}
}
}
class ZipkinDiscoveryClient implements DiscoveryClient {
@Override
public String description() {
return "";
}
@Override
public ServiceInstance getLocalServiceInstance() {
return null;
}
@Override
public List<ServiceInstance> getInstances(String s) {
if ("zipkin".equals(s)) {
return Collections.singletonList(new ServiceInstance() {
@Override
public String getServiceId() {
return "zipkin";
}
@Override
public String getHost() {
return "localhost";
}
@Override
public int getPort() {
return URI.create(ZIPKIN_RULE.httpUrl()).getPort();
}
@Override
public boolean isSecure() {
return false;
}
@Override
public URI getUri() {
return URI.create(ZIPKIN_RULE.httpUrl());
}
@Override
public Map<String, String> getMetadata() {
@Bean LoadBalancerClient loadBalancerClient() {
return new LoadBalancerClient() {
@Override public <T> T execute(String serviceId,
LoadBalancerRequest<T> request) throws IOException {
return null;
}
});
@Override public <T> T execute(String serviceId,
ServiceInstance serviceInstance, LoadBalancerRequest<T> request)
throws IOException {
return null;
}
@Override public URI reconstructURI(ServiceInstance instance,
URI original) {
return null;
}
@Override public ServiceInstance choose(String serviceId) {
return new ServiceInstance() {
@Override
public String getServiceId() {
return "zipkin";
}
@Override
public String getHost() {
return "localhost";
}
@Override
public int getPort() {
return URI.create(ZIPKIN_RULE.httpUrl()).getPort();
}
@Override
public boolean isSecure() {
return false;
}
@Override
public URI getUri() {
return URI.create(ZIPKIN_RULE.httpUrl());
}
@Override
public Map<String, String> getMetadata() {
return null;
}
};
}
};
}
return Collections.emptyList();
}
@Override
public List<String> getServices() {
return Collections.singletonList("zipkin");
}
}