Zuul
Zuul
Zuul is an edge service that acts as an API gateway, handling routing, monitoring, and security in a microservice architecture.
- Dynamic routing of incoming requests
- Acts as an API Gateway
- Provides load balancing and rate limiting
- Enhances security through authentication and authorization

Example zuul +Eureka

Route configuration examples
# Route requests with /service1/** to http://localhost:8081/
zuul.routes.service1.path=/service1/**
zuul.routes.service1.url=http://localhost:8081
# Route requests with /service2/** to http://localhost:8082/
zuul.routes.service2.path=/service2/**
zuul.routes.service2.url=http://localhost:8082
# Dynamic routing based on Eureka (if using Eureka)
# Enables service discovery
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka
zuul.routes.service3.path=/service3/**
zuul.routes.service3.serviceId=SERVICE3 # Eureka service ID
Eureka Discovery server
package com.emsi.discoveryserver1;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class Discoveryserver1Application {
public static void main(String[] args) {
SpringApplication.run(Discoveryserver1Application.class, args);
}
}
Eureka Discovery server:application.properties
#Port for Registry service
server.port=8761
#Service should not register with itself
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
#Managing the logging
logging.level.com.netflix.eureka=OFF
logging.level.com.netflix.discovery=OFF
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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.emsi</groupId>
<artifactId>discoveryserver1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>discoveryserver1</name>
<description>Demo project for Spring Boot</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
<spring-cloud.version>2023.0.3</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Client :microserviceA :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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.emsi</groupId>
<artifactId>microserviceclient1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>microserviceclient1</name>
<description>Demo project for Spring Boot</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
<spring-cloud.version>2023.0.3</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Main
package com.emsi.microserviceclient1;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Microserviceclient1Application {
public static void main(String[] args) {
SpringApplication.run(Microserviceclient1Application.class, args);
}
}
application.properties
# Server Config
server.port=8081
# Spring Application Name
spring.application.name=microserviceclient1
# Eureka Client Config
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
Test Rest
package com.emsi.microserviceclient1.controllers;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class RestC {
@GetMapping("/test")
public String hi()
{
return "hi";
}
}
Zuul gateway
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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.9.RELEASE</version> <!-- Spring Boot 2.1.x -->
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.emsi</groupId>
<artifactId>zuulgateway1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>zuulgateway1</name>
<description>Zuul Gateway for Spring Boot</description>
<properties>
<java.version>11</java.version> <!-- Use Java 11 -->
<spring-cloud.version>Greenwich.SR6</spring-cloud.version> <!-- Spring Cloud Greenwich for Zuul -->
</properties>
<dependencies>
<!-- Spring Boot Web Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Zuul Starter -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<!-- Eureka Client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- Spring Boot DevTools (Optional) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- Spring Boot Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Application.properties
# Server Port
server.port=8086
# Application Name
spring.application.name=zuulgateway1
# Eureka Configuration
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
# Zuul Routes
zuul.routes.hello.path=/hello/**
zuul.routes.hello.serviceId=microserviceclient1
Main
package com.emsi.zuulgateway1;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@SpringBootApplication
@EnableZuulProxy
public class Zuulgateway1Application {
public static void main(String[] args) {
SpringApplication.run(Zuulgateway1Application.class, args);
}
}
Main
http://localhost:8086/hello/test
Filter
package com.emsi.zuulgateway1.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.List;
@Component
public class CustomZuulFilter extends ZuulFilter {
@Override
public String filterType() {
return "pre"; // This filter will execute before routing the request
}
@Override
public int filterOrder() {
return 1; // Lower values are executed first
}
public boolean shouldFilter() {
return true; // This filter will apply for all requests
}
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
// Example 1: Log Request Method and URL
System.out.println("Request Method: " + request.getMethod());
System.out.println("Request URL: " + request.getRequestURL().toString());
// Example 2: Authentication Check
String authToken = request.getHeader("Authorization");
if (authToken == null || !isValidToken(authToken)) {
ctx.setResponseStatusCode(401); // Unauthorized
ctx.setResponseBody("Unauthorized access - token is missing or invalid.");
ctx.setSendZuulResponse(false); // Prevent routing the request
return null; // Early exit from filter
}
// Example 3: Modify Request Headers
ctx.addZuulRequestHeader("X-Custom-Header", "CustomValue");
// Example 4: Rate Limiting (Basic Example)
Integer requestCount = (Integer) ctx.get("requestCount");
if (requestCount == null) {
requestCount = 0;
}
requestCount++;
ctx.set("requestCount", requestCount);
// Assuming a simple rate limit of 100 requests
if (requestCount > 100) {
ctx.setResponseStatusCode(429); // Too Many Requests
ctx.setResponseBody("Rate limit exceeded - please try again later.");
ctx.setSendZuulResponse(false); // Prevent routing the request
return null; // Early exit from filter
}
// Example 5: Add Request Time to Header
long startTime = System.currentTimeMillis();
ctx.addZuulRequestHeader("X-Request-Time", String.valueOf(startTime));
// Example 6: Validate Request Parameters
String requiredParam = request.getParameter("requiredParam");
if (requiredParam == null || requiredParam.isEmpty()) {
ctx.setResponseStatusCode(400); // Bad Request
ctx.setResponseBody("Missing required parameter: requiredParam");
ctx.setSendZuulResponse(false); // Prevent routing the request
return null; // Early exit from filter
}
// Example 7: IP Whitelisting
List allowedIPs = Arrays.asList("192.168.1.100", "192.168.1.101"); // Example IPs
String clientIP = request.getRemoteAddr();
if (!allowedIPs.contains(clientIP)) {
ctx.setResponseStatusCode(403); // Forbidden
ctx.setResponseBody("Forbidden access - your IP is not allowed.");
ctx.setSendZuulResponse(false); // Prevent routing the request
return null; // Early exit from filter
}
// Example 8: Service Discovery and Redirection (Custom Logic)
String serviceId = "microserviceclient1";
if (serviceId.equals(request.getParameter("serviceId"))) {
ctx.set("serviceId", serviceId); // Optionally set serviceId in context
}
// Example 9: Modify Request URL Path
String newPath = "/modified-path" + request.getRequestURI();
ctx.set("requestURI", newPath); // Modify the request URI
ctx.set("requestPath", newPath); // Optionally set modified request path in context
return null; // Returning null means no additional processing
}
// Method to validate the token (placeholder logic)
private boolean isValidToken(String token) {
return "valid-token".equals(token); // Replace with actual validation logic
}
}