Microservices İletişim Desenleri
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: 1sCircuit 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, internal | gRPC |
| Arka plan işlemleri | Message Queue |
| Bildirim, email, log | Event-Driven + MQ |
| Distributed transaction | Saga 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
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
Observer Design Pattern: Nesneler Arası Olay Tabanlı İletişim
Observer Design Pattern nedir, nasıl implemente edilir ve gerçek projelerde nasıl kullanılır? Java ve Spring örnekleriyl...
Strategy Design Pattern: Koşullu Mantığı Zarif Kodla Değiştirmenin Yolu
Strategy Design Pattern nedir, neden kullanılır, Java ve Spring Boot ile nasıl uygulanır? if-else cehenneminden kurtulma...
Clean Code: Okunabilir Kod Yazmanın 10 Altın Kuralı
Robert C. Martin'in Clean Code prensiplerinden ilham alan 10 altın kural: anlamlı isimlendirme, küçük fonksiyonlar, DRY,...