Loadbalancing fails when a server is down - spring

I have written a simple set of micro-services with the following architecture:
For all, I have added spring-boot-starter-actuator in order to add /health endpoint.
In Zuul/Ribbon configuration I have added :
zuul:
ignoredServices: "*"
routes:
home-service:
path: /service/**
serviceId: home-service
retryable: true
home-service:
ribbon:
listOfServers: localhost:8080,localhost:8081
eureka.enabled: false
ServerListRefreshInterval: 1
So that, each time client will call GET http://localhost:7070/service/home, loadbalancer will choose one of two HomeService which runs on 8080 or 8081 port and will call its endpoint /home.
But, when one of HomeService is shutdown, the loadbalancer does not seem to be aware (in spite of ServerListRefreshInterval configuration) and will fail with error=500 if it tries to call the shutdown instance.
How could I fix it?

I have received and tested a solution from spring-cloud team.
Solution is here in github
To summarize:
I have added org.springframework.retry.spring-retry to my zuul classpath
I have added #EnableRetry to my zuul application
I have put the following properties in my zuul configuration
application.yml
server:
port: ${PORT:7070}
spring:
application:
name: gateway
endpoints:
health:
enabled: true
sensitive: true
restart:
enabled: true
shutdown:
enabled: true
zuul:
ignoredServices: "*"
routes:
home-service:
path: /service/**
serviceId: home-service
retryable: true
retryable: true
home-service:
ribbon:
listOfServers: localhost:8080,localhost:8081
eureka.enabled: false
ServerListRefreshInterval: 100
retryableStatusCodes: 500
MaxAutoRetries: 2
MaxAutoRetriesNextServer: 1
OkToRetryOnAllOperations: true
ReadTimeout: 10000
ConnectTimeout: 10000
EnablePrimeConnections: true
ribbon:
eureka:
enabled: false
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 30000

Debugging timeouts may be tricky, considering there are 3 levels of routing alone (Zuul→Hystrix→Ribbon), not including async execution layers and the retry engine. The following scheme is valid for Spring Cloud releases Camden.SR6 and newer (I've checked this on Dalston.SR1):
Zuul routes the request through RibbonRoutingFilter, which creates a Ribbon command with the request context. Ribbon command then creates a LoadBalancer command, which uses spring-retry for command execution, choosing retry policy for the RetryTemplate according to Zuul settings. #EnableRetry does nothing in this case, because this annotation enables wrapping methods with #Retryable annotation in retrying proxies.
This means, your command duration is limited to the lesser value of these two (see this post):
[HystrixTimeout], which is a timeout for invoked Hystrix command
[RibbonTimeout * MaxAutoRetries * MaxAutoRetriesNextServer] (retries kick in only if Zuul has them enabled in its configuration), where [RibbonTimeout = ConnectTimeout + ReadTimeout] on the http client.
For debugging, it's convenient to create a breakpoint in RetryableRibbonLoadBalancingHttpClient#executeWithRetry or RetryableRibbonLoadBalancingHttpClient#execute method. At this point, you have:
ContextAwareRequest instance (e.g. RibbonApacheHttpRequest or OkHttpRibbonRequest) with request context, which containes Zuul's retryable property;
LoadBalancedRetryPolicy intsance with load balancer context, which contains Ribbon's maxAutoRetries, maxAutoRetriesNextServer and okToRetryOnAllOperations properties;
RetryCallback instance with a requestConfig, which contains HttpClient's connectTimeout and socketTimeout properties;
RetryTemplate instance with chosen retry policy.
If the breakpoint is not hit, it means that org.springframework.cloud.netflix.ribbon.apache.RetryableRibbonLoadBalancingHttpClient bean was not instantiated. This happenes when the spring-retry library is not in the classpath.

Related

spring zuul gateway in kubernetes

I am introducing in microservices with spring and kubernetes.
I have gateway services made with spring-cloud-starter-netflix-zuul that works like an apigateway
I define Zull gateway like this:
server:
port: 8080
use-forward-headers: true
security:
basic:
enabled: false
oauth2:
resource:
jwk.key-set-uri: ${OAUTH_KEYSETURI}
spring:
config:
name: proxy-service
application:
name: proxy-service
zuul:
routes:
service-one:
path: /service-one/**
url: http://service-one:8080
serviceId: service-one
service-two:
path: /service-two/**
url: http://service-two:8080
serviceId: service-two
ribbon:
eureka:
enabled: false
eureka:
client:
enabled: false
error:
whitelabel:
enabled: false
But I found some problems, for example, that the requests have a limit per service, so I added the following lines:
zuul:
host:
max-per-route-connections: 100000
max-total-connections: 100000
I want to know. What is the most performant way to integrate spring-zuul with Kubernetes? I have read that it can also be integrated with spring-kubernetes-config, ribbon, and eureka. But doing it is more performant?
Recently I also read about spring-cloud-gateway. What is the difference with this project? Why spring has two gateway projects very similar? Are there differences in performance? Will both be supported in the future? What do you recommend to use?

How to Configure Zuul Multiple instances of one microservice

i'm working in a spring cloud project and i have an eruka service, zull proxy and a microservice with the name defects-service (zull proxy and my defects-service are clients for eurka) and all works fine.
in my zull proxy i have the following configuration :
eureka:
client:
register-with-eureka: true
fetch-registry: true
serviceUrl:
defaultZone: http://localhost:8370/eureka/
instance:
hostname: localhost
zuul:
prefix: /api
routes:
defects-service:
path: /defects-service/**
url: http://localhost:8300
my issue is now i start two instances of my defects-service on diferents ports 8301 and 8302 (registred successfully in eruka service ) but i don't know how to configure my zull proxy to do a load balancing and start redirect requests to my three instances on ports (8300,8301,8302).
note , zull knows only the instance on http://localhost:8300
can anyone please helpe me to resolve this isuue .
Best regards .
Zuul support load balancing out of the box, instead of giving url in your configuration, use serviceId:
zuul:
prefix: /api
routes:
defects-service:
path: /defects-service/**
serviceId: defects-service
the solution of this problem is by using the following config :
zuul:
prefix: /api
routes:
defects-service:
path: /defects-service/**
serviceId: defects-service
after that if you got any error like Hystrix Readed time out.. just add the following config :
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 11000
ribbon:
ConnectTimeout: 10000
ReadTimeout: 10000

spring-cloud-zuul timeout configuration does not work

according to spring cloud zuul 8.1.3 zuul timeout, the following configuration should make zuul timeout after 20 seconds, however, it times out at 10 seconds.
server:
port: 8769
spring:
application:
name: service-zuul
zuul:
host:
connect-timeout-millis: 20000
socket-timeout-millis: 60000
zuul:
routes:
tp:
path: /**
url: http://localhost:9998
ribbon:eureka:enabled: false
From console output one can see that the interval between filter and timeout exception is only 10 seconds. which means the zuul.host.connect-timeout-millis does not work.
I finally found a solution:
I used properties before and this is the first time to try yml, it seems that an item must be aggregated into one prefix:
zuul:
host:
connect-timeout-millis: 20000
socket-timeout-millis: 60000
routes:
tp:
path: /**
url: http://localhost:9998
In this way the backend server could have at most 60 seconds to respond. Besides, I also found that the sequence in configuration matters.
Try to define the below properties instead if you are using Zuul withe Eureka.
ribbon:
ReadTimeout: 60000
ConnectTimeout: 20000
If you are using Zuul with Eureka, Zuul will use RibbonRoutingFilter for routing instead of SimpleHostRoutingFilter. In this case, HTTP requests are handled by Ribbon.
Add the below config to Zuul gateway to make it work:
hystrix.command.default.execution.timeout.enabled= true
ribbon.ReadTimeout=5000
ribbon.ConnectTimeout=5000
#(ribbon.ReadTimeout + ribbon.connectTimeout) * (1+ribbon.maxAutoRetries(default 0) * (ribbon.MaxAutoRetriesNextServer (default 1) + 1)
#timeoutInMilliseconds= (5000 + 5000) * (1+0)*(1+1) =20000
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=21000
Note: Though the IDEs complain this is unknown property, it still works.

Add Zuul to Netflix OSS installation

I have a netflix oss stack running on a linux box. this stack has Eureka, and a bunch of microservices (hello services) installed, all running in individual docker containers.
It was installed using Maven and Spring Boot.
My question is: How can I add Zuul to this stack? I've found some things on the net but they install Eureka too, and I'm affarid to overwrite the current Eureka if I run this.
You need to add the #EnableZuulProxy annotation to the main class
#SpringBootApplication
#EnableZuulProxy
#EnableHystrixDashboard
public class ZuulApp{
public static void main(String[] args) {
SpringApplication.run(ZuulApp.class, args);
}
}
and in the application.yml file add the routes with the names of your eureka-microservices
zuul:
ignoredServices: '*'
routes:
microservice1:
path: /microservice1/**
stripPrefix: false
microservice2:
path: /microservice2/**
stripPrefix: false
...
#If you have hystrix
hystrix:
threadpool:
default:
maxQueueSize: 100
queueSizeRejectionThreshold: 100
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 60000
#load balancing
ribbon:
MaxAutoRetries: 2
MaxAutoRetriesNextServer: 2
OkToRetryOnAllOperations: true
ServerListRefreshInterval: 2000
ConnectTimeout: 50000
ReadTimeout: 50000
By default zuul runs on port 8500.
The maven dependency
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
Also, ensure that you use serviceid, for url routing. E.g:
zuul:
routes:
httpbin:
path: /**
serviceId: httpbin
Zuul should be aware of eureka to be able to fetch the correct ip's for various services which are running.
ribbon:
eureka:
enabled: false
The above configuration ensure that you can dynamically add more servers for specific services in future without modifying the zull routing server configuration as it will be able to query and update the servers where specific services are running by querying eureka

Eureka registration of Https micro services

Eureka does not recognized HTTPS endpoints like '/info' and '/health' and always points to HTTP endpoints after enabling HTTPS. How to enable HTTPS micro-service url registration at Eureka ?
You have to explicitly define these URLs as Eureka always points to HTTP internally. Read Here for more about it.
You can add following into your yaml file in the microservice.
eureka:
instance:
nonSecurePortEnabled: false
securePortEnabled: true
statusPageUrl: 'https://${eureka.instance.hostName}:${server.port}/info'
healthCheckUrl: 'https://${eureka.instance.hostName}:${server.port}/health'
homePageUrl: 'https://${eureka.instance.hostName}:${server.port}/'
Here "eureka.instance.hostName" and "server.port" values will be taken from the environment.
For me this configuration works:
eureka:
instance:
nonSecurePortEnabled: false
securePortEnabled: true
securePort: ${server.port}
statusPageUrl: https://${eureka.instance.hostname}:${eureka.instance.securePort}/info
homePageUrl: https://${eureka.instance.hostname}:${eureka.instance.securePort}/
For me below configuration works fine:-
eureka:
instance:
statusPageUrl: https://${eureka.hostname}:${server.port}/actuator/info
healthCheckUrl: https://${eureka.hostname}:${server.port}/health
homePageUrl: https://${eureka.hostname}:${server.port}/

Resources