Sharing Data Between Microservices: Verinin Mikroservisler Arasında Paylaşımı
Veriyi Paylaşmak Neden Bu Kadar Zor?
Bir mikroservis mimarisinde, veriyi paylaşmak, aynı anda birden fazla iş yapmaya çalışmak gibi karmaşık bir süreçtir. Her mikroservisin kendi veritabanına sahip olması, bağımsızlık ve esneklik sağlar, ancak aynı zamanda veri paylaşımı ve tutarlılığı gibi zorlukları da beraberinde getirir.
Bu yazıda, mikroservisler arasında veri paylaşımının neden zor olduğunu, bu zorlukları aşmak için hangi yöntemlerin kullanıldığını ve hangi araçların bu süreçte işinize yarayacağını detaylı bir şekilde anlatacağız.
Mikroservislerde Veri Paylaşımı: Temel Zorluklar
- Tutarlılık (Consistency): Her mikroservis kendi veritabanına sahip olduğunda, veri tutarlılığı sağlamak zor olabilir. Bir mikroserviste yapılan değişikliklerin diğer mikroservislerde de güncellenmesi gerekir.
- Bağımsızlık (Independence): Mikroservisler bağımsız olmalıdır, ancak veri paylaşımı bu bağımsızlığı tehlikeye atabilir. Bir mikroservis diğerine bağımlı hale gelebilir.
- Veri Senkronizasyonu (Data Synchronization): Farklı veritabanları arasında verinin senkronize edilmesi karmaşık ve hataya açık bir süreçtir. Aynı verinin farklı mikroservislerde aynı anda güncellenmesi tutarsızlıklara yol açabilir.
Veri Paylaşımı Yöntemleri
1. API Tabanlı İletişim
API tabanlı iletişim, mikroservisler arasında veri paylaşımının en yaygın yöntemlerinden biridir. Bir mikroservis, başka bir mikroservisin API’sine istek göndererek veri alabilir veya gönderebilir. Bu yöntem, mikroservislerin birbirleriyle doğrudan etkileşime geçmesini sağlar.
Örnek: RestTemplate Kullanarak API İletişimi
import org.springframework.web.client.RestTemplate;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
private final RestTemplate restTemplate;
public OrderService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public void placeOrder(String productId, int quantity) {
// Sipariş işlemi
String response = restTemplate.getForObject("http://inventory-service/api/stock/" + productId, String.class);
// Stok kontrolü ve güncelleme işlemleri
}
}
Yukarıdaki örnekte, OrderService
sınıfı, RestTemplate
kullanarak inventory-service
mikroservisinin API'sine istek gönderir. Bu şekilde stok bilgilerini alır ve sipariş işlemini gerçekleştirir.
Örnek: Feign Client Kullanarak API İletişimi
Spring Cloud Feign, RESTful web servisleri çağırmak için kullanımı kolay bir HTTP istemcisi sağlar.
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name = "inventory-service")
public interface InventoryClient {
@GetMapping("/api/stock/{productId}")
String getStock(@PathVariable("productId") String productId);
}
OrderService
sınıfı, InventoryClient
arayüzünü kullanarak stok bilgilerini alır.
import org.springframework.stereotype.Service;
@Service
public class OrderService {
private final InventoryClient inventoryClient;
public OrderService(InventoryClient inventoryClient) {
this.inventoryClient = inventoryClient;
}
public void placeOrder(String productId, int quantity) {
String stock = inventoryClient.getStock(productId);
// Stok kontrolü ve güncelleme işlemleri
}
}
2. Mesajlaşma Sistemleri
Mesajlaşma sistemleri, mikroservisler arasında veri paylaşımını asenkron bir şekilde gerçekleştirir. Bu yöntem, veri tutarlılığını sağlamak ve bağımsızlığı korumak için etkilidir. Mikroservisler arasında mesajlar gönderilir ve alıcı mikroservisler bu mesajları işler.
Örnek: Kafka Kullanarak Mesajlaşma
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.Producer;
import org.apache.kafka.clients.producer.ProducerRecord;
import java.util.Properties;
public class InventoryService {
private Producer<String, String> producer;
public InventoryService() {
Properties properties = new Properties();
properties.put("bootstrap.servers", "localhost:9092");
properties.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
properties.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
producer = new KafkaProducer<>(properties);
}
public void updateStock(String productId, int quantity) {
// Stok güncelleme işlemi
producer.send(new ProducerRecord<>("stock-updates", productId, Integer.toString(quantity)));
}
}
Bu örnekte, InventoryService
sınıfı, Kafka kullanarak stok güncellemelerini stock-updates
konusuna (topic) gönderir. Bu şekilde diğer mikroservisler bu güncellemeleri dinleyebilir ve işleyebilir.
Kafka Consumer Örneği
import org.apache.kafka.clients.consumer.Consumer;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import java.util.Collections;
import java.util.Properties;
public class OrderService {
private Consumer<String, String> consumer;
public OrderService() {
Properties properties = new Properties();
properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
properties.put(ConsumerConfig.GROUP_ID_CONFIG, "order-service-group");
properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");
properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");
consumer = new KafkaConsumer<>(properties);
consumer.subscribe(Collections.singletonList("stock-updates"));
}
public void consumeStockUpdates() {
while (true) {
ConsumerRecords<String, String> records = consumer.poll(100);
for (ConsumerRecord<String, String> record : records) {
String productId = record.key();
int quantity = Integer.parseInt(record.value());
// Stok güncelleme işlemlerini burada yapın
}
}
}
}
Bu örnekte, OrderService
sınıfı, Kafka kullanarak stock-updates
konusundan gelen mesajları tüketir. Bu mesajlar, stok güncellemelerini içerir ve OrderService
bu güncellemeleri işler.
3. Veri Replikasyonu
Veri replikasyonu, bir mikroservisin verisinin başka bir mikroservis tarafından kopyalanmasını içerir. Bu yöntem, veri tutarlılığını sağlamak için kullanılabilir. Özellikle okuma ağırlıklı sistemlerde veri replikasyonu, performans artışı sağlar.
Örnek: Debezium Kullanarak Değişiklik Veri Yakalama (CDC)
Debezium, veri tabanı değişikliklerini yakalayan ve bu değişiklikleri Kafka gibi bir mesajlaşma sistemine gönderen bir araçtır.
PostgreSQL İçin Debezium Konfigürasyonu
<connector class="io.debezium.connector.postgresql.PostgresConnector">
<tasks.max>1</tasks.max>
<database.hostname>localhost</database.hostname>
<database.port>5432</database.port>
<database.user>postgres</database.user>
<database.password>password</database.password>
<database.dbname>inventory</database.dbname>
<database.server.name>dbserver1</database.server.name>
<table.include.list>public.products</table.include.list>
</connector>
Debezium, PostgreSQL veritabanındaki products
tablosundaki değişiklikleri yakalar ve Kafka konusuna gönderir.
Kafka Consumer İle Debezium Mesajlarını İşleme
import org.apache.kafka.clients.consumer.Consumer;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import java.util.Collections;
import java.util.Properties;
public class ProductService {
private Consumer<String, String> consumer;
public ProductService() {
Properties properties = new Properties();
properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
properties.put(ConsumerConfig.GROUP_ID_CONFIG, "product-service-group");
properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");
properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");
consumer = new KafkaConsumer<>(properties);
consumer.subscribe(Collections.singletonList("dbserver1.public.products"));
}
public void consumeProductUpdates() {
while (true) {
ConsumerRecords<String, String> records = consumer.poll(100);
for (ConsumerRecord<String, String> record : records) {
String productUpdate = record.value();
// Ürün güncellemelerini burada işleyin
}
}
}
}
Bu örnekte, ProductService
sınıfı, Debezium tarafından yakalanan ve Kafka'ya gönderilen ürün güncellemelerini tüketir ve işler.
Online Alışveriş Senaryosu
Bir online alışveriş sitesinde, sipariş mikroservisi (order service) ve stok mikroservisi (inventory service) arasındaki veri paylaşımını düşünün. Sipariş mikroservisi, bir ürün sipariş edildiğinde stok mikroservisine bilgi gönderir ve stok güncellenir. Bu senaryoda, API tabanlı iletişim veya mesajlaşma sistemleri kullanılabilir.
Özet ve Sonuç
Mikroservisler arasında veri paylaşımı, çeşitli zorluklar ve karmaşıklıklar içerir. API tabanlı iletişim, mesajlaşma sistemleri ve veri replikasyonu gibi yöntemler, bu zorlukları aşmak için kullanılabilir. Her yöntemin avantajları ve dezavantajları vardır; bu nedenle, uygulamanızın gereksinimlerine ve mimari tercihlerine göre en uygun yöntemi seçmek önemlidir.
Veri paylaşımı yöntemlerini doğru kullanarak, mikroservislerinizin performansını ve verimliliğini artırabilirsiniz. Bu bilgilerle daha güçlü ve esnek mikroservis mimarileri tasarlayabilirsiniz!
Kaynaklar
- Building Microservices by Sam Newman
- Designing Data-Intensive Applications by Martin Kleppmann
Mutlu kodlamalar! 🚀