Bu serinin ilk bölümünde, temel bir mesajlaşma altyapısı kurup publisher ile broker arasında mesaj gönderiminin nasıl güvenli şekilde yapılabileceği ile ilgili konuşmuş, çeşitli örnekler ile de konuştuklarımızı deneyimlemiştik. Bahsettiğimiz güvenli iletişimi sağlamak için çeşitli yaklaşımlar kullanıp güvenli iletimi sağlamıştık. Yazımızın ikinci bölümünde ise, benimsediğimiz yaklaşımların nelere sebep olabileceği ile ilgili konuşacağız. Devamında da bu etkileri nasıl ortadan kaldırabileceğimiz hakkında çeşitli yaklaşımlar öğreneceğiz.
İsterseniz vakit kaybetmeden devam edelim.
1. Part 1’den Part 2’ye: Reliable Publish Neden Duplicate Mesaj Üretebilir?
Hatırlayacağınız üzere, güvenli iletişimi sağlayabilmek adına mesajlarımızı takip ediyor, broker’dan gönderilen ACK/NACK bildirilerine göre mesajların iletim durumları hakkında bilgi sahibi oluyorduk. Buna ek olarak herhangi bir connection fail durumunda mesajlarımızın iletim durumlarının başarılı yada başarısız olup olmadığı ile ilgili kesin bir kanıya varamayacağımız için bu mesajları tekrardan broker’a publish ediyorduk. Böylece her bir mesajımızın broker’a en az bir kere iletildiğinden emin olduğumuz bir yapıyı kurgulamıştık.
Mesajların en az bir kez iletilmesini bizim başarı kıstasımızdı. Peki ya birden fazla kez iletilirse?
İsterseniz alttaki akış ile bunu canlandıralım:

Gördüğümüz üzere broker’a bir mesaj iletildi, broker da bu mesajı başarılı şekilde alıp ACK bildirisini publisher’ın dinlemesi için yayınladı. Bu sırada bağlantı koptuğu için ACK bildirisi publisher’a iletilemediğinden dolayı publisher tarafı #1 mesajının durumu hakkında bilgiye sahip olamadı.

Her mesajın en az bir kez broker’a iletilmesi gerektiğinden dolayı aynı mesaj tekrardan broker’a üstte görüldüğü üzere iletildi. Mesajın republish edilmesi ile birlikte, publisher kendi sorumluluğı olan ‘at least one delivery ’i tamamlamış oldu. Peki bunu yaparken nelere sebebiyet verdi?
Duplicate Delivery.

Gördüğümüz üzere artık queue’da aynı mesajdan 2 adet var. Peki aynı mesajdan iki adet olması bizim istediğimiz bir şey mi?
Tabii ki de değil.
Bazen bir yeri düzeltmek isterken başka yerleri kaçınılmaz şekilde etkileyebiliyoruz. Mesajlarımızın güvenilir şekilde iletilmesi için bir yaklaşım ortaya koyduk, fakat bu bizi bambaşka bir şekilde etkiledi.
Bize olan etkisini görmek istersek şu örneğe bakabiliriz:

Az önce queue’ya order-created mesajını istemeyerek de olsa iki kopya şeklinde yazmıştık. Bu mesajlar consumer tarafından alındı ve kullanıcının uygulamasına ‘siparişiniz oluşturuldu’ bildiriminin iki defa gönderilmesine sebebiyet verdi. Siz müşteri olsaydınız ne düşünürdünüz?
Ben sipariş geçmişime bakıp ‘acaba yanlışlıkla iki defa sipariş mi verdim?’ diye bakardım.
Bu verdiğim örnek sadece customer’ı endişelendiren, belki de çok önemli olmayan bir örnek. Peki ya bu duplicate message bizim tutarlılığımızı etkileseydi ne olurdu? Belki de geri döndüremeyeceğimiz şeylere yol açacaktı.
Peki burada biz nasıl bir şey olsun isterdik bunu düşünelim.
Aynı mesajdan iki tane var ise, ben birini işlerim, diğerini de pas geçerim. Böylece mesajların duplicate olması beni etkilemez.
Böyle bir çözüm yolu aslında idempotent olmanın ta kendisidir.
Idempotency kelimesinin kökeni aslında matematikten geliyor. Latince “idem” kelimesi “aynı” anlamına gelirken, “potent” ise “etkili” anlamına geliyor, ikisini birleştirince de “aynı etkiye sahip” anlamı çıkıyor.
Idempotency’in ne olduğu ve nasıl kullanıldığı ile ilgili daha geniş bir anlatım isterseniz ilgili yazıma göz atabilirsiniz.
Lafın kısası, duplicate message oluşmasını her zaman engelleyemeyebiliriz; fakat consumer’larımızı idempotent tasarlayarak aynı mesajın birden fazla kez işlenmesinin sistemimiz üzerindeki etkisini ortadan kaldırabiliriz.
2. Idempotent Consumer Nedir ve Nasıl Tasarlanmalıdır?
Idemponent consumer, aynı mesaj birden fazla kez kendisine ulaştığında bu mesajın oluşturacağı iş etkisini yalnızca bir kez gerçekleştiren consumer’dır.
Aslında buradaki temel fikir az önce bahsettiğimiz gibidir:
“Bu mesajı daha önce işledim mi?”
Consumer bir mesajı aldığında, ilk olarak o mesajı benzersiz olarak tanımlayan kimlik bilgisi üzerinden bir kontrol sağlar. Bu kontrol, ilgili mesajın daha önce işlenip işlenmediğinin sorgulanmasıyla ilgilidir. İlgili mesaj daha önceden işlenmediyse işlenir ve o benzersiz kimliğe ait mesajın işlendiğine dair bir bilgi oluşturur. Bundan sonraki süreçte eğer aynı kimliğe sahip bir mesaj gelirse, o kimliğe sahip bir mesajın daha önce işlendiğini kayıtlardan anlayacak ve mesajı işlemeyecektir.
Burada mesaja benzersiz kimlik değerini de MessageId ile sağlıyoruz.
MessageId değeri, ilgili mesaja publish esnasında atadığımız değerdir. Bu değer benzersiz olacak şekilde belirlenir, dolayısıyla her bir mesaj benzersiz bir kimliğe sahiptir.
MessageId değerini şu şekilde oluşturup mesaja atayabiliriz:
...
var messageId = Guid.NewGuid().ToString();
var properties = new BasicProperties
{
MessageId = messageId,
ContentType = "application/json",
Persistent = true
};
await channel.BasicPublishAsync(
exchange: "",
routingKey: "order-created",
mandatory: true,
basicProperties: properties,
body: body
);
Bu şekilde artık publish ettiğimiz her bir mesaj bir kimliğe sahip oluyor.
3. İlk Idempotency Deneyi: ConcurrentDictionary ile Duplicate Kontrolü
Idempotency mantığını ilk olarak en basit haliyle görmek için, consumer tarafında işlenen MessageId değerlerini bir ConcurrentDictionary içerisinde tutabiliriz.
var processedMessages = new ConcurrentDictionary<string, byte>();
Artık işlenen mesajlarımızı burada tutacak, kontrollerimizi de buradan yapacağız. İsterseniz mesaj geldiğinde nasıl davranmamız gerektiğini de tanımlayalım:
consumer.ReceivedAsync += async (sender, args) =>
{
var messageId = .BasicProperties?.MessageId;
if (messageId == null)
{
Console.WriteLine("MessageId is null. Rejecting message.");
await channel.BasicRejectAsync(.DeliveryTag, requeue: false);
return;
}
if (!processedMessages.TryAdd(messageId, 0))
{
Console.WriteLine($"Message {messageId} has already been processed. Acknowledging message.");
await channel.BasicAckAsync(.DeliveryTag, multiple: false);
return;
}
Console.WriteLine($"Processing message {messageId}.");
// ... process the message here ...
await channel.BasicAckAsync(.DeliveryTag, multiple: false);
};
Bu tanımlama ile birlikte artık aynı MessageId değerine sahip mesajı iki kez publish ettiğimizde, ilk mesaj başarıyla işlenirken ikinci mesaj ignore’lanacaktır.

Bu ilk örneğimizde consumer’ımıza temel bir idempotentlik kattık ve genel mantığın nasıl işlediğini gördük. Uygulama içerisinde bir ConcurrentDictionary tutarak mesaj takibini sağladık.
Bu örnekte işlediğimiz mesajları memory’de sakladık. Dolayısıyla mesajlarımızın ömrü, uygulamanın ömrüyle eşdeğer oldu.

Dolayısıyla üstteki GIF’te olduğu gibi, uygulama her çöktüğünde processedMessages kayıtları siliniyor, uygulama her ayağa kalktığında da idempotentlik o anki mesajlar için sağlanmamış oluyor.
Buradan anlıyoruz ki mesajların geçici bir yerde saklanması, consumer’ların her zaman idempotent davranış göstermesi için ideal değil.
Bu sebeple bir diğer başlıkta, işlediğimiz mesajları Inbox Pattern ile birlikte kalıcı bir yerde tutacağız ve consumer’ların idempotent davranışını daha kalıcı hale getireceğiz.
4. Production’da Kalıcı Idempotency: ProcessedMessages ve Inbox Pattern
Consumer tarafında gelen ve işlenen mesajların kalıcı şekilde takip edilmesi için Inbox Pattern yaklaşımını kullanacağız.
Inbox Pattern’i kısaca şu şekilde açıklayabiliriz:
Consumer, işlediği mesajları kalıcı şekilde kaydeder ve aynı mesaj tekrar geldiğinde, bu kayıtları kullanarak ilgili kaydın daha önce işlenip işlenmediğini kontrol eder.
Biz de bu bölümde, Inbox Pattern’i oldukça sade bir şekilde kullanacak, Idempotent şekilde çalışmanın temeli olacak olan ProcessedMessages tablosunu oluşturacağız.
İşlediğimiz mesajları kalıcı olarak tutacağımızı söylemiştik. İsterseniz mesajlarımızı nasıl bir yapıda tutacağımızı ProccessedMessages tablosunu oluşturarak başlayalım.
public class ProcessedMessage
{
public string MessageId { get; set; } = null!;
public DateTime ProcessedAtUtc { get; set; }
}
Bu sınıfımız, veritabanında şöyle bir tabloya tekabül edecektir:

Burada MessageId alanının unique olarak tanımlanması oldukça önemlidir. Çünkü aynı mesaj farklı consumer instance’ları tarafından aynı anda işlenmeye çalışıldığında uygulama seviyesindeki kontrol yeterli olmayabilir. Bu sebeple hem uygulama tarafında, hem de veritabanı tarafında benzersizliği garanti ederek olası duplicate işlemi engellemeyi amaçlıyoruz.
Dipnot: Bu yazıda tek bir consumerdan söz ediyoruz. Eğer birbirinden farklı consumerlarımız varsa ve fanout exchange type’ını kullanıyorsak, ortak tablo kullanımında unique constraint tanımlamasını (MessageId,Consumer) olarak tanımlayabiliriz.
ProcessedMessages tablosunu işlediğimiz mesajların takibini yapmak ve duplicate mesajların işlenmesini engellemek adına oluşturduk.
İsterseniz şimdi de consumer’ın processedMessages tablo ile olan ilişkisinin nasıl olacağını, ne zaman tablo kontrolünün yapılması gerektiğini ve hangi anlarda bu tabloya kayıt eklenmesi gerektiğini kod seviyesinde tanımlayalım.
var messageId = @event.BasicProperties.MessageId;
var alreadyProcessed = await dbContext.ProcessedMessages
.AnyAsync(x => x.MessageId == messageId);
if (alreadyProcessed)
{
Console.WriteLine($"Message {messageId} has already been processed.");
await channel.BasicAckAsync( @event.DeliveryTag, multiple: false);
return;
}
Consumer, mesajı ilk aldığında ilgili mesajı daha öncesinde işleyip işlemediğini kontrol etmesi gerekir. Bu kontrolün sonraki satırlara sarkması, gereksiz yere işlemlerin yapılmasına sebebiyet verebilir ve response’un daha geç oluşmasına sebep olabilir.
Eğer ilgili mesaj daha önce işlenmediyse, mesajı işlemeye devam edebiliriz.
Console.WriteLine($"Processing message {messageId}.");
// Business işlemlerinin burada tamamlandığını farzedelim.
// Örneğin sipariş bildirimi oluşturma, stok güncelleme vb.
dbContext.ProcessedMessages.Add(
new ProcessedMessage {
MessageId = messageId,
ProcessedAtUtc = DateTime.UtcNow }
);
await dbContext.SaveChangesAsync();
await channel.BasicAckAsync( .DeliveryTag, multiple: false);
Mesajı başarıyla işlediğimiz senaryoda ProcessedMessages tablosuna mesajı işlediğimize dair kaydımızı yapıyoruz. Dolayısıyla aynı MessageId’ye sahip mesaj tekrardan consumer’a iletilirse ilgili kaydın varlığı sebebiyle mesaj işlenmeyecek ve idempotent davranışı sağlamış olacağız.
Bu noktada akışımızı şu şekilde görselleştirebiliriz:

Bu yaklaşımımız ile birlikte mesajlarımızın durumunu geçici tutmak yerine kalıcı hale getirdik ve idempotency’in kalıcı hale getirmiş olduk.
Tabii ki burada dikkat etmemiz gereken bir nokta var. Buradaki business process ile ProcessedMessages tablosuna kayıt ekleme işimiz birbiriyle doğrudan ilişkili olduğu aşikar.

İşlem başarılıysa kayıt eklenmeli, değilse eklenmemeli. Peki bu ilişki kod seviyesinde de garanti edildi mi?
5. Business Operation ile Inbox Kaydı Neden Aynı Transaction’da Olmalı?
Bir önceki bölümde consumer’ın işlediği mesajları ProcessedMessages tablosunda saklayarak kalıcı bir idempotency mekanizması oluşturduk.
Fakat burada dikkat etmemiz gereken önemli bir nokta var:
Business operation başarılı olurken ProcessedMessages kaydı oluşturulamazsa ne olur?
İsterseniz bunu basit bir senaryo üzerinden düşünelim.
Consumer bir OrderCreated mesajı aldı ve siparişe ait ürünün stok miktarını 1 azalttı. Yani business operation başarıyla tamamlandı. Fakat hemen sonrasında uygulamanın çöktüğünü ve mesajın işlendiğine dair ProcessedMessages kaydının veritabanına yazılamadığını düşünelim.
Aynı zamanda consumer RabbitMQ’ya ACK gönderemediği için broker, mesajın başarıyla işlendiğinden emin olamaz ve aynı mesajı tekrar consumer’a iletebilir.
Consumer mesajı ikinci kez aldığında ProcessedMessages tablosunu kontrol eder. Ancak önceki işlemde herhangi bir kayıt oluşturulamadığı için ilgili MessageId değerini bulamaz ve mesajı daha önce hiç işlememiş gibi tekrar işler.
Sonuç olarak aynı OrderCreated mesajı iki kez business etkisi oluşturmuş olur:
İlk teslimat → Stok -1
İkinci teslimat → Stok -1
Günün sonunda tek bir sipariş için stok miktarı iki kez azaltılmış olur.
İşte tam olarak bu yüzden, business operation ile mesajın işlendiğine dair Inbox kaydının birbirinden bağımsız işlemler olarak ele alınmaması gerekir.
await using var transaction =
await dbContext.Database.BeginTransactionAsync();
try
{
var alreadyProcessed = await dbContext.ProcessedMessages
.AnyAsync(x => x.MessageId == messageId);
if (alreadyProcessed)
{
await transaction.RollbackAsync();
await channel.BasicAckAsync(args.DeliveryTag, false);
return;
}
// Business operation
product.Stock--;
// Inbox kaydı
dbContext.ProcessedMessages.Add(new ProcessedMessage
{
MessageId = messageId,
ProcessedAtUtc = DateTime.UtcNow
});
await dbContext.SaveChangesAsync();
await transaction.CommitAsync();
// Commit'ten sonra ACK
await channel.BasicAckAsync(args.DeliveryTag, false);
}
catch
{
await transaction.RollbackAsync();
await channel.BasicNackAsync(
args.DeliveryTag,
multiple: false,
requeue: true);
}
Bu sebeple birbiriyle doğrudan ilişkili olan bu iki işlemi ortak transaction ile atomik hale getirebiliriz.
6. ACK Neden Database Commit’ten Sonra Gönderilmeli?
await dbContext.SaveChangesAsync();
await transaction.CommitAsync();
// Commit'ten sonra ACK
await channel.BasicAckAsync(args.DeliveryTag, false);
Burada ACK bildirisinin commit’ten sonra olması oldukça önemlidir. Bunun sebebi ACK bildirisinin yapılması ile veritabanı değişikliklerinin uygulanmasının birbirinden farklı şeyler olmasıdır. Eğer biz commit öncesinde ACK bildirisi yaparsak ve herhangi bir commit hatası alırsak, yaptığımız business işlemler geçersiz olacak ve ACK göndereceğimiz için mesaj queue’dan silinecektir.
Bu kısımda da tek bir satır konumunun, tutarlılığı nasıl etkileyebileceğini görmüş olduk.
7. Producer Tarafındaki Problem: Dual Write
Yazımızın bu noktasına kadar güvenilir mesajlaşmayı sağlamak adına odağımız consumer tarafındaydı. Fakat güvenilir mesajlaşmada producer tarafında da önemli bir problemimiz var.
Bu problem, Dual Write.
Bir sipariş oluşturduğumuzu düşünelim. Uygulamamız ilk önce sipariş bilgisini veritabanına kaydediyor, ardından da OrderCreated mesajını RabbitMQ’ya publish ediyor. Akışımız şu şekilde olsun:

İlk bakışta bir problem yokmuş gibi gözüküyor. Kod satırlarının ne ile ilgili olduğuna baktığımızda şunu çıkarıyoruz:

Göreceğimiz üzere kod parçası hem database, hem de RabbitMQ ile ilgili işlemler yapıyor ve bu işlemler sıralı şekilde tanımlanmış.

Peki bu sıralı işlemler ya yarıda kalırsa ne olacak? Yine bir tutarsızlık söz konusu.
Hatırlarsanız şuanda yaşadığımız problemin bir benzerini consumer tarafında yaşamış ve bunu inbox pattern ile çözmüştük. Gerçekleştirdiğimiz business işlem ile processedMessages tablosuna yapacağımız işlemleri tek bir transaction içerisine alıp reliability’i sağlamıştık.
Çözümümüz şu şekildeydi: İki işlem, tek transaction.
Peki consumer tarafında ne yapabiliriz? Inbox Pattern’indeki çözüm şekli, bize yardımcı olabilir mi?
8. Outbox Pattern Nedir ve Dual Write Problemini Nasıl Çözer?
Consumer tarafında uyguladığımız Inbox Pattern’de iki farklı işlemi aynı transaction içerisine alarak tutarlılığı sağlamıştık. Producer tarafında da benzer bir yaklaşım uygulayabiliriz.
Buradaki temel fikir şu:
Business verisini ve publish edilmesi gereken mesajı aynı database transaction içerisinde kaydetmek.
Yani siparişi oluşturduğumuz anda mesajı doğrudan RabbitMQ’ya publish etmek yerine, önce bu mesajı veritabanındaki bir OutboxMessages tablosuna kaydederiz.
await using var transaction = await dbContext.Database.BeginTransactionAsync();
dbContext.Orders.Add(order);
dbContext.OutboxMessages.Add(new OutboxMessage
{
Id = Guid.NewGuid(),
Type = "OrderCreated",
Payload = JsonSerializer.Serialize(orderCreatedEvent),
CreatedAtUtc = DateTime.UtcNow
});
await dbContext.SaveChangesAsync();
await transaction.CommitAsync();
Bu durumda iki işlem de aynı transaction içerisinde gerçekleşir:

Transaction başarılı olursa hem sipariş hem de ona ait mesaj kaydedilmiş olur. Herhangi bir hata yaşanırsa ise ikisi de rollback edilir.
Böylece artık “Order kaydedildi ama OrderCreated mesajı kayboldu.” gibi bir durumun yaşanması önlenmiş olur.
Peki mesaj RabbitMQ’ya nasıl ulaşacak?

Burada devreye ayrı bir Outbox Publisher girer. Bu yapı belirli aralıklarla henüz publish edilmemiş OutboxMessages kayıtlarını okur ve RabbitMQ’ya gönderir.
Mesaj broker tarafından güvenli şekilde kabul edildiğinde ise ilgili Outbox kaydı publish edilmiş olarak işaretlenir.
Bu yaklaşım sayesinde database ile RabbitMQ arasında doğrudan tek bir transaction kurmaya çalışmak yerine, önce güvenilir olan database transaction’ından faydalanmış oluruz.
İşte bu yaklaşım Outbox Pattern olarak adlandırılır.
Kısacası:
Inbox, consumer tarafında mesajın birden fazla kez işlenmesini engellerken; Outbox, producer tarafında business işlem ile mesaj üretimi arasındaki tutarlılığı korur.
9. Outbox Publisher, MessageId ve PublishedAtUtc
Outbox tablosuna kaydettiğimiz mesajların RabbitMQ’ya iletilmesi için bir Outbox Publisher kullanırız. Bu yapı belirli aralıklarla henüz publish edilmemiş kayıtları bulur ve broker’a gönderir.
var messages = await dbContext.OutboxMessages
.Where(x => x.PublishedAtUtc == null)
.ToListAsync();
Her Outbox kaydının benzersiz Id değerini, RabbitMQ mesajının MessageId değeri olarak kullanabiliriz:
properties.MessageId = outboxMessage.Id.ToString();Böylece aynı Outbox mesajı tekrar publish edilmek zorunda kalırsa yeni bir MessageId üretmek yerine aynı kimliği koruruz. Böylece olası duplicate message senaryosunda messageId aynı kalacağı için consumer’ın idempotent şekilde davranmasını sağlayabiliriz.
Gönderdiğimiz mesaj broker tarafından başarıyla kabul edildiğinde ilgili kayıt işaretlenir:
outboxMessage.PublishedAtUtc = DateTime.UtcNow;
await dbContext.SaveChangesAsync();
PublishedAtUtc, bize bu Outbox kaydının artık publish sürecinden geçtiğini gösterir. Böylece publisher sonraki taramalarda yalnızca henüz publish edilmemiş kayıtlarla ilgilenir.
10. Outbox Duplicate Publish’i Tamamen Engeller mi?
Outbox Pattern’in ana amacı duplicate publish’i engellemek değil, olası mesaj kayıplarını engellemektir. Dolayısıyla duplicate publish’in engellenmesi hususunda rol oynamaz.
Şu senaryoyu düşünelim:
Mesaj RabbitMQ'ya ulaştı
↓
Broker ACK gönderdi
↓
Publisher ACK'i alamadan bağlantı koptu
↓
PublishedAtUtc güncellenemedi
Outbox kaydı hala publish edilmemiş gibi göründüğü için Outbox Publisher daha sonra aynı mesajı tekrar gönderebilir.
Sonuç olarak aynı MessageId değerine sahip mesaj broker’a birden fazla kez ulaşabilir.
Bu aslında daha önce de bahsettiğimiz durumun aynısıdır: at-least-once delivery duplicate mesaj üretebilir.
Dolayısıyla Outbox Pattern ile Inbox Pattern birbirinin tamamlayıcısıdır:
Outbox mesajın kaybolmamasını sağlar, Inbox ise aynı mesajın birden fazla kez işlenmesinin etkisini engeller.
Böylece producer ve consumer tarafını birlikte ele aldığımızda daha güvenilir bir mesajlaşma yapısı elde etmiş oluruz.
11. Outbox ve Inbox Birlikte Nasıl Çalışır?
Outbox ve Inbox Pattern aslında birbirini tamamlayan iki yapıdır.
Outbox Pattern, producer tarafında mesajların kaybolmasını engeller. Business işlem ile publish edilmesi gereken mesaj aynı transaction içerisinde saklanır ve mesaj, broker’a başarıyla iletilene kadar tekrar gönderilebilir.
Bu yaklaşım bize at-least-once delivery garantisi sağlar. Ancak bu garanti beraberinde duplicate message riskini de getirir.
İşte bu noktada Inbox Pattern devreye girer. Consumer, daha önce işlediği mesajları MessageId üzerinden takip ederek aynı mesaj tekrar ulaştığında business işlemi yeniden gerçekleştirmez.
Bu iki pattern birlikte kullanıldığında, producer ve consumer tarafında daha güvenilir ve tutarlı bir mesajlaşma akışı elde edilir.
12. Part 3’e Geçiş: Consumer Mesajı İşleyemezse Ne Olacak?
RabbitMQ Reliability serisinin ilk bölümünde, publisher tarafından iletilen mesajların broker’a sağlıklı şekilde iletilmesini hakkında konuştuk.
Bu bölümde ise broker’a güvenli şekilde iletilen mesajların sahip olduğu olası duplicate durumlarını consumer tarafında nasıl yönetebileceğimizi imceledik. İşlenen her bir mesajın durumunu kayıt altına alarak consumer’larımızın idempotent şekilde çalışmasını sağladık. Publisher tarafında da oluşturulan event’ların kaybolmamasını Outbox Pattern ile sağlamış olduk.
Bu serinin son bölümü olacak Part-3'te ise , consumer’larımızın mesajları işleyemedikleri durumlarda nasıl davranması gerektiği ile ilgili olan Retry,DLQ, Backoff ve Jitter kavramlarından bahsederek bu seriyi sonlandıracağım.
Yazılarımda henüz yeni öğrendiğim şeylerden bahsediyorum. Dolayısıyla yanlış öğrendiğim/bahsettiğim yerler olabilir, mazur görünüz.
İyi okumalar dilerim.
Comments 0
You cannot comment because you are not logged in.
Log in to comment.