Load Balancing?

Eventually you'll reach a point where you need to run multiple instances of an application or a service for high availability or to manage increased load. That's what load balancers are for. There's generally two different types:

  1. Server-side load balancers
  2. Client-side load balancers

Server-side Load Balancing

What many people would call a "load balancer" is actually a server-side load balancer. It can be implemented in hardware or software. The traffic is sent to a dedicated service that decides where to send the traffic, using an algorithm like round-robin, to one of the many instances. Examples of server-side load balancers are hardware-based ones like the devices from F5 Networks or software-based ones like AWS's ELBs.

Server-side load balancing

Client-side Load Balancing

Many are familiar with what server-side load balancing is but the lesser known, client-side load balancing, has begun to climb in popularity due to SOA and microservices. Instead of relying on another service to distribute the load, the client itself, is responsible for deciding where to send the traffic also using an algorithm like round-robin. It can either discover the instances, via service discovery, or can be configured with a predefined list. Netflix Ribbon is an example of a client-side load balancer.

Client-side load balancing

Netflix Ribbon with Spring Cloud

The Spring Cloud Netflix project marries the best of Netflix open source projects and Spring Boot, making it super easy to add Ribbon to your project. To get started, make sure you have the following Maven (or Gradle) dependencies:

  • spring-cloud-starter-ribbon

With Service Discovery

By far, the easiest way to use Ribbon is via Service Discovery. Assuming you've already got your service discovery server setup (easy to do with the @EurekaServer annotation), you really only need two annotations: @EnableDiscoveryClient and @LoadBalanced. 

x
 
1
@SpringBootApplication
2
@EnableDiscoveryClient
3
public class RibbonExampleApplication {
4
5
    @Inject
6
    private RestTemplate restTemplate;
7
8
    public static void main(String[] args) {
9
        SpringApplication.run(RibbonExampleApplication.class, args);
10
    }
11
  
12
    @Bean
13
    @LoadBalanced
14
    public RestTemplate restTemplate() {
15
        return new RestTemplate();
16
    }
17
}

The first annotation, @EnableDiscoveryClient makes it so your application can discover other services via service discovery. The second annotation, @LoadBalanced, which you apply to a RestTemplate you create, enables Ribbon functionality on your RestTemplate. What does that mean? Well, it allows you to use the service name as the address of your service and Ribbon will discover the addresses of all the instances of that service (via Service Discovery).

For example, suppose you had a service named: catalog-service. You can now use the RestTemplate like this:

1
 
1
restTemplate.getForEntity("http://catalog-service/api/v1/123", String.class);

Notice how there isn't a domain name or an ip address that you're accessing. You're instead using the service name when calling the service!

Without Service Discovery

If you're not using Service Discovery, you'll still use the @LoadBalanced annotation on your RestTemplate but there's a little more setup involved. 

On your main application class, you define an @RibbonClient annotation and set the name property. Try to make the name property something useful as you'll use this value to configure the Ribbon client in your application.properties.

Here's an example of a single @RibbonClient named "myservice".

17
 
1
@SpringBootApplication
2
@RibbonClient(name = "myservice") // <--- newly added annotation
3
public class RibbonExampleApplication {
4
  
5
    @Inject
6
    private RestTemplate restTemplate;
7
8
    public static void main(String[] args) {
9
        SpringApplication.run(RibbonExampleApplication.class, args);
10
    }
11
  
12
    @Bean
13
    @LoadBalanced // <--- still have @LoadBalanced
14
    public RestTemplate restTemplate() {
15
        return new RestTemplate();
16
    }
17
}

Now in your application.properties, you can configure specific settings for your Ribbon client by prefixing the properties with the client name (<client-name>.some.property=foo). There's two properties you'll need to set. One to disable Service Discovery. And another to configure the list of servers Ribbon will use to access your service.

Using our "myservice" client and 2 services that are located at localhost:9000 and localhost:9100, you'd configure Ribbon like this:

2
 
1
myservice.ribbon.eureka.enabled=false
2
myservice.ribbon.listOfServers=localhost:9000,localhost:9100

And then, instead of using the name of the service, you'll use the name of your Ribbon client when calling a service with your RestTemplate

1
 
1
restTemplate.getForEntity("http://myservice/api/v1/123", String.class);

And that's all there is to it.