Spring Cloud LoadBalancer: Ribbon වලට සමුදී Microservices Load Balancing කරන්න SC Guide

Spring Cloud LoadBalancer: Ribbon වලට සමුදී Microservices Load Balancing කරන්න SC Guide

ආයුබෝවන්, developer යාළුවනේ!

අද අපි කතා කරන්නේ Spring Cloud ecosystem එකේ ගොඩක් වැදගත් වෙන, ඒ වගේම අලුත් projects වලට අත්‍යවශ්‍යම topic එකක් ගැන: ඒ තමයි Spring Cloud LoadBalancer. ඔයාලා දැනටමත් Spring Cloud එක්ක වැඩ කරනවා නම්, Ribbon කියන නම අහලා ඇති. කාලයක් තිස්සේ, Spring Cloud ecosystem එකේ client-side load balancing කියන එකට Ribbon තමයි රජ කරගෙන හිටියේ. ඒත්, කාලයත් එක්ක හැමදේම වෙනස් වෙනවනේ. Ribbon දැන් maintenance mode එකේ තියෙන්නේ, ඒ නිසා අලුත් projects වලට මේක පාවිච්චි නොකර ඉන්න එක හොඳයි. එහෙනම්, මේකට ආපු අලුත් වීරයා තමයි Spring Cloud LoadBalancer. මේ Guide එකෙන් අපි Ribbon වලට සමුදීලා, Spring Cloud LoadBalancer කොහොමද අපේ Microservices වලට එකතු කරගන්නේ කියලා පියවරෙන් පියවර බලමු.

මේ Guide එකෙන් ඔයාට මොනවද ඉගෙන ගන්න පුළුවන්?

  • Microservices වල Load Balancing කියන්නේ මොකක්ද?
  • Ribbon වලට වඩා Spring Cloud LoadBalancer වල තියෙන වාසි මොනවද?
  • Spring Cloud LoadBalancer අපේ Spring Boot Project එකකට integrate කරගන්නේ කොහොමද?
  • සරල Code Examples හරහා Load Balancing ක්‍රියාත්මක කරන හැටි.

එහෙනම්, වැඩි වෙලාවක් නැතුව අපි වැඩේට බහිමු!

Microservices වල Load Balancing කියන්නේ මොකක්ද?

අපි Microservices architecture එකක් ගැන කතා කරනකොට, services ගොඩක් තියෙනවා, ඒ වගේම ඒ services වල instances කිහිපයක් එකවර run වෙන්න පුළුවන්. උදාහරණයක් විදියට, UserService එකට requests ගොඩක් එනවා කියලා හිතමු. ඒ වෙලාවට අපි UserService එකේ instances 3ක්, 4ක් run කරනවා. එතකොට client එකකින් UserService එකට request එකක් එනකොට, මේ run වෙන instances අතරින් request එක යවන්නේ මොන instance එකටද? මේක තමයි Load Balancing කියන්නේ. ඒ කියන්නේ එන requests ටික, available instances අතරේ සමබරව බෙදා හැරීම.

මේකෙන් ප්‍රධාන වාසි දෙකක් තියෙනවා:

  1. Scalability: වැඩිපුර requests ආවොත්, අලුත් service instances add කරන්න පුළුවන්.
  2. High Availability: එක service instance එකක් down වුණොත්, අනිත් instances ටික හරහා requests continue කරන්න පුළුවන්.

Spring Cloud ecosystem එකේදී, client-side load balancing කියන concept එක තමයි ගොඩක් දුරට පාවිච්චි කරන්නේ. ඒ කියන්නේ, service එකට request එක යවන client එකම (ඒ කියන්නේ consumer service එක) තීරණය කරනවා request එක යවන්නේ මොන service instance එකටද කියලා.

Ribbon වලට වඩා Spring Cloud LoadBalancer වල වාසි මොනවද?

ඉස්සර Ribbon තමයි මේ වැඩේට තිබුණ default library එක. ඒත් දැන් ඒක deprecated කරලා තියෙන්නේ. Spring Cloud LoadBalancer කියන්නේ Ribbon වලට ආපු නූතන, වඩා හොඳ විකල්පයක්. මේකේ ප්‍රධාන වාසි කිහිපයක් තියෙනවා:

  • Modern Architecture: Spring Cloud LoadBalancer, Spring Framework එකේ reactive programming models වලට හොඳට ගැළපෙන විදියට හදලා තියෙනවා. Ribbon වලට වඩා lightweight.
  • Flexibility: Custom load balancing algorithms add කරන්න ලේසියි.
  • Better Integration: Spring Cloud ecosystem එකේ අනිත් components (Eureka, Consul, Kubernetes වගේ service discovery mechanisms) එක්ක seamless integration එකක් තියෙනවා.
  • Active Development: මෙය actively developed වෙන project එකක් නිසා, අලුත් features, bug fixes ඉක්මනින්ම ලැබෙනවා.

මේ නිසා, අලුත් Spring Cloud projects වලට Spring Cloud LoadBalancer කියන්නේ අනිවාර්යයෙන්ම පාවිච්චි කරන්න ඕන library එකක්.

Spring Cloud LoadBalancer ක්‍රියාත්මක කරමු!

දැන් අපි බලමු කොහොමද Spring Cloud LoadBalancer අපේ Spring Boot project එකකට add කරගෙන, simple microservice communication එකක් load balance කරන්නේ කියලා. මේකෙන් අපි සරල UserService එකක් (Provider Service) සහ OrderService එකක් (Consumer Service) හදලා, OrderService එකෙන් UserService එකට requests යවනකොට load balancing වෙන හැටි බලමු.

1. Dependencies එකතු කිරීම

මුලින්ම, අපේ pom.xml (Maven) වලට අවශ්‍ය dependencies add කරගමු. Provider Service එකටයි, Consumer Service එකටයි දෙකටම මේක පොදුයි.

<dependencies>
    <!-- Spring Web Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Spring Cloud Starter Netflix Eureka Client (for Service Discovery) -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>

    <!-- Spring Cloud Starter LoadBalancer (replaces Ribbon) -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-loadbalancer</artifactId>
    </dependency>

    <!-- Spring Boot DevTools (Optional, for hot reloading) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
</dependencies>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>2023.0.0</version> <!-- Replace with your Spring Cloud version -->
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

Microservices වලදී, services එකිනෙකා හොයාගන්න Service Discovery එකක් අවශ්‍යයි. අපි මේ සඳහා Eureka Server එකක් පාවිච්චි කරමු. මේක වෙනම Spring Boot Project එකක් විදියට හදාගන්න පුළුවන්. (ඔබට දැනටමත් Eureka Server එකක් තිබේ නම්, මෙම පියවර මඟ හරින්න.)

Eureka Server Application:

EurekaServerApplication.java

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {

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

application.yml for Eureka Server:

server:
  port: 8761

eureka:
  client:
    register-with-eureka: false
    fetch-registry: false
  server:
    wait-time-in-ms-for-sync-millis: 0

3. Provider Service (UserService) එකක් හදමු

මේක තමයි අපේ UserService එක. මේකේ instances කිහිපයක් run කරලා, load balancing වෙන හැටි බලමු.

UserServiceApplication.java:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@EnableDiscoveryClient
@RestController
public class UserServiceApplication {

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

    @GetMapping("/users/{id}")
    public String getUserById(@PathVariable String id) {
        String port = System.getProperty("server.port");
        System.out.println("User Service instance on port " + port + " serving request for user " + id);
        return "User " + id + " from User Service on port " + port;
    }
}

application.yml for UserService:

application.yml එකේ spring.application.name එක වැදගත්. මේ නමින් තමයි service discovery එකේ register වෙන්නේ.

server:
  port: 8081 # Change port for different instances (e.g., 8081, 8082, 8083)

spring:
  application:
    name: user-service

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

ප්‍රධානම දේ: UserService එකේ instances කිහිපයක් run කරන්න. Command Line එකෙන් run කරනවා නම්:

java -jar target/user-service-0.0.1-SNAPSHOT.jar --server.port=8081
java -jar target/user-service-0.0.1-SNAPSHOT.jar --server.port=8082
java -jar target/user-service-0.0.1-SNAPSHOT.jar --server.port=8083

මේ වගේ user-service instances 3ක් අපි run කරනවා.

4. Consumer Service (OrderService) එකක් හදමු

මේක තමයි UserService එකට requests යවන OrderService එක. මෙතන තමයි අපි Spring Cloud LoadBalancer ක්‍රියාත්මක කරන්නේ.

OrderServiceApplication.java:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableDiscoveryClient
@RestController
public class OrderServiceApplication {

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

    @Bean
    @LoadBalanced // This is the magic for client-side load balancing!
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    private final RestTemplate restTemplate;

    public OrderServiceApplication(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @GetMapping("/orders/user/{userId}")
    public String getOrderDetailsForUser(@PathVariable String userId) {
        // Instead of http://localhost:8081, we use the service name 'user-service'
        String userResponse = restTemplate.getForObject(
                "http://user-service/users/" + userId, String.class);

        return "Order details for user " + userId + ". Fetched User: " + userResponse;
    }
}

වැදගත්ම දේ මෙතනයි:

  • @LoadBalanced annotation එක RestTemplate Bean එකට add කරලා තියෙනවා. මේක තමයි Spring Cloud LoadBalancer activate කරන්නේ.
  • restTemplate.getForObject("http://user-service/users/" + userId, String.class); මෙතන අපි IP address එකක් හෝ Port එකක් වෙනුවට user-service කියන service name එක පාවිච්චි කරනවා. @LoadBalanced RestTemplate එක automatically Eureka server එකෙන් user-service එකේ instances හොයාගෙන, ඒ අතරින් එකක් තෝරගෙන request එක යවනවා. Default විදියට Round Robin algorithm එක පාවිච්චි කරනවා.

application.yml for OrderService:

server:
  port: 8080

spring:
  application:
    name: order-service

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

5. Custom Load Balancing Rules (විකල්ප)

Spring Cloud LoadBalancer default විදියට Round Robin algorithm එක පාවිච්චි කරනවා. ඔබට අවශ්‍ය නම් වෙනත් algorithm එකක් (Random, Weighted Round Robin වගේ) හෝ ඔබේම custom algorithm එකක් implement කරන්න පුළුවන්.

මේ සඳහා ServiceInstanceListSupplier interface එක implement කරන්න පුළුවන්. උදාහරණයක් විදියට, RandomLoadBalancerConfiguration එකක් හදමු:

import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.RandomServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import reactor.core.publisher.Flux;

public class RandomLoadBalancerConfiguration {

    @Bean
    public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
            ConfigurableApplicationContext context) {
        return new RandomServiceInstanceListSupplier(
                ServiceInstanceListSupplier.builder()
                        .with  DiscoveryClient() // Uses Eureka or other discovery client
                        .withBlockingHealthChecks()
                        .build(context));
    }
}

මේ configuration එක activate කරන්න නම්, OrderServiceApplication එකේ @SpringBootApplication annotation එකට උඩින් @LoadBalancerClient annotation එක add කරන්න පුළුවන්:

// ... imports ...
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;

@SpringBootApplication
@EnableDiscoveryClient
@RestController
@LoadBalancerClient(name = "user-service", configuration = RandomLoadBalancerConfiguration.class)
public class OrderServiceApplication {
    // ... rest of the code ...
}

මෙහිදී, user-service සඳහා පමණක් Random load balancing භාවිතා වේ.

Load Balancing එක Test කරමු!

දැන් අපි හැමදේම set up කරලා ඉවරයි. දැන් අපි test කරලා බලමු load balancing වැඩ කරනවද කියලා.

  1. මුලින්ම Eureka Server Application එක run කරන්න (port 8761).
  2. ඊට පස්සේ UserServiceApplication එකේ instances කිහිපයක් run කරන්න (උදා: port 8081, 8082, 8083).
  3. අන්තිමට OrderServiceApplication එක run කරන්න (port 8080).

දැන් ඔයාගේ browser එකෙන් හෝ Postman/Insomnia වගේ tool එකකින් මේ URL එකට requests කිහිපයක් යවන්න:

http://localhost:8080/orders/user/123

ඔබ requests කිහිපයක් යවන විට, UserService එකේ Console logs බැලුවොත්, requests 8081, 8082, 8083 යන ports අතරේ මාරුවෙන් මාරුවට බෙදී යනවා ඔබට පෙනේවි (Round Robin default algorithm එක නිසා). System.out.println("User Service instance on port " + port + " serving request for user " + id); කියන log එක හරහා ඔබට මේක පැහැදිලිවම දකින්න පුළුවන්.

මෙයින් පැහැදිලිවම පෙනී යන්නේ Spring Cloud LoadBalancer මගින් requests සාර්ථකව multiple service instances අතරේ බෙදා හරින බවයි.

අවසන් වචන

ඉතින්, අද අපි Spring Cloud LoadBalancer ගැන ගොඩක් දේවල් ඉගෙන ගත්තා. Ribbon වලට සමුදීලා, නූතන Microservices architecture එකේ Load Balancing සඳහා Spring Cloud LoadBalancer කොහොමද පාවිච්චි කරන්නේ කියලා අපි දැක්කා. මේක Spring Cloud ecosystem එකේ අත්‍යවශ්‍ය component එකක්. මේකෙන් ඔයාගේ applications වල Scalability සහ High Availability වැඩි දියුණු කරගන්න පුළුවන්.

මේ Guide එක ඔයාලට ප්‍රයෝජනවත් වෙන්න ඇති කියලා හිතනවා. මතක තියාගන්න, ඕනෑම අලුත් technology එකක් ඉගෙන ගන්න කොට පොඩි අභියෝග එන්න පුළුවන්, ඒත් නොපසුබට උත්සාහයෙන් ඒවා ජය ගන්න පුළුවන්.

ඔයාලගේ අදහස්, ප්‍රශ්න හෝ මේ topic එක ගැන තියෙන experiences පහළින් comment කරන්න. අපි හැමෝටම මේ Community එකෙන් තව ගොඩක් දේවල් ඉගෙන ගන්න පුළුවන්!