Aracılığıyla paylaş


Olaylara abone olma

Tavsiye

Bu içerik, .NET Docs veya çevrimdışı olarak okunabilen ücretsiz indirilebilir bir PDF olarak sağlanan Kapsayıcılı .NET Uygulamaları için .NET Mikro Hizmet Mimarisi adlı e-Kitap'tan bir alıntıdır.

.NET Mikro Hizmetler Mimarisi Kapsayıcılı .NET Uygulamaları için eKitabın kapak küçük resmi .

Etkinlik veri yolunu kullanmanın ilk adımı, mikro hizmetleri almayı istedikleri etkinliklere abone etmektir. Bu işlev alıcı mikro hizmetlerinde yapılmalıdır.

Aşağıdaki basit kod, her alıcı mikro hizmetinin hizmeti başlatırken ( Startup yani sınıfında) ihtiyaç duyduğu olaylara abone olması için uygulaması gerekenleri gösterir. Bu durumda, basket-api mikro hizmetin ProductPriceChangedIntegrationEvent ve OrderStartedIntegrationEvent iletilerine abone olması gerekir.

Örneğin, olaya abone ProductPriceChangedIntegrationEvent olurken, sepet mikro hizmetini ürün fiyatındaki değişiklikleri fark eder ve söz konusu ürün kullanıcının sepetindeyse kullanıcıyı değişiklik hakkında uyarmasına olanak tanır.

var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>();

eventBus.Subscribe<ProductPriceChangedIntegrationEvent,
                   ProductPriceChangedIntegrationEventHandler>();

eventBus.Subscribe<OrderStartedIntegrationEvent,
                   OrderStartedIntegrationEventHandler>();

Bu kod çalıştırıldıktan sonra abone mikro hizmeti RabbitMQ kanallarını dinler. ProductPriceChangedIntegrationEvent türünde bir ileti geldiğinde, kod ona geçirilen olay işleyicisini çağırır ve olayı işler.

Etkinlik veri yolu aracılığıyla etkinlikleri yayımlama

Son olarak, ileti gönderen (kaynak mikro hizmeti) tümleştirme olaylarını aşağıdaki örneğe benzer bir kodla yayımlar. (Bu yaklaşım, bölünmezliği hesaba katmayan basitleştirilmiş bir örnektir.) Bir olayın genellikle kaynak mikro hizmetten veri veya işlem işledikten hemen sonra birden çok mikro hizmete yayılması gerektiğinde benzer kodu uygularsınız.

İlk olarak, aşağıdaki kodda olduğu gibi olay veri yolu nesnesi (RabbitMQ tabanlı veya service bus tabanlı) denetleyicinin yapıcısına eklenir:

[Route("api/v1/[controller]")]
public class CatalogController : ControllerBase
{
    private readonly CatalogContext _context;
    private readonly IOptionsSnapshot<Settings> _settings;
    private readonly IEventBus _eventBus;

    public CatalogController(CatalogContext context,
        IOptionsSnapshot<Settings> settings,
        IEventBus eventBus)
    {
        _context = context;
        _settings = settings;
        _eventBus = eventBus;
    }
    // ...
}

Ardından bunu UpdateProduct yöntemindeki gibi denetleyicinizin yöntemlerinden kullanırsınız:

[Route("items")]
[HttpPost]
public async Task<IActionResult> UpdateProduct([FromBody]CatalogItem product)
{
    var item = await _context.CatalogItems.SingleOrDefaultAsync(
        i => i.Id == product.Id);
    // ...
    if (item.Price != product.Price)
    {
        var oldPrice = item.Price;
        item.Price = product.Price;
        _context.CatalogItems.Update(item);
        var @event = new ProductPriceChangedIntegrationEvent(item.Id,
            item.Price,
            oldPrice);
        // Commit changes in original transaction
        await _context.SaveChangesAsync();
        // Publish integration event to the event bus
        // (RabbitMQ or a service bus underneath)
        _eventBus.Publish(@event);
        // ...
    }
    // ...
}

Bu durumda, kaynak mikro hizmeti basit bir CRUD mikro hizmeti olduğundan, bu kod doğrudan bir Web API denetleyicisine yerleştirilir.

Daha gelişmiş mikro hizmetlerde, örneğin CQRS yaklaşımlarını kullanırken, CommandHandler sınıfında Handle() yöntemi ile uygulanabilir.

Olay Veri Yoluna Yayın Yaparken Atomiklik ve Dayanıklılığı Tasarlamak

Tümleştirme olaylarını olay veri yolunuz gibi dağıtılmış bir mesajlaşma sistemi aracılığıyla yayımladığınızda, özgün veritabanını atomik olarak güncellemek ve bir olay yayımlamak (yani, her iki işlem de tamamlanır ya da hiçbiri tamamlanmaz) sorununu yaşarsınız. Örneğin, daha önce gösterilen basitleştirilmiş örnekte kod, ürün fiyatı değiştirildiğinde verileri veritabanına işler ve ardından bir ProductPriceChangedIntegrationEvent iletisi yayımlar. Başlangıçta, bu iki işlemin atomik olarak gerçekleştirilmesi önemli görünebilir. Ancak, Microsoft Message Queuing (MSMQ) gibi eski sistemlerde yaptığınız gibi veritabanı ve ileti aracısı ile ilgili dağıtılmış bir işlem kullanıyorsanız, CAP teoreminde açıklanan nedenlerden dolayı bu yaklaşım önerilmez.

Temel olarak, ölçeklenebilir ve yüksek oranda kullanılabilir sistemler oluşturmak için mikro hizmetleri kullanırsınız. Basitleştirmek gerekirse, CAP teoremi, sürekli kullanılabilir, kesin tutarlı ve herhangi bir bölünmeye dayanıklı bir (dağıtılmış) veritabanı (veya modeline sahip bir mikro hizmet) oluşturamayacağınızı söyler. Bu üç özellik arasından iki tane seçmelisiniz.

Mikro hizmet tabanlı mimarilerde kullanılabilirlik ve toleransı seçmeli ve güçlü tutarlılığı geri plana atmalısınız. Bu nedenle, çoğu modern mikro hizmet tabanlı uygulamada, MSMQ ile Windows Dağıtılmış İşlem Düzenleyicisi'ni (DTC) temel alan dağıtılmış işlemleri uygularken yaptığınız gibi, genellikle mesajlaşmada dağıtılmış işlemleri kullanmak istemezsiniz.

İlk soruna ve örneğine geri dönelim. Veritabanı güncelleştirildikten sonra hizmet kilitleniyorsa (bu örnekte, _context.SaveChangesAsync() kod satırından hemen sonra), ancak entegrasyon olayı yayımlanmadan önce sistemin tamamı tutarsız hale gelebilir. Bu yaklaşım, ilgilendiğiniz belirli bir iş operasyonuna bağlı olarak iş açısından kritik olabilir.

Mimari bölümünde daha önce belirtildiği gibi, bu sorunla ilgilenmek için çeşitli yaklaşımlarınız olabilir:

Bu senaryo için, tam Olay Kaynağı (ES) desenini kullanmak, en iyi yaklaşımlardan biri, en iyisi bile olabilir. Ancak, birçok uygulama senaryosunda tam bir ES sistemi uygulayamayabilirsiniz. ES, geçerli durum verilerini depolamak yerine yalnızca etki alanı olaylarını işlem veritabanınızda depolamak anlamına gelir. Yalnızca etki alanı olaylarını depolamak, sisteminizin geçmişinin kullanılabilir olması ve geçmişte herhangi bir anda sisteminizin durumunu belirleyebilme gibi büyük avantajlara sahip olabilir. Ancak, tam bir ES sistemi uygulamak için sisteminizin büyük bir bölümünü yeniden oluşturmanız gerekir ve diğer birçok karmaşıklık ve gereksinim ortaya çıkar. Örneğin, Olay Deposu gibi olay kaynağını belirlemek için özel olarak yapılmış bir veritabanı veya Azure Cosmos DB, MongoDB, Cassandra, CouchDB veya RavenDB gibi belge odaklı bir veritabanı kullanmak isteyebilirsiniz. ES bu sorun için harika bir yaklaşımdır, ancak olay kaynağını önceden bilmiyorsanız en kolay çözüm değildir.

İşlem günlüğü madenciliği kullanma seçeneği başlangıçta saydam görünür. Ancak bu yaklaşımı kullanmak için mikro hizmetin SQL Server işlem günlüğü gibi RDBMS işlem günlüğünüzle birleştirilmiş olması gerekir. Bu yaklaşım büyük olasılıkla arzu edilmez. Başka bir dezavantajı, işlem günlüğüne kaydedilen alt düzey güncelleştirmelerin üst düzey tümleştirme olaylarınızla aynı düzeyde olmamasıdır. Öyleyse, bu işlem günlüğü işlemlerini tersine mühendislik işlemi zor olabilir.

Dengeli yaklaşım, işlemsel veritabanı tablosunun ve basitleştirilmiş ES deseninin bir karışımıdır. "Özgün olayda ayarladığınız 'etkinliği yayımlamaya hazır' gibi bir durumu, etkinliği tümleştirme olayları tablosuna kaydederken kullanabilirsiniz." Ardından etkinliği, etkinlik veri yoluna yayımlamayı deneyin. Olay yayımlama eylemi başarılı olursa, kaynak hizmette başka bir işlem başlatır ve durumu "olayı yayımlamaya hazır" durumundan "olay zaten yayımlanmış" durumuna taşırsınız.

Olay veri yolundaki olay yayımlama eylemi başarısız olursa, kaynak mikro hizmet içinde veriler tutarsız olmayacaktır; hâlâ "olayı yayımlamaya hazır" olarak işaretlenir ve diğer hizmetlerle zamanla tutarlı hale gelecektir. her zaman işlemlerin veya tümleştirme olaylarının durumunu denetleen arka plan işleriniz olabilir. İş "olayı yayımlamaya hazır" durumunda bir olay bulursa, bu olayı olay veri yoluna yeniden yayımlamayı deneyebilir.

Bu yaklaşımla yalnızca her kaynak mikro hizmet için tümleştirme olaylarını ve yalnızca diğer mikro hizmetlere veya dış sistemlere iletmek istediğiniz olayları kalıcı hale eklediğinize dikkat edin. Buna karşılık, tam bir ES sisteminde tüm etki alanı olaylarını da depolarsınız.

Bu nedenle bu dengeli yaklaşım basitleştirilmiş bir ES sistemidir. Entegrasyon olaylarının geçerli durumlarıyla birlikte bir listesine ("yayına hazır" ve "yayınlandı") ihtiyacınız vardır. Ancak yalnızca tümleştirme olayları için bu durumları uygulamanız gerekir. Bu yaklaşımda, tüm etki alanı verilerinizi tam bir ES sisteminde olduğu gibi işlem veritabanında olaylar olarak depolamanız gerekmez.

zaten bir ilişkisel veritabanı kullanıyorsanız, tümleştirme olaylarını depolamak için işlem tablosu kullanabilirsiniz. Uygulamanızda bölünmezlik elde etmek için yerel işlemleri temel alan iki adımlı bir işlem kullanırsınız. Temel olarak, etki alanı varlıklarınızın bulunduğu veritabanında bir IntegrationEvent tablonuz vardır. Bu tablo, kalıcı olarak kaydedilmiş tümleştirme olaylarını, etki alanı verilerinizi işleyen aynı işlemlere dahil ederek tamlığı sağlamanın bir yolu olarak işlev görür.

Adım adım işlem şöyle devam eder:

  1. Uygulama yerel bir veritabanı işlemi başlatır.

  2. Ardından etki alanı varlıklarınızın durumunu güncelleştirir ve tümleştirme olay tablosuna bir olay ekler.

  3. Son olarak işlemi tamamlayarak istenen atomikliği elde edersiniz ve sonra

  4. Olayı bir şekilde yayımlarsınız (sonraki).

Olayları yayımlama adımlarını uygularken şu seçeneklere sahip olursunuz:

  • İşlemi işledikten hemen sonra tümleştirme olayını yayımlayın ve tablodaki olayları yayımlandı olarak işaretlemek için başka bir yerel işlem kullanın. Ardından, uzak mikro hizmetlerde sorun olması durumunda tümleştirme olaylarını izlemek ve depolanan tümleştirme olaylarını temel alan telafi eylemleri gerçekleştirmek için tabloyu bir yapıt olarak kullanın.

  • Tabloyu bir tür sıra olarak kullanın. Ayrı bir uygulama iş parçacığı veya işlem tümleştirme olay tablosunu sorgular, olayları olay veri yolu üzerinde yayımlar ve ardından olayları yayımlandı olarak işaretlemek için yerel bir işlem kullanır.

Şekil 6-22'de bu yaklaşımlardan ilkinin mimarisi gösterilmektedir.

Çalışan mikro hizmeti olmadan yayımlarken bölünmezlik diyagramı.

Şekil 6-22. Olay veri yolu için olayları yayımlarken bölünmezlik

Şekil 6-22'de gösterilen yaklaşımda, yayımlanan tümleştirme olaylarının başarısını denetlemek ve onaylamakla sorumlu ek bir çalışan mikro hizmeti eksiktir. Hata durumunda, bu ek denetleyici çalışanı mikro hizmeti tablodaki olayları okuyabilir ve yeniden yayımlayabilir, yani 2. adımı tekrarlayabilir.

İkinci yaklaşım hakkında: EventLog tablosunu kuyruk olarak kullanırsınız ve iletileri yayımlamak için her zaman bir çalışan mikro hizmeti kullanırsınız. Bu durumda, süreç Şekil 6-23'te gösterilen gibidir. Bu ek bir mikro hizmeti gösterir ve tablo olayları yayımlarken tek kaynaktır.

İşçi mikrosunucu ile yayımlarken atomiklik diyagramı.

Şekil 6-23. Olayları bir çalışan mikro hizmeti kullanarak olay veriyoluna yayınlarken atomiklik sağlama

Kolaylık olması için eShopOnContainers örneği ilk yaklaşımı (ek işlemler veya denetleyici mikro hizmetleri olmadan) ve olay veri yolunu kullanır. Ancak, eShopOnContainers örneği tüm olası hata durumlarını işlemez. Buluta dağıtılan gerçek bir uygulamada, sorunların sonunda ortaya çıkacağı gerçeğini benimsemeli ve bu denetim ve yeniden gönderme mantığını uygulamanız gerekir. Tabloyu kuyruk olarak kullanmak, bu tabloyu olay veri yolu aracılığıyla yayımlarken (çalışanla) tek bir olay kaynağı olarak kullanıyorsanız ilk yaklaşımdan daha etkili olabilir.

Olay veri yolu aracılığıyla tümleştirme olayları yayımlarken bölünmezlik uygulama

Aşağıdaki kodda, güncelleştirilmekte olan özgün verilerle ilgili bir bağlam ve IntegrationEventLog tablosuyla ilgili ikinci bağlam olmak üzere birden çok DbContext nesnesi içeren tek bir işlemi nasıl oluşturabileceğiniz gösterilmektedir.

Aşağıdaki örnek koddaki işlem, veritabanı bağlantılarında kodun çalıştığı sırada herhangi bir sorun olması durumunda dayanıklı olmayacaktır. Bu durum Azure SQL DB gibi bulut tabanlı sistemlerde gerçekleşebilir ve bu da veritabanlarını sunucular arasında taşıyabilir. Birden çok bağlamda dayanıklı işlemler uygulamak için bu kılavuzun devamında yer alan Dayanıklı Entity Framework Core SQL bağlantılarını uygulama bölümüne bakın.

Netlik sağlamak için aşağıdaki örnekte tüm işlem tek bir kod parçasında gösterilmiştir. Ancak, eShopOnContainers uygulaması yeniden düzenlenir ve bakımı daha kolay olması için bu mantığı birden çok sınıfa böler.

// Update Product from the Catalog microservice
//
public async Task<IActionResult> UpdateProduct([FromBody]CatalogItem productToUpdate)
{
  var catalogItem =
       await _catalogContext.CatalogItems.SingleOrDefaultAsync(i => i.Id ==
                                                               productToUpdate.Id);
  if (catalogItem == null) return NotFound();

  bool raiseProductPriceChangedEvent = false;
  IntegrationEvent priceChangedEvent = null;

  if (catalogItem.Price != productToUpdate.Price)
          raiseProductPriceChangedEvent = true;

  if (raiseProductPriceChangedEvent) // Create event if price has changed
  {
      var oldPrice = catalogItem.Price;
      priceChangedEvent = new ProductPriceChangedIntegrationEvent(catalogItem.Id,
                                                                  productToUpdate.Price,
                                                                  oldPrice);
  }
  // Update current product
  catalogItem = productToUpdate;

  // Just save the updated product if the Product's Price hasn't changed.
  if (!raiseProductPriceChangedEvent)
  {
      await _catalogContext.SaveChangesAsync();
  }
  else  // Publish to event bus only if product price changed
  {
        // Achieving atomicity between original DB and the IntegrationEventLog
        // with a local transaction
        using (var transaction = _catalogContext.Database.BeginTransaction())
        {
           _catalogContext.CatalogItems.Update(catalogItem);
           await _catalogContext.SaveChangesAsync();

           await _integrationEventLogService.SaveEventAsync(priceChangedEvent);

           transaction.Commit();
        }

      // Publish the integration event through the event bus
      _eventBus.Publish(priceChangedEvent);

      _integrationEventLogService.MarkEventAsPublishedAsync(
                                                priceChangedEvent);
  }

  return Ok();
}

ProductPriceChangedIntegrationEvent tümleştirme olayı oluşturulduktan sonra, özgün etki alanı işlemini depolayan işlem (katalog öğesini güncelleştirin) eventLog tablosundaki olayın kalıcılığını da içerir. Bu, bunu tek bir işlem yapar ve olay iletilerinin gönderilip gönderilmediğini her zaman denetleyebilirsiniz.

Olay günlüğü tablosu, aynı veritabanına karşı yerel bir işlem kullanılarak özgün veritabanı işlemiyle atomik olarak güncelleştirilir. İşlemlerden herhangi biri başarısız olursa, bir özel durum oluşturulur ve işlem tamamlanan işlemleri geri alır, böylece etki alanı işlemleri ile tabloya kaydedilen olay iletileri arasında tutarlılığı korur.

Alıcı mikro hizmetlerdeki olay işleyicileri: aboneliklerden gelen mesajları almak

Olay aboneliği mantığına ek olarak, tümleştirme olay işleyicileri için iç kodu (geri çağırma yöntemi gibi) uygulamanız gerekir. Olay işleyicisi, belirli bir türdeki olay iletilerinin alınacağı ve işleneceğini belirttiğiniz yerdir.

Olay işleyicisi önce etkinlik otobüsünden bir olay örneği alır. Ardından, bu tümleştirme etkinliğiyle ilgili işlenecek bileşeni bulur, olayı alıcı mikro hizmette bir durum değişikliği olarak yayar ve kalıcı hale getirir. Örneğin, bir ProductPriceChanged olayı katalog mikro hizmetinden kaynaklanıyorsa, sepet mikro hizmetinde işlenir ve aşağıdaki kodda gösterildiği gibi bu alıcı sepeti mikro hizmetindeki durumu da değiştirir.

namespace Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.EventHandling
{
    public class ProductPriceChangedIntegrationEventHandler :
        IIntegrationEventHandler<ProductPriceChangedIntegrationEvent>
    {
        private readonly IBasketRepository _repository;

        public ProductPriceChangedIntegrationEventHandler(
            IBasketRepository repository)
        {
            _repository = repository;
        }

        public async Task Handle(ProductPriceChangedIntegrationEvent @event)
        {
            var userIds = await _repository.GetUsers();
            foreach (var id in userIds)
            {
                var basket = await _repository.GetBasket(id);
                await UpdatePriceInBasketItems(@event.ProductId, @event.NewPrice, basket);
            }
        }

        private async Task UpdatePriceInBasketItems(int productId, decimal newPrice,
            CustomerBasket basket)
        {
            var itemsToUpdate = basket?.Items?.Where(x => int.Parse(x.ProductId) ==
                productId).ToList();
            if (itemsToUpdate != null)
            {
                foreach (var item in itemsToUpdate)
                {
                    if(item.UnitPrice != newPrice)
                    {
                        var originalPrice = item.UnitPrice;
                        item.UnitPrice = newPrice;
                        item.OldUnitPrice = originalPrice;
                    }
                }
                await _repository.UpdateBasket(basket);
            }
        }
    }
}

Olay işleyicisinin, ürünün sepet örneklerinden herhangi birinde mevcut olup olmadığını doğrulaması gerekir. Ayrıca, ilgili her sepet satırı öğesi için madde fiyatını da güncelleştirir. Son olarak, Şekil 6-24'te gösterildiği gibi kullanıcıya fiyat değişikliği hakkında görüntülenecek bir uyarı oluşturur.

Kullanıcı sepetindeki fiyat değişikliği bildirimini gösteren tarayıcının ekran görüntüsü.

Şekil 6-24. Tümleştirme olaylarının bildirdiği şekilde sepette bir öğe fiyat değişikliğini görüntüleme

Güncelleme mesajı olaylarında idempotentlik

Güncelleştirme iletisi olaylarının önemli bir yönü, iletişimin herhangi bir noktasındaki bir hatanın iletinin yeniden denenmesine neden olmasıdır. Aksi takdirde, bir arka plan görevi zaten yayımlanmış olan bir olayı yayımlamaya çalışabilir ve bir yarış koşulu oluşturabilir. Güncelleştirmelerin ya etkisizleştirilmiş olduğundan ya da yinelenen bir öğeyi algılayıp atabileceğinizden ve yalnızca bir yanıt gönderebileceğinizden emin olmanızı sağlayacak yeterli bilgiyi sağladığından emin olun.

Daha önce belirtildiği gibi, bir işlemin sonucu değiştirmeden birden çok kez gerçekleştirilebileceği anlamına gelir. Bir mesajlaşma ortamında, olaylar iletişim kurarken olduğu gibi, alıcı mikro hizmetinin sonucu değiştirilmeden birden çok kez teslim edilebiliyorsa bir olay bir kez etkili olur. Bu, olayın doğası gereği veya sistemin olayı işleme şekli nedeniyle gerekli olabilir. İleti eşzamanlılığı, yalnızca olay veri yolu desenini uygulayan uygulamalarda değil, mesajlaşma kullanan tüm uygulamalarda önemlidir.

Idempotent bir işleme örnek olarak, yalnızca bu veriler tabloda yoksa tabloya veri ekleyen bir SQL ifadesi verilebilir. Bu insert SQL deyimini kaç kez çalıştırdığınız önemli değildir; sonuç aynı olacaktır; tabloda bu veriler yer alır. Mesajlar birden çok kez gönderilip işlenebilirse, mesajlarla ilgilenirken bu tür bir idempotentlik de gerekli olabilir. Örneğin, yeniden deneme mantığı bir gönderenin aynı mesajı birden fazla kez göndermesine neden oluyorsa, her seferinde aynı sonucun elde edilmesini sağlamalısınız.

İdempotent iletiler tasarlamak mümkündür. Örneğin, "ürün fiyatına 5 ABD doları ekleyin" yerine "ürün fiyatını 25 TL olarak ayarlayın" yazan bir olay oluşturabilirsiniz. İlk iletiyi istediğiniz sayıda güvenle işleyebilirsiniz ve sonuç aynı olur. İkinci ileti için bu doğru değildir. Ancak ilk durumda bile ilk olayı işlemekten kaçınmak isteyebilirsiniz çünkü sistem daha yeni bir fiyat değişikliği olayı da göndermiş olabilir ve yeni fiyatı geçersiz kılabilirsiniz.

Bir diğer örnek de birden çok aboneye yayılan sipariş tamamlandı olayı olabilir. Aynı sipariş tamamlandı olayı için yinelenen ileti olayları olsa bile, uygulamanın diğer sistemlerde sipariş bilgilerinin yalnızca bir kez güncelleştirildiğinden emin olması gerekir.

Her olayın alıcı başına yalnızca bir kez işlenmesini zorunlu kılan bir mantık oluşturabilmeniz için olay başına bir tür kimliğin olması uygundur.

Bazı mesaj işleme süreçleri doğası gereği idempotenttir. Örneğin, bir sistem görüntü küçük resimleri oluşturuyorsa, oluşturulan küçük resimle ilgili iletinin kaç kez işlendiği önemli olmayabilir; sonuç olarak küçük resimler oluşturulur ve her seferinde aynı olur. Öte yandan, kredi kartından ücret almak için ödeme ağ geçidi çağırma gibi işlemler hiç etkili olmayabilir. Böyle durumlarda, bir iletiyi birden çok kez işlemenin beklediğiniz etkiye sahip olduğundan emin olmanız gerekir.

Ek kaynaklar

Tümleştirme olay iletilerinin yinelenenlerini kaldırma

İleti olaylarının farklı düzeylerde abone başına yalnızca bir kez gönderilmesini ve işlenmesini sağlayabilirsiniz. Bunun bir yolu, kullandığınız mesajlaşma altyapısı tarafından sunulan yinelenenleri kaldırma özelliğini kullanmaktır. Bir diğeri de hedef mikro hizmetinizde özel mantık uygulamaktır. Doğrulamaların hem aktarım düzeyinde hem de uygulama düzeyinde olması en iyi sonucu sağlar.

EventHandler düzeyinde ileti olaylarını tekrarlayanları kaldırma

Bir olayın herhangi bir alıcı tarafından yalnızca bir kez işlenmesini sağlamanın bir yolu, olay işleyicilerinde ileti olaylarını işlerken belirli bir mantık uygulamaktır. Örneğin, bir tümleştirme olayı aldığında UserCheckoutAcceptedIntegrationEvent görebileceğiniz gibi eShopOnContainers uygulamasında kullanılan yaklaşım bu şekildedir. (Bu durumda, komut işleyicisine gönderilmeden önce CreateOrderCommand tanımlayıcı olarak kullanılarak IdentifiedCommandeventMsg.RequestId ile sarılır).

RabbitMQ kullanırken iletileri yinelemeleri kaldırma

Aralıklı ağ hataları olduğunda, iletiler yinelenebilir ve ileti alıcısı bu yinelenen iletileri işlemeye hazır olmalıdır. Mümkünse, alıcılar iletileri, yinelenenleri kaldırarak açıkça işlemeye kıyasla, idempotent bir şekilde yani değişmez işlemle ele almalıdır.

RabbitMQ belgelerine göre, "Bir ileti bir tüketiciye teslim edilir ve sonra yeniden sorgulanırsa (örneğin, tüketici bağlantısı bırakılmadan önce onaylanmadığı için) RabbitMQ, yeniden teslim edildiğinde (aynı tüketiciye veya farklı bir tüketiciye) yeniden teslim edilen bayrağı ayarlar.

"Yeniden teslim edildi" bayrağı ayarlanırsa, ileti zaten işlenmiş olabileceğinden alıcının bunu hesaba katması gerekir. Ama bu garanti değildir; ileti, ileti aracısını bıraktıktan sonra, ağ sorunları nedeniyle alıcıya hiç ulaşmamış olabilir. Öte yandan, "yeniden teslim edildi" bayrağı ayarlanmamışsa, iletinin birden çok kez gönderilmediği garanti edilir. Bu nedenle, alıcının iletileri yinelenenleri kaldırması veya iletileri yalnızca iletide "yeniden teslim edildi" bayrağı ayarlanmışsa aynı anda işlemesi gerekir.

Ek kaynaklar