İçeriğe geç

Microservices İletişim Desenleri

T
Tolgahan
· · 13 dk okuma · 493 görüntülenme

Microservices Neden Farklı İletişim Gerektirir?

Monolith mimaride tüm bileşenler aynı process içinde çalışır. Bir servisin diğerini çağırması basit bir method invocation'dır — hızlıdır, güvenilirdir, transaction yönetimi kolaydır. Microservice mimarisine geçtiğinizde her şey değişir: servisler farklı process'lerde, farklı makinelerde, hatta farklı data center'larda çalışabilir. Network her zaman güvenilir değildir, latency vardır, servisler geçici olarak erişilemez olabilir.

Bu yazıda microservice'ler arası iletişim desenlerini, her birinin avantaj ve dezavantajlarını, ve hangi senaryoda hangisinin kullanılacağını inceleyeceğiz.

Sync vs Async İletişim

Microservice iletişimi temelde iki kategoriye ayrılır:

Synchronous (Senkron): İstemci istek gönderir ve yanıt gelene kadar bekler. HTTP/REST ve gRPC bu kategoridedir.

Asynchronous (Asenkron): İstemci mesajı gönderir ve yanıt beklemez. Message Queue (RabbitMQ, Kafka) bu kategoridedir.

Senkron:
Client --> Service A --> Service B --> Response --> Client
          (bekler)      (bekler)

Asenkron:
Client --> Service A --> Message Queue
                              |
                         Service B (mesajı işler)
Client <-- Response (hemen)

Kural: Kullanıcının anında yanıt beklediği işlemlerde senkron, arka planda çalışabilecek işlemlerde asenkron kullanın.

REST: Basit ve Yaygın

REST, microservice iletişiminde en yaygın kullanılan protokoldür. HTTP tabanlıdır, her dil ve platform tarafından desteklenir.

// Spring Boot ile RestTemplate (senkron)
@Service
public class OrderService {

    private final RestTemplate restTemplate;

    public OrderService(RestTemplateBuilder builder) {
        this.restTemplate = builder
            .setConnectTimeout(Duration.ofSeconds(3))
            .setReadTimeout(Duration.ofSeconds(5))
            .build();
    }

    public ProductDto getProduct(Long productId) {
        return restTemplate.getForObject(
            "http://product-service/api/products/{id}",
            ProductDto.class,
            productId
        );
    }
}

Spring Boot 3 ile gelen RestClient daha modern bir alternatiftir:

@Service
public class OrderService {

    private final RestClient restClient;

    public OrderService(RestClient.Builder builder) {
        this.restClient = builder
            .baseUrl("http://product-service")
            .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
            .build();
    }

    public ProductDto getProduct(Long productId) {
        return restClient.get()
            .uri("/api/products/{id}", productId)
            .retrieve()
            .body(ProductDto.class);
    }
}

REST Avantajları: Basit, yaygın, debugging kolay, tarayıcıdan test edilebilir.

REST Dezavantajları: Text-based (JSON) — binary'ye göre yavaş, HTTP overhead, sıkı coupling riski.

gRPC: Yüksek Performans

gRPC, Google tarafından geliştirilen yüksek performanslı bir RPC framework'üdür. Protocol Buffers (protobuf) ile binary serialization kullanır ve HTTP/2 üzerinde çalışır.

// product.proto - Servis tanımı
syntax = "proto3";
package product;

service ProductService {
    rpc GetProduct (ProductRequest) returns (ProductResponse);
    rpc ListProducts (ListRequest) returns (stream ProductResponse);
}

message ProductRequest {
    int64 id = 1;
}

message ProductResponse {
    int64 id = 1;
    string name = 2;
    double price = 3;
    string category = 4;
}

Spring Boot ile gRPC entegrasyonu:

@GrpcService
public class ProductGrpcService extends ProductServiceGrpc.ProductServiceImplBase {

    private final ProductRepository productRepository;

    @Override
    public void getProduct(ProductRequest request,
                          StreamObserver<ProductResponse> responseObserver) {
        Product product = productRepository.findById(request.getId())
            .orElseThrow(() -> new StatusRuntimeException(Status.NOT_FOUND));

        ProductResponse response = ProductResponse.newBuilder()
            .setId(product.getId())
            .setName(product.getName())
            .setPrice(product.getPrice())
            .build();

        responseObserver.onNext(response);
        responseObserver.onCompleted();
    }
}

gRPC Avantajları: REST'e göre 7-10x hızlı (binary + HTTP/2), type-safe (protobuf), bidirectional streaming.

gRPC Dezavantajları: Tarayıcıdan direkt çağrılamaz, debugging zor, protobuf öğrenme eğrisi.

Message Queue: Asenkron İletişim

RabbitMQ ve Apache Kafka, asenkron iletişimin temel araçlarıdır. Servisler arasına bir kuyruk koyarak gevşek bağlılık (loose coupling) sağlarsınız.

RabbitMQ ile Mesajlaşma

// Producer (sipariş servisi)
@Service
public class OrderEventPublisher {

    private final RabbitTemplate rabbitTemplate;

    public void publishOrderCreated(Order order) {
        OrderCreatedEvent event = new OrderCreatedEvent(
            order.getId(),
            order.getCustomerId(),
            order.getTotalAmount(),
            Instant.now()
        );
        rabbitTemplate.convertAndSend("orders.exchange", "order.created", event);
    }
}

// Consumer (bildirim servisi)
@Service
public class NotificationService {

    @RabbitListener(queues = "notification.order.created")
    public void handleOrderCreated(OrderCreatedEvent event) {
        // Email gonder
        emailService.sendOrderConfirmation(event.customerId(), event.orderId());
        // Push notification gonder
        pushService.notify(event.customerId(), "Siparisiz alindi!");
    }
}

RabbitMQ vs Kafka

  • RabbitMQ: Geleneksel message broker, routing kuralları esnek, mesaj silinir (consume sonrası), düşük-orta throughput

  • Kafka: Distributed event log, mesajlar saklanır (retention), çok yüksek throughput, event sourcing için ideal

Kural olarak: RabbitMQ task distribution ve point-to-point iletişim için, Kafka event streaming ve event sourcing için tercih edilir.

Circuit Breaker Pattern

Bir microservice'in bağımlı olduğu servis çöktüğünde ne olur? İstek timeout'a gider, thread pool dolar, cascade failure başlar ve tüm sistem çöker. Circuit Breaker bu felaketi önler.

// Resilience4j ile Circuit Breaker
@Service
public class ProductService {

    private final RestClient restClient;

    @CircuitBreaker(name = "productService", fallbackMethod = "getProductFallback")
    @Retry(name = "productService")
    @TimeLimiter(name = "productService")
    public ProductDto getProduct(Long productId) {
        return restClient.get()
            .uri("/api/products/{id}", productId)
            .retrieve()
            .body(ProductDto.class);
    }

    // Fallback: servis erişilemezken cache'den veya default değer dön
    private ProductDto getProductFallback(Long productId, Throwable t) {
        return new ProductDto(productId, "Urun bilgisi geçici olarak kullanilamıyor", 0.0);
    }
}
# application.yml
resilience4j:
  circuitbreaker:
    instances:
      productService:
        slidingWindowSize: 10
        failureRateThreshold: 50
        waitDurationInOpenState: 30s
        permittedNumberOfCallsInHalfOpenState: 3
  retry:
    instances:
      productService:
        maxAttempts: 3
        waitDuration: 1s

Circuit Breaker üç durumda çalışır:

  • CLOSED: Normal çalışma, istekler geçer

  • OPEN: Hata oranı eşiği aştı, istekler direkt fallback'e yönlendirilir

  • HALF-OPEN: Belirli süre sonra test istekleri geçirilir, başarılıysa CLOSED'a döner

Saga Pattern: Distributed Transaction

Microservice'lerde birden fazla servisi kapsayan transaction yönetimi en zor problemlerden biridir. Saga Pattern, büyük bir transaction'ı küçük local transaction'lara böler.

Choreography-based Saga: Servisler event'ler aracılığıyla birbirleriyle iletişim kurar. Merkezi orkestrasyon yoktur.

Order Service --> "order.created" --> Payment Service
Payment Service --> "payment.completed" --> Inventory Service
Inventory Service --> "inventory.reserved" --> Shipping Service

// Hata durumunda compensating transaction:
Payment Service --> "payment.failed" --> Order Service (rollback)

Orchestration-based Saga: Merkezi bir orkestratör (saga manager) tüm adımları yönetir.

@Service
public class OrderSaga {

    public void createOrder(CreateOrderRequest request) {
        // Step 1: Siparis olustur
        Order order = orderService.create(request);

        try {
            // Step 2: Odeme al
            paymentService.charge(order.getCustomerId(), order.getTotalAmount());

            // Step 3: Stok ayir
            inventoryService.reserve(order.getItems());

            // Step 4: Kargo olustur
            shippingService.schedule(order);

            order.setStatus(OrderStatus.CONFIRMED);
        } catch (PaymentException e) {
            // Compensate: Siparisi iptal et
            orderService.cancel(order.getId());
            throw e;
        } catch (InventoryException e) {
            // Compensate: Odemeyi iade et, siparisi iptal et
            paymentService.refund(order.getCustomerId(), order.getTotalAmount());
            orderService.cancel(order.getId());
            throw e;
        }
    }
}

Event-Driven Architecture

Event-Driven Architecture (EDA), tüm sistemin event'ler etrafında tasarlandığı bir mimari yaklaşımdır. Servisler event üretir ve tüketir, birbirlerini doğrudan tanımazlar.

// Domain Event
public record OrderCreatedEvent(
    String eventId,
    Long orderId,
    Long customerId,
    BigDecimal totalAmount,
    List<OrderItemEvent> items,
    Instant occurredAt
) {}

// Event Producer
@Service
@Transactional
public class OrderService {

    private final ApplicationEventPublisher eventPublisher;

    public Order createOrder(CreateOrderRequest request) {
        Order order = orderRepository.save(new Order(request));
        eventPublisher.publishEvent(new OrderCreatedEvent(
            UUID.randomUUID().toString(),
            order.getId(),
            order.getCustomerId(),
            order.getTotalAmount(),
            mapItems(order.getItems()),
            Instant.now()
        ));
        return order;
    }
}

// Event Consumer
@Service
public class AnalyticsService {

    @EventListener
    public void onOrderCreated(OrderCreatedEvent event) {
        analyticsRepository.save(new OrderAnalytics(
            event.orderId(), event.totalAmount(), event.occurredAt()
        ));
    }
}

Hangi Pattern Ne Zaman?

SenaryoÖnerilen Pattern
Basit CRUD servisleri arasıREST
Yüksek performans, internalgRPC
Arka plan işlemleriMessage Queue
Bildirim, email, logEvent-Driven + MQ
Distributed transactionSaga Pattern
Harici servis çağrısıCircuit Breaker + REST

Özet

  • Sync (REST/gRPC) kullanıcının anında yanıt beklediği yerlerde, Async (MQ) arka plan işlemlerinde kullanın

  • REST basit ve yaygın, gRPC yüksek performanslı internal iletişim için tercih edin

  • Message Queue (RabbitMQ/Kafka) ile servisler arasında loose coupling sağlayın

  • Circuit Breaker ile cascade failure'ı önleyin, fallback stratejisi belirleyin

  • Saga Pattern ile distributed transaction'ları yönetin — choreography veya orchestration

  • Event-Driven Architecture ile tamamen decouple edilmiş, ölçeklenebilir sistemler kurun

Paylaş:
Son güncelleme: Apr 20, 2026

Yorumlar

Giriş yapın ve yorum bırakın.

Henüz yorum yok

Düşüncelerinizi paylaşan ilk siz olun!

Bu yazıyı beğendiniz mi?

Bültene abone olun ve yeni yazılardan ilk siz haberdar olun. Spam yok, söz.

İlgili Yazılar