Spring Retry, RestTemplate, and client.ribbon.MaxAutoRetriesNextServer - spring-boot

I'm using Spring Boot 2.3.3 along with Spring Retry 1.2.5.
We're replacing usages of Ribbon with Spring Cloud Load Balancer. One of the bits of functionality that I can't get to work is setting the max retries of our RestTemplates. In the old code we have these properties set:
client.ribbon.MaxAutoRetries: 2
client.ribbon.MaxAutoRetriesNextServer: 3
The documentation for for Spring Cloud Commons, Retrying Failed Requests indicates:
"The load-balanced RestTemplate honors some of the Ribbon configuration values related to retrying failed requests. You can use client.ribbon.MaxAutoRetries, client.ribbon.MaxAutoRetriesNextServer, and client.ribbon.OkToRetryOnAllOperations properties."
https://docs.spring.io/spring-cloud-commons/docs/2.2.4.RELEASE/reference/html/#retrying-failed-requests
I have Spring Retry added to my maven POM
I have #EnableRetry annotation on the Application startup class
I have the settings for MaxAutoRetries and MaxAutoRetriesNextServer set in the application.yml
I have RestTemplate bean defined with the #LoadBalanced annotation
The code works when all the services are running
When I shut down one of the services the RestTemplate is calling, the RestTemplate doesn't attempt to make the call multiple times as it's configured. I'm specifically interested in the MaxAutoRetriesNextServer as that's the most important bit.
Question 1:
What else do I need to do to get Spring Retry to make the RestTemplate retry, and retry on the next server etc.?
Question 2:
Why is the MaxAutoRetriesNextServer property not exposed on the Spring Retry api? I only see a property for setMaxAttempts in the SimpleRetryPolicy.

Related

How to test a Spring Boot actuator endpoint when the 'server.port' and 'management.server.port' properties are different

I would like to test the /health endpoint of my Spring Boot microservice with the help of the MockMvc bean.
But in my case the server.port and management.server.port properties have different values.
And the GET request issued by my MockMvc always ends on the port defined in server.port.
There is already an answer there : Spring boot's actuator unavailable when set management port
But I cannot find this ManagementContextResolver class in Spring Boot 2.2.2.
So far, I found out that the 'management' ConfigurableWebServerApplicationContext is created in org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration.DifferentManagementContextConfiguration, but I don't see how I can obtain a reference to it..
try this, its from the SpringBoot Docs :
#WebIntegrationTest({"server.port=8080", "management.port=8090"})

Avoid resetting HikariCP datasource connection pool on property change/refresh

I am using spring boot 2 with PCF config server to use centrallized config. My microservice is basic crud rest service. What I noticed is that whenever a property is being changed and http post is being made on "actuator/refresh" endpoint, spring boot 2 drops all connection including active ones and rebuilds the connection pool. How can I avoid this? I am also using spring-boot-starter amqp and cloud bus to notify all my service instance to refresh the properties so it is also happening for http post on "actuator/bus-refresh".
Also to clarify, I didnot change any property related to datasource config, instead I am changing application specific property, so why does spring boot refreshing datasource, I did not understood.

Spring Cloud Gateway - Return 503 when no Service Instance Available

Trying out Spring Cloud Gateway with Spring Boot microservices registered/discovered via Eureka server. The setup is working fine and clients can call the discovered services via the gateway without problem which is great.
Now, I am trying to test a scenario where a client calls a service (via the gateway) for which currently there are no instances available to service the call (i.e. service is down). When we test this scenario, the default behaviour is that the client gets a 500 Internal Server Error response. What would be the simplest way to change this default behaviour to return a 503 Service Unavilable response instead?
Current Stack:
Java 8
Spring Boot 2.0.0.M6
Spring Cloud 2.0.0.M3
Spring Cloud Starter Eureka 2.0.0.M2 (in Gateway JVM)
Spring Cloud Starter Netflix Ribbon 2.0.0.M3 (in Gateway JVM)
Spring Cloud Starter Netflix Eureka Server 2.0.0.M3 (in Eureka Server JVM)
In the end we have decided to leave the error response as it is. i.e. HTTP 500 - Unable to find instance for test-service-name. We have however added functionality to the Gateway's Routing mechanism to include an enabled: true|false flag to each Route which now allows us to temporarily enable/disable access to those microservices as desired. We are doing this by re-building Routes which are marked with enabled: false to include a GatewayFilter in them which returns:
Mono.error(new WebRequestException(HttpStatus.SERVICE_UNAVAILABLE, "SERVICE_UNAVAILABLE",
"Service is down for maintenance"));
whenever a call to that enabled: false Route is called.
The WebRequestException is our own Throwable class which we then intercept in our own WebExceptionHandler which handles the ServerWebExchange's ServerHttpResponse as needed.
Note 1: The Gateway is using spring-boot-starter-webflux within.
Note 2: Whenever we change the state of a Route's enabled flag we: 1) Re-build the Route, 2) Call CachingRouteLocator Bean's .refresh() method in order for the Route to be re-cached with|without the GatewayFilter in place.
The question is quite similar to:
https://stackoverflow.com/a/69257852/12952273
but is asked in different way.
There is a pattern known as Circuit Breaker and it helps avoid cascade failures (cascade failure can happen if the being called service is not responding)
https://martinfowler.com/bliki/CircuitBreaker.html
This might be a little hacky, but why not add a controller advice? Something like this should get you there.
#ControllerAdvice
public class ServiceNotFoundHandler {
#ExceptionHandler(NotFoundException.class)
#ResponseBody
#ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE)
public String handleUnavailableService(NotFoundException e) {
return "service unavailable";
}
}

Spring RestTemplate netflix loadblancer not configuring httpClient header Host

We have a complicated app based on Spring Cloud, Netflix Loadbalancer, to make calls between micro-services ms1<client>-->ms2<server>
We are using a restTemplate.exchange call to a URI hostname that is a Eureka Key for FQDN lookup.
This configuration works in other micro-services, in fact the the restTemplate bean works for a different component's micro-service call.
The receiving the rest call is Cloud Foundry Go-router which I believe is just a ngineX proxy server, the httpClient request should have the header variable set to "Host":"FQDN" this allows the proxy to route the request to the proper instance in the space.
PROBLEM:
httpClient from ms makes the call to the ms
CompletionException. cause: org.springframework.web.client.HttpClientErrorException: 404 NOT_FOUND
This is the response from the CF go-router (simple proxy server), the request never gets http--> the ms instance.
When the RestClient configures the request it sets the header "Host" as localhost:8090 or whatever the ms hostname is???
Discussion related Questions:
So apparently we have a configuration problem here.
Any advice on how the netflix ribbon loadbalancer client stuff sets the httpClient headers?
What package class interceptor does this magic?
What configuration variables effect this?
Code debugging indicates that netflix.client.SimpleVipAddressResolver is running.
We've traced the debug all the way to the Apache httpClient and it has the header Host set to the ms hostname, it's set to that value in the netflix httpClient wrapper too.
I tried to create a simple reference implementation of this, but can't.
Any recommendations on troubleshooting?
Where to look or read docs on what com.netflix package?
Using the Camden Spring. Using profiles,
From the memory debugging;
ClientClassName:com.netflix.niws.client.http.RestClient
VipAddressResolverClassName:com.netflix.client.SimpleVipAddressResolver
NIWSServerListClassName:com.netflix.loadbalancer.ConfigurationBasedServerList
NFLoadBalancerClassName:com.netflix.loadbalancer.ZoneAwareLoadBalancer
NFLoadBalancerRuleClassName:com.netflix.loadbalancer.AvailabilityFilteringRule
EnablePrimeConnections:false,
CustomSSLSocketFactoryClassName:null,
TrustStorePassword:null,
EnableConnectionPool:true,
listOfServers:,
OkToRetryOnAllOperations:false,
RequestIdHeaderName:null
Our suspicion is that some application.properties, .yml, or bootstrap.yml is being set or not being set some where in the scan path ???.
We had just upgrade the platform spring boot 1.3.x to 1.4.2.
The ms inbound controller had the annotation
#RequestHeader HttpHeaders httpHeaders
Which is what we attached into the restTemplate.execute as a parameter and eventually found it way to the rest-netflix-httpClient as the request headers being used to call the ms. The Go-Router on CF must be using that value to perform the proxying to the instance.
Apparently, somewhere in the upgrades, one of two things happen, either
A) boot Controller #RequestHeader in version 1.3.2 did not put header Host.
B) previous versions of spring Cloud-netflix-ribbon overwrote the Host httpHeader value with the Ribbon lookup.
Either way, there was no special interceptor.
Spring cloud netflix Eureka will take whatever httpHeaders you provide (even if that header is key:Host and use those values.

Spring cloud Camden.SR1 hystrix.stream hangs

I am trying to enable a hystrix stream in a Spring Cloud service, using Camden.SR1 in a spring boot app.
I enable hystrix by having a compile-time dependency:
compile 'org.springframework.cloud:spring-cloud-starter-hystrix'
and by adding a #EnableHystrix in the Application configuration.
The /mappings actuator shows the /hystrix.stream endpoint available but when I do a GET on it, the request hangs indefinitely.
I have tried the same using Brixton.SR6 (thus using Spring Boot 1.3) and the hystrix.stream endpoint works as expected.
Am I doing something wrong or is this a regression?
This issue seems linked with the Hystrix issue described here: https://github.com/Netflix/Hystrix/issues/1117. The workaround detailed here solves it https://github.com/Netflix/Hystrix/issues/1117#issuecomment-192222569
/hystrix.stream becames available amongst the actuator endpoints (add spring-boot-starter-hateoas and browse to /actuator). Regardless of any hystrix-enabled functionality in this service, this advertised endpoint should not hang.

Resources