Spring Cloud, Eureka and Zuul
Spring Cloud, Eureka and Zuul
Introduction
I was looking online for a simple end-to-end working example of Spring Cloud, Eureka and Zuul. I did not find a single post that gave a simple end-to-end implementation. I found so many posts that were really confusing and did not give a simple explanation of how these work together or how to set them up. Hence I came up with this post to explain how you can setup microservices using Spring Cloud, Eureka and Zuul.
Eureka in simple terms is a Discovery and Service registration server. Multiple microservices can register themselves to Eureka at runtime.
Zuul in simple terms is an Edge Server or a Gateway Service. It provides dynamic routing, monitoring, resiliency, security and a single entry point to multiple microservices. Zuul uses Ribbon to lookup available services in Eureka and routes the external request to an appropriate service instance.
Ribbon in simple terms provides Dynamic Routing and Load Balancing to multiple service instances. Ribbon uses the information available in Eureka to locate appropriate service instances. If Ribbon finds more than one service instance then it will apply load balancing to spread the requests over the available service instances.
Read more about Eureka, Zuul and Ribbon under the heading: Common Runtime Services & Libraries in the website: Netflix OSS.
In this tutorial, we will be addressing the following use cases using Eureka and Zuul:
Eureka, Zuul and Ribbon
Eureka, Zuul and Ribbon are part of Netflix Open Source Software (OSS).Eureka in simple terms is a Discovery and Service registration server. Multiple microservices can register themselves to Eureka at runtime.
Zuul in simple terms is an Edge Server or a Gateway Service. It provides dynamic routing, monitoring, resiliency, security and a single entry point to multiple microservices. Zuul uses Ribbon to lookup available services in Eureka and routes the external request to an appropriate service instance.
Ribbon in simple terms provides Dynamic Routing and Load Balancing to multiple service instances. Ribbon uses the information available in Eureka to locate appropriate service instances. If Ribbon finds more than one service instance then it will apply load balancing to spread the requests over the available service instances.
Read more about Eureka, Zuul and Ribbon under the heading: Common Runtime Services & Libraries in the website: Netflix OSS.
In this tutorial, we will be addressing the following use cases using Eureka and Zuul:
- Setting up a single gateway for multiple microservices so that they can be accessed from one entry point.
- Load balancing of multiple service instances.
Requirements to run the application
- Java
- Maven
- IDE of your choice
We will setup the multiple microservices with Eureka and Zuul in few simple steps.
Step 1: Setup Eureka Server
pom.xml
Spring cloud related dependencies need to be added to pom.xml, Here are the dependencies:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Brixton.SR5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
Here is the full pom.xml:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.aj.eurekaservice</groupId> <artifactId>EurekaService</artifactId> <version>1.0.0</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.4.0.RELEASE</version> </parent> <properties> <jdk.version>1.8</jdk.version> <java.version>1.8</java.version> <packaging>jar</packaging> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.3.2.RELEASE</version> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Brixton.SR5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.0</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
Main Class: EurekaServiceApplication
The annotation: @EnableEurekaServer needs to be added to the main class to enable the application as a Eureka Server or a Discovery and Service registration server.
Here is the main class, EurekaServiceApplication.java:
package com.aj.eurekaservice; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @SpringBootApplication @EnableEurekaServer public class EurekaServiceApplication { private static final Logger logger = LoggerFactory.getLogger(EurekaServiceApplication.class); public static void main(String[] args) throws Exception { SpringApplication.run(EurekaServiceApplication.class,args); logger.info("Eureka Service is up and running..."); } }
application.yml
The application port is setup and other configurations are added to setup the Eureka service. Here is the file, application.yml:
server: port: 8761 eureka: client: registerWithEureka: false fetchRegistry: false
Step 2: Setup Zuul Server
pom.xml
Here is the full pom.xml:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.aj.zuulservice</groupId> <artifactId>ZuulService</artifactId> <version>1.0.0</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.4.0.RELEASE</version> </parent> <properties> <jdk.version>1.8</jdk.version> <java.version>1.8</java.version> <packaging>jar</packaging> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zuul</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.3.2.RELEASE</version> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Brixton.SR5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.0</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
Main Class: ZuulServiceApplication
The annotations: @EnableDiscoveryClient and @EnableZuulProxy need to be added to the main class to enable the application as an Edge Server or a Gateway Service.
Here is the main class, ZuulServiceApplication.java:
package com.aj.zuulservice; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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; @SpringBootApplication @EnableZuulProxy @EnableDiscoveryClient public class ZuulServiceApplication { private static final Logger logger = LoggerFactory.getLogger(ZuulServiceApplication.class); public static void main(String[] args) throws Exception { SpringApplication.run(ZuulServiceApplication.class,args); logger.info("Zuul Service is up and running..."); } }
application.yml
The application port is setup as 8762.
The application is named as zuul-service.
The Eureka Service URL needs to be configured so that the Zuul Service can find it.
Boolean flags are set to true to register with Eureka and fetch its registry to discover all the microservices registered with Eureka.
Here is the file, application.yml:
The application is named as zuul-service.
The Eureka Service URL needs to be configured so that the Zuul Service can find it.
Boolean flags are set to true to register with Eureka and fetch its registry to discover all the microservices registered with Eureka.
Here is the file, application.yml:
spring: application: name: zuul-service server: port: 8762 eureka: instance: preferIpAddress: true serviceurl: defaultzone: http://localhost:8761/eureka/ client: registerWithEureka: true fetchRegistry: true
Step 3: Register the microservices
In this post I am using Spring Boot based microservices. I am assuming that you have a Basic Knowledge of Spring Boot and have a Basic Spring Boot Application running in your machine. If not, please check my blog post on Basic Spring Boot Application by going to the link: Spring Boot Tutorial
I will be using 2 Microservices:
- Student Service: A service to list Student details.
- Grading Service: A service to list grades got by the students.
To show you how load balancing is done by Zuul and Ribbon, I will run 2 instances of Student Service on two ports: 8081 and 8082. Only one instance of Grading Service will be setup.
Student Service
Other than the normal annotation in any Spring Boot Application: @SpringBootApplication, the main class: StudentServiceApplication has the annotation @EnableEurekaClient. Here is the main class:
package com.aj.studentservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @EnableEurekaClient @SpringBootApplication public class StudentServiceApplication { public static void main(String[] args) throws Exception { SpringApplication.run(StudentServiceApplication.class,args); } }
The application name, server port and Eureka configurations are mentioned in the application.yml. Here is the application.yml for Student Service running on port 8081:
I have created 2 GET APIs:
spring: application: name: student-service server: port: 8081 eureka: client: serviceUrl: defaultZone: ${EUREKA_URI:http://localhost:8761/eureka} instance: preferIpAddress: trueapplication.yml for Student Service running on port 8082 will be similar to the above file, except that the port will be 8082.
I have created 2 GET APIs:
- Ping API: GET API to ping the service. In the response, I am mentioning the port number on which the API is getting executed.
- Students API: GET API to get the list of Students.
package com.aj.studentservice.controller; import com.aj.studentservice.model.Student; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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.RestController; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @RestController @RequestMapping("/") public class StudentController { private static final Logger logger = LoggerFactory.getLogger(StudentController.class); @RequestMapping(value = "ping", method = RequestMethod.GET) public ResponseEntity<Map<String, String>> ping() { Map<String, String> response = new HashMap<>(); response.put("message", "pong: 8081"); return new ResponseEntity<>(response, HttpStatus.OK); } @RequestMapping(value = "students", method = RequestMethod.GET) public ResponseEntity<List<Student>> getStudents() { logger.info("In StudentController.getStudents(), using port 8081, fetching list of students"); List<Student> students = new ArrayList<>(); students.add(new Student("P001", "Jane", "Physics")); students.add(new Student("C001", "Jim", "Chemistry")); students.add(new Student("M001", "John", "Maths")); return new ResponseEntity<>(students, HttpStatus.OK); } }
Grading Service
Just like the Student Service, Grading Service has the annotations: @SpringBootApplication and @EnableEurekaClient in the main class. Here is the class: GradingServiceApplication.java:
package com.aj.gradingservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @EnableEurekaClient @SpringBootApplication public class GradingServiceApplication { public static void main(String[] args) throws Exception { SpringApplication.run(GradingServiceApplication.class,args); } }
We allow Spring Boot choose a random port for us because later we are accessing this service with its name and so the port is mentioned as 0 in the application.yml:
I have created 3 APIs:
spring: application: name: grading-service server: port: 0 eureka: client: serviceUrl: defaultZone: ${EUREKA_URI:http://localhost:8761/eureka} instance: preferIpAddress: trueController Class: GradeController.java:
I have created 3 APIs:
- Ping API: A GET API to ping the service.
- Grades API: A GET API to get the list of Student Grades.
- Grade API: A POST API to create a Student Grade.
package com.aj.gradingservice.controller; import com.aj.gradingservice.model.Grade; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @RestController @RequestMapping("/") public class GradeController { private static final Logger logger = LoggerFactory.getLogger(GradeController.class); @RequestMapping(value = "ping", method = RequestMethod.GET) public ResponseEntity<Map<String, String>> ping() { Map<String, String> response = new HashMap<>(); response.put("message", "pong"); return new ResponseEntity<>(response, HttpStatus.OK); } @RequestMapping(value = "grades", method = RequestMethod.GET) public ResponseEntity<List<Grade>> getGrades() { logger.info("In GradeController.getGrades(), fetching list of grades"); List<Grade> grades = new ArrayList<>(); grades.add(new Grade(1, "P001", "A+")); grades.add(new Grade(2, "C001", "A")); grades.add(new Grade(3, "M001", "B+")); return new ResponseEntity<>(grades, HttpStatus.OK); } @RequestMapping(value = "grade", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE,produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<Grade> createGrade(@RequestBody Grade grade) { logger.info("Request received is: " + grade ); Grade gradeCreated = new Grade(grade.getId(),grade.getStudentId(),grade.getGrade()); return new ResponseEntity<>(gradeCreated, HttpStatus.OK); } }
Project Setup
For Java Setup, please refer to: Java Setup
For Maven Setup, please refer to: Maven Setup
For Git and Project Setup, please refer to: Git and Project Setup
For Maven Setup, please refer to: Maven Setup
For Git and Project Setup, please refer to: Git and Project Setup
Run Application
1. Run Eureka Service. As per the configuration, this runs on port 8761. So once it has started, we can access the Eureka Dashboard using the URL: http://localhost:8761
2. Run Zuul Service
3. Run Student Service on port 8081 and 8082
4. Run Grading Service
5. Now check the Eureka Dashboard and you will see that the Zuul Service, 2 Instances of Student Service and Grading Service have registered with Eureka.
API calls and results
Zuul Service will act as the only single Gateway for all the microservices. Zuul Service is running on port 8762.
1. All the routes available for Zuul that are discovered by Eureka can be checked using the GET API:
http://localhost:8762/routes
1. All the routes available for Zuul that are discovered by Eureka can be checked using the GET API:
http://localhost:8762/routes
Response:
{ "/student-service/**": "student-service", "/grading-service/**": "grading-service" }
2. GET API to ping Student Service. Observe in the response that when we call this API multiple times we get some responses from port 8081 and some from 8082
http://localhost:8762/student-service/ping
3. GET API to get all Students
http://localhost:8762/student-service/students
4. GET API to ping Grading Service
http://localhost:8762/grading-service/ping
5. GET API to get all student grades from Grading Service
http://localhost:8762/grading-service/grades
6. POST API to create a Student Grade in the Grading Service
http://localhost:8762/grading-service/grade
JSON Request Body:
http://localhost:8762/student-service/ping
3. GET API to get all Students
http://localhost:8762/student-service/students
4. GET API to ping Grading Service
http://localhost:8762/grading-service/ping
5. GET API to get all student grades from Grading Service
http://localhost:8762/grading-service/grades
6. POST API to create a Student Grade in the Grading Service
http://localhost:8762/grading-service/grade
JSON Request Body:
{ "id": 1, "studentId": "P001", "grade": "A+" }
Conclusion and GitHub link:
In this post I have given a comprehensive overview of setting up microservices using Spring Cloud, Eureka and Zuul.
The code used in this post is available on GitHub:
The code used in this post is available on GitHub:
- Eureka Service
- Zuul Service
- Grading Service
- Student Service: Running on Port 8081
- Student Service1: Running on Port 8082
Learn the most popular and trending technologies like Machine Learning, Angular 5, Internet of Things (IoT), Akka HTTP, Play Framework, Dropwizard, Spring Cloud, Docker, Elastic Stack, Spring Boot and Flask in simple steps by reading my most popular blog posts at Software Developer Central.
If you like my post, please feel free to share it using the share button just below this paragraph or next to the heading of the post. You can also tweet with #SoftwareDeveloperCentral on Twitter. To get a notification on my latest posts or to keep the conversation going, you can follow me on Twitter. Please leave a note below if you have any questions or comments.
If you like my post, please feel free to share it using the share button just below this paragraph or next to the heading of the post. You can also tweet with #SoftwareDeveloperCentral on Twitter. To get a notification on my latest posts or to keep the conversation going, you can follow me on Twitter. Please leave a note below if you have any questions or comments.
Comments
Post a Comment