Merge branch 'vanroy-feign-ribbon'
This commit is contained in:
@@ -1,16 +1,21 @@
|
||||
package org.springframework.cloud.netflix.feign;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Contract;
|
||||
import feign.Feign;
|
||||
import feign.Logger;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.cloud.netflix.feign.ribbon.FeignRibbonClient;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
|
||||
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Julien Roy
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(Feign.class)
|
||||
@@ -36,4 +41,8 @@ public class FeignAutoConfiguration {
|
||||
public Contract feignContract() {
|
||||
return new SpringMvcContract();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnClass(ILoadBalancer.class)
|
||||
public Client feignRibbonClient() { return new FeignRibbonClient(); }
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.springframework.cloud.netflix.feign;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Contract;
|
||||
import feign.Feign;
|
||||
import feign.Logger;
|
||||
@@ -36,9 +37,10 @@ public class FeignConfigurer {
|
||||
@Autowired
|
||||
Contract contract;
|
||||
|
||||
@Autowired(required = false)
|
||||
Client ribbonClient;
|
||||
|
||||
protected Feign.Builder feign() {
|
||||
//ConfigurationManager.getConfigInstance().setProperty("exampleBackend.ribbon.listOfServers", "localhost:7080");
|
||||
//exampleBackend.ribbon.NIWSServerListClassName=my.package.MyServerList
|
||||
return Feign.builder()
|
||||
.logger(logger)
|
||||
.encoder(encoder)
|
||||
@@ -53,7 +55,12 @@ public class FeignConfigurer {
|
||||
protected <T> T loadBalance(Feign.Builder builder, Class<T> type, String schemeName) {
|
||||
String name = URI.create(schemeName).getHost();
|
||||
ribbonClientPreprocessor.preprocess(name);
|
||||
return builder.target(LoadBalancingTarget.create(type, schemeName));
|
||||
|
||||
if(ribbonClient != null) {
|
||||
return builder.client(ribbonClient).target(type, schemeName);
|
||||
} else {
|
||||
return builder.target(LoadBalancingTarget.create(type, schemeName));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package org.springframework.cloud.netflix.feign.ribbon;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import com.netflix.client.ClientException;
|
||||
import com.netflix.client.ClientFactory;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
|
||||
import dagger.Lazy;
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Response;
|
||||
|
||||
/**
|
||||
* @author Julien Roy
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class FeignRibbonClient implements Client {
|
||||
|
||||
private Client defaultClient = new Default(
|
||||
new Lazy<SSLSocketFactory>() {
|
||||
@Override
|
||||
public SSLSocketFactory get() {
|
||||
return (SSLSocketFactory) SSLSocketFactory.getDefault();
|
||||
}
|
||||
}, new Lazy<HostnameVerifier>() {
|
||||
@Override
|
||||
public HostnameVerifier get() {
|
||||
return HttpsURLConnection.getDefaultHostnameVerifier();
|
||||
}
|
||||
});
|
||||
|
||||
@Override
|
||||
public Response execute(Request request, Request.Options options) throws IOException {
|
||||
try {
|
||||
|
||||
URI asUri = URI.create(request.url());
|
||||
String clientName = asUri.getHost();
|
||||
URI uriWithoutSchemeAndPort = URI.create(request.url().replace(asUri.getScheme() + "://" + asUri.getHost(), ""));
|
||||
RibbonLoadBalancer.RibbonRequest ribbonRequest = new RibbonLoadBalancer.RibbonRequest(request, uriWithoutSchemeAndPort);
|
||||
return lbClient(clientName).executeWithLoadBalancer(ribbonRequest).toResponse();
|
||||
|
||||
} catch (ClientException e) {
|
||||
if (e.getCause() instanceof IOException) {
|
||||
throw IOException.class.cast(e.getCause());
|
||||
}
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
|
||||
private RibbonLoadBalancer lbClient(String clientName) {
|
||||
IClientConfig config = ClientFactory.getNamedConfig(clientName);
|
||||
ILoadBalancer lb = ClientFactory.getNamedLoadBalancer(clientName);
|
||||
return new RibbonLoadBalancer(defaultClient, lb, config);
|
||||
}
|
||||
|
||||
public void setDefaultClient(Client defaultClient) {
|
||||
this.defaultClient = defaultClient;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package org.springframework.cloud.netflix.feign.ribbon;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import com.netflix.client.AbstractLoadBalancerAwareClient;
|
||||
import com.netflix.client.ClientException;
|
||||
import com.netflix.client.ClientRequest;
|
||||
import com.netflix.client.IResponse;
|
||||
import com.netflix.client.RequestSpecificRetryHandler;
|
||||
import com.netflix.client.RetryHandler;
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.RequestTemplate;
|
||||
import feign.Response;
|
||||
|
||||
public class RibbonLoadBalancer extends AbstractLoadBalancerAwareClient<RibbonLoadBalancer.RibbonRequest, RibbonLoadBalancer.RibbonResponse> {
|
||||
|
||||
private final Client delegate;
|
||||
private final int connectTimeout;
|
||||
private final int readTimeout;
|
||||
private final IClientConfig clientConfig;
|
||||
|
||||
public RibbonLoadBalancer(Client delegate, ILoadBalancer lb, IClientConfig clientConfig) {
|
||||
super(lb, clientConfig);
|
||||
this.setRetryHandler(RetryHandler.DEFAULT);
|
||||
this.clientConfig = clientConfig;
|
||||
this.delegate = delegate;
|
||||
connectTimeout = clientConfig.get(CommonClientConfigKey.ConnectTimeout);
|
||||
readTimeout = clientConfig.get(CommonClientConfigKey.ReadTimeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RibbonResponse execute(RibbonRequest request, IClientConfig configOverride) throws IOException {
|
||||
Request.Options options;
|
||||
if (configOverride != null) {
|
||||
options = new Request.Options(configOverride.get(CommonClientConfigKey.ConnectTimeout, connectTimeout), (configOverride.get(CommonClientConfigKey.ReadTimeout, readTimeout)));
|
||||
} else {
|
||||
options = new Request.Options(connectTimeout, readTimeout);
|
||||
}
|
||||
Response response = delegate.execute(request.toRequest(), options);
|
||||
return new RibbonResponse(request.getUri(), response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(
|
||||
RibbonRequest request, IClientConfig requestConfig) {
|
||||
if (clientConfig.get(CommonClientConfigKey.OkToRetryOnAllOperations, false)) {
|
||||
return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(), requestConfig);
|
||||
}
|
||||
if (!request.toRequest().method().equals("GET")) {
|
||||
return new RequestSpecificRetryHandler(true, false, this.getRetryHandler(), requestConfig);
|
||||
} else {
|
||||
return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(), requestConfig);
|
||||
}
|
||||
}
|
||||
|
||||
static class RibbonRequest extends ClientRequest implements Cloneable {
|
||||
|
||||
private final Request request;
|
||||
|
||||
RibbonRequest(Request request, URI uri) {
|
||||
this.request = request;
|
||||
setUri(uri);
|
||||
}
|
||||
|
||||
Request toRequest() {
|
||||
return new RequestTemplate()
|
||||
.method(request.method())
|
||||
.append(getUri().toASCIIString())
|
||||
.headers(request.headers())
|
||||
.body(request.body(), request.charset())
|
||||
.request();
|
||||
}
|
||||
|
||||
public Object clone() {
|
||||
return new RibbonRequest(request, getUri());
|
||||
}
|
||||
}
|
||||
|
||||
static class RibbonResponse implements IResponse {
|
||||
|
||||
private final URI uri;
|
||||
private final Response response;
|
||||
|
||||
RibbonResponse(URI uri, Response response) {
|
||||
this.uri = uri;
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPayload() throws ClientException {
|
||||
return response.body();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPayload() {
|
||||
return response.body() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuccess() {
|
||||
return response.status() == 200;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getRequestedURI() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Collection<String>> getHeaders() {
|
||||
return response.headers();
|
||||
}
|
||||
|
||||
Response toResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (response != null && response.body() != null) {
|
||||
response.body().close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user