Implementing Client-Side Load Balancing with Spring Cloud Ribbon

To facilitate communication between microservices, such as an Order Management system retrieving details from an Inventory system, client-side load balancing is essential. Spring Cloud Ribbon provides this capability seamlessly when integrated with Spring's RestTemplate.

Project Configuration

Begin by initializing the order management module. The critical step involves configuring the HTTP client bean. By applying the @LoadBalanced annotation, the RestTemplate becomes aware of the service discovery mechanism, allowing it to resolve service IDs to actual network locations.

package com.example.orders;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class OrderManagementApplication {

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

    @Bean
    @LoadBalanced
    public RestTemplate httpClient() {
        return new RestTemplate();
    }
}

API Controller Layer

Expoce an endpoint to trigger the order creation process. This controller accepts account and item identifiers, delegating the business logic to the service layer.

package com.example.orders.controller;

import com.example.orders.service.OrderProcessingService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/orders/v1")
public class OrderCreationController {

    @Autowired
    private OrderProcessingService processingService;

    @PostMapping("/create")
    public Object createOrder(
            @RequestParam("account_id") int accountId,
            @RequestParam("sku_id") int skuId) {

        return processingService.processOrder(accountId, skuId);
    }
}

Service Interface Definition

Define the contract for order processing. This abstraction allows different implementation strategies for remote service calls.

package com.example.orders.service;

import com.example.orders.domain.OrderRecord;

public interface OrderProcessingService {

    OrderRecord processOrder(int accountId, int skuId);
}

Implementation Strategy 1: Direct Service ID

The simplest approach utilizes the configured LoadBalanced RestTemplate. You can directly reference the target service name (e.g., inventory-service) within the URL string. Ribbon intercepts this call to perform the load balancing.

package com.example.orders.service.impl;

import com.example.orders.domain.OrderRecord;
import com.example.orders.service.OrderProcessingService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.Date;
import java.util.Map;
import java.util.UUID;

@Service
public class OrderProcessingServiceImpl implements OrderProcessingService {

    @Autowired
    private RestTemplate httpClient;

    @Override
    public OrderRecord processOrder(int accountId, int skuId) {
        String serviceUrl = "http://inventory-service/items/v1/query?sku=" + skuId;
        Map<String, Object> itemDetails = httpClient.getForObject(serviceUrl, Map.class);

        OrderRecord order = new OrderRecord();
        order.setTimestamp(new Date());
        order.setAccountId(accountId);
        order.setTransactionId(UUID.randomUUID().toString());
        order.setItemName(itemDetails.get("title").toString());
        order.setAmount(Integer.parseInt(itemDetails.get("cost").toString()));
        
        return order;
    }
}

Implementation Strategy 2: Explicit Load Balancer Client

For scenarios requiring more control over instance selection, inject the LoadBalancerClient. This allows manual retrieval of the host and port before constructing request URL.

package com.example.orders.service.impl;

import com.example.orders.domain.OrderRecord;
import com.example.orders.service.OrderProcessingService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.Date;
import java.util.Map;
import java.util.UUID;

@Service
public class AdvancedOrderServiceImpl implements OrderProcessingService {

    @Autowired
    private RestTemplate httpClient;

    @Autowired
    private LoadBalancerClient balancer;

    @Override
    public OrderRecord processOrder(int accountId, int skuId) {
        ServiceInstance instance = balancer.choose("inventory-service");
        String baseUrl = String.format("http://%s:%d/items/v1/query?sku=%d", 
            instance.getHost(), instance.getPort(), skuId);
            
        Map<String, Object> itemDetails = httpClient.getForObject(baseUrl, Map.class);

        OrderRecord order = new OrderRecord();
        order.setTimestamp(new Date());
        order.setAccountId(accountId);
        order.setTransactionId(UUID.randomUUID().toString());
        order.setItemName(itemDetails.get("title").toString());
        order.setAmount(Integer.parseInt(itemDetails.get("cost").toString()));
        
        return order;
    }
}

Tags: spring-cloud Ribbon RestTemplate microservices load-balancing

Posted on Sun, 19 Jul 2026 16:30:11 +0000 by Collin