Mikroservisler Arasında Güvenilir İletişim: Transactional Outbox Pattern'a Derinlemesine Bir Bakış
[en] Read in English 2023-06-27
1. Giriş: Transactional Outbox Pattern’ın Tanımı ve Kullanım Alanları
Transactional Outbox Pattern, mikroservisler arasında güvenilir bir şekilde iletişim kurmayı sağlayan bir tasarım modelidir. Bu model, bir mikroservisin veritabanı işlemlerini ve dışarıya mesaj gönderme işlemlerini aynı işlem içinde gerçekleştirmesini sağlar. Bu sayede, her iki işlem de başarılı olur veya başarısız olur, böylece veri tutarlılığı sağlanır.
Bu modelin temel bileşenleri bir “Outbox” tablosu ve bir “Publisher” servisidir. İşlem gerçekleştiğinde, mikroservis önce veritabanında bir işlem gerçekleştirir ve ardından aynı işlem içinde Outbox tablosuna bir mesaj yazar. Publisher servisi daha sonra bu mesajı alır ve bir mesaj brokerına (örneğin, RabbitMQ, Kafka vb.) gönderir.
Transactional Outbox Pattern, özellikle aşağıdaki durumlarda kullanılır:
-
Veri tutarlılığı gerektiren durumlar: Bir mikroservis, bir işlemi gerçekleştirdikten sonra diğer mikroservislere bilgi vermek isteyebilir. Bu durumda, veritabanı işlemi ve mesaj gönderme işlemi aynı işlem içinde gerçekleşmelidir. Eğer herhangi biri başarısız olursa, her ikisi de geri alınmalıdır.
-
Yüksek erişilebilirlik gerektiren durumlar: Eğer bir mikroservis çökerse, Outbox tablosunda bekleyen mesajlar kaybolmaz. Publisher servisi mikroservis tekrar çalışmaya başladığında bu mesajları alabilir ve işleyebilir.
Aşağıdaki diyagram, Transactional Outbox Pattern’ın işleyişini göstermektedir:
Bu diyagramda, bir istemci bir servise istek gönderir. Servis, bu isteği Outbox’a yazar ve istemciye işlemin başarıyla gerçekleştiğini bildirir. Daha sonra, Publisher servisi Outbox’dan mesajı alır ve bir Message Broker’a gönderir. Son olarak, Publisher mesajı Outbox’dan siler. Bu, Transactional Outbox Pattern’ın temel işleyişidir.
2. Transactional Outbox Pattern’ın Temel Bileşenleri: Outbox Tablosu ve Transactional Outbox Publisher
Transactional Outbox Pattern’ın iki ana bileşeni vardır: Outbox Tablosu ve Transactional Outbox Publisher. Bu bileşenler, bu modelin işleyişinde kritik bir rol oynar.
Outbox Tablosu: Bu tablo, bir mikroservis tarafından gönderilecek mesajları saklar. Bir mikroservis, bir işlemi gerçekleştirdiğinde, bu işlemle ilgili bilgileri içeren bir mesajı Outbox tablosuna yazar. Bu mesaj, daha sonra bir Message Broker’a gönderilmek üzere bekler.
Transactional Outbox Publisher: Bu servis, Outbox tablosundan mesajları alır ve bir Message Broker’a gönderir. Mesaj başarıyla gönderildikten sonra, Publisher servisi bu mesajı Outbox tablosundan siler.
3. Transactional Outbox Pattern’ın Çalışma Prensibi
Transactional Outbox Pattern’ın çalışma prensibi, bir dizi adımdan oluşur. Bu adımlar, bir mikroservisin bir işlemi gerçekleştirdiği ve bu işlemle ilgili bir mesajı bir Message Broker’a gönderdiği süreci kapsar.
Aşağıda, Transactional Outbox Pattern’ın çalışma prensibini adım adım gösteren bir diyagram bulunmaktadır:
Bu diyagramda, aşağıdaki adımlar gösterilmektedir:
-
İstemci, bir servise istek gönderir: Bu, bir işlemi başlatmak için yapılan bir istek olabilir.
-
Servis, bir veritabanı işlemi gerçekleştirir: Bu, bir veri ekleme, güncelleme veya silme işlemi olabilir.
-
Servis, Outbox’a bir mesaj yazar: Bu mesaj, gerçekleştirilen işlemle ilgili bilgileri içerir.
-
Servis, istemciye işlemin başarıyla gerçekleştiğini bildirir: Bu, istemcinin işlemin durumunu bilmesini sağlar.
-
Publisher, Outbox’dan mesajı alır: Publisher, Outbox’da bekleyen mesajları alır ve işler.
-
Publisher, mesajı bir Message Broker’a gönderir: Bu, mesajın diğer mikroservislerle paylaşılmasını sağlar.
-
Publisher, mesajı Outbox’dan siler: Bu, mesajın başarıyla işlendiğini ve artık gerekli olmadığını belirtir.
Bu adımlar, Transactional Outbox Pattern’ın temel çalışma prensibini oluşturur. Bu model, mikroservisler arasında güvenilir ve tutarlı bir iletişim sağlar.
4. Transactional Outbox Pattern’ın Avantajları
Transactional Outbox Pattern, mikroservisler arasında güvenilir ve tutarlı bir iletişim sağlar. Bu modelin birçok avantajı vardır:
Veri Tutarlılığı: Transactional Outbox Pattern, bir mikroservisin veritabanı işlemlerini ve dışarıya mesaj gönderme işlemlerini aynı işlem içinde gerçekleştirmesini sağlar. Bu sayede, her iki işlem de başarılı olur veya başarısız olur, böylece veri tutarlılığı sağlanır.
İşlem Güvenliği: Eğer bir mikroservis çökerse, Outbox tablosunda bekleyen mesajlar kaybolmaz. Publisher servisi mikroservis tekrar çalışmaya başladığında bu mesajları alabilir ve işleyebilir.
Yüksek Erişilebilirlik: Outbox tablosu ve Publisher servisi, bir mikroservisin çökmesi durumunda bile mesajların güvenli bir şekilde iletilmesini sağlar. Bu, mikroservisler arasında yüksek erişilebilirlik sağlar.
5. Transactional Outbox Pattern’ın Dezavantajları
Her ne kadar Transactional Outbox Pattern, mikroservisler arasında güvenilir ve tutarlı bir iletişim sağlasa da, bu modelin bazı dezavantajları da bulunmaktadır:
Ekstra Depolama ve İşlem Maliyeti: Outbox tablosu, bir mikroservis tarafından gönderilecek mesajları saklar. Bu, ekstra depolama alanı gerektirir. Ayrıca, Publisher servisinin Outbox’dan mesajları alıp bir Message Broker’a göndermesi, ekstra işlem maliyeti oluşturur.
Zamanlama ve Sıralama Sorunları: Publisher servisi, Outbox’dan mesajları alır ve bir Message Broker’a gönderir. Ancak, bu işlem asenkron olduğu için, mesajların sırası karışabilir. Bu, zamanlama ve sıralama sorunlarına neden olabilir.
Kompleksite: Transactional Outbox Pattern, bir mikroservisin işleyişini daha karmaşık hale getirir. Bir mikroservis, hem veritabanı işlemlerini hem de dışarıya mesaj gönderme işlemlerini yönetmek zorundadır. Bu, hata olasılığını artırabilir ve hata ayıklamayı zorlaştırabilir.
Bu dezavantajlar, Transactional Outbox Pattern’ın kullanımını sınırlayabilir. Ancak, bu modelin sunduğu avantajlar, genellikle bu dezavantajları aşar. Bu nedenle, Transactional Outbox Pattern, mikroservisler arasında güvenilir ve tutarlı bir iletişim sağlamak için sıkça kullanılan bir modeldir.
6. Transactional Outbox Pattern’ın Uygulama Örnekleri: Gerçek Dünya C# ve Go Örnekleri
Transactional Outbox Pattern’ın gerçek dünya uygulamalarını daha iyi anlamak için C# ve Go dillerinde örnekler üzerinden gidelim.
C# Örneği
C#‘ta Transactional Outbox Pattern’ı uygulamak için Entity Framework Core gibi bir ORM (Object-Relational Mapping) aracı kullanabiliriz. Bu örnekte, bir e-ticaret uygulamasında bir siparişin oluşturulduğunu ve bu siparişin detaylarının bir Outbox tablosuna yazıldığını düşünelim:
public class OrderService
{
private readonly DbContext _context;
public OrderService(DbContext context)
{
_context = context;
}
public async Task CreateOrderAsync(Order order)
{
using var transaction = _context.Database.BeginTransaction();
try
{
// Save the order to the database
_context.Orders.Add(order);
await _context.SaveChangesAsync();
// Write a message to the outbox
var outboxMessage = new OutboxMessage(order.Id, "OrderCreated", JsonConvert.SerializeObject(order));
_context.OutboxMessages.Add(outboxMessage);
await _context.SaveChangesAsync();
await transaction.CommitAsync();
}
catch
{
await transaction.RollbackAsync();
throw;
}
}
}
Bu örnekte, CreateOrderAsync
metodu bir veritabanı işlemi başlatır. Bu işlem içinde, sipariş veritabanına kaydedilir ve ardından Outbox tablosuna bir mesaj eklenir. Eğer herhangi bir hata oluşursa, işlem geri alınır.
Go Örneği
Go’da Transactional Outbox Pattern’ı uygulamak için SQL işlemlerini kullanabiliriz. Bu örnekte, bir kullanıcının kaydının oluşturulduğunu ve bu kullanıcının detaylarının bir Outbox tablosuna yazıldığını düşünelim:
package main
import (
"database/sql"
"encoding/json"
"log"
)
type User struct {
Id int
Name string
Email string
}
type OutboxMessage struct {
EntityId int
EntityType string
EventType string
Payload string
}
func CreateUser(db *sql.DB, user User) error {
tx, err := db.Begin()
if err != nil {
return err
}
// Save the user to the database
_, err = tx.Exec("INSERT INTO Users (Name, Email) VALUES (?, ?)", user.Name, user.Email)
if err != nil {
tx.Rollback()
return err
}
// Write a message to the outbox
payload, err := json.Marshal(user)
if err != nil {
tx.Rollback()
return err
}
outboxMessage := OutboxMessage{EntityId: user.Id, EntityType: "User", EventType: "UserCreated", Payload: string(payload)}
_, err = tx.Exec("INSERT INTO Outbox (EntityId, EntityType, EventType, Payload) VALUES (?, ?, ?, ?)", outboxMessage.EntityId, outboxMessage.EntityType, outboxMessage.EventType, outboxMessage.Payload)
if err != nil {
tx.Rollback()
return err
}
return tx.Commit()
}
7. Transactional Outbox Pattern’ın Alternatifleri
Transactional Outbox Pattern, mikroservisler arasında güvenilir ve tutarlı bir iletişim sağlamak için kullanılan bir modeldir. Ancak, bu modelin alternatifleri de bulunmaktadır. İki popüler alternatif, Two-Phase Commit (2PC) ve Saga Pattern’dir.
Two-Phase Commit (2PC): Bu model, bir işlemi birden çok kaynak arasında atomik bir şekilde gerçekleştirmek için kullanılır. İlk aşamada, işlem koordinatörü tüm kaynaklara işlemi gerçekleştirme niyetini bildirir. Eğer tüm kaynaklar olumlu yanıt verirse, ikinci aşamada işlem koordinatörü tüm kaynaklara işlemi gerçekleştirmelerini söyler. Ancak, bu modelin bir dezavantajı vardır: Eğer bir kaynak çökerse veya ağda bir hata oluşursa, tüm işlem durur ve kaynaklar kilitlenir.
Saga Pattern: Bu model, bir işlemi birden çok adıma böler ve her adımı ayrı bir işlem olarak gerçekleştirir. Eğer bir adımda bir hata oluşursa, Saga Pattern önceki adımların tersini gerçekleştirir (compensating transactions). Bu model, uzun süreli işlemler ve mikroservisler arasında işlemler için uygundur. Ancak, bu modelin bir dezavantajı vardır: İşlemlerin sırası ve zamanlaması karmaşık olabilir ve hata durumunda geri alınması gereken işlemlerin yönetilmesi zor olabilir.
Bu alternatifler, Transactional Outbox Pattern’ın sunduğu avantajları sunar, ancak kendi dezavantajlarına sahiptirler. Hangi modelin kullanılacağı, uygulamanın gereksinimlerine ve kısıtlamalarına bağlıdır.
Sonuç: Transactional Outbox Pattern’ın Genel Değerlendirmesi
Transactional Outbox Pattern, mikroservisler arasında güvenilir ve tutarlı bir iletişim sağlamak için etkili bir modeldir. Bu model, bir mikroservisin veritabanı işlemlerini ve dışarıya mesaj gönderme işlemlerini aynı işlem içinde gerçekleştirmesini sağlar. Bu sayede, her iki işlem de başarılı olur veya başarısız olur, böylece veri tutarlılığı sağlanır.
Bu modelin birçok avantajı vardır. Öncelikle, veri tutarlılığı sağlar. İkincil olarak, işlem güvenliği sunar. Eğer bir mikroservis çökerse, Outbox tablosunda bekleyen mesajlar kaybolmaz. Üçüncü olarak, yüksek erişilebilirlik sağlar. Outbox tablosu ve Publisher servisi, bir mikroservisin çökmesi durumunda bile mesajların güvenli bir şekilde iletilmesini sağlar.
Ancak, bu modelin bazı dezavantajları da vardır. Ekstra depolama ve işlem maliyeti gerektirir. Zamanlama ve sıralama sorunlarına neden olabilir. Ayrıca, bir mikroservisin işleyişini daha karmaşık hale getirir.
Bu modelin alternatifleri arasında Two-Phase Commit (2PC) ve Saga Pattern bulunur. Ancak, hangi modelin kullanılacağı, uygulamanın gereksinimlerine ve kısıtlamalarına bağlıdır.
Sonuç olarak, Transactional Outbox Pattern, mikroservisler arasında güvenilir ve tutarlı bir iletişim sağlamak için önemli bir modeldir. Bu modelin gelecekte daha fazla otomasyon, entegrasyon, performans ve ölçeklenebilirlik sunması beklenmektedir. Bu nedenle, Transactional Outbox Pattern’ın kullanımı, modern yazılım geliştirme uygulamalarında önemli bir yer tutmaya devam edecektir.