Ribbon සමග Client-Side Load Balancing: ඔබේ Microservices බලවත් කරමු!

Ribbon සමග Client-Side Load Balancing: ඔබේ Microservices බලවත් කරමු!

Microservices ලෝකේ, අපේ systems වල performance එකයි, reliability එකයි කියන්නේ මාරම වැදගත් දේවල්. Application එකක් හදනකොට, Users ලා වැඩි වෙනකොට, requests ගොඩක් එනකොට කොහොමද ඒ හැම එකක්ම අදාල service instance එකට හරියට යවන්නේ කියන එක ලොකු ප්‍රශ්නයක් වෙනවා. අපි හිතමු අපිට ஒரே service එකේ instances කිහිපයක් තියෙනවා කියලා. ඒ හැම එකක්ම වැඩ කරනවා, ඒත් කොහොමද අපි එන requests ඒ හැමෝටම සමානව බෙදලා දෙන්නේ? මේකට තියෙන සුපිරිම විසඳුමක් තමයි Load Balancing.

Load Balancing කියන්නේ සරලවම, incoming network traffic එක servers ගොඩක් අතර බෙදලා දෙන එක. මේකෙන් අපේ system එකේ capacity එක වැඩි වෙනවා වගේම, මොකක් හරි server එකක් down වුණොත්, අනිත් servers වලට traffic එක යවලා system එකේ availability එක maintain කරන්නත් පුළුවන් වෙනවා.

අද අපි කතා කරමු Load Balancing වල තවත් වැදගත් පැතිකඩක් ගැන - ඒ තමයි Client-Side Load Balancing, විශේෂයෙන්ම Netflix Ribbon කියන ටෙක්නොලොජි එක කොහොමද මේකට උදව් කරන්නේ කියලා. අපි Ribbon ගැන විතරක් නෙවෙයි, ඒක අපේ Spring Boot microservice එකකට add කරගෙන කොහොමද client-side load balancing වැඩ කරන්නේ කියලා practical විදිහට බලමු. මේක Spring Cloud පරිසරයක වැඩ කරන ඔයාලට අනිවාර්යයෙන්ම දැනගෙන ඉන්න ඕන දෙයක්, මොකද මේකෙන් අපේ services වල resilience එකයි, scalability එකයි මාරම විදිහට වැඩි කරගන්න පුළුවන්.


Load Balancing කියන්නේ මොකක්ද, ඇයි ඒක වැදගත්?

හරි, මුලින්ම බලමු Load Balancing කියන්නේ මොකක්ද කියලා. හිතන්නකෝ ඔයාට ලොකු Online Shop එකක් තියෙනවා කියලා. මේ Shop එකට එකපාරටම ලක්ෂ ගාණක් Customers ලා එනවා. ඔයාගේ backend service එක මේ හැම request එකක්ම handle කරන්න උත්සාහ කළොත් මොකද වෙන්නේ? එකපාරටම crush වෙන්න පුළුවන්, නැත්නම් response slow වෙන්න පුළුවන්. මේ වගේ වෙලාවට තමයි Load Balancing උදව් කරන්නේ.

Load Balancer එකක් කරන්නේ incoming traffic එක server instances ගොඩක් අතර smart විදිහට බෙදලා දෙන එක. මේකෙන් වෙන්නේ:

  • Performance වැඩි වෙනවා: එක server එකකට විතරක් traffic එන එක නවතිනවා, request ගොඩක් servers අතර බෙදෙන නිසා responses ඉක්මන් වෙනවා.
  • Availability වැඩි වෙනවා: මොකක් හරි server එකක් down වුණොත්, Load Balancer එක ඒ server එකට traffic යවන එක නවත්වලා, අනිත් healthy servers වලට විතරක් traffic යවනවා. ඒ නිසා Users ලට අඛණ්ඩ සේවාවක් ලැබෙනවා.
  • Scalability වැඩි වෙනවා: Demand එක වැඩි වෙනකොට අපිට තවත් server instances add කරලා system එකේ capacity එක ලේසියෙන්ම වැඩි කරගන්න පුළුවන්. Load Balancer එක auto-matically අලුත් instances වලටත් traffic යවන්න පටන් ගන්නවා.

Load Balancing ප්‍රධාන වශයෙන් ආකාර දෙකකට බෙදෙනවා:

  1. Server-Side Load Balancing: මේකේදී Load Balancer එක වෙනම server එකක් විදිහට, client එකයි actual service instances ටිකයි අතර මැද ඉන්නවා. client එක request එක යවන්නේ Load Balancer එකට, ඊට පස්සේ Load Balancer එක තමයි request එක යවන්න ඕන service instance එක තීරණය කරන්නේ. Nginx, HAProxy, AWS ELB වගේ ඒවා මේ ගණයට අයිති වෙනවා. මේවා external entity එකක් විදිහට තමයි වැඩ කරන්නේ.
  2. Client-Side Load Balancing: මේක තමයි අද අපේ කතාවේ ප්‍රධාන මාතෘකාව. මේකේදී Load Balancing logic එක තියෙන්නේ request එක යවන client service එක ඇතුලෙමයි. Client service එකට service instances කොහෙද තියෙන්නේ කියලා දැනගන්න service discovery mechanism එකක් (උදා: Eureka, Consul) එක්ක connect වෙන්න වෙනවා. ඊට පස්සේ client එක තමන්ටම තීරණය කරනවා available instances වලින් මොන instance එකටද request එක යවන්නේ කියලා. Ribbon කියන්නේ client-side load balancer එකක්.

Microservices architecture එකක Client-Side Load Balancing වලට ලොකු වාසි තියෙනවා. මොකද Microservices වලදී services ගොඩක් තියෙනවා, ඒ හැම service එකක්ම වෙනත් services එක්ක communicate කරනවා. Server-Side Load Balancer එකක් හැම service to service call එකකටම අතරමැදියෙක් විදිහට දාන එක practicality එකක් නෙවෙයි. ඒ වෙනුවට Client-Side Load Balancer එකක් පාවිච්චි කරනකොට, service එකටම තමන්ට අවශ්‍ය service instance එක තෝරගන්න පුළුවන්. ඒක service discovery එක්ක හොඳටම ගැලපෙනවා.


Ribbon: Client-Side Load Balancing වල නියමුවා

දැන් අපි කතා කරමු අපේ මේ Client-Side Load Balancing වල සුපිරිම වීරයා වන Netflix Ribbon ගැන. Ribbon කියන්නේ Netflix එකෙන් develop කරපු client-side load balancer එකක්. Spring Cloud ecosystem එකේදී Ribbon, Spring Cloud Netflix project එකේ කොටසක් විදිහට මාරම ජනප්‍රිය වුණා.

Ribbon වැඩ කරන්නේ මෙහෙමයි:

  1. Service Discovery Integration: Ribbon තනියම වැඩ කරන්නේ නැහැ. ඒකට අනිවාර්යයෙන්ම Service Registry එකක් (වගේ Eureka) එක්ක connect වෙන්න වෙනවා. Client service එකට මොකක් හරි service එකක් (උදා: product-service) call කරන්න ඕන වුණාම, Ribbon මුලින්ම Service Registry එකෙන් අහනවා "product-service කියන service එකේ දැනට available instances මොනවද?" කියලා.
  2. Instance List: Service Registry එකෙන් product-service එකේ available instances ටික (IP addresses සහ port numbers එක්ක) Ribbon එකට ලබා දෙනවා.
  3. Load Balancing Rule: Ribbon මේ ලැබුණු instances list එකෙන් request එක යවන්න ඕන instance එක තෝරගන්න load balancing rule එකක් apply කරනවා. Ribbon එකේ default rule එක තමයි Round Robin. ඒ කියන්නේ, පළවෙනි request එක පළවෙනි instance එකට, දෙවෙනි request එක දෙවෙනි instance එකට, ආයෙත් තුන්වෙනි request එක පළවෙනි instance එකට වගේ විදිහට ක්‍රමානුකූලව instance එකෙන් instance එකට මාරු කරලා request යවනවා. මේකෙදි instance එකක load එකක් ගැන හිතන්නේ නැහැ, හැමෝටම සමානව බෙදලා දෙනවා.
  4. Request Execution: තෝරගත්තු instance එකට client service එක කෙලින්ම request එක යවනවා.

Ribbon එකේ Round Robin වලට අමතරව තවත් Load Balancing rules කිහිපයක් තියෙනවා:

  • Random: Available instances වලින් random විදිහට instance එකක් තෝරගන්නවා.
  • Weighted Response Time: Instances වල past response times අනුව weight එකක් assign කරලා, අඩු response time තියෙන instances වලට වැඩි requests ප්‍රමාණයක් යවනවා.
  • Availability Filtering: Circuit Breaker (Hystrix වගේ) එක්ක integrate වෙලා, down වෙලා තියෙන instances ටික filter කරලා healthy instances වලට විතරක් request යවනවා.

Spring Cloud වලදී, Ribbon එක RestTemplate වගේ clients එක්ක auto-matically integrate වෙනවා. අපිට අවශ්‍ය වෙන්නේ @LoadBalanced annotation එක client bean එකට add කරන එක විතරයි. එතකොට ඒ RestTemplate එක හරහා යන හැම request එකක්ම Ribbon හරහා Load Balanced වෙනවා. මාරම ලේසියි නේද?


අපිම Ribbon Implement කරමු! Practical Example එකක්

හරි, දැන් අපි theoretical පැත්ත ඇති, කෙලින්ම coding වලට බහිමු! අපි හදමු simple microservices setup එකක්, ඒකේදී Ribbon කොහොමද වැඩ කරන්නේ කියලා බලමු. මේකට අපිට අවශ්‍ය වෙනවා:

  • Java 11+
  • Maven/Gradle
  • Spring Boot
  • IDE එකක් (IntelliJ IDEA, VS Code, Eclipse වගේ)

අපි හදන්නේ services 3ක්. පළවෙනි එක Service Registry එකක් (Eureka Server), දෙවෙනි එක Product Service එක (මේකෙන් instances 2ක් run කරමු), තුන්වෙනි එක Order Service එක (මේකෙන් Product Service එක call කරමු).

පියවර 1: Eureka Server එක හදමු

මුලින්ම අපි Service Registry එක හදමු. මේක තමයි අපේ services ඔක්කොම register වෙන්නේ.

Spring Initializr (start.spring.io) එකට ගිහින් dependencies විදිහට Eureka Server එකතු කරලා project එකක් හදාගන්න. Maven project එකක් විදිහට හදාගන්න එක ලේසියි.

pom.xml (dependencies):

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

Main Application Class (e.g., EurekaServerApplication.java):

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

@SpringBootApplication
@EnableEurekaServer // මේක තමයි Eureka Server එක enable කරන්නේ
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}

application.properties (or application.yml) for Eureka Server:

server.port=8761
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.instance.hostname=localhost

register-with-eureka=false සහ fetch-registry=false කියන්නේ මේ server එකට තමන්වම register කරගන්න හෝ අනිත් services fetch කරන්න ඕන නැහැ කියලා. මොකද මේක server එකක් විතරයි.

පියවර 2: Product Service එක හදමු (instances 2ක්)

දැන් අපි Product Service එක හදමු. මේකෙන් තමයි product details ලබා දෙන්නේ. මේකේ instances දෙකක් run කරලා, ඒවා Eureka Server එකට register කරමු.

Spring Initializr එකෙන් Eureka Discovery Client සහ Spring Web dependencies එක්ක project එකක් හදාගන්න.

pom.xml (dependencies):

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Main Application Class (e.g., ProductServiceApplication.java):

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@EnableEurekaClient // මේක තමයි Eureka Client එක enable කරන්නේ
public class ProductServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProductServiceApplication.class, args);
    }
}

@RestController
@RequestMapping("/products")
class ProductController {
    @GetMapping("/hello")
    public String getProductHello() {
        // මේකෙන් මේ request එක handle කරපු instance එකේ port එක පෙන්නනවා.
        // Load Balancing වැඩ කරනවද බලන්න මේක උදව් වෙනවා.
        return "Hello from Product Service, running on port: " + System.getProperty("server.port");
    }
}

application.properties for Product Service:

spring.application.name=product-service
eu.client.service-url.defaultZone=http://localhost:8761/eureka

දැන් මේ product-service එකේ instances දෙකක් run කරමු. පළවෙනි instance එක run කරන්න server.port=8081 කියලා අලුතින් add කරලා run කරන්න. දෙවෙනි instance එක server.port=8082 කියලා add කරලා run කරන්න. IntelliJ IDEA වගේ IDE එකක නම් run configurations copy කරලා port එක වෙනස් කරලා run කරන්න පුළුවන්. නැත්නම් command line එකෙන් run කරනවා නම්:

java -jar product-service-0.0.1-SNAPSHOT.jar --server.port=8081
java -jar product-service-0.0.1-SNAPSHOT.jar --server.port=8082

Eureka Dashboard එකට (http://localhost:8761) ගියාම ඔයාට පේයි PRODUCT-SERVICE කියන නමින් instances දෙකක් register වෙලා තියෙනවා කියලා.

පියවර 3: Order Service එක හදමු (Ribbon client)

අවසාන වශයෙන්, අපි Order Service එක හදමු. මේකෙන් තමයි Product Service එක call කරන්නේ, Ribbon client එක විදිහට. මේකත් Spring Initializr එකෙන් Eureka Discovery Client, Spring Web, සහ Spring Cloud Starter Netflix Ribbon dependencies එක්ක හදාගන්න.

pom.xml (dependencies):

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

නෝට්: spring-cloud-starter-netflix-eureka-client එකේම Ribbon එනවා සමහර version වල. ඒත් පැහැදිලිව spring-cloud-starter-netflix-ribbon add කරන එක හොඳයි.

Main Application Class (e.g., OrderServiceApplication.java):

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

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

    @Bean
    @LoadBalanced // මේක තමයි මැජික් එක! RestTemplate එක Ribbon එක්ක integrate කරන්නේ මේකෙන්
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

@RestController
@RequestMapping("/orders")
class OrderController {

    private final RestTemplate restTemplate;

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

    @GetMapping("/create")
    public String createOrder() {
        // Product Service එකට call එකක් යවනවා. මෙතනදී අපි 'product-service' කියන service ID එක පාවිච්චි කරනවා
        // IP address එකයි, Port එකයි දෙන්න ඕන නැහැ. Ribbon ඒක Eureka එකෙන් හොයාගන්නවා.
        String productInfo = restTemplate.getForObject("http://product-service/products/hello", String.class);
        return "Order created successfully! Product Info: " + productInfo;
    }
}

application.properties for Order Service:

server.port=8080
spring.application.name=order-service
eu.client.service-url.defaultZone=http://localhost:8761/eureka

පියවර 4: සියල්ල Run කරලා Test කරමු!

හරි, දැන් අපි හැම service එකම run කරමු.

  1. මුලින්ම EurekaServerApplication එක run කරන්න (port 8761).
  2. ඊට පස්සේ ProductServiceApplication එක instances දෙකක් විදිහට run කරන්න (ports 8081 සහ 8082).
  3. අන්තිමට OrderServiceApplication එක run කරන්න (port 8080).

සියල්ලම run වුණාට පස්සේ, ඔයාගේ browser එකේ http://localhost:8080/orders/create කියන URL එකට ගිහින් refresh කරන්න. නැත්නම් Postman/curl වගේ එකකින් requests කිහිපයක් යවන්න.

curl http://localhost:8080/orders/create

ඔයාට පෙනෙයි request එකෙන් ලැබෙන response එක වරින් වර වෙනස් වෙනවා. එක පාරක් "Hello from Product Service, running on port: 8081" කියලා ලැබෙනවා නම්, ඊළඟ පාර "Hello from Product Service, running on port: 8082" කියලා ලැබෙන්න ඕන. ඒ කියන්නේ Ribbon client එක requests ටික product service instances දෙක අතර round-robin විදිහට බෙදලා දෙනවා. වැඩේ හරි!


Ribbon Configurations සහ Advanced Tips

Ribbon කියන්නේ හරිම flexible tool එකක්. අපිට අවශ්‍ය විදිහට configure කරන්න පුළුවන්. Spring Cloud හරහා අපිට application.properties (හෝ .yml) හරහා Ribbon වල default behavior එක වෙනස් කරන්න පුළුවන්.

Customizing Load Balancing Rule

ඔයාට Round Robin වලට වඩා වෙනස් rule එකක් පාවිච්චි කරන්න අවශ්‍ය නම්, IRule interface එක implement කරලා තමන්ගේම custom rule එකක් හදාගන්න පුළුවන්, නැත්නම් Ribbon එකේ තියෙන අනිත් rules වලින් එකක් තෝරගන්න පුළුවන්. ඒකට @RibbonClient annotation එක පාවිච්චි කරන්න වෙනවා. උදාහරණයක් විදිහට RandomRule එකක් පාවිච්චි කරනවා නම්:

Configuration Class එකක් හදන්න:

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RibbonConfiguration {
    @Bean
    public IRule ribbonRule() {
        return new RandomRule(); // RandomRule එක පාවිච්චි කරන්න
    }
}

OrderServiceApplication.java එකේදී:

import org.springframework.cloud.netflix.ribbon.RibbonClient;
// ... අනිත් imports ...

@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "product-service", configuration = RibbonConfiguration.class) // product-service එකට මේ configuration එක apply කරන්න
public class OrderServiceApplication {
    // ...
}

මේ විදිහට ඔයාට service එකෙන් service එකට වෙනස් rules apply කරන්න පුළුවන්.

Timeouts සහ Retries

Microservices වලදී network issues, server slow downs වගේ දේවල් සාමාන්‍යයි. Ribbon මේවාටත් solutions දෙනවා. Request timeout එකක් වගේම, fail වුණොත් retry කරන්නත් පුළුවන්.

application.properties එකේ:

# product-service කියන client එකට අදාල configurations
product-service.ribbon.ConnectTimeout=2000 # Connection timeout එක (millisecond)
product-service.ribbon.ReadTimeout=5000   # Read timeout එක (millisecond)
product-service.ribbon.MaxAutoRetries=1   # එක instance එකක උපරිම retry ගණන
product-service.ribbon.MaxAutoRetriesNextServer=1 # ඊළඟ instance එකට මාරු වෙලා retry කරන වාර ගණන
product-service.ribbon.OkToRetryOnAllOperations=true # හැම HTTP operation එකක්ම retry කරන්න ඉඩ දෙන්නද

මේවා හරහා ඔයාගේ client service එකේ resilience එක වැඩි කරගන්න පුළුවන්. Network එකේ පොඩි අවුලක් ආවාම කෙලින්ම error එකක් දෙන්නේ නැතුව, ටිකක් වෙලා බලලා, වෙන instance එකකට ගිහින් ආයෙත් try කරන්න මේකෙන් පුළුවන් වෙනවා.

Important Note: Ribbon's Future

මචං, මේක පොඩ්ඩක් වැදගත්. Netflix Ribbon project එක දැන් maintenance mode එකේ තියෙන්නේ. ඒ කියන්නේ අලුත් features add වෙන්නේ නැහැ, හැබැයි bug fixes නම් වෙන්න පුළුවන්. Spring Cloud project එක දැන් Spring Cloud LoadBalancer කියන module එකට move වෙලා තියෙනවා, ඒක Ribbon වලට වඩා modern approach එකක්. හැබැයි Ribbon තාමත් ගොඩක් existing systems වල පාවිච්චි වෙනවා, ඒ නිසා මේක දැනගෙන ඉන්න එක වටිනවා. අලුත් projects වලට නම් Spring Cloud LoadBalancer එක recommend කරනවා.


අවසන් වශයෙන්

හරි යාළුවනේ, ඔන්න ඔය විදිහට තමයි Client-Side Load Balancing, විශේෂයෙන්ම Netflix Ribbon අපේ Spring Boot microservices වල වැඩ කරන්නේ. මේකෙන් අපේ applications වල scalability එක, reliability එක, සහ fault tolerance එක මාරම විදිහට වැඩි කරගන්න පුළුවන්. Requests එක server එකකට විතරක් යවන්නේ නැතුව, available instances අතර බෙදා හරින නිසා අපේ system එක ස්ථාවරව තියාගන්න පුළුවන් වෙනවා.

මේ concept එක තේරුම් ගන්න එක විතරක් මදි, අතින්ම කරලා බලන්න ඕන. ඒ නිසා අදම මේක ඔයාගේ project එකකට add කරලා test කරලා බලන්න. පොඩි පොඩි experiments කරලා, different rules apply කරලා, timeouts වෙනස් කරලා බලන්න. එතකොට තව හොඳට මේක තේරෙයි.

ඔබේ අදහස්, ප්‍රශ්න, හෝ මේ ගැන තව මොනවා හරි දැනගන්න තියෙනවා නම්, Comment Section එකේ දාන්න අමතක කරන්න එපා! අපි හැමෝම එකතු වෙලා knowledge එක share කරගමු. අලුත් දෙයක් ඉගෙනගන්න එක නම් මාරම ගැම්මක් නේද?