Load balancing

Load balancing is required to service requests in a manner that maximizes speed, capacity utilization, and it makes sure that no server is overloaded with requests. The load balancer also redirects requests to the remaining host servers if a server goes down. In microservice architecture, a microservice can serve internal or external requests. Based on this, we can have two types of load balancing – client-side and server-side load balancing.

Client-side load balancing

Microservices need interprocess communication so that services can communicate with each other. Spring Cloud uses Netflix Ribbon, a client-side load balancer that plays this critical role and can handle both HTTP and TCP. Ribbon is cloud-enabled and provides built-in failure resiliency. Ribbon also allows you to use multiple and pluggable load balancing rules. It integrates clients with load balancers.

In the last chapter, we added Eureka Server. Ribbon is integrated with Eureka Server in Spring Cloud by default. This integration provides the following features:

  • You don't need to hardcode remote server URLs for discovery when Eureka Server is used. This is a prominent advantage, although you can still use the configured server list (listOfServers) in application.yml if required.
  • The server list gets populated from Eureka Server. Eureka Server overrides ribbonServerList with DiscoveryEnabledNIWSServerList.
  • The request to find out whether the server is up is delegated to Eureka. The DiscoveryEnabledNIWSServerList interface is used in place of Ribbon's IPing.

There are different clients available in Spring Cloud that use Ribbon, such as RestTemplate or FeignClient. These clients allow microservices to communicate with each other. Clients use instance IDs in place of hostnames and ports for making an HTTP call to service instances when Eureka Server is used. The client passes the service ID to Ribbon, Ribbon then uses the load balancer to pick the instance from the Eureka Server.

If there are multiple instances of services available in Eureka, as shown in the following screenshot, Ribbon picks only one for the request, based on load balancing algorithms:

Client-side load balancing

Multiple service registration – Restaurant service

We can use DiscoveryClient to find all the available service instances in Eureka Server, as shown in the following code. Method getLocalServiceInstance() of class DiscoveryClientSample returns the all local service instances available in Eureka Server.

DiscoveryClient sample:

@Component
class DiscoveryClientSample implements CommandLineRunner {

    @Autowired
    private DiscoveryClient;

    @Override
    public void run(String... strings) throws Exception {
   //print the Discovery Client Description
        System.out.println(discoveryClient.description());
   // Get restaurant-service instances and prints its info
        discoveryClient.getInstances("restaurant-service").forEach((ServiceInstance serviceInstance) -> {
            System.out.println(new StringBuilder("Instance --> ").append(serviceInstance.getServiceId())
                    .append("
Server: ").append(serviceInstance.getHost()).append(":").append(serviceInstance.getPort())
                    .append("
URI: ").append(serviceInstance.getUri()).append("


"));
        });
    }
}

When executed, this code prints the following information. It shows two instances of the Restaurant service:

Spring Cloud Eureka Discovery Client
Instance: RESTAURANT-SERVICE
Server: SOUSHARM-IN:3402
URI: http://SOUSHARM-IN:3402
Instance --> RESTAURANT-SERVICE
Server: SOUSHARM-IN:3368
URI: http://SOUSHARM-IN:3368

The following samples showcase how these clients can be used. You can see that in both clients, the service name restaurant-service is used in place of a service hostname and port. These clients call /v1/restaurants to get a list of restaurants containing the name given in the name query parameter:

Rest Template sample:

@Override
public void run(String... strings) throws Exception {
ResponseEntity<Collection<Restaurant>> exchange
= this.restTemplate.exchange(
"http://restaurant-service/v1/restaurants?name=o",
                   HttpMethod.GET,
                   null,
                   new ParameterizedTypeReference<Collection<Restaurant>>() {
                  },
                   ( "restaurants");
exchange.getBody().forEach((Restaurant restaurant) -> {
System.out.println(new StringBuilder("


[ ").append(restaurant.getId()).append(" ").append(restaurant.getName()).append("]"));
});
}

FeignClient sample:

@Component
class FeignSample implements CommandLineRunner {

    @Autowired
    private RestaurantClient restaurantClient;

    @Override
    public void run(String... strings) throws Exception {
        this.restaurantClient.getRestaurants("o").forEach((Restaurant restaurant) -> {
            System.out.println(restaurant);
        });
    }
}

@FeignClient("restaurant-service")
interface RestaurantClient {

    @RequestMapping(method = RequestMethod.GET, value = "/v1/restaurants")
    Collection<Restaurant> getRestaurants(@RequestParam("name") String name);
}

All preceding examples will print the following output:

[ 1 Big-O Restaurant]
[ 2 O Restaurant]

Server-side load balancing

After client-side load balancing, it is important for us to define server-side load balancing. In addition, from the microservice architecture's point of view, it is important to define the routing mechanism for our OTRS app. For example, / may be mapped to our UI application, /restaurantapi is mapped to restaurant service, and /userapi is mapped to user service.

We'll use the Netflix Zuul Server as our Edge Server. Zuul is a JVM-based router and server-side load balancer. Zuul supports any JVM language for writing rules and filters and having the in-built support for Java and Groovy.

The external world (the UI and other clients) calls the Edge server, which uses the routes defined in application.yml to call internal services and provide the response. Your guess is right if you think it acts as a proxy server, carries gateway responsibility for internal networks, and calls internal services for defined and configured routes.

Normally, it is recommended to have a single Edge Server for all requests. However, few companies use a single Edge Server per client to scale. For example, Netflix uses a dedicated Edge Server for each device type.

An Edge Server will also be used in the next chapter, when we configure and implement microservice security.

Configuring and using the Edge Server is pretty simple in Spring Cloud. You need to use the following steps:

  1. Define the Zuul Server dependency in pom.xml:
    <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-zuul</artifactId>
    </dependency>
  2. Use the @EnableZuulProxy annotation in your application class. It also internally uses @EnableDiscoveryClient: therefore it is also registered to Eureka Server automatically. You can find the registered Zuul Server in the last figure: Multiple service registration – Restaurant service".
  3. Update the Zuul configuration in application.yml, as the following shows:
    • zuul:ignoredServices: This skips the automatic addition of services. We can define service ID patterns here. * denotes that we are ignoring all services. In the following sample, all services are ignored except restaurant-service.
    • Zuul.routes: This contains the path attribute that defines the URI's pattern. Here, /restaurantapi is mapped to Restaurant Service using serviceId. serviceId represents the service in Eureka Server. You can use a URL in place of a service, if Eureka Server is not used. We have also used the stripPrefix attribute to strip the prefix (/restaurantapi), and the resultant /restaurantapi/v1/restaurants/1 call converts to /v1/restaurants/1 while calling the service:
      application.yml
      info:
          component: Zuul Server
      # Spring properties
      spring:
        application:
           name: zuul-server  # Service registers under this name
      
      endpoints:
          restart:
              enabled: true
          shutdown:
              enabled: true
          health:
              sensitive: false
      
      zuul:
          ignoredServices: "*"
          routes:
              restaurantapi:
                  path: / restaurantapi/**
                  serviceId: restaurant-service
                  stripPrefix: true
      
      server:
          port: 8765
      
      # Discovery Server Access
      eureka:
        instance:
          leaseRenewalIntervalInSeconds: 3
          metadataMap:
            instanceId: ${vcap.application.instance_id:${spring.application.name}:${spring.application.instance_id:${random.value}}}
          serviceUrl:
            defaultZone: http://localhost:8761/eureka/
          fetchRegistry: false

Let's see a working Edge Server. First, we'll call the restaurant service deployed on port 3402, shown as follows:

Server-side load balancing

Direct Restaurant service call

Then, we'll call the same service using the Edge Server that is deployed on port 8765. You can see that the /restaurantapi prefix is used for calling /v1/restaurants?name=o, and it gives the same result:

Server-side load balancing

Restaurant Service call using Edge Server

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset