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
Related
Spring Boot version: 2.1.6.RELEASE
Spring Cloud Version: Greenwich.SR1
Objective is to set up two instances of Eureka Servers in a cluster and have all microservices registered to both, in order to achieve HA. Currently I am testing this on my local machine running Ubuntu 18.04.
So as mentioned in the official docs, I have set up the peer to peer awareness of Eureka Instances. On bringing both the Eureka Server Instances up, on the dashboard of Instance-1(Port 8080) under available replicas showing Instance-2 name and on dashboard of Instance-2(Port 8081) it is showing Instance-1 name. So far so good.
I have deployed them on external tomcat server as war and not running them as jar. War name is "eureka-naming-server". So my access URL becomes: http://localhost:8080/eureka-naming-server/eureka and http://localhost:8081/eureka-naming-server/eureka.
I have set up a load balancer using Apache 2 and mod_jk. So instead of accessing them using their individual URLs i.e. http://localhost:8080/eureka-naming-server/eureka, http://localhost:8081/eureka-naming-server/eureka, I can access them using Apache URL as http://localhost:80/eureka-naming-server/eureka and I can see load-balancing happening properly.
Now the issue is that once I give Eureka URL as http://localhost:80/eureka-naming-server/eureka in the microservice, it registers only on one instance depending on which Eureka instance the load-balancer has redirected the request. But even though peer to peer awareness is setup, microservice is not getting registered on the other eureka instance.
In Eureak servers, I'm using the below dependency:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
Eureka-1 props:
spring:
application:
name: eureka-naming-server
jmx:
default-domain: eureka-naming-server
eureka:
client:
service-url:
defaultZone: http://eureka-server-2:8081/eureka-naming-server/eureka
#register-with-eureka: false
#fetch-registry: false
instance:
hostname: eureka-server-1
Eureka-2 props:
spring:
application:
name: eureka-naming-server
jmx:
default-domain: eureka-naming-server
eureka:
client:
service-url:
defaultZone: http://eureka-server-1:8080/eureka-naming-server/eureka
#register-with-eureka: false
#fetch-registry: false
instance:
hostname: eureka-server-2
In microservices, I'm using below dependencies:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
Microservice props:
eureka:
instance:
metadata-map:
configPath: /config
client:
service-url:
defaultZone: http://localhost/eureka-naming-server/eureka
In the machine's /etc/hosts file I have set it up as:
127.0.0.1 eureka-server-1
127.0.0.1 eureka-server-2
The eureka instance on which it is getting registered I can see the below in the logs:
WARN 13748 --- [nio-8009-exec-3] Registered instance CONFIG-SERVER/192.168.1.16:config-server:8888 with status UP (replication=false)
The other Eureka instance where it is not registered, when it receives the heart-beat because of the load-balancer, shows this in the logs:
WARN 13748 --- [nio-8009-exec-3] c.n.e.registry.AbstractInstanceRegistry : DS: Registry: lease doesn't exist, registering resource: CONFIG-SERVER - 192.168.1.16:config-server:8888
2019-12-26 19:00:34.995 WARN 13748 --- [nio-8009-exec-3] c.n.eureka.resources.InstanceResource : Not Found (Renew): CONFIG-SERVER - 192.168.1.16:config-server:8888
Eureka-1 Dashboard
Eureka-2 Dashboard
I have already tried suggestions mentioned here and here. But nothing seems to work.
So what could I be missing here?
Issue:
spring:
application:
name: eureka-naming-server
You put the same name for an application so it's displayed like this.
you need to change it to,
Eureka-1 props:
spring:
application:
name: eureka-naming-server-1
Eureka-2 props:
spring:
application:
name: eureka-naming-server-2
It may also be worth checking your security configuration for your eureka servers. If you do have spring boot security enabled, you will need to be sure that you include the corresponding username/password in the defaultZone portion of your config.
i.e. in your eureka server 1 config: eureka.client.serviceUrl.defaultZone=http://:#eureka-server-2:8761/eureka
(and visa versa for your eureka server 2 config)
I only mention this because I had left out my own username/password values in that config value and it was not registering my microservices on all my eureka instances. After make this change, it worked as I was expecting.
I am trying to run an example with zuul and Eureka server.
Below are my configurations:
eureka-service
server.port=8761
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
logging.level.com.netflix.eureka=OFF
logging.level.com.netflix.discovery=OFF
eureka -client (bootstrap.properties):
spring.application.name=ab
management.server.port=9001
management.server.address: 127.0.0.1
server.port=8081
edge-service(zuul) :
zuul.routes.ab.url=http://localhost:8081
ribbon.eureka.enabled=false
spring.application.name=API-Gateway
eureka.client.eureka-server-port=8761
server.port=8080
While I am hitting the URL http:localhost:8080/clientapi, I get a white label error page, what configuration seems to be missing, as I could see edge-service, as well as eureka-client, registered correctly with the eureka -server.
I am using spring boot: 2.1.4.
Let me show you my example:
eureka in application.yml file:
server:
port: 8761
eureka:
client:
register-with-eureka: false
fetch-registry: true
instance-info-replication-interval-seconds: 10
server:
eviction-interval-timer-in-ms: 50000
wait-time-in-ms-when-sync-empty: 5
zuul:
zuul.routes.1.url=http://localhost:8081
zuul.routes.2.url=http://localhost:8082
zuul.prefix=/client
zuul.host.max-per-route-connections=10
zuul.host.max-total-connections=10
I have 2 services (1 - localhost:8081 and 2 - localhost:8082)
You need annotate main class Zuul:
#EnableZuulProxy
#EnableDiscoveryClient
Then start application. When you go to
localhost/8070/client/1
you will redirect to first service (localhost:8081)
When you go to
localhost/8070/client/2
I hope it will help you.
you will redirect to second service (localhost:8082)
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
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.
According to this blog https://spring.io/blog/2015/07/14/microservices-with-spring which is based on eureka service discovery and where the service discovery is working properly.
But when have switched to use Consul instead Eureka the service discovery is not working and getting this error:
java.lang.IllegalStateException: No instances available for ACCOUNTS-SERVICE
at org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.execute(RibbonLoadBalancerClient.java:79)
at org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor.intercept(LoadBalancerInterceptor.java:46) ...
UPDATED: After have fixed the previous error by providing the correct health-check endpoint (see the answer below), when deploying the services to Cloud Foundry with properly provided host and port of the Consul server in bootstrap.yml (Consul based PropertySource loaded during the 'bootstrap' phase):
---
spring:
profiles: cloud
cloud:
consul:
host: <consul host or ip>
port: 8500
Consul is registering the service, but with critical state (failing)!
Would appreciate any help or guidance.
Thanks
The issue was related to the Consul health check default path which is set to the /health endpoint.
Thus after have enabled the spring-actuator in all clients applications (web-server and micro-service) this issue was resolved.
Or you may change the default Consul health-check endpoint in the bootstrap.yml file:
cloud:
consul:
discovery:
healthCheckPath: /test
NB. To enable spring-actuator in maven the following dependency was added to the pom.xml file:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
For more information see: http://cloud.spring.io/spring-cloud-consul/spring-cloud-consul.html
When deploying (pushing) to CF (CloudFoundry) the URI of the deployed application should be provided to Consul for service discovery process (CF provides application's URIs in vcap.application.uris environment variable), thus the following configuration should be added to the bootsrap.yml file:
---
spring:
profiles: cloud
cloud:
consul:
host: <consul host or ip>
port: 8500
discovery:
instanceId: ${spring.application.name}:${vcap.application.application_name}:${vcap.application.instance_id}
hostname: ${vcap.application.uris[0]}
port: 80
NB. instanceId is used by Consul to register the application
(microservice) instance.