Skip to content

Spring Cloud Zuul

Furkan Kürşat Danışmaz edited this page Oct 9, 2018 · 11 revisions

Source Folder: netflix-zuul

Tech Stack:

  • spring-boot
  • spring-mvc
  • netflix-zuul
  • netflix-eureka
  • log4j2 (slf4j impl)
  • project lombok

This is a ready-to-use Maven Java project template with the libraries listed above.

What is Zuul and Why Do We Need It?

In a microservices architecture it is typical to have many system components independent of each other. This brings problems like:

  • UI developers need to know the addresses of each microservice they need to consume
  • CORS - Cross Origin Resource Sharing
  • Multiple Authentication

Zuul is used as an API gateway which receives all requests from the users and calls corresponding underlying microservices based on the configurations. With this architecture:

  • we won't have CORS issues
  • we will have centralized security implementation which will be applied to all incoming requests.
  • we can apply filters to all incoming requests
  • when needed we will be able to change the security and filter implementations easily (since they are centralized)
  • API gateway will be another microservice meaning that it will be scalable

A Sample Solution with Eureka and Zuul

In this solution I will create:

  • eureka-server : The service registry
  • customer-service: A microservice that gives information about customers.
  • zuul-proxy: The API gateway

In short, the clients will make requests to zuul proxy. The zuul proxy will redirect the request to corresponding microservice and return the result.

In this example:

  • customer-service is implemented with eureka-client (it is registered to the service registry on startup)
  • zuul-proxy is integrated with eureka so that it can find underlying services with their names (instead of direct URLs)

The Eureka Server

dependencies:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

configuration

server:
  port: 8761

eureka:
  client:
    register-with-eureka: false
    fetch-registry: false

The entry point:

@EnableEurekaServer
@SpringBootApplication
public class App {

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }

}

The Customer Service

The customer service is a simple Spring Boot application. The only difference is that the eureka-client is enabled. It registers itself to the service registry on startup.

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>

configuration:

server:
  port: 8090

spring:
  application:
    name: customer-service

eureka:
  client:
    serviceUrl:
      defaultZone: ${EUREKA_URI:http://localhost:8761/eureka}
  instance:
    preferIpAddress: true

The entry point:

@EnableDiscoveryClient
@SpringBootApplication
public class App {

    public static void main(String[] args) {
        SpringApplication.run(App.class);
    }
}

The customer model:

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Customer {

    private String id;
    private String name;
    private String company;
    private LocalDate birthdate;
}

The customer list resource:

@RestController
@RequestMapping("/api/customers")
public class CustomerController {

    @GetMapping
    public List<Customer> customerList() {
        Customer c1 = Customer.builder()
                .id("1").name("john doe").company("acme").birthdate(LocalDate.now().minusYears(28)).build();
        Customer c2 = Customer.builder()
                .id("2").name("jane doe").company("acme").birthdate(LocalDate.now().minusYears(24)).build();
        List<Customer> result = new ArrayList<>();
        result.add(c1);
        result.add(c2);
        return result;
    }
}

You can run and test your customer service right now. You should be able to access the customer list resource with the http://localhost:8090/api/customers URL

Now, it is time to enable the Zuul Proxy

The Zuul Proxy

dependencies:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>

configuration:

server:
  port: 8080

spring:
  application:
    name: zuul-proxy

eureka:
  client:
    serviceUrl:
      defaultZone: ${EUREKA_URI:http://localhost:8761/eureka}
  instance:
    preferIpAddress: true

zuul:
  routes:
    customer-service:
      serviceId: customer-service
  host:
    socket-timeout-millis: 30000

The entry point:

@EnableZuulProxy
@EnableDiscoveryClient
@SpringBootApplication
public class App {

    public static void main(String[] args) {
        SpringApplication.run(App.class);
    }
}

Now we are ready. Make sure that all three applications are running and then you can test your zuul proxy.

  • Send request to the zuul proxy: http://localhost:8080/customer-service/api/customers.
  • The zuul proxy will redirect the request to customer-service/api/customers and with eureka service registry, it can resolve the exact path of the customer-service
  • You should be able to get the customer list response:
[
    {
        "id": "1",
        "name": "john doe",
        "company": "acme",
        "birthdate": "1990-10-08"
    },
    {
        "id": "2",
        "name": "jane doe",
        "company": "acme",
        "birthdate": "1994-10-08"
    }
]

Load Balancing

Zuul uses Netflix Ribbon to discover all the instances of a service from the Eureka Service Discovery Server. It automatically finds the physical locations of each service instance and redirects the requests to the actual services holding the resources to be accessed.

You can test it for yourself. Just add some logs into the customerList handler method and start multiple instances of the customer service. Call the http://localhost:8080/customer-service/api/customers URL multiple times and see the logs of each instance to understand which one is called on each request.

From the Netflix Tech Blog

Because Zuul can add, change, and compile filters at run-time, system behavior can be quickly altered. We add new routes, assign authorization access rules, and categorize routes all by adding or modifying filters. And when unexpected conditions arise, Zuul has the ability to quickly intercept requests so we can explore, workaround, or fix the problem.

The dynamic filtering capability of Zuul allows us to find and isolate problems that would normally be difficult to locate among our large volume of requests. A filter can be written to route a specific customer or device to a separate API cluster for debugging.

Netflix Tech Blog - Zuul

Zuul Filters

Sample scenarios where Zuul filters can be used:

  • logging
  • routing
  • ddos prevention
  • reverse proxying

Clone this wiki locally