Zuul and Eureka – Load Balancing Example

In this tutorial you will learn how to use Zuul API Gateway to enable the load balancing of your RESTful Web Services registered with Eureka Discovery Service.

Because Zuul API Gateway internally uses Ribbon Load Balancer there is almost nothing you need to do to have this load balancer work for you.

To be able to follow this tutorial and see how Zuul’s internal load balancer(Ribbon) works, you will need to have the following 3 steps done. Please follow the following three tutorial to have the basic setup done.

  1. Start up Eureka Discovery Service,
  2. Register Microservices with Eureka Discovery Service,
  3. Start up Zuul API Gateway.

Make sure you have done the above 3 steps and you can send HTTP Requests to your Microservice via the Zuul API Gateway.

Update Microservice to See Port Number

Load balancer, balances HTTP requests between multiple instances of your Web Service running on different port numbers. Later in this tutorial you will learn how to start up a new instance of your Web Service on a different port number.

For you to be able to see that the load balancer works and that HTTP requests are being load-balanced between multiple instances of your Web Service, let’s update our Web Service so that we can see which instance(on which port number) is handling the request.

I will update the Rest Controller of my application to print our the port number it is running on.

package com.appsdeveloperblog.photoapp.api.users.io.controllers;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;

@RestController
@RequestMapping("/users")
public class UsersController {
    
    @Autowired
    private Environment env;
    
    @GetMapping("/status/check")
    public String status() {
        return "Working on port " + env.getProperty("local.server.port");
    }
}

When HTTP Get request is sent to a /users/status/check I should see “Working on port 8099” printed in the browser. Let’s try this out.

Starting up Multiple Instances of a Microservice

To see how load balancer works we need to have at least 2 instances of the same Microservice running. Each instance should run on its own port number. If you have one instance of a Microservice running, then let’s start up one more instance.

When starting up a new instance of a Microservice you need to make sure that it runs on a different port number. You can provide port number on which you want a new instance of Microservice to run at a start up time the following way:

mvn spring-boot:run -Dspring-boot.run.arguments=--server.port=8099

Once you start a new instance of a Microservice, refresh the Eureka Discovery Server page and you should see that the AMIs counter for a registered application has increased. On the picture below you see that my USERS-WS Microservice has now 2 AMIs running. If I start one more instance of USERS-WS Microservice on a different port, the AMIs counter will increase again.

Trying How Load Balancer Works

If you started more than one Microservice and on the Eureka Discovery page you see that the counter of running AMIs is more than 1, you should be ready to send HTTP request to your Microservice and see the port number printed. Make sure you send HTTP Requests via Zuul API Gateway. Send multiple(5 or 6) HTTP requests in a row and you should see a different port number used almost every time. If a different port number is printed, then this means that HTTP requests are being load-balanced between different instances of your Microservice.

In my case the URL of a USERS-WS Web Service via the Zuul API Gateway looks like this:

http://localhost:8011/users-ws/users/status/check

If I open this URL in the browser window I will see the following output.

If I refresh the page, Zuul API Gateway will route this HTTP Request to a different port number and I will see a different output.

Multiple Instances and an Instance Id

If you used server.port=0 to assign a random port number to a Microservice then you might have also noticed that when starting multiple instances of the same Microservice, all HTTP requests are routed to the same port number which is 0. This is because the random port number is being assigned to a Microservice already after it has registered with Eureka. Because of it, every instance of a Microservice you start with the same name and same server.port=0 , simply overrides a previous registration. To resolve this issue, add the following property to your application.properties file, so that each instance of a Microservice that starts up has a unique instance ID.

eureka.instance.instance-id=${spring.application.name}:${spring.application.instance_id:${random.value}}

Below is a complete application.properties file which enables a random port number assignment and also a unique instance id value being assigned to each of the instances of a Microservice.

Complete Application Properties File

server.port=${PORT:0}
spring.application.name=users-ws
eureka.client.serviceUrl.defaultZone = http://localhost:8010/eureka
spring.devtools.restart.enabled = true
eureka.instance.instance-id=${spring.application.name}:${spring.application.instance_id:${random.value}}

I hope this tutorial was helpful for you. If you are interested to learn more about building RESTful Web Services with Spring Cloud, have a look at the list of video courses below.