本文共 10506 字,大约阅读时间需要 35 分钟。
This tutorial will guide you through the process to setup zone affinity in Spring Cloud Netflix Eureka.
You will build three applications:
It doesn’t matter which kind of architectural style the application is using, it’s a common use case to have the same application deployed in different regions/data centers and use some technique to keep the requests within the same zone.
In microservices architecture, there’s also a need to achieve the same thing but the technique needs to be applied using the .
makes it easy to implement the necessary .
In this guide, we’ll create three applications, and if you’re familiar with spring-cloud, it will be an easy job, all the created applications are nothing more than a simple runnable spring-boot jar.
The main part here is the configuration files that will be shown further on.
Add the following dependencies for all the applications. If there’s any diff for any specific application, it will be mentioned in each specific thread.
org.springframework.boot spring-boot-starter-parent 1.5.9.RELEASE org.springframework.cloud spring-cloud-dependencies Edgware.SR2 pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-eureka org.projectlombok lombok org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-maven-plugin
The first application we’ll create will be the API Gateway using . First, add the following dependency in the pom.xml.
org.springframework.cloud spring-cloud-starter-zuul
Now just create the main SpringApplication class adding @EnableZuulProxy.
import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;import org.springframework.cloud.netflix.zuul.EnableZuulProxy;@EnableZuulProxy@EnableDiscoveryClient@SpringBootApplicationpublic class GatewayApplication { public static void main(String... args) { SpringApplication.run(GatewayApplication.class, args); }}
The second application we’ll create will be the Service Registry using . First, add the following dependency in the pom.xml.
org.springframework.cloud spring-cloud-starter-eureka-server
Now just create the main SpringApplication class adding @EnableEurekaServer.
import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;@EnableEurekaServer@SpringBootApplicationpublic class ServiceDiscoveryApplication { public static void main(String... args) { SpringApplication.run(ServiceDiscoveryApplication.class, args); }}
The third application contains nothing more than a REST endpoint to make sure that each call from each region will remain in the requested region. For this application, there’s nothing to add to the base pom.xml.
To make things easier, I’m creating the main class with a nested RestController, the following controller returns which zone is this application deployed.
import org.springframework.beans.factory.annotation.Value;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8_VALUE;@EnableDiscoveryClient@SpringBootApplicationpublic class SimpleService { public static void main(String... args) { SpringApplication.run(SimpleService.class, args); } @RestController class SimpleController { @Value("${eureka.instance.metadataMap.zone}") private String zone; @GetMapping(value = "/zone", produces = APPLICATION_JSON_UTF8_VALUE) public String zone() { return "{\"zone\"=\"" + zone + "\"}"; } }}
As it was previously mentioned, each application needs to run twice in order to simulate two distinct regions, to make it easier we’ll create the configuration based on , for each application create the following three files:
src/main/resources/application.ymlsrc/main/resources/application-zone1.ymlsrc/main/resources/application-zone2.yml
The suffix in the filename will be used as the profile name.
# src/main/resources/application.ymleureka: client: register-with-eureka: false fetch-registry: false region: region-1 service-url: zone1: http://localhost:8761/eureka/ zone2: http://127.0.0.1:8762/eureka/ availability-zones: region-1: zone1,zone2spring.profiles.active: zone1
All the following properties are in eureka.client namespace.
Property | Description |
---|---|
region | A String containing a name for the region where the application will be deployed |
service-url | A Map containing the list of available zones for the given region |
availability-zones | A Map containing a comma-separated list of zones for the given region |
The properties register-with-eureka and fetch-registry are disabling the Service Registry to be added in the applications list but it’s not that important for this setup.
# src/main/resources/application-zone1.ymlserver.port: 8761eureka: instance: hostname: localhost metadataMap.zone: zone1
# src/main/resources/application-zone2.ymlserver.port: 8762eureka: instance: hostname: 127.0.0.1 metadataMap.zone: zone2
For the -zone1 and -zone2 profiles the only difference are the server.port, the actual zone configured in eureka.metadataMap.zone, and in this case the hostname, each Eureka Server needs to run in a different hostname; as I’m running both in the same machine I’m naming it as 127.0.01 and localhost.
It’s not necessary to add the hostname in case you are running on different machines.
# src/main/resources/application.ymleureka: client: prefer-same-zone-eureka: true region: region-1 service-url: zone1: http://localhost:8761/eureka/ zone2: http://127.0.0.1:8762/eureka/ availability-zones: region-1: zone1,zone2spring: profiles.active: zone1 application.name: gatewaymanagement.security.enabled: false
The main difference here is the property eureka.client.prefer-same-zone-eureka, it is telling to the application that whenever it needs to make a call to another EurekaClient it will call it using the same zone where the caller is deployed. In case there’s no available client in the same zone, it will call from another zone in which it’s available.
# src/main/resources/application-zone1.ymlserver.port: 8080eureka: instance: metadataMap.zone: zone1
# src/main/resources/application-zone2.ymlserver.port: 8081eureka: instance: metadataMap.zone: zone2
As before, this per profile configuration only changes the availability zone and the running port.
The configuration for the service itself contains the same configuration as the Gateway.
# src/main/resources/application.ymleureka: client: prefer-same-zone-eureka: true region: region-1 service-url: zone1: http://localhost:8761/eureka/ zone2: http://127.0.0.1:8762/eureka/ availability-zones: region-1: zone1,zone2spring: profiles.active: zone1 application.name: simple-service
# src/main/resources/application-zone1.ymlserver.port: 8181eureka: instance: metadataMap.zone: zone1
# src/main/resources/application-zone2.ymlserver.port: 8182eureka: instance: metadataMap.zone: zone2
It’s time to build the applications; if you are building the application using maven (like I did), just build them executing:
$ mvn clean package
Right after that just run each application adding the specific profile to the command line, e.g:
$ java -jar target/*.jar --spring.profiles.active=zone1
Remember that you need to run each application twice, once each profile: zone1 and zone2.
To validate if the requests are respecting each zone we need to make a request to the simple-service through each gateway.
$ curl http://localhost:8080/simple-service/zone{ "zone"="zone1"}$ curl http://localhost:8081/simple-service/zone{ "zone"="zone2"}
The difference between each zone here is the server.port.
To validate the failover between zones you just need to stop one of the instances and make a request to the opposite zone, e.g:
$ curl http://localhost:8081/simple-service/zone
The expected result now will be a JSON containing {“zone”=“zone1”}.
Once the simple-service for zone1 is up, running and registered in Eureka Server the same curl has to respond {“zone”=“zone2”} again.It takes a while to the simple-service be available in the opposite zone, be patient and have fun!
Congratulations! You just created and configured an API Gateway, Service Registry and a Simple REST Service that respects zone affinity bringing to your microservices more resilience and HA.
The code used for this tutorial can be found on
转载地址:http://pggmb.baihongyu.com/