Spring Boot Microservices අතර Communication - SC Guide

Spring Boot Microservices අතර Communication - SC Guide

Microservices වලට Communication ඇයි මෙච්චර වැදගත්?

ආයුබෝවන් යාළුවනේ! අද අපි කතා කරන්න යන්නේ Software Engineering වල හරිම වැදගත් මාතෘකාවක් ගැන – ඒ තමයි Microservices අතර communication කරන විදිහ. දැන් කාලේ ගොඩක් Application හදන්නේ Monolithic Architecture එකෙන් අයින් වෙලා Microservices Architecture එකට. මේකේ ලොකු වාසි තියෙනවා තමයි, හැබැයි එක service එකක් තව service එකක් එක්ක කතා කරන විදිහ හරි ගස්සගන්නේ නැත්නම්, මුළු system එකම අවුල් වෙන්න පුළුවන්.

ඔයාලා Spring Boot පාවිච්චි කරන developer කෙනෙක් නම්, මේක ඔයාලට අනිවාර්යයෙන්ම දැනගන්න ඕන දෙයක්. අපි බලමු Microservices අතර communication කරන්න පුණුවන් ප්‍රධාන ක්‍රම මොනවද, ඒවායේ වාසි අවාසි මොනවද, සහ කවදාද මොන ක්‍රමය පාවිච්චි කරන්න ඕන කියලා.

1. Synchronous Communication - Request-Response

මේක තමයි Microservices අතර communication කරන්න තියෙන හරිම පොදු සහ සරලම විදිහ. මේකේදී එක service එකක් (Client) අනිත් service එකට (Server) request එකක් යවනවා, ඊට පස්සේ ඒ service එකෙන් response එකක් එනකල් Client එක බලාගෙන ඉන්නවා. හරියට ඔයාලා phone call එකක් ගන්නවා වගේ. කතා කරන කෙනා උත්තර දෙනකල් ඔයාලා අහගෙන ඉන්නවා.

REST APIs (Representational State Transfer APIs)

Synchronous Communication වලට ප්‍රධාන වශයෙන්ම පාවිච්චි කරන්නේ REST APIs. මේවා HTTP Protocol එක පදනම් කරගෙන තමයි හදලා තියෙන්නේ. Request එකක් යවන්න GET, POST, PUT, DELETE වගේ HTTP Methods පාවිච්චි කරනවා.

වාසි (Pros):

  • සරලයි (Simplicity): තේරුම් ගන්න සහ implement කරන්න හරිම ලේසියි.
  • හොඳටම හුරුයි (Widespread Adoption): ගොඩක් developer ලා මේ ගැන දන්නවා.
  • ඉක්මන් ප්‍රතිචාර (Immediate Response): Request එකට ඉක්මනින්ම response එකක් එනවා.

අවාසි (Cons):

  • බැඳීම (Tight Coupling): Services අතර strong dependency එකක් හැදෙනවා. එක service එකක් නැතිනම් අනිත් service එකට වැඩ කරන්න බැරි වෙන්න පුළුවන්.
  • කාර්යසාධනය (Performance Latency): Request එකක් ගිහින් response එකක් එනකල් client service එක බලාගෙන ඉන්න ඕන නිසා, Latency එක වැඩි වෙන්න පුළුවන්. විශේෂයෙන්ම ගොඩක් Services අතර මේ වගේ calls තියෙනවා නම්.
  • Fault Tolerance අඩුයි (Reduced Fault Tolerance): එක් service එකක් down වුණොත්, ඒක මත depend වෙන අනිත් services වලටත් බලපානවා.

Spring Boot වලින් REST API Call කරන හැටි (Practical Example):

Spring Boot වලදී වෙන Microservice එකකට REST API call එකක් යවන්න WebClient කියන එක recommend කරනවා. ඒක non-blocking, reactive client එකක් නිසා.

මුලින්ම, ඔබේ pom.xml එකට මේ dependency එක එකතු කරන්න:


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

දැන්, service එකක් ඇතුළේ WebClient පාවිච්චි කරලා call එකක් කරන්නේ මෙහෙමයි:


import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

@Service
public class OrderService {

    private final WebClient productWebClient;

    public OrderService(WebClient.Builder webClientBuilder) {
        this.productWebClient = webClientBuilder.baseUrl("http://localhost:8081/products").build();
    }

    public Mono<String> getProductDetails(String productId) {
        return productWebClient.get()
                .uri("/{id}", productId)
                .retrieve()
                .bodyToMono(String.class);
    }

    public Mono<Void> createOrderWithProductCheck(String productId, int quantity) {
        return getProductDetails(productId)
                .flatMap(productInfo -> {
                    System.out.println("Product details received: " + productInfo);
                    // Logic to create order with productInfo
                    return Mono.empty(); // Or return a Mono with order confirmation
                })
                .onErrorResume(e -> {
                    System.err.println("Error fetching product details: " + e.getMessage());
                    return Mono.error(new RuntimeException("Failed to create order due to product unavailability"));
                });
    }
}

මේ උදාහරණයේ OrderService එක ProductService එකෙන් product details ගන්න WebClient එකක් පාවිච්චි කරනවා. baseUrl එකෙන් අපි කතා කරන service එකේ address එක දෙනවා. retrieve().bodyToMono(String.class) කියන එකෙන් response එක Mono<String> එකක් විදිහට ගන්නවා.

2. Asynchronous Communication - Event-Driven

Asynchronous Communication කියන්නේ Synchronous වලට වඩා වෙනස්ම approach එකක්. මේකේදී service එකක් තව service එකකට message එකක් (event) එකක් යැව්වාම, response එකක් එනකල් බලාගෙන ඉන්නේ නැහැ. Message එක යවන service එකට ඒක deliver වුණාද නැද්ද කියන එකත් වැදගත් නැහැ. හරියට කවුරුහරි Facebook post එකක් දානවා වගේ. ඒකට කවුරු comment කරයිද share කරයිද කියන එක ගැන ඒ වෙලාවේම බල බලා ඉන්නේ නැහැ. මේ communication pattern එක Message Broker (Message Queue) එකක් හරහා තමයි සිද්ධ වෙන්නේ.

Message Queues (Kafka, RabbitMQ, ActiveMQ)

Message Queue එකක් කියන්නේ messages තාවකාලිකව save කරලා තියාගන්න මැදිහත්කරුවෙක් (Broker). Service එකක් message එකක් මේ Queue එකට යවනවා (Publish කරනවා), ඊට පස්සේ අනිත් service එකට පුළුවන් ඒ Queue එකෙන් messages ගන්න (Consume කරනවා).

වාසි (Pros):

  • වෙන්වීම (Loose Coupling/Decoupling): Services අතර dependency එකක් නැති වෙනවා. එක service එකක් down වුණොත්, අනිත් service වලට බලපෑමක් වෙන්නේ නැහැ. Messages Queue එකේ තියෙන නිසා, down වුණ service එක ආයෙත් up වුණාම ඒ messages processing කරන්න පුළුවන්.
  • වැඩි දියුණු කළ ඔරොත්තු දීමේ හැකියාව (Improved Resilience): Service එකක් down වුණත්, messages නැති වෙන්නේ නැහැ.
  • ප්‍රමාණයට වෙනස් කළ හැකි වීම (Scalability): Messages queue එකේ තියෙන නිසා, ඕන තරම් Consumers ලා add කරලා messages එකවර process කරන්න පුළුවන්.
  • කාර්යසාධනය (Better Performance): Request එකක් response එකක් එනකල් බලාගෙන ඉන්න ඕන නැති නිසා, client service එකට ඉක්මනින්ම තමන්ගේ වැඩ කරගෙන යන්න පුළුවන්.

අවාසි (Cons):

  • සංකීර්ණත්වය (Complexity): Setup කරන්න සහ maintain කරන්න ටිකක් සංකීර්ණයි. Message Broker එකක්, Consumer Group වගේ concepts තියෙනවා.
  • අවසාන වශයෙන් ස්ථාවරත්වය (Eventual Consistency): Synchronous වගේ immediate response එකක් නැහැ. Message එකක් deliver වෙලා process වෙන්න ටික වෙලාවක් යන්න පුළුවන්.
  • debugging අපහසුයි (Harder to Debug): Communication flow එක linear නැති නිසා, ගැටලු හොයන එක ටිකක් අමාරු වෙන්න පුළුවන්.

Spring Boot වලින් Message Queue එකක් පාවිච්චි කරන හැටි (Practical Example):

Spring Boot වලදී Message Queues එක්ක වැඩ කරන්න Spring Cloud Stream (Kafka/RabbitMQ) හෝ Spring AMQP (RabbitMQ) වගේ libraries පාවිච්චි කරනවා. අපි RabbitMQ උදාහරණයක් ගමු.

මුලින්ම, pom.xml එකට මේ dependency එක එකතු කරන්න:


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

දැන්, message එකක් publish කරන Service එකක් හදන හැටි:


import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class NotificationPublisher {

    @Autowired
    private RabbitTemplate template;

    @Autowired
    private Queue notificationQueue;

    public void sendNotification(String message) {
        this.template.convertAndSend(notificationQueue.getName(), message);
        System.out.println(" [x] Sent '" + message + "'");
    }

    // RabbitMQ Queue Configuration (in a @Configuration class)
    // @Bean
    // public Queue notificationQueue() {
    //     return new Queue("my.notification.queue");
    // }
}

ඊළඟට, ඒ messages listen කරන (consume කරන) Service එකක් හදන හැටි:


import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class NotificationListener {

    @RabbitListener(queues = "my.notification.queue")
    public void receiveMessage(String message) {
        System.out.println(" [x] Received '" + message + "'");
        // Process the received message
    }
}

මේ උදාහරණයේ, NotificationPublisher එක my.notification.queue කියන Queue එකට message එකක් යවනවා. NotificationListener එක @RabbitListener Annotation එක පාවිච්චි කරලා ඒ Queue එකෙන් messages අහගෙන ඉන්නවා. Message එකක් ආවම receiveMessage method එක trigger වෙනවා.

කවදාද මොනවා පාවිච්චි කරන්නේ? (When to use which?)

මේ ප්‍රශ්නයට නිශ්චිත උත්තරයක් නැහැ. ඒක ගොඩක් දුරට ඔයා හදන Application එකේ requirement වලට අනුව වෙනස් වෙනවා.

  • Synchronous (REST APIs) පාවිච්චි කරන්න:
    • ඉක්මන් ප්‍රතිචාර (Real-time Interaction) ඕන තැන්වල: User Interface එකට වහාම data ඕන වෙලාවට. උදාහරණයක් විදිහට, User login කරනකොට Authentication Service එකක් එක්ක කතා කරනවා වගේ.
    • Transactions (ගනුදෙනු) තියෙන තැන්වල: එක request එකක් අසාර්ථක වුණොත් මුළු operation එකම revert කරන්න ඕන වෙලාවට.
    • සරල communication (Simple Communication): Services දෙකක් අතර direct, සරල interaction එකක් ඕන වෙලාවට.
  • Asynchronous (Message Queues) පාවිච්චි කරන්න:
    • වැඩි දියුණු කළ ඔරොත්තු දීමේ හැකියාව (High Resilience) ඕන තැන්වල: Services down වුණත් data නැති වෙන්න බැරි, system එක දිගටම වැඩ කරන්න ඕන තැන්වල.
    • Scalability ඕන තැන්වල: Requests ගොඩක් එකවර එන අවස්ථාවල, parallel processing ඕන තැන්වල. උදාහරණයක් විදිහට, E-commerce order එකක් create වුණාම, inventory update කරන්න, email notification යවන්න, billing process කරන්න වගේ දේවල් කරන්න.
    • Decoupling ඕන තැන්වල: Services අතර dependency එක අඩු කරන්න ඕන තැන්වල.
    • Background Processing: user ට immediate feedback එකක් ඕන නැති, වෙලාව යන operations වලට.

අවසාන වශයෙන් (Conclusion)

Microservices Architecture එකේ සාර්ථකත්වය තීරණය වෙන්නේ ඒ services කොච්චර හොඳට communication කරනවද කියන එක මත. Synchronous (REST APIs) සහ Asynchronous (Message Queues) කියන මේ ක්‍රම දෙකටම තමන්ගේම වාසි අවාසි තියෙනවා. හරිම තීරණය ගන්න නම්, ඔයාගේ system එකේ specific requirements, Scalability needs, Resilience requirements වගේ දේවල් ගැන හිතන්න ඕන.

හොඳම approach එක තමයි මේ දෙකම එකට පාවිච්චි කරන එක (Hybrid Approach). එතකොට ඔයාගේ Application එකේ හැම කොටසකටම ගැලපෙන communication pattern එකක් තෝරගන්න පුළුවන්.

මේ ගැන ඔයාලට තව මොනවා හරි දැනගන්න ඕන නම්, නැත්නම් මේක ගැන ඔයාලගේ අත්දැකීම් මොනවද කියලා පහළින් comment කරන්න! මේ concepts ඔයාලගේ project වලට අත්හදා බලන්න අමතක කරන්න එපා. ජයවේවා!