Merge branch 'master' into 2.0.x
This commit is contained in:
@@ -605,6 +605,14 @@ One of the main benefits of Hystrix is the set of metrics it gathers about each
|
||||
.Hystrix Dashboard
|
||||
image::Hystrix.png[]
|
||||
|
||||
== Hystrix Timeouts And Ribbon Clients
|
||||
|
||||
When using Hystrix commands that wrap Ribbon clients you want to make sure your Hystrix timeout
|
||||
is configured to be longer than the configured Ribbon timeout, including any potential
|
||||
retries that might be made. For example, if your Ribbon connection timeout is one second and
|
||||
the Ribbon client might retry the request three times, than your Hystrix timeout should
|
||||
be slightly more than three seconds.
|
||||
|
||||
|
||||
[[netflix-hystrix-dashboard-starter]]
|
||||
=== How to Include Hystrix Dashboard
|
||||
@@ -1589,7 +1597,8 @@ If you are using `@EnableZuulProxy` with tha Spring Boot Actuator you
|
||||
will enable (by default) an additional endpoint, available via HTTP as
|
||||
`/routes`. A GET to this endpoint will return a list of the mapped
|
||||
routes. A POST will force a refresh of the existing routes (e.g. in
|
||||
case there have been changes in the service catalog).
|
||||
case there have been changes in the service catalog). You can disable
|
||||
this endpoint by setting `endpoints.routes.enabled` to `false`.
|
||||
|
||||
NOTE: the routes should respond automatically to changes in the
|
||||
service catalog, but the POST to /routes is a way to force the change
|
||||
@@ -1893,6 +1902,10 @@ Route filters:
|
||||
|
||||
* `SimpleHostRoutingFilter`: This filter sends requests to predetermined URLs via an Apache HttpClient. URLs are found in `RequestContext.getRouteHost()`.
|
||||
|
||||
==== Custom Zuul Filter examples
|
||||
|
||||
Most of the following "How to Write" examples below are included https://github.com/spring-cloud-samples/sample-zuul-filters[Sample Zuul Filters] project. There are also examples of manipulating the request or response body in that repository.
|
||||
|
||||
==== How to Write a Pre Filter
|
||||
|
||||
Pre filters are used to set up data in the `RequestContext` for use in filters downstream. The main use case is to set information required for route filters.
|
||||
@@ -2448,6 +2461,22 @@ certain Ribbon properties. The properties you can use are
|
||||
`client.ribbon.OkToRetryOnAllOperations`. See the https://github.com/Netflix/ribbon/wiki/Getting-Started#the-properties-file-sample-clientproperties[Ribbon documentation]
|
||||
for a description of what there properties do.
|
||||
|
||||
In addition you may want to retry requests when certain status codes are returned in the
|
||||
response. You can list the response codes you would like the Ribbon client to retry using the
|
||||
property `clientName.ribbon.retryableStatusCodes`. For example
|
||||
|
||||
[source,yaml]
|
||||
----
|
||||
clientName:
|
||||
ribbon:
|
||||
retryableStatusCodes: 404,502
|
||||
----
|
||||
|
||||
You can also create a bean of type `LoadBalancedRetryPolicy` and implement the `retryableStatusCode`
|
||||
method to determine whether you want to retry a request given the status code.
|
||||
|
||||
|
||||
|
||||
==== Zuul
|
||||
|
||||
You can turn off Zuul's retry functionality by setting `zuul.retryable` to `false`. You
|
||||
|
||||
@@ -248,7 +248,7 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
|
||||
|
||||
private String getUrl(Map<String, Object> attributes) {
|
||||
String url = resolve((String) attributes.get("url"));
|
||||
if (StringUtils.hasText(url) && !(url.startsWith("#{") && url.endsWith("}"))) {
|
||||
if (StringUtils.hasText(url) && !(url.startsWith("#{") && url.contains("}"))) {
|
||||
if (!url.contains("://")) {
|
||||
url = "http://" + url;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFact
|
||||
import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
import org.springframework.cloud.netflix.ribbon.support.RetryableStatusCodeException;
|
||||
import org.springframework.retry.RetryCallback;
|
||||
import org.springframework.retry.RetryContext;
|
||||
import org.springframework.retry.policy.NeverRetryPolicy;
|
||||
@@ -69,7 +70,7 @@ public class RetryableFeignLoadBalancer extends FeignLoadBalancer implements Ser
|
||||
else {
|
||||
options = new Request.Options(this.connectTimeout, this.readTimeout);
|
||||
}
|
||||
LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryPolicyFactory.create(this.getClientName(), this);
|
||||
final LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryPolicyFactory.create(this.getClientName(), this);
|
||||
RetryTemplate retryTemplate = new RetryTemplate();
|
||||
retryTemplate.setRetryPolicy(retryPolicy == null ? new NeverRetryPolicy()
|
||||
: new FeignRetryPolicy(request.toHttpRequest(), retryPolicy, this, this.getClientName()));
|
||||
@@ -89,6 +90,9 @@ public class RetryableFeignLoadBalancer extends FeignLoadBalancer implements Ser
|
||||
feignRequest = request.toRequest();
|
||||
}
|
||||
Response response = request.client().execute(feignRequest, options);
|
||||
if(retryPolicy.retryableStatusCode(response.status())) {
|
||||
throw new RetryableStatusCodeException(RetryableFeignLoadBalancer.this.getClientName(), response.status());
|
||||
}
|
||||
return new RibbonResponse(request.getUri(), response);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -90,12 +90,14 @@ public class RibbonAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
|
||||
@ConditionalOnMissingBean
|
||||
public LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory(SpringClientFactory clientFactory) {
|
||||
return new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingClass(value = "org.springframework.retry.support.RetryTemplate")
|
||||
@ConditionalOnMissingBean
|
||||
public LoadBalancedRetryPolicyFactory neverRetryPolicyFactory() {
|
||||
return new LoadBalancedRetryPolicyFactory.NeverRetryFactory();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2013-2016 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.ribbon;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
|
||||
import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.util.StringUtils;
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.client.config.IClientConfigKey;
|
||||
|
||||
/**
|
||||
* {@link LoadBalancedRetryPolicy} for Ribbon clients.
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class RibbonLoadBalancedRetryPolicy implements LoadBalancedRetryPolicy {
|
||||
|
||||
public static final IClientConfigKey<String> RETRYABLE_STATUS_CODES = new CommonClientConfigKey<String>("retryableStatusCodes") {};
|
||||
private int sameServerCount = 0;
|
||||
private int nextServerCount = 0;
|
||||
private String serviceId;
|
||||
private RibbonLoadBalancerContext lbContext;
|
||||
private ServiceInstanceChooser loadBalanceChooser;
|
||||
List<Integer> retryableStatusCodes = new ArrayList<>();
|
||||
|
||||
public RibbonLoadBalancedRetryPolicy(String serviceId, RibbonLoadBalancerContext context, ServiceInstanceChooser loadBalanceChooser) {
|
||||
this.serviceId = serviceId;
|
||||
this.lbContext = context;
|
||||
this.loadBalanceChooser = loadBalanceChooser;
|
||||
}
|
||||
|
||||
public RibbonLoadBalancedRetryPolicy(String serviceId, RibbonLoadBalancerContext context, ServiceInstanceChooser loadBalanceChooser,
|
||||
IClientConfig clientConfig) {
|
||||
this.serviceId = serviceId;
|
||||
this.lbContext = context;
|
||||
this.loadBalanceChooser = loadBalanceChooser;
|
||||
String retryableStatusCodesProp = clientConfig.getPropertyAsString(RETRYABLE_STATUS_CODES, "");
|
||||
String[] retryableStatusCodesArray = retryableStatusCodesProp.split(",");
|
||||
for(String code : retryableStatusCodesArray) {
|
||||
if(!StringUtils.isEmpty(code)) {
|
||||
try {
|
||||
retryableStatusCodes.add(Integer.valueOf(code));
|
||||
} catch (NumberFormatException e) {
|
||||
//TODO log
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canRetry(LoadBalancedRetryContext context) {
|
||||
HttpMethod method = context.getRequest().getMethod();
|
||||
return HttpMethod.GET == method || lbContext.isOkToRetryOnAllOperations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRetrySameServer(LoadBalancedRetryContext context) {
|
||||
return sameServerCount < lbContext.getRetryHandler().getMaxRetriesOnSameServer() && canRetry(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRetryNextServer(LoadBalancedRetryContext context) {
|
||||
//this will be called after a failure occurs and we increment the counter
|
||||
//so we check that the count is less than or equals to too make sure
|
||||
//we try the next server the right number of times
|
||||
return nextServerCount <= lbContext.getRetryHandler().getMaxRetriesOnNextServer() && canRetry(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(LoadBalancedRetryContext context) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerThrowable(LoadBalancedRetryContext context, Throwable throwable) {
|
||||
//Check if we need to ask the load balancer for a new server.
|
||||
//Do this before we increment the counters because the first call to this method
|
||||
//is not a retry it is just an initial failure.
|
||||
if(!canRetrySameServer(context) && canRetryNextServer(context)) {
|
||||
context.setServiceInstance(loadBalanceChooser.choose(serviceId));
|
||||
}
|
||||
//This method is called regardless of whether we are retrying or making the first request.
|
||||
//Since we do not count the initial request in the retry count we don't reset the counter
|
||||
//until we actually equal the same server count limit. This will allow us to make the initial
|
||||
//request plus the right number of retries.
|
||||
if(sameServerCount >= lbContext.getRetryHandler().getMaxRetriesOnSameServer() && canRetry(context)) {
|
||||
//reset same server since we are moving to a new server
|
||||
sameServerCount = 0;
|
||||
nextServerCount++;
|
||||
if(!canRetryNextServer(context)) {
|
||||
context.setExhaustedOnly();
|
||||
}
|
||||
} else {
|
||||
sameServerCount++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retryableStatusCode(int statusCode) {
|
||||
return retryableStatusCodes.contains(statusCode);
|
||||
}
|
||||
}
|
||||
@@ -15,12 +15,9 @@
|
||||
*/
|
||||
package org.springframework.cloud.netflix.ribbon;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
@@ -34,60 +31,10 @@ public class RibbonLoadBalancedRetryPolicyFactory implements LoadBalancedRetryPo
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoadBalancedRetryPolicy create(final String serviceId, final ServiceInstanceChooser loadBalanceChooser) {
|
||||
final RibbonLoadBalancerContext lbContext = this.clientFactory
|
||||
public LoadBalancedRetryPolicy create(String serviceId, ServiceInstanceChooser loadBalanceChooser) {
|
||||
RibbonLoadBalancerContext lbContext = this.clientFactory
|
||||
.getLoadBalancerContext(serviceId);
|
||||
return new LoadBalancedRetryPolicy() {
|
||||
private int sameServerCount = 0;
|
||||
private int nextServerCount = 0;
|
||||
private ServiceInstance lastServiceInstance = null;
|
||||
public boolean canRetry(LoadBalancedRetryContext context) {
|
||||
HttpMethod method = context.getRequest().getMethod();
|
||||
return HttpMethod.GET == method || lbContext.isOkToRetryOnAllOperations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRetrySameServer(LoadBalancedRetryContext context) {
|
||||
return sameServerCount < lbContext.getRetryHandler().getMaxRetriesOnSameServer() && canRetry(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRetryNextServer(LoadBalancedRetryContext context) {
|
||||
//this will be called after a failure occurs and we increment the counter
|
||||
//so we check that the count is less than or equals to too make sure
|
||||
//we try the next server the right number of times
|
||||
return nextServerCount <= lbContext.getRetryHandler().getMaxRetriesOnNextServer() && canRetry(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(LoadBalancedRetryContext context) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerThrowable(LoadBalancedRetryContext context, Throwable throwable) {
|
||||
//Check if we need to ask the load balancer for a new server.
|
||||
//Do this before we increment the counters because the first call to this method
|
||||
//is not a retry it is just an initial failure.
|
||||
if(!canRetrySameServer(context) && canRetryNextServer(context)) {
|
||||
context.setServiceInstance(loadBalanceChooser.choose(serviceId));
|
||||
}
|
||||
//This method is called regardless of whether we are retrying or making the first request.
|
||||
//Since we do not count the initial request in the retry count we don't reset the counter
|
||||
//until we actually equal the same server count limit. This will allow us to make the initial
|
||||
//request plus the right number of retries.
|
||||
if(sameServerCount >= lbContext.getRetryHandler().getMaxRetriesOnSameServer() && canRetry(context)) {
|
||||
//reset same server since we are moving to a new server
|
||||
sameServerCount = 0;
|
||||
nextServerCount++;
|
||||
if(!canRetryNextServer(context)) {
|
||||
context.setExhaustedOnly();
|
||||
}
|
||||
} else {
|
||||
sameServerCount++;
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
return new RibbonLoadBalancedRetryPolicy(serviceId, lbContext, loadBalanceChooser, clientFactory.getClientConfig(serviceId));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
|
||||
import org.springframework.cloud.netflix.feign.ribbon.FeignRetryPolicy;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
import org.springframework.cloud.netflix.ribbon.support.RetryableStatusCodeException;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.retry.RetryCallback;
|
||||
import org.springframework.retry.RetryContext;
|
||||
@@ -65,7 +66,8 @@ public class RetryableRibbonLoadBalancingHttpClient extends RibbonLoadBalancingH
|
||||
CommonClientConfigKey.FollowRedirects, this.followRedirects));
|
||||
|
||||
final RequestConfig requestConfig = builder.build();
|
||||
return this.executeWithRetry(request, new RetryCallback() {
|
||||
final LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryPolicyFactory.create(this.getClientName(), this);
|
||||
RetryCallback retryCallback = new RetryCallback() {
|
||||
@Override
|
||||
public RibbonApacheHttpResponse doWithRetry(RetryContext context) throws Exception {
|
||||
//on retries the policy will choose the server and set it in the context
|
||||
@@ -88,13 +90,17 @@ public class RetryableRibbonLoadBalancingHttpClient extends RibbonLoadBalancingH
|
||||
}
|
||||
HttpUriRequest httpUriRequest = newRequest.toRequest(requestConfig);
|
||||
final HttpResponse httpResponse = RetryableRibbonLoadBalancingHttpClient.this.delegate.execute(httpUriRequest);
|
||||
if(retryPolicy.retryableStatusCode(httpResponse.getStatusLine().getStatusCode())) {
|
||||
throw new RetryableStatusCodeException(RetryableRibbonLoadBalancingHttpClient.this.clientName,
|
||||
httpResponse.getStatusLine().getStatusCode());
|
||||
}
|
||||
return new RibbonApacheHttpResponse(httpResponse, httpUriRequest.getURI());
|
||||
}
|
||||
});
|
||||
};
|
||||
return this.executeWithRetry(request, retryPolicy, retryCallback);
|
||||
}
|
||||
|
||||
private RibbonApacheHttpResponse executeWithRetry(RibbonApacheHttpRequest request, RetryCallback<RibbonApacheHttpResponse, IOException> callback) throws Exception {
|
||||
LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryPolicyFactory.create(this.getClientName(), this);
|
||||
private RibbonApacheHttpResponse executeWithRetry(RibbonApacheHttpRequest request, LoadBalancedRetryPolicy retryPolicy, RetryCallback<RibbonApacheHttpResponse, IOException> callback) throws Exception {
|
||||
RetryTemplate retryTemplate = new RetryTemplate();
|
||||
boolean retryable = request.getContext() == null ? true :
|
||||
BooleanUtils.toBooleanDefaultIfNull(request.getContext().getRetryable(), true);
|
||||
|
||||
@@ -63,10 +63,11 @@ public class RibbonApacheHttpRequest extends ContextAwareRequest implements Clon
|
||||
entity.setContent(this.context.getRequestEntity());
|
||||
// if the entity contentLength isn't set, transfer-encoding will be set
|
||||
// to chunked in org.apache.http.protocol.RequestContent. See gh-1042
|
||||
if (this.context.getContentLength() != null) {
|
||||
entity.setContentLength(this.context.getContentLength());
|
||||
} else if ("GET".equals(this.context.getMethod())) {
|
||||
Long contentLength = this.context.getContentLength();
|
||||
if ("GET".equals(this.context.getMethod()) && (contentLength == null || contentLength < 0)) {
|
||||
entity.setContentLength(0);
|
||||
} else if (contentLength != null) {
|
||||
entity.setContentLength(contentLength);
|
||||
}
|
||||
builder.setEntity(entity);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
@@ -30,6 +29,7 @@ import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
|
||||
import org.springframework.cloud.netflix.feign.ribbon.FeignRetryPolicy;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
import org.springframework.cloud.netflix.ribbon.support.RetryableStatusCodeException;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.retry.RetryCallback;
|
||||
import org.springframework.retry.RetryContext;
|
||||
@@ -55,10 +55,9 @@ public class RetryableOkHttpLoadBalancingClient extends OkHttpLoadBalancingClien
|
||||
this.loadBalancedRetryPolicyFactory = loadBalancedRetryPolicyFactory;
|
||||
}
|
||||
|
||||
private OkHttpRibbonResponse executeWithRetry(OkHttpRibbonRequest request,
|
||||
RetryCallback<OkHttpRibbonResponse, IOException> callback)
|
||||
private OkHttpRibbonResponse executeWithRetry(OkHttpRibbonRequest request, LoadBalancedRetryPolicy retryPolicy,
|
||||
RetryCallback<OkHttpRibbonResponse, Exception> callback)
|
||||
throws Exception {
|
||||
LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryPolicyFactory.create(this.getClientName(), this);
|
||||
RetryTemplate retryTemplate = new RetryTemplate();
|
||||
boolean retryable = request.getContext() == null ? true :
|
||||
BooleanUtils.toBooleanDefaultIfNull(request.getContext().getRetryable(), true);
|
||||
@@ -70,7 +69,8 @@ public class RetryableOkHttpLoadBalancingClient extends OkHttpLoadBalancingClien
|
||||
@Override
|
||||
public OkHttpRibbonResponse execute(final OkHttpRibbonRequest ribbonRequest,
|
||||
final IClientConfig configOverride) throws Exception {
|
||||
return this.executeWithRetry(ribbonRequest, new RetryCallback() {
|
||||
final LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryPolicyFactory.create(this.getClientName(), this);
|
||||
RetryCallback<OkHttpRibbonResponse, Exception> retryCallback = new RetryCallback<OkHttpRibbonResponse, Exception>() {
|
||||
@Override
|
||||
public OkHttpRibbonResponse doWithRetry(RetryContext context) throws Exception {
|
||||
//on retries the policy will choose the server and set it in the context
|
||||
@@ -95,9 +95,13 @@ public class RetryableOkHttpLoadBalancingClient extends OkHttpLoadBalancingClien
|
||||
|
||||
final Request request = newRequest.toRequest();
|
||||
Response response = httpClient.newCall(request).execute();
|
||||
if(retryPolicy.retryableStatusCode(response.code())) {
|
||||
throw new RetryableStatusCodeException(RetryableOkHttpLoadBalancingClient.this.clientName, response.code());
|
||||
}
|
||||
return new OkHttpRibbonResponse(response, newRequest.getUri());
|
||||
}
|
||||
});
|
||||
};
|
||||
return this.executeWithRetry(ribbonRequest, retryPolicy, retryCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2013-2016 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.ribbon.support;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Exception to be thrown when the status code is deemed to be retryable.
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class RetryableStatusCodeException extends IOException {
|
||||
|
||||
private static final String MESSAGE = "Service %s returned a status code of %d";
|
||||
|
||||
public RetryableStatusCodeException(String serviceId, int statusCode) {
|
||||
super(String.format(MESSAGE, serviceId, statusCode));
|
||||
}
|
||||
}
|
||||
@@ -20,74 +20,43 @@ import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.netflix.zuul.filters.Route;
|
||||
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.jmx.export.annotation.ManagedAttribute;
|
||||
import org.springframework.jmx.export.annotation.ManagedOperation;
|
||||
import org.springframework.jmx.export.annotation.ManagedResource;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
/**
|
||||
* Endpoint to display and reset the zuul proxy routes
|
||||
* Endpoint to display the zuul proxy routes
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Dave Syer
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@ManagedResource(description = "Can be used to list and reset the reverse proxy routes")
|
||||
public class RoutesEndpoint implements MvcEndpoint, ApplicationEventPublisherAware {
|
||||
@ManagedResource(description = "Can be used to list the reverse proxy routes")
|
||||
@ConfigurationProperties(prefix = "endpoints.routes")
|
||||
public class RoutesEndpoint extends AbstractEndpoint<Map<String, String>> {
|
||||
|
||||
private static final String ID = "routes";
|
||||
|
||||
private RouteLocator routes;
|
||||
|
||||
private ApplicationEventPublisher publisher;
|
||||
|
||||
@Override
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
|
||||
this.publisher = publisher;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public RoutesEndpoint(RouteLocator routes) {
|
||||
super(ID, true);
|
||||
this.routes = routes;
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
@ManagedOperation
|
||||
public Map<String, String> reset() {
|
||||
this.publisher.publishEvent(new RoutesRefreshedEvent(this.routes));
|
||||
return getRoutes();
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
@ManagedAttribute
|
||||
public Map<String, String> getRoutes() {
|
||||
public Map<String, String> invoke() {
|
||||
Map<String, String> map = new LinkedHashMap<>();
|
||||
for (Route route : this.routes.getRoutes()) {
|
||||
map.put(route.getFullPath(), route.getLocation());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return "/routes";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSensitive() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Endpoint<?>> getEndpointType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2013-2016 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.zuul;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter;
|
||||
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.jmx.export.annotation.ManagedOperation;
|
||||
import org.springframework.jmx.export.annotation.ManagedResource;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
/**
|
||||
* Endpoint used to reset the reverse proxy routes
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@ManagedResource(description = "Can be used to reset the reverse proxy routes")
|
||||
public class RoutesMvcEndpoint extends EndpointMvcAdapter implements ApplicationEventPublisherAware {
|
||||
|
||||
private RouteLocator routes;
|
||||
private ApplicationEventPublisher publisher;
|
||||
|
||||
public RoutesMvcEndpoint(RoutesEndpoint endpoint, RouteLocator routes) {
|
||||
super(endpoint);
|
||||
this.routes = routes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||
this.publisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
@ManagedOperation
|
||||
public Object reset() {
|
||||
this.publisher.publishEvent(new RoutesRefreshedEvent(this.routes));
|
||||
return super.invoke();
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,8 @@ import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.metrics.CounterService;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
@@ -43,6 +45,9 @@ import org.springframework.cloud.netflix.zuul.filters.pre.FormBodyWrapperFilter;
|
||||
import org.springframework.cloud.netflix.zuul.filters.pre.Servlet30WrapperFilter;
|
||||
import org.springframework.cloud.netflix.zuul.filters.pre.ServletDetectionFilter;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.SendForwardFilter;
|
||||
import org.springframework.cloud.netflix.zuul.metrics.DefaultCounterFactory;
|
||||
import org.springframework.cloud.netflix.zuul.metrics.EmptyCounterFactory;
|
||||
import org.springframework.cloud.netflix.zuul.metrics.EmptyTracerFactory;
|
||||
import org.springframework.cloud.netflix.zuul.web.ZuulController;
|
||||
import org.springframework.cloud.netflix.zuul.web.ZuulHandlerMapping;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
@@ -53,8 +58,12 @@ import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
|
||||
import com.netflix.zuul.FilterLoader;
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import com.netflix.zuul.filters.FilterRegistry;
|
||||
import com.netflix.zuul.http.ZuulServlet;
|
||||
import com.netflix.zuul.monitoring.CounterFactory;
|
||||
import com.netflix.zuul.monitoring.TracerFactory;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
@@ -178,8 +187,39 @@ public class ZuulConfiguration {
|
||||
private Map<String, ZuulFilter> filters;
|
||||
|
||||
@Bean
|
||||
public ZuulFilterInitializer zuulFilterInitializer() {
|
||||
return new ZuulFilterInitializer(this.filters);
|
||||
public ZuulFilterInitializer zuulFilterInitializer(
|
||||
CounterFactory counterFactory, TracerFactory tracerFactory) {
|
||||
FilterLoader filterLoader = FilterLoader.getInstance();
|
||||
FilterRegistry filterRegistry = FilterRegistry.instance();
|
||||
return new ZuulFilterInitializer(this.filters, counterFactory, tracerFactory, filterLoader, filterRegistry);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(CounterService.class)
|
||||
protected static class ZuulCounterFactoryConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(CounterService.class)
|
||||
public CounterFactory counterFactory(CounterService counterService) {
|
||||
return new DefaultCounterFactory(counterService);
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class ZuulMetricsConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(CounterFactory.class)
|
||||
public CounterFactory counterFactory() {
|
||||
return new EmptyCounterFactory();
|
||||
}
|
||||
|
||||
@ConditionalOnMissingBean(TracerFactory.class)
|
||||
@Bean
|
||||
public TracerFactory tracerFactory() {
|
||||
return new EmptyTracerFactory();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,13 +22,15 @@ import java.util.Map;
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContextListener;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import com.netflix.zuul.FilterLoader;
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import com.netflix.zuul.filters.FilterRegistry;
|
||||
import com.netflix.zuul.monitoring.MonitoringHelper;
|
||||
import com.netflix.zuul.monitoring.CounterFactory;
|
||||
import com.netflix.zuul.monitoring.TracerFactory;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
@@ -38,10 +40,22 @@ import com.netflix.zuul.monitoring.MonitoringHelper;
|
||||
@CommonsLog
|
||||
public class ZuulFilterInitializer implements ServletContextListener {
|
||||
|
||||
private Map<String, ZuulFilter> filters;
|
||||
private final Map<String, ZuulFilter> filters;
|
||||
private final CounterFactory counterFactory;
|
||||
private final TracerFactory tracerFactory;
|
||||
private final FilterLoader filterLoader;
|
||||
private final FilterRegistry filterRegistry;
|
||||
|
||||
public ZuulFilterInitializer(Map<String, ZuulFilter> filters) {
|
||||
public ZuulFilterInitializer(Map<String, ZuulFilter> filters,
|
||||
CounterFactory counterFactory,
|
||||
TracerFactory tracerFactory,
|
||||
FilterLoader filterLoader,
|
||||
FilterRegistry filterRegistry) {
|
||||
this.filters = filters;
|
||||
this.counterFactory = counterFactory;
|
||||
this.tracerFactory = tracerFactory;
|
||||
this.filterLoader = filterLoader;
|
||||
this.filterRegistry = filterRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -49,32 +63,31 @@ public class ZuulFilterInitializer implements ServletContextListener {
|
||||
|
||||
log.info("Starting filter initializer context listener");
|
||||
|
||||
// FIXME: mocks monitoring infrastructure as we don't need it for this simple app
|
||||
MonitoringHelper.initMocks();
|
||||
|
||||
FilterRegistry registry = FilterRegistry.instance();
|
||||
TracerFactory.initialize(tracerFactory);
|
||||
CounterFactory.initialize(counterFactory);
|
||||
|
||||
for (Map.Entry<String, ZuulFilter> entry : this.filters.entrySet()) {
|
||||
registry.put(entry.getKey(), entry.getValue());
|
||||
filterRegistry.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextDestroyed(ServletContextEvent sce) {
|
||||
log.info("Stopping filter initializer context listener");
|
||||
FilterRegistry registry = FilterRegistry.instance();
|
||||
for (Map.Entry<String, ZuulFilter> entry : this.filters.entrySet()) {
|
||||
registry.remove(entry.getKey());
|
||||
filterRegistry.remove(entry.getKey());
|
||||
}
|
||||
clearLoaderCache();
|
||||
|
||||
TracerFactory.initialize(null);
|
||||
CounterFactory.initialize(null);
|
||||
}
|
||||
|
||||
private void clearLoaderCache() {
|
||||
FilterLoader instance = FilterLoader.getInstance();
|
||||
Field field = ReflectionUtils.findField(FilterLoader.class, "hashFiltersByType");
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
@SuppressWarnings("rawtypes")
|
||||
Map cache = (Map) ReflectionUtils.getField(field, instance);
|
||||
Map cache = (Map) ReflectionUtils.getField(field, filterLoader);
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -139,6 +139,11 @@ public class ZuulProxyConfiguration extends ZuulConfiguration {
|
||||
return new RoutesEndpoint(routeLocator);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RoutesMvcEndpoint zuulMvcEndpoint(RouteLocator routeLocator, RoutesEndpoint endpoint) {
|
||||
return new RoutesMvcEndpoint(endpoint, routeLocator);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ProxyRequestHelper proxyRequestHelper(ZuulProperties zuulProperties) {
|
||||
TraceProxyRequestHelper helper = new TraceProxyRequestHelper();
|
||||
|
||||
@@ -199,7 +199,7 @@ public class SimpleRouteLocator implements RouteLocator, Ordered {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
log.debug("adjustedPath=" + path);
|
||||
log.debug("adjustedPath=" + adjustedPath);
|
||||
return adjustedPath;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,36 +21,24 @@ import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.cloud.netflix.ribbon.support.RibbonRequestCustomizer;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Data
|
||||
@RequiredArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class RibbonCommandContext {
|
||||
@NonNull
|
||||
private final String serviceId;
|
||||
@NonNull
|
||||
private final String method;
|
||||
@NonNull
|
||||
private final String uri;
|
||||
private final Boolean retryable;
|
||||
@NonNull
|
||||
private final MultiValueMap<String, String> headers;
|
||||
@NonNull
|
||||
private final MultiValueMap<String, String> params;
|
||||
private final InputStream requestEntity;
|
||||
@NonNull
|
||||
private final List<RibbonRequestCustomizer> requestCustomizers;
|
||||
private Long contentLength;
|
||||
|
||||
@@ -65,6 +53,34 @@ public class RibbonCommandContext {
|
||||
new ArrayList<RibbonRequestCustomizer>(), null);
|
||||
}
|
||||
|
||||
public RibbonCommandContext(String serviceId, String method, String uri,
|
||||
Boolean retryable, MultiValueMap<String, String> headers,
|
||||
MultiValueMap<String, String> params, InputStream requestEntity,
|
||||
List<RibbonRequestCustomizer> requestCustomizers) {
|
||||
this(serviceId, method, uri, retryable, headers, params, requestEntity, requestCustomizers, null);
|
||||
}
|
||||
|
||||
public RibbonCommandContext(String serviceId, String method, String uri,
|
||||
Boolean retryable, MultiValueMap<String, String> headers,
|
||||
MultiValueMap<String, String> params, InputStream requestEntity,
|
||||
List<RibbonRequestCustomizer> requestCustomizers, Long contentLength) {
|
||||
Assert.notNull(serviceId, "serviceId may not be null");
|
||||
Assert.notNull(method, "method may not be null");
|
||||
Assert.notNull(uri, "uri may not be null");
|
||||
Assert.notNull(headers, "headers may not be null");
|
||||
Assert.notNull(params, "params may not be null");
|
||||
Assert.notNull(requestCustomizers, "requestCustomizers may not be null");
|
||||
this.serviceId = serviceId;
|
||||
this.method = method;
|
||||
this.uri = uri;
|
||||
this.retryable = retryable;
|
||||
this.headers = headers;
|
||||
this.params = params;
|
||||
this.requestEntity = requestEntity;
|
||||
this.requestCustomizers = requestCustomizers;
|
||||
this.contentLength = contentLength;
|
||||
}
|
||||
|
||||
public URI uri() {
|
||||
try {
|
||||
return new URI(this.uri);
|
||||
@@ -83,4 +99,81 @@ public class RibbonCommandContext {
|
||||
public String getVerb() {
|
||||
return this.method;
|
||||
}
|
||||
|
||||
public String getServiceId() {
|
||||
return serviceId;
|
||||
}
|
||||
|
||||
public String getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public String getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
public Boolean getRetryable() {
|
||||
return retryable;
|
||||
}
|
||||
|
||||
public MultiValueMap<String, String> getHeaders() {
|
||||
return headers;
|
||||
}
|
||||
|
||||
public MultiValueMap<String, String> getParams() {
|
||||
return params;
|
||||
}
|
||||
|
||||
public InputStream getRequestEntity() {
|
||||
return requestEntity;
|
||||
}
|
||||
|
||||
public List<RibbonRequestCustomizer> getRequestCustomizers() {
|
||||
return requestCustomizers;
|
||||
}
|
||||
|
||||
public Long getContentLength() {
|
||||
return contentLength;
|
||||
}
|
||||
|
||||
public void setContentLength(Long contentLength) {
|
||||
this.contentLength = contentLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
RibbonCommandContext that = (RibbonCommandContext) o;
|
||||
return Objects.equals(serviceId, that.serviceId) &&
|
||||
Objects.equals(method, that.method) &&
|
||||
Objects.equals(uri, that.uri) &&
|
||||
Objects.equals(retryable, that.retryable) &&
|
||||
Objects.equals(headers, that.headers) &&
|
||||
Objects.equals(params, that.params) &&
|
||||
Objects.equals(requestEntity, that.requestEntity) &&
|
||||
Objects.equals(requestCustomizers, that.requestCustomizers) &&
|
||||
Objects.equals(contentLength, that.contentLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(serviceId, method, uri, retryable, headers, params, requestEntity, requestCustomizers, contentLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuffer sb = new StringBuffer("RibbonCommandContext{");
|
||||
sb.append("serviceId='").append(serviceId).append('\'');
|
||||
sb.append(", method='").append(method).append('\'');
|
||||
sb.append(", uri='").append(uri).append('\'');
|
||||
sb.append(", retryable=").append(retryable);
|
||||
sb.append(", headers=").append(headers);
|
||||
sb.append(", params=").append(params);
|
||||
sb.append(", requestEntity=").append(requestEntity);
|
||||
sb.append(", requestCustomizers=").append(requestCustomizers);
|
||||
sb.append(", contentLength=").append(contentLength);
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ public class RibbonRoutingFilter extends ZuulFilter {
|
||||
.buildZuulRequestQueryParams(request);
|
||||
String verb = getVerb(request);
|
||||
InputStream requestEntity = getRequestBody(request);
|
||||
if (request.getContentLength() < 0) {
|
||||
if (request.getContentLength() < 0 && !verb.equalsIgnoreCase("GET")) {
|
||||
context.setChunkedRequestBody();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.zuul.metrics;
|
||||
|
||||
import org.springframework.boot.actuate.metrics.CounterService;
|
||||
|
||||
import com.netflix.zuul.monitoring.CounterFactory;
|
||||
|
||||
/**
|
||||
* A counter based monitoring factory that uses {@link CounterService} to increment counters.
|
||||
*
|
||||
* @author Anastasiia Smirnova
|
||||
*/
|
||||
public class DefaultCounterFactory extends CounterFactory {
|
||||
|
||||
private final CounterService counterService;
|
||||
|
||||
public DefaultCounterFactory(CounterService counterService) {
|
||||
this.counterService = counterService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void increment(String name) {
|
||||
counterService.increment(name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.zuul.metrics;
|
||||
|
||||
import com.netflix.zuul.monitoring.CounterFactory;
|
||||
|
||||
/**
|
||||
* A counter based monitoring factory that does nothing.
|
||||
*
|
||||
* @author Anastasiia Smirnova
|
||||
*/
|
||||
public class EmptyCounterFactory extends CounterFactory {
|
||||
@Override
|
||||
public void increment(String name) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.zuul.metrics;
|
||||
|
||||
import com.netflix.zuul.monitoring.Tracer;
|
||||
import com.netflix.zuul.monitoring.TracerFactory;
|
||||
|
||||
/**
|
||||
* A time based monitoring factory that does nothing.
|
||||
*
|
||||
* @author Anastasiia Smirnova
|
||||
*/
|
||||
public class EmptyTracerFactory extends TracerFactory {
|
||||
|
||||
private final EmptyTracer emptyTracer = new EmptyTracer();
|
||||
|
||||
@Override
|
||||
public Tracer startMicroTracer(String name) {
|
||||
return emptyTracer;
|
||||
}
|
||||
|
||||
private static final class EmptyTracer implements Tracer {
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopAndLog() {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,13 +88,13 @@ public class FeignHttpClientUrlTests {
|
||||
Hello getHello();
|
||||
}
|
||||
|
||||
@FeignClient(name = "beanappurl", url = "#{SERVER_URL}")
|
||||
@FeignClient(name = "beanappurl", url = "#{SERVER_URL}path")
|
||||
protected interface BeanUrlClient {
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/hello")
|
||||
Hello getHello();
|
||||
}
|
||||
|
||||
@FeignClient(name = "beanappurlnoprotocol", url = "#{SERVER_URL_NO_PROTOCOL}")
|
||||
@FeignClient(name = "beanappurlnoprotocol", url = "#{SERVER_URL_NO_PROTOCOL}path")
|
||||
protected interface BeanUrlClientNoProtocol {
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/hello")
|
||||
Hello getHello();
|
||||
@@ -113,6 +113,11 @@ public class FeignHttpClientUrlTests {
|
||||
return new Hello("hello world 1");
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/path/hello")
|
||||
public Hello getHelloWithPath() {
|
||||
return getHello();
|
||||
}
|
||||
|
||||
@Bean(name="SERVER_URL")
|
||||
public String serverUrl() {
|
||||
return "http://localhost:" + port + "/";
|
||||
|
||||
@@ -138,9 +138,9 @@ class LocalRibbonClientConfiguration {
|
||||
|
||||
@Bean
|
||||
public ServerList<Server> ribbonServerList() {
|
||||
return new StaticServerList<>(new Server("___mybadhost__", 10001),
|
||||
new Server("___mybadhost2__", 10002),
|
||||
new Server("___mybadhost3__", 10003), new Server("localhost", this.port));
|
||||
return new StaticServerList<>(new Server("mybadhost", 80),
|
||||
new Server("mybadhost2", 10002),
|
||||
new Server("mybadhost3", 10003), new Server("localhost", this.port));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
|
||||
import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryPolicy;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerContext;
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
@@ -44,6 +45,7 @@ import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.http.HttpRequest;
|
||||
|
||||
import com.netflix.client.RequestSpecificRetryHandler;
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
@@ -60,6 +62,7 @@ import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyBoolean;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
@@ -98,6 +101,14 @@ public class RetryableFeignLoadBalancerTest {
|
||||
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config);
|
||||
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
|
||||
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
|
||||
IClientConfig config = mock(IClientConfig.class);
|
||||
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
|
||||
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
|
||||
doReturn(true).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
|
||||
doReturn(defaultConnectTimeout).when(config).get(eq(CommonClientConfigKey.ConnectTimeout));
|
||||
doReturn(defaultReadTimeout).when(config).get(eq(CommonClientConfigKey.ReadTimeout));
|
||||
doReturn("404,502,foo, ,").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq(""));
|
||||
doReturn(config).when(clientFactory).getClientConfig(eq("default"));
|
||||
RibbonLoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory = new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
|
||||
HttpRequest springRequest = mock(HttpRequest.class);
|
||||
Request feignRequest = Request.create("GET", "http://foo", new HashMap<String, Collection<String>>(),
|
||||
@@ -139,6 +150,14 @@ public class RetryableFeignLoadBalancerTest {
|
||||
public void executeRetry() throws Exception {
|
||||
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config);
|
||||
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
|
||||
IClientConfig config = mock(IClientConfig.class);
|
||||
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
|
||||
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
|
||||
doReturn(true).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
|
||||
doReturn(defaultConnectTimeout).when(config).get(eq(CommonClientConfigKey.ConnectTimeout));
|
||||
doReturn(defaultReadTimeout).when(config).get(eq(CommonClientConfigKey.ReadTimeout));
|
||||
doReturn("").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq(""));
|
||||
doReturn(config).when(clientFactory).getClientConfig(eq("default"));
|
||||
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
|
||||
RibbonLoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory = new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
|
||||
HttpRequest springRequest = mock(HttpRequest.class);
|
||||
@@ -154,6 +173,34 @@ public class RetryableFeignLoadBalancerTest {
|
||||
verify(client, times(2)).execute(any(Request.class), any(Request.Options.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void executeRetryOnStatusCode() throws Exception {
|
||||
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config);
|
||||
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
|
||||
IClientConfig config = mock(IClientConfig.class);
|
||||
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
|
||||
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
|
||||
doReturn(true).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
|
||||
doReturn(defaultConnectTimeout).when(config).get(eq(CommonClientConfigKey.ConnectTimeout));
|
||||
doReturn(defaultReadTimeout).when(config).get(eq(CommonClientConfigKey.ReadTimeout));
|
||||
doReturn("404").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq(""));
|
||||
doReturn(config).when(clientFactory).getClientConfig(eq("default"));
|
||||
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
|
||||
RibbonLoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory = new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
|
||||
HttpRequest springRequest = mock(HttpRequest.class);
|
||||
Request feignRequest = Request.create("GET", "http://foo", new HashMap<String, Collection<String>>(),
|
||||
new byte[]{}, StandardCharsets.UTF_8);
|
||||
Client client = mock(Client.class);
|
||||
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://foo"));
|
||||
Response response = Response.builder().status(200).headers(new HashMap<String, Collection<String>>()).build();
|
||||
Response fourOFourResponse = Response.builder().status(404).headers(new HashMap<String, Collection<String>>()).build();
|
||||
doReturn(fourOFourResponse).doReturn(response).when(client).execute(any(Request.class), any(Request.Options.class));
|
||||
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, loadBalancedRetryPolicyFactory);
|
||||
FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null);
|
||||
assertEquals(200, ribbonResponse.toResponse().status());
|
||||
verify(client, times(2)).execute(any(Request.class), any(Request.Options.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRequestSpecificRetryHandler() throws Exception {
|
||||
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config);
|
||||
|
||||
@@ -86,6 +86,7 @@ public class RibbonLoadBalancedRetryPolicyFactoryTest {
|
||||
doReturn(nextServer).when(config).getPropertyAsInteger(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
|
||||
doReturn(retryOnAllOps).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), anyBoolean());
|
||||
doReturn(retryOnAllOps).when(config).getPropertyAsBoolean(eq(CommonClientConfigKey.OkToRetryOnAllOperations), anyBoolean());
|
||||
doReturn("").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq(""));
|
||||
doReturn(server.getServiceId()).when(config).getClientName();
|
||||
doReturn(config).when(clientFactory).getClientConfig(eq(server.getServiceId()));
|
||||
clientFactory.getLoadBalancerContext(server.getServiceId()).setRetryHandler(new DefaultLoadBalancerRetryHandler(config));
|
||||
@@ -97,6 +98,7 @@ public class RibbonLoadBalancedRetryPolicyFactoryTest {
|
||||
LoadBalancedRetryContext context = new LoadBalancedRetryContext(null, request);
|
||||
assertThat(policy.canRetryNextServer(context), is(true));
|
||||
assertThat(policy.canRetrySameServer(context), is(false));
|
||||
assertThat(policy.retryableStatusCode(400), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -112,6 +114,7 @@ public class RibbonLoadBalancedRetryPolicyFactoryTest {
|
||||
doReturn(nextServer).when(config).getPropertyAsInteger(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
|
||||
doReturn(retryOnAllOps).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), anyBoolean());
|
||||
doReturn(retryOnAllOps).when(config).getPropertyAsBoolean(eq(CommonClientConfigKey.OkToRetryOnAllOperations), anyBoolean());
|
||||
doReturn("").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq(""));
|
||||
doReturn(server.getServiceId()).when(config).getClientName();
|
||||
doReturn(config).when(clientFactory).getClientConfig(eq(server.getServiceId()));
|
||||
clientFactory.getLoadBalancerContext(server.getServiceId()).setRetryHandler(new DefaultLoadBalancerRetryHandler(config));
|
||||
@@ -123,6 +126,7 @@ public class RibbonLoadBalancedRetryPolicyFactoryTest {
|
||||
LoadBalancedRetryContext context = new LoadBalancedRetryContext(null, request);
|
||||
assertThat(policy.canRetryNextServer(context), is(false));
|
||||
assertThat(policy.canRetrySameServer(context), is(false));
|
||||
assertThat(policy.retryableStatusCode(400), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -138,6 +142,7 @@ public class RibbonLoadBalancedRetryPolicyFactoryTest {
|
||||
doReturn(nextServer).when(config).getPropertyAsInteger(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
|
||||
doReturn(retryOnAllOps).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), anyBoolean());
|
||||
doReturn(retryOnAllOps).when(config).getPropertyAsBoolean(eq(CommonClientConfigKey.OkToRetryOnAllOperations), anyBoolean());
|
||||
doReturn("").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq(""));
|
||||
doReturn(server.getServiceId()).when(config).getClientName();
|
||||
doReturn(config).when(clientFactory).getClientConfig(eq(server.getServiceId()));
|
||||
clientFactory.getLoadBalancerContext(server.getServiceId()).initWithNiwsConfig(config);
|
||||
@@ -149,6 +154,7 @@ public class RibbonLoadBalancedRetryPolicyFactoryTest {
|
||||
LoadBalancedRetryContext context = new LoadBalancedRetryContext(null, request);
|
||||
assertThat(policy.canRetryNextServer(context), is(true));
|
||||
assertThat(policy.canRetrySameServer(context), is(true));
|
||||
assertThat(policy.retryableStatusCode(400), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -161,6 +167,7 @@ public class RibbonLoadBalancedRetryPolicyFactoryTest {
|
||||
doReturn(nextServer).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
|
||||
doReturn(false).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
|
||||
doReturn(config).when(clientFactory).getClientConfig(eq(server.getServiceId()));
|
||||
doReturn("").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq(""));
|
||||
clientFactory.getLoadBalancerContext(server.getServiceId()).setRetryHandler(new DefaultLoadBalancerRetryHandler(config));
|
||||
RibbonLoadBalancerClient client = getRibbonLoadBalancerClient(server);
|
||||
RibbonLoadBalancedRetryPolicyFactory factory = new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
|
||||
@@ -189,9 +196,32 @@ public class RibbonLoadBalancedRetryPolicyFactoryTest {
|
||||
}
|
||||
}
|
||||
assertThat(context.isExhaustedOnly(), is(true));
|
||||
assertThat(policy.retryableStatusCode(400), is(false));
|
||||
verify(context, times(4)).setServiceInstance(any(ServiceInstance.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRetryableStatusCodest() throws Exception {
|
||||
int sameServer = 3;
|
||||
int nextServer = 3;
|
||||
RibbonServer server = getRibbonServer();
|
||||
IClientConfig config = mock(IClientConfig.class);
|
||||
doReturn(sameServer).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
|
||||
doReturn(nextServer).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
|
||||
doReturn(false).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
|
||||
doReturn(config).when(clientFactory).getClientConfig(eq(server.getServiceId()));
|
||||
doReturn("404,502,foo, ,").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq(""));
|
||||
clientFactory.getLoadBalancerContext(server.getServiceId()).setRetryHandler(new DefaultLoadBalancerRetryHandler(config));
|
||||
RibbonLoadBalancerClient client = getRibbonLoadBalancerClient(server);
|
||||
RibbonLoadBalancedRetryPolicyFactory factory = new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
|
||||
LoadBalancedRetryPolicy policy = factory.create(server.getServiceId(), client);
|
||||
HttpRequest request = mock(HttpRequest.class);
|
||||
doReturn(HttpMethod.GET).when(request).getMethod();
|
||||
assertThat(policy.retryableStatusCode(400), is(false));
|
||||
assertThat(policy.retryableStatusCode(404), is(true));
|
||||
assertThat(policy.retryableStatusCode(502), is(true));
|
||||
}
|
||||
|
||||
protected RibbonLoadBalancerClient getRibbonLoadBalancerClient(
|
||||
RibbonServer ribbonServer) {
|
||||
given(this.loadBalancer.getName()).willReturn(ribbonServer.getServiceId());
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.cloud.netflix.ribbon.apache;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.StatusLine;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
@@ -29,6 +30,7 @@ import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryPolicy;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerContext;
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
@@ -185,7 +187,7 @@ public class RibbonLoadBalancingHttpClientTests {
|
||||
private RetryableRibbonLoadBalancingHttpClient setupClientForRetry(int retriesNextServer, int retriesSameServer,
|
||||
boolean retryable, boolean retryOnAllOps,
|
||||
String serviceName, String host, int port,
|
||||
HttpClient delegate, ILoadBalancer lb) throws Exception {
|
||||
HttpClient delegate, ILoadBalancer lb, String statusCodes) throws Exception {
|
||||
ServerIntrospector introspector = mock(ServerIntrospector.class);
|
||||
RetryHandler retryHandler = new DefaultLoadBalancerRetryHandler(retriesSameServer, retriesNextServer, retryable);
|
||||
doReturn(new Server(host, port)).when(lb).chooseServer(eq(serviceName));
|
||||
@@ -193,10 +195,12 @@ public class RibbonLoadBalancingHttpClientTests {
|
||||
clientConfig.set(CommonClientConfigKey.OkToRetryOnAllOperations, retryOnAllOps);
|
||||
clientConfig.set(CommonClientConfigKey.MaxAutoRetriesNextServer, retriesNextServer);
|
||||
clientConfig.set(CommonClientConfigKey.MaxAutoRetries, retriesSameServer);
|
||||
clientConfig.set(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES, statusCodes);
|
||||
clientConfig.setClientName(serviceName);
|
||||
RibbonLoadBalancerContext context = new RibbonLoadBalancerContext(lb, clientConfig, retryHandler);
|
||||
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
|
||||
doReturn(context).when(clientFactory).getLoadBalancerContext(eq(serviceName));
|
||||
doReturn(clientConfig).when(clientFactory).getClientConfig(eq(serviceName));
|
||||
LoadBalancedRetryPolicyFactory factory = new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
|
||||
RetryableRibbonLoadBalancingHttpClient client = new RetryableRibbonLoadBalancingHttpClient(clientConfig, introspector, factory);
|
||||
client.setLoadBalancer(lb);
|
||||
@@ -217,10 +221,13 @@ public class RibbonLoadBalancingHttpClientTests {
|
||||
URI uri = new URI("http://" + host + ":" + port);
|
||||
HttpClient delegate = mock(HttpClient.class);
|
||||
final HttpResponse response = mock(HttpResponse.class);
|
||||
StatusLine statusLine = mock(StatusLine.class);
|
||||
doReturn(200).when(statusLine).getStatusCode();
|
||||
doReturn(statusLine).when(response).getStatusLine();
|
||||
doThrow(new IOException("boom")).doReturn(response).when(delegate).execute(any(HttpUriRequest.class));
|
||||
ILoadBalancer lb = mock(ILoadBalancer.class);
|
||||
RetryableRibbonLoadBalancingHttpClient client = setupClientForRetry(retriesNextServer, retriesSameServer, retryable, retryOnAllOps,
|
||||
serviceName, host, port, delegate, lb);
|
||||
serviceName, host, port, delegate, lb, "");
|
||||
RibbonApacheHttpRequest request = mock(RibbonApacheHttpRequest.class);
|
||||
doReturn(uri).when(request).getURI();
|
||||
doReturn(method).when(request).getMethod();
|
||||
@@ -246,11 +253,14 @@ public class RibbonLoadBalancingHttpClientTests {
|
||||
URI uri = new URI("http://" + host + ":" + port);
|
||||
HttpClient delegate = mock(HttpClient.class);
|
||||
final HttpResponse response = mock(HttpResponse.class);
|
||||
StatusLine statusLine = mock(StatusLine.class);
|
||||
doReturn(200).when(statusLine).getStatusCode();
|
||||
doReturn(statusLine).when(response).getStatusLine();
|
||||
doThrow(new IOException("boom")).doThrow(new IOException("boom again")).doReturn(response).
|
||||
when(delegate).execute(any(HttpUriRequest.class));
|
||||
ILoadBalancer lb = mock(ILoadBalancer.class);
|
||||
RetryableRibbonLoadBalancingHttpClient client = setupClientForRetry(retriesNextServer, retriesSameServer, retryable, retryOnAllOps,
|
||||
serviceName, host, port, delegate, lb);
|
||||
serviceName, host, port, delegate, lb, "");
|
||||
RibbonApacheHttpRequest request = mock(RibbonApacheHttpRequest.class);
|
||||
doReturn(uri).when(request).getURI();
|
||||
doReturn(method).when(request).getMethod();
|
||||
@@ -276,11 +286,14 @@ public class RibbonLoadBalancingHttpClientTests {
|
||||
URI uri = new URI("http://" + host + ":" + port);
|
||||
HttpClient delegate = mock(HttpClient.class);
|
||||
final HttpResponse response = mock(HttpResponse.class);
|
||||
StatusLine statusLine = mock(StatusLine.class);
|
||||
doReturn(200).when(statusLine).getStatusCode();
|
||||
doReturn(statusLine).when(response).getStatusLine();
|
||||
doThrow(new IOException("boom")).doThrow(new IOException("boom again")).doReturn(response).
|
||||
when(delegate).execute(any(HttpUriRequest.class));
|
||||
ILoadBalancer lb = mock(ILoadBalancer.class);
|
||||
RetryableRibbonLoadBalancingHttpClient client = setupClientForRetry(retriesNextServer, retriesSameServer, retryable, retryOnAllOps,
|
||||
serviceName, host, port, delegate, lb);
|
||||
serviceName, host, port, delegate, lb, "");
|
||||
RibbonApacheHttpRequest request = mock(RibbonApacheHttpRequest.class);
|
||||
doReturn(method).when(request).getMethod();
|
||||
doReturn(uri).when(request).getURI();
|
||||
@@ -309,7 +322,7 @@ public class RibbonLoadBalancingHttpClientTests {
|
||||
when(delegate).execute(any(HttpUriRequest.class));
|
||||
ILoadBalancer lb = mock(ILoadBalancer.class);
|
||||
RetryableRibbonLoadBalancingHttpClient client = setupClientForRetry(retriesNextServer, retriesSameServer, retryable, retryOnAllOps,
|
||||
serviceName, host, port, delegate, lb);
|
||||
serviceName, host, port, delegate, lb, "");
|
||||
RibbonApacheHttpRequest request = mock(RibbonApacheHttpRequest.class);
|
||||
doReturn(method).when(request).getMethod();
|
||||
doReturn(uri).when(request).getURI();
|
||||
@@ -326,6 +339,42 @@ public class RibbonLoadBalancingHttpClientTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRetryOnStatusCode() throws Exception {
|
||||
int retriesNextServer = 0;
|
||||
int retriesSameServer = 1;
|
||||
boolean retryable = true;
|
||||
boolean retryOnAllOps = false;
|
||||
String serviceName = "foo";
|
||||
String host = serviceName;
|
||||
int port = 80;
|
||||
HttpMethod method = HttpMethod.GET;
|
||||
URI uri = new URI("http://" + host + ":" + port);
|
||||
HttpClient delegate = mock(HttpClient.class);
|
||||
final HttpResponse response = mock(HttpResponse.class);
|
||||
StatusLine statusLine = mock(StatusLine.class);
|
||||
doReturn(200).when(statusLine).getStatusCode();
|
||||
doReturn(statusLine).when(response).getStatusLine();
|
||||
final HttpResponse fourOFourResponse = mock(HttpResponse.class);
|
||||
StatusLine fourOFourStatusLine = mock(StatusLine.class);
|
||||
doReturn(404).when(fourOFourStatusLine).getStatusCode();
|
||||
doReturn(fourOFourStatusLine).when(fourOFourResponse).getStatusLine();
|
||||
doReturn(fourOFourResponse).doReturn(response).when(delegate).execute(any(HttpUriRequest.class));
|
||||
ILoadBalancer lb = mock(ILoadBalancer.class);
|
||||
RetryableRibbonLoadBalancingHttpClient client = setupClientForRetry(retriesNextServer, retriesSameServer, retryable, retryOnAllOps,
|
||||
serviceName, host, port, delegate, lb, "404");
|
||||
RibbonApacheHttpRequest request = mock(RibbonApacheHttpRequest.class);
|
||||
doReturn(uri).when(request).getURI();
|
||||
doReturn(method).when(request).getMethod();
|
||||
doReturn(request).when(request).withNewUri(any(URI.class));
|
||||
HttpUriRequest uriRequest = mock(HttpUriRequest.class);
|
||||
doReturn(uri).when(uriRequest).getURI();
|
||||
doReturn(uriRequest).when(request).toRequest(any(RequestConfig.class));
|
||||
RibbonApacheHttpResponse returnedResponse = client.execute(request, null);
|
||||
verify(delegate, times(2)).execute(any(HttpUriRequest.class));
|
||||
verify(lb, times(0)).chooseServer(eq(serviceName));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class UseDefaults {
|
||||
|
||||
@@ -395,8 +444,12 @@ public class RibbonLoadBalancingHttpClientTests {
|
||||
|
||||
ReflectionTestUtils.setField(client, "delegate", delegate);
|
||||
ReflectionTestUtils.setField(client, "lb", loadBalancer);
|
||||
HttpResponse httpResponse = mock(HttpResponse.class);
|
||||
StatusLine statusLine = mock(StatusLine.class);
|
||||
doReturn(200).when(statusLine).getStatusCode();
|
||||
doReturn(statusLine).when(httpResponse).getStatusLine();
|
||||
given(delegate.execute(any(HttpUriRequest.class))).willReturn(
|
||||
mock(HttpResponse.class));
|
||||
httpResponse);
|
||||
RibbonApacheHttpRequest request = mock(RibbonApacheHttpRequest.class);
|
||||
doReturn(uri).when(request).getURI();
|
||||
doReturn(request).when(request).withNewUri(any(URI.class));
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2013-2016 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.ribbon.support;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class RetryableStatusCodeExceptionTest {
|
||||
|
||||
@Test
|
||||
public void testMessage() {
|
||||
RetryableStatusCodeException ex = new RetryableStatusCodeException("foo", 404);
|
||||
assertEquals("Service foo returned a status code of 404", ex.getMessage());
|
||||
}
|
||||
}
|
||||
@@ -61,7 +61,7 @@ public class ContextPathZuulProxyApplicationTests {
|
||||
private DiscoveryClientRouteLocator routes;
|
||||
|
||||
@Autowired
|
||||
private RoutesEndpoint endpoint;
|
||||
private RoutesMvcEndpoint endpoint;
|
||||
|
||||
@Before
|
||||
public void setTestRequestContext() {
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2013-2016 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.zuul;
|
||||
|
||||
import java.util.Map;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
value = {"zuul.routes.sslservice.url=https://localhost:8443", "management.security.enabled=false"})
|
||||
@DirtiesContext
|
||||
public class RoutesEndpointIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate restTemplate;
|
||||
|
||||
@Autowired
|
||||
private SimpleZuulProxyApplication.RoutesRefreshListener refreshListener;
|
||||
|
||||
@Test
|
||||
public void getRoutesTest() {
|
||||
Map<String, String> routes = restTemplate.getForObject("/admin/routes", Map.class);
|
||||
assertEquals("https://localhost:8443", routes.get("/sslservice/**"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postRoutesTest() {
|
||||
Map<String, String> routes = restTemplate.postForObject("/admin/routes", null, Map.class);
|
||||
assertEquals("https://localhost:8443", routes.get("/sslservice/**"));
|
||||
assertTrue(refreshListener.wasCalled());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@RestController
|
||||
@EnableZuulProxy
|
||||
static class SimpleZuulProxyApplication {
|
||||
@Component
|
||||
static class RoutesRefreshListener implements ApplicationListener<RoutesRefreshedEvent> {
|
||||
private boolean called = false;
|
||||
@Override
|
||||
public void onApplicationEvent(RoutesRefreshedEvent routesRefreshedEvent) {
|
||||
called = true;
|
||||
}
|
||||
|
||||
public boolean wasCalled() {
|
||||
return called;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2013-2016 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.zuul;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.netflix.zuul.filters.Route;
|
||||
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class RoutesEndpointTests {
|
||||
|
||||
private RouteLocator locator;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
this.locator = new RouteLocator() {
|
||||
@Override
|
||||
public Collection<String> getIgnoredPaths() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Route> getRoutes() {
|
||||
List<Route> routes = new ArrayList<>();
|
||||
routes.add(new Route("foo", "foopath", "foolocation", null, true, Collections.EMPTY_SET));
|
||||
routes.add(new Route("bar", "barpath", "barlocation", null, true, Collections.EMPTY_SET));
|
||||
return routes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Route getMatchingRoute(String path) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvoke() {
|
||||
RoutesEndpoint endpoint = new RoutesEndpoint(locator);
|
||||
Map<String, String> result = new HashMap<String, String>();
|
||||
for(Route r : locator.getRoutes()) {
|
||||
result.put(r.getFullPath(), r.getLocation());
|
||||
}
|
||||
assertEquals(result , endpoint.invoke());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testId() {
|
||||
RoutesEndpoint endpoint = new RoutesEndpoint(locator);
|
||||
assertEquals("routes", endpoint.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsSensitive() {
|
||||
RoutesEndpoint endpoint = new RoutesEndpoint(locator);
|
||||
assertTrue(endpoint.isSensitive());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2013-2016 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.zuul;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.netflix.zuul.filters.Route;
|
||||
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Matchers.isA;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@SpringBootTest
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class RoutesMvcEndpointTests {
|
||||
private RouteLocator locator;
|
||||
private RoutesEndpoint endpoint;
|
||||
@Mock
|
||||
private ApplicationEventPublisher publisher;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
this.locator = new RouteLocator() {
|
||||
@Override
|
||||
public Collection<String> getIgnoredPaths() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Route> getRoutes() {
|
||||
List<Route> routes = new ArrayList<>();
|
||||
routes.add(new Route("foo", "foopath", "foolocation", null, true, Collections.EMPTY_SET));
|
||||
routes.add(new Route("bar", "barpath", "barlocation", null, true, Collections.EMPTY_SET));
|
||||
return routes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Route getMatchingRoute(String path) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
endpoint = spy(new RoutesEndpoint(locator));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reset() throws Exception {
|
||||
RoutesMvcEndpoint mvcEndpoint = new RoutesMvcEndpoint(endpoint, locator);
|
||||
mvcEndpoint.setApplicationEventPublisher(publisher);
|
||||
Map<String, String> result = new HashMap<String, String>();
|
||||
for(Route r : locator.getRoutes()) {
|
||||
result.put(r.getFullPath(), r.getLocation());
|
||||
}
|
||||
assertEquals(result , mvcEndpoint.reset());
|
||||
verify(endpoint, times(1)).invoke();
|
||||
verify(publisher, times(1)).publishEvent(isA(RoutesRefreshedEvent.class));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -66,7 +66,7 @@ public class ServletPathZuulProxyApplicationTests {
|
||||
private DiscoveryClientRouteLocator routes;
|
||||
|
||||
@Autowired
|
||||
private RoutesEndpoint endpoint;
|
||||
private RoutesMvcEndpoint endpoint;
|
||||
|
||||
@Before
|
||||
public void setTestRequestContext() {
|
||||
|
||||
@@ -66,7 +66,7 @@ public class SimpleZuulProxyApplicationTests {
|
||||
private DiscoveryClientRouteLocator routes;
|
||||
|
||||
@Autowired
|
||||
private RoutesEndpoint endpoint;
|
||||
private RoutesMvcEndpoint endpoint;
|
||||
|
||||
@Before
|
||||
public void setTestRequestContext() {
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2013-2017 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.zuul;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletContextEvent;
|
||||
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import com.netflix.zuul.FilterLoader;
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import com.netflix.zuul.filters.FilterRegistry;
|
||||
import com.netflix.zuul.monitoring.CounterFactory;
|
||||
import com.netflix.zuul.monitoring.TracerFactory;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class ZuulFilterInitializerTests {
|
||||
|
||||
private static final ServletContextEvent DUMMY_SERVLET_CONTEXT_EVENT = mock(
|
||||
ServletContextEvent.class);
|
||||
|
||||
private Map<String, ZuulFilter> filters = getFilters();
|
||||
private CounterFactory counterFactory = mock(CounterFactory.class);
|
||||
private TracerFactory tracerFactory = mock(TracerFactory.class);
|
||||
private FilterLoader filterLoader = new FilterLoader();
|
||||
private FilterRegistry filterRegistry = getFilterRegistry();
|
||||
|
||||
private final ZuulFilterInitializer initializer = new ZuulFilterInitializer(filters,
|
||||
counterFactory, tracerFactory, filterLoader, filterRegistry);
|
||||
|
||||
@Test
|
||||
public void shouldSetupOnContextInitializedEvent() throws Exception {
|
||||
initializer.contextInitialized(DUMMY_SERVLET_CONTEXT_EVENT);
|
||||
|
||||
assertEquals(tracerFactory, TracerFactory.instance());
|
||||
assertEquals(counterFactory, CounterFactory.instance());
|
||||
assertThat(filterRegistry.getAllFilters())
|
||||
.containsAll(filters.values());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCleanupOnContextDestroyed() throws Exception {
|
||||
initializer.contextDestroyed(DUMMY_SERVLET_CONTEXT_EVENT);
|
||||
|
||||
assertEquals(null, ReflectionTestUtils.getField(TracerFactory.class, "INSTANCE"));
|
||||
assertEquals(null,
|
||||
ReflectionTestUtils.getField(CounterFactory.class, "INSTANCE"));
|
||||
assertTrue(FilterRegistry.instance().getAllFilters().isEmpty());
|
||||
assertTrue(getHashFiltersByType().isEmpty());
|
||||
}
|
||||
|
||||
private Map getHashFiltersByType() {
|
||||
Field field = ReflectionUtils.findField(FilterLoader.class, "hashFiltersByType");
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
return (Map) ReflectionUtils.getField(field, FilterLoader.getInstance());
|
||||
}
|
||||
|
||||
private Map<String, ZuulFilter> getFilters() {
|
||||
Map<String, ZuulFilter> filters = new HashMap<>();
|
||||
filters.put("key1", mock(ZuulFilter.class));
|
||||
filters.put("key2", mock(ZuulFilter.class));
|
||||
return filters;
|
||||
}
|
||||
|
||||
private FilterRegistry getFilterRegistry() {
|
||||
try {
|
||||
Constructor<FilterRegistry> constructor = FilterRegistry.class
|
||||
.getDeclaredConstructor(new Class[0]);
|
||||
constructor.setAccessible(true);
|
||||
return constructor.newInstance(new Object[0]);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright 2013-2017 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.zuul;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.context.embedded.LocalServerPort;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = ZuulProxyApplicationTests.ZuulProxyApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT, properties = {
|
||||
"zuul.routes.simple:/simple/**", "logging.level.org.apache.http: DEBUG" })
|
||||
@DirtiesContext
|
||||
public class ZuulProxyApplicationTests {
|
||||
|
||||
@LocalServerPort
|
||||
private int port;
|
||||
|
||||
@Before
|
||||
public void setTestRequestcontext() {
|
||||
RequestContext context = new RequestContext();
|
||||
RequestContext.testSetCurrentContext(context);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getHasCorrectTransferEncoding() {
|
||||
ResponseEntity<String> result = new TestRestTemplate().getForEntity(
|
||||
"http://localhost:" + this.port + "/simple/transferencoding", String.class);
|
||||
assertEquals(HttpStatus.OK, result.getStatusCode());
|
||||
assertEquals("missing", result.getBody());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postHasCorrectTransferEncoding() {
|
||||
ResponseEntity<String> result = new TestRestTemplate().postForEntity(
|
||||
"http://localhost:" + this.port + "/simple/transferencoding", new HttpEntity<>("hello"),
|
||||
String.class);
|
||||
assertEquals(HttpStatus.OK, result.getStatusCode());
|
||||
assertEquals("missing", result.getBody());
|
||||
}
|
||||
|
||||
// Don't use @SpringBootApplication because we don't want to component scan
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@RestController
|
||||
@EnableZuulProxy
|
||||
@RibbonClient(name = "simple", configuration = TestRibbonClientConfiguration.class)
|
||||
static class ZuulProxyApplication {
|
||||
|
||||
@RequestMapping(value = "/transferencoding", method = RequestMethod.GET)
|
||||
public String get(@RequestHeader(name = "Transfer-Encoding", required = false) String transferEncoding) {
|
||||
if (transferEncoding == null) {
|
||||
return "missing";
|
||||
}
|
||||
return transferEncoding;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/transferencoding", method = RequestMethod.POST)
|
||||
public String post(@RequestHeader(name = "Transfer-Encoding", required = false) String transferEncoding,
|
||||
@RequestBody String hello) {
|
||||
if (transferEncoding == null) {
|
||||
return "missing";
|
||||
}
|
||||
return transferEncoding;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Load balancer with fixed server list for "simple" pointing to localhost
|
||||
@Configuration
|
||||
static class TestRibbonClientConfiguration {
|
||||
|
||||
@LocalServerPort
|
||||
private int port;
|
||||
|
||||
@Bean
|
||||
public ServerList<Server> ribbonServerList() {
|
||||
return new StaticServerList<>(new Server("localhost", this.port));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
|
||||
import org.springframework.cloud.netflix.zuul.RoutesEndpoint;
|
||||
import org.springframework.cloud.netflix.zuul.RoutesMvcEndpoint;
|
||||
import org.springframework.cloud.netflix.zuul.ZuulProxyConfiguration;
|
||||
import org.springframework.cloud.netflix.zuul.filters.discovery.DiscoveryClientRouteLocator;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilter;
|
||||
@@ -59,7 +59,7 @@ public class CustomHostRoutingFilterTests {
|
||||
private DiscoveryClientRouteLocator routes;
|
||||
|
||||
@Autowired
|
||||
private RoutesEndpoint endpoint;
|
||||
private RoutesMvcEndpoint endpoint;
|
||||
|
||||
@Before
|
||||
public void setTestRequestcontext() {
|
||||
|
||||
@@ -22,7 +22,7 @@ import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
|
||||
import org.springframework.cloud.netflix.zuul.RoutesEndpoint;
|
||||
import org.springframework.cloud.netflix.zuul.RoutesMvcEndpoint;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpEntity;
|
||||
@@ -59,7 +59,7 @@ public class PatternServiceRouteMapperIntegrationTests {
|
||||
private DiscoveryClientRouteLocator routes;
|
||||
|
||||
@Autowired
|
||||
private RoutesEndpoint endpoint;
|
||||
private RoutesMvcEndpoint endpoint;
|
||||
|
||||
@Before
|
||||
public void setTestRequestcontext() {
|
||||
|
||||
@@ -47,7 +47,8 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
"disableretry.ribbon.MaxAutoRetriesNextServer: 1",
|
||||
"zuul.routes.globalretrydisabled: /globalretrydisabled/**",
|
||||
"globalretrydisabled.ribbon.MaxAutoRetries: 1",
|
||||
"globalretrydisabled.ribbon.MaxAutoRetriesNextServer: 1"
|
||||
"globalretrydisabled.ribbon.MaxAutoRetriesNextServer: 1",
|
||||
"retryable.ribbon.retryableStatusCodes: 404,403"
|
||||
})
|
||||
@DirtiesContext
|
||||
public class HttpClientRibbonRetryIntegrationTests extends RibbonRetryIntegrationTestBase {
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
"zuul.routes.retryable: /retryable/**",
|
||||
"zuul.routes.retryable.retryable: true",
|
||||
"retryable.ribbon.OkToRetryOnAllOperations: true",
|
||||
"retryable.ribbon.retryableStatusCodes: 404",
|
||||
"retryable.ribbon.MaxAutoRetries: 1",
|
||||
"retryable.ribbon.MaxAutoRetriesNextServer: 1",
|
||||
"zuul.routes.getretryable: /getretryable/**",
|
||||
|
||||
@@ -25,8 +25,15 @@ import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClients;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryPolicy;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerContext;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -37,6 +44,7 @@ import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.netflix.loadbalancer.Server;
|
||||
@@ -74,6 +82,15 @@ public abstract class RibbonRetryIntegrationTestBase {
|
||||
assertEquals(HttpStatus.OK, result.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void retryableFourOFour() {
|
||||
String uri = "/retryable/404everyothererror";
|
||||
ResponseEntity<String> result = new TestRestTemplate().exchange(
|
||||
"http://localhost:" + this.port + uri, HttpMethod.GET,
|
||||
new HttpEntity<>((Void) null), String.class);
|
||||
assertEquals(HttpStatus.OK, result.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postRetryOK() {
|
||||
String uri = "/retryable/posteveryothererror";
|
||||
@@ -161,6 +178,17 @@ public abstract class RibbonRetryIntegrationTestBase {
|
||||
return timeout();
|
||||
}
|
||||
|
||||
@RequestMapping("/404everyothererror")
|
||||
@ResponseStatus(HttpStatus.NOT_FOUND)
|
||||
public ResponseEntity<String> fourOFourError() {
|
||||
boolean shouldError = error;
|
||||
error = !error;
|
||||
if(shouldError) {
|
||||
return new ResponseEntity<String>("not found", HttpStatus.NOT_FOUND);
|
||||
}
|
||||
return new ResponseEntity<String>("no error", HttpStatus.OK);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@@ -173,6 +201,46 @@ public abstract class RibbonRetryIntegrationTestBase {
|
||||
public ServerList<Server> ribbonServerList() {
|
||||
return new StaticServerList<>(new Server("localhost", this.port));
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class FourOFourRetryableRibbonConfiguration extends RibbonClientConfiguration {
|
||||
|
||||
@Bean
|
||||
public LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory(SpringClientFactory factory) {
|
||||
return new MyRibbonRetryPolicyFactory(factory);
|
||||
}
|
||||
|
||||
public static class MyRibbonRetryPolicyFactory extends RibbonLoadBalancedRetryPolicyFactory {
|
||||
|
||||
private SpringClientFactory factory;
|
||||
|
||||
public MyRibbonRetryPolicyFactory(SpringClientFactory clientFactory) {
|
||||
super(clientFactory);
|
||||
this.factory = clientFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoadBalancedRetryPolicy create(String serviceId, ServiceInstanceChooser loadBalanceChooser) {
|
||||
RibbonLoadBalancerContext lbContext = this.factory
|
||||
.getLoadBalancerContext(serviceId);
|
||||
return new MyLoadBalancedRetryPolicy(serviceId, lbContext, loadBalanceChooser);
|
||||
}
|
||||
|
||||
class MyLoadBalancedRetryPolicy extends RibbonLoadBalancedRetryPolicy {
|
||||
|
||||
public MyLoadBalancedRetryPolicy(String serviceId, RibbonLoadBalancerContext context, ServiceInstanceChooser loadBalanceChooser) {
|
||||
super(serviceId, context, loadBalanceChooser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retryableStatusCode( int statusCode) {
|
||||
if(statusCode == HttpStatus.NOT_FOUND.value()) {
|
||||
return true;
|
||||
}
|
||||
return super.retryableStatusCode(statusCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorContro
|
||||
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorAttributes;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
import org.springframework.cloud.netflix.zuul.RoutesEndpoint;
|
||||
import org.springframework.cloud.netflix.zuul.RoutesMvcEndpoint;
|
||||
import org.springframework.cloud.netflix.zuul.filters.Route;
|
||||
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
|
||||
import org.springframework.cloud.netflix.zuul.filters.discovery.DiscoveryClientRouteLocator;
|
||||
@@ -88,7 +88,7 @@ public abstract class ZuulProxyTestBase {
|
||||
protected DiscoveryClientRouteLocator routes;
|
||||
|
||||
@Autowired
|
||||
protected RoutesEndpoint endpoint;
|
||||
protected RoutesMvcEndpoint endpoint;
|
||||
|
||||
@Autowired
|
||||
protected RibbonCommandFactory<?> ribbonCommandFactory;
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.zuul.metrics;
|
||||
|
||||
import org.springframework.boot.actuate.metrics.CounterService;
|
||||
|
||||
import com.netflix.zuul.monitoring.CounterFactory;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
public class DefaultCounterFactoryTests {
|
||||
|
||||
private static final String NAME = "my-super-metric-name";
|
||||
private final CounterService counterService = mock(CounterService.class);
|
||||
private final CounterFactory factory = new DefaultCounterFactory(counterService);
|
||||
|
||||
@Test
|
||||
public void shouldIncrement() throws Exception {
|
||||
factory.increment(NAME);
|
||||
|
||||
verify(counterService).increment(NAME);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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.zuul.metrics;
|
||||
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.cloud.ClassPathExclusions;
|
||||
import org.springframework.cloud.netflix.zuul.ZuulConfiguration;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import com.netflix.zuul.monitoring.CounterFactory;
|
||||
import com.netflix.zuul.monitoring.TracerFactory;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@ClassPathExclusions({ "spring-boot-starter-actuator-*.jar",
|
||||
"spring-boot-actuator-*.jar" })
|
||||
public class ZuulEmptyMetricsApplicationTests {
|
||||
|
||||
private AnnotationConfigApplicationContext context;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
context.register(ZuulEmptyMetricsApplicationTestsConfiguration.class,
|
||||
ZuulConfiguration.class);
|
||||
context.refresh();
|
||||
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
if (this.context != null) {
|
||||
this.context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSetupDefaultCounterFactoryIfCounterServiceIsPresent()
|
||||
throws Exception {
|
||||
CounterFactory factory = this.context.getBean(CounterFactory.class);
|
||||
|
||||
assertEquals(EmptyCounterFactory.class, factory.getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSetupEmptyTracerFactory() throws Exception {
|
||||
TracerFactory factory = this.context.getBean(TracerFactory.class);
|
||||
|
||||
assertEquals(EmptyTracerFactory.class, factory.getClass());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ZuulEmptyMetricsApplicationTestsConfiguration {
|
||||
|
||||
@Bean
|
||||
ServerProperties serverProperties() {
|
||||
return new ServerProperties();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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.zuul.metrics;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.metrics.CounterService;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.netflix.zuul.EnableZuulServer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import com.netflix.zuul.exception.ZuulException;
|
||||
import com.netflix.zuul.monitoring.CounterFactory;
|
||||
import com.netflix.zuul.monitoring.TracerFactory;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = {
|
||||
ZuulMetricsApplicationTests.ZuulMetricsApplicationTestsConfiguration.class,
|
||||
ZuulMetricsApplicationTests.ZuulConfig.class }, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@DirtiesContext
|
||||
public class ZuulMetricsApplicationTests {
|
||||
|
||||
private static final Map<String, Long> counters = new HashMap<>();
|
||||
|
||||
@Autowired
|
||||
CounterFactory counterFactory;
|
||||
@Autowired
|
||||
TracerFactory tracerFactory;
|
||||
|
||||
@Test
|
||||
public void shouldSetupDefaultCounterFactoryIfCounterServiceIsPresent()
|
||||
throws Exception {
|
||||
assertEquals(DefaultCounterFactory.class, counterFactory.getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSetupEmptyTracerFactory() throws Exception {
|
||||
assertEquals(EmptyTracerFactory.class, tracerFactory.getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldIncrementCounters() throws Exception {
|
||||
new ZuulException("any", 500, "cause");
|
||||
new ZuulException("any", 500, "cause");
|
||||
|
||||
assertEquals((long) counters.get("ZUUL::EXCEPTION:cause:500"), 2L);
|
||||
|
||||
new ZuulException("any", 404, "cause2");
|
||||
new ZuulException("any", 404, "cause2");
|
||||
new ZuulException("any", 404, "cause2");
|
||||
|
||||
assertEquals((long) counters.get("ZUUL::EXCEPTION:cause2:404"), 3L);
|
||||
}
|
||||
|
||||
// Don't use @SpringBootApplication because we don't want to component scan
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@EnableZuulServer
|
||||
static class ZuulConfig {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ZuulMetricsApplicationTestsConfiguration {
|
||||
|
||||
@Bean
|
||||
public ServerProperties serverProperties() {
|
||||
return new ServerProperties();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CounterService counterService() {
|
||||
return new CounterService() {
|
||||
// not thread safe, but we are ok with it in tests
|
||||
@Override
|
||||
public void increment(String metricName) {
|
||||
Long counter = counters.get(metricName);
|
||||
if (counter == null) {
|
||||
counter = 0L;
|
||||
}
|
||||
counters.put(metricName, ++counter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decrement(String metricName) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset(String metricName) {
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import java.util.Map;
|
||||
import org.springframework.cloud.client.DefaultServiceInstance;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.netflix.appinfo.EurekaInstanceConfig;
|
||||
import com.netflix.appinfo.InstanceInfo;
|
||||
@@ -102,7 +103,8 @@ public class EurekaDiscoveryClient implements DiscoveryClient {
|
||||
public static class EurekaServiceInstance implements ServiceInstance {
|
||||
private InstanceInfo instance;
|
||||
|
||||
EurekaServiceInstance(InstanceInfo instance) {
|
||||
public EurekaServiceInstance(InstanceInfo instance) {
|
||||
Assert.notNull(instance, "Service instance required");
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
|
||||
@@ -126,6 +126,10 @@
|
||||
color: red;
|
||||
}
|
||||
|
||||
.badRequest {
|
||||
color: #00CC99;
|
||||
}
|
||||
|
||||
.dependencies .rejected {
|
||||
color: purple;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<div class="cell borderRight">
|
||||
<a href="javascript://" title="Successful Request Count" class="line tooltip success"><%= addCommas(rollingCountSuccess) %></a>
|
||||
<a href="javascript://" title="Short-circuited Request Count" class="line tooltip shortCircuited"><%= addCommas(rollingCountShortCircuited) %></a>
|
||||
<a href="javascript://" title="Bad Request Count" class="line tooltip badRequest"><%= addCommas(rollingCountBadRequests) %></a>
|
||||
<br>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -93,6 +93,10 @@ h3.sectionHeader {
|
||||
color: brown;
|
||||
}
|
||||
|
||||
.badRequest {
|
||||
color: #00CC99;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1100px) {
|
||||
.container {
|
||||
padding-left: 5px;
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
<a href="javascript://" onclick="hystrixMonitor.sortByLatency995();">99.5</a>
|
||||
</div>
|
||||
<div class="menu_legend">
|
||||
<span class="success">Success</span> | <span class="shortCircuited">Short-Circuited</span> | <span class="timeout">Timeout</span> | <span class="rejected">Rejected</span> | <span class="failure">Failure</span> | <span class="errorPercentage">Error %</span>
|
||||
<span class="success">Success</span> | <span class="shortCircuited">Short-Circuited</span> | <span class="badRequest"> Bad Request</span> | <span class="timeout">Timeout</span> | <span class="rejected">Rejected</span> | <span class="failure">Failure</span> | <span class="errorPercentage">Error %</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user