Event Driven Yöntemi İle Microservice Mimarisinde Haberleşme – RabbitMQ – CAP
Merhaba,
Bu içeriğimizde Microservice mimarilerde servislerin kendi aralarında haberleşebilmesi için Event Driven yönteminin nasıl çalıştığını ve kullanıldığını ele alacağız.
Microservice’lerde Event Driven Nedir?
Her bir servisin işlevsel açıdan yapacağı operasyon neticesinde bir event’i fire ederek sonucu/değeri/neticeyi herhangi bir message queue(RabbitMQ, Kafka vs.) sistemine atarak bu kuyrukları dinleyen diğer servisleri uyarmasıdır.
Event Driven’ın Avantajları Nelerdir?
- Gevşek bağlı(loosely coupled) bir mimari oluşturulmasına olanak sağlar.
- Servisler arasında asenkron bir iletişim sağlanacağı için performansı arttırır.
- Servisler arasında mesajlaşma sistemleri kullanılacağından dolayı, event fire edildiği taktirde consumer servis’e bir sebepten dolayı erişilemezse eğer kuyruk verinin kalıcılığını sağlayacak ve tekrar erişilebilir olunca tüketim devam edecektir. Böylece süreçte veri kaybı yaşamaksızın sağlıklı işlevsellik söz konusu olacaktır.
- Ölçeklenebilirlik(scalability) sağlar.
RabbitMQ Nedir? Nasıl Kullanacağız?
RabbitMQ’nun ne olduğu ve nasıl kullanıldığına dair uzun uzun kaleme aldığımız RabbitMQ yazı dizisini okuyabilirsiniz. Lakin bizler bu içeriğimizde RabbitMQ’yu Docker ile kurup o şekilde kullanmayı tercih edeceğiz. Bunun için Docker’da RabbitMQ Ayağa Kaldırma başlıklı makalemi incelemenizi tavsiye ederim.
CAP Nedir? Nasıl Kullanılır?
CAP, distributed sistemlerdeki producer-consumer yapısının güvenilirliği tam olarak garanti etmemesi durumuna istinaden, yönetilebilirlik açısından kolaylık sağlayan ve Event Bus işlevine sahip olan, hafif, kolay ve verimli bir .NET tabanlı açık kaynak(open source) kütüphanedir.Microservice yaklaşımını benimseyen sistemlerde yukarıda da bahsedildiği üzere mesaj kuyruk sisteminin basitçe kullanılması güvenilirliği garanti etmeyecektir. İşte bu olası duruma istinaden dağıtılmış sistemlerin birbirini çağırması sürecinde oluşabilecek istisnaları çözümlemek için CAP’ı kullanmayı tercih edeceğiz.
CAP, microservice mimarisinde servislerin birbirleriyle iletişime geçmesinde oluşacak istisnaları çözümleyebilmek için mevcut veritabanı ile entegre çalışan Local Message Table yaklaşımını benimsemiş bir kütüphanedir.
Nasıl kullanacağız? sorusuna gelirsek eğer zaten bu içeriğimizde komple ilgili kütüphane üzerine kurulu bir anlatım sergileyeceğiz 🙂
Örnek Senaryo
Servislerin Oluşturulması ve CAP Entegrasyonu
İlk olarak aralarında iletişim kuracak olan ‘ProducerAPI’ ve ‘ConsumerAPI’ isminde iki adet servis oluşturalım.
dotnet new webapi --name producerAPI
dotnet new webapi --name consumerAPI
Ardından her iki projeyede DotNetCore.CAP kütüphanesini
dotnet add package DotNetCore.CAP
komutuyla, kullanacağımız RabbitMQ mesaj kuyruk sistemi içinse DotNetCore.CAP.RabbitMQ kütüphanesini
dotnet add package DotNetCore.CAP.RabbitMQ
komutuyla ve son olarak servisler arasında iletişim için yaratılan olayları loglayabilmek için DotNetCore.CAP.SqlServer kütüphanesini
dotnet add package DotNetCore.CAP.SqlServer
komutu aracılığıyla yükleyiniz.
İlgili kütüphaneleri yükledikten sonra her iki projeninde ‘Startup.cs’ dosyasında aşağıdaki konfigürasyonların yapılması gerekmektedir;
public void ConfigureServices(IServiceCollection services) { services.AddDbContext<ExampleContext>(options => options.UseSqlServer("Server=.;Database=MicroserviceExampleDB;Trusted_Connection=True;")); services.AddCap(options => { options.UseEntityFramework<ExampleContext>(); options.UseSqlServer("Server=.;Database=MicroserviceExampleDB;Trusted_Connection=True;"); options.UseRabbitMQ(options => { options.ConnectionFactoryOptions = options => { options.Ssl.Enabled = false; options.HostName = "localhost"; options.UserName = "guest"; options.Password = "guest"; options.Port = 5672; }; }); }); services.AddControllers(); }
Port Ayarlaması
Örneklendirmemizi localde gerçekleştireceğimizden dolayı her iki uygulamanında farklı portlarda ayağa kaldırılması gerekmektedir. Bunun için her iki projenin ‘launchSettings.json’ dosyasında aşağıdaki gibi port ayarlamasında bulunulması gerekmektedir;
ProducerAPI;
"producerAPI": { "commandName": "Project", "launchBrowser": false, "applicationUrl": "https://localhost:5001;http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }
ConsumerAPI;
"consumerAPI": { "commandName": "Project", "launchBrowser": false, "applicationUrl": "https://localhost:5003;http://localhost:5002", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }
Mesaj Yayınlama
Şimdi ProducerAPI servisi üzerinden bir işlem gerçekleştirelim ve neticede ConsumerAPI servisini uyaralım. Bunun için ProducerAPI uygulamasında aşağıdaki gibi bir controller geliştirelim;
[ApiController] [Route("api/[controller]")] public class ProducerController : ControllerBase { private readonly ICapPublisher _capPublisher; public ProducerController(ICapPublisher capPublisher) { _capPublisher = capPublisher; } public async Task<IActionResult> ProducerTransaction() { using ExampleContext context = new ExampleContext(); using var transaction = context.Database.BeginTransaction(_capPublisher, autoCommit: true); var date = DateTime.Now; await _capPublisher.PublishAsync<DateTime>("producer.transaction", date); return Ok(date); } }
Dikkat ederseniz burada ‘ICapPublisher’ interface’i ile CAP kütüphanesi inject edilmekte ve context nesnesi üzerinden başlatılan transactionda kullanılmaktadır. Ayrıca ilgili arayüz aracılığıyla ‘producer.transaction’ değeri altında veri olarak o anın tarih bilgisini yayınlamaktadır.
Mesaj Yakalama
ProducerAPI tarafından yayınlanan mesajı ConsumerAPI’da elde edebilmek için ilgili projede aşağıdaki gibi bir controller geliştirilmelidir;
[ApiController] [Route("[controller]")] public class ConsumerController : ControllerBase { [CapSubscribe("producer.transaction")] public void Consumer(DateTime date) { Console.WriteLine(date); } }
Yukarıdaki kod bloğunu incelerseniz eğer consumer’ın dinleyici fonksiyonunu ‘CapSubscribe’ attribute’u ile işaretleyerek producer’de ki publish değeri olan ‘producer.transaction’ ile ilişkilendiriyor ve ilgili yayına abone yapıyoruz. Böylece producer bir mesaj yayınladığı an direkt olarak consumer haberdar ediliyor.
Business Logic Service’lerde Mesaja Yakalama
Eğer ki yayınlanan mesajı controllerda değil business logic’de yakalamak istiyorsanız ilgili sınıfın aşağıdaki gibi geliştirilmesi gerekmektedir.
public class ConsumerService : ICapSubscribe { [CapSubscribe("producer.transaction")] public void Consumer(DateTime date) { Console.WriteLine("Servis : " + date); } }
Burada sınıfı incelerseniz eğer ‘ICapSubscribe’ interface’inden türemesi gerekmekte ve ‘CapSubscribe’ attribute’u ile tekrardan ilgili producer değerini taşıyacak şekilde işaretlenmesi gerekmektedir. Bu işlemin ardından inşa edilen ilgili sınıfın uygulamaya aşağıdaki gibi servis olarak eklenmesi gerekmektedir.
public void ConfigureServices(IServiceCollection services) { . . . services.AddTransient<ConsumerService>(); services.AddCap(options => { . . . }); . . . }
Abone Grupları Oluşturma
CAP, gelen bir mesajı birden fazla gruba dağıtarak daha fazla servis yahut controller tarafından dinlenilmesini sağlayabilmektedir. Şöyle ki;
Service;
[CapSubscribe("producer.transaction", Group = "group2")] public void Consumer(DateTime date) { Console.WriteLine("Servis : " + date); }
Controller;
[ApiController] [Route("[controller]")] public class ConsumerController : ControllerBase { [CapSubscribe("producer.transaction", Group = "group1")] public void Consumer(DateTime date) { Console.WriteLine(date); } }
Aynı gruba abone olan yapılardan sadece bir tanesi tetiklenmekte, bilakis farklı gruplarda ise her biri ayrı ayrı tetiklenmektedir.
Gösterge Paneli Oluşturma(Dashboard)
CAP, gönderilen ve alınan mesajları kolayca gerçek zamanlı görebilmemiz için Dashboard özelliği taşımaktadır. Dashboard entegrasyonu için uygulamaya DotNetCore.CAP.Dashboard kütüphanesini aşağıdaki komutla yükleyiniz.
dotnet add package DotNetCore.CAP.Dashboard
Ardından ‘Startup.cs’ dosyasında aşağıdaki konfigürasyonu gerçekleştiriniz.
public void ConfigureServices(IServiceCollection services) { . . . services.AddCap(options => { . . . options.UseDashboard(o => o.PathMatch = "/cap-dashboard"); . . . }); . . . }
Yukarıdaki kod bloğunu incelerseniz eğer ‘AddCap’ servisi altında ‘UseDashboard’ servisini uygulamaya dahil etmekte ve ‘/cap-dashboard’ adresi altında erişim sağlanabileceği bildirilmektedir.
CAP Faydasını Görelim
Her event veritabanı seviyesinde tutularak olası hata yahut kesinti durumlarında veri kaybını önlemekte ve iletişimin tekrar sağlandığı durumlarda mesaj kuyruk sistemine gönderilmemiş ve expire süresi dolmayan mesajları tekrardan göndermekte ve böylece kullanılan mesaj kuyruk sistemi ve hatta hatalar yahut kesintilerden izole bir şekilde geliştirme yapılmasına olanak sağlamaktadır.
Örnek veritabanı görüntüsü için aşağıdaki ekran alıntısını inceleyiniz;
İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…
Not : Örnek çalışmayı indirmek için buraya tıklayınız.
paylaşım için teşekkürler.
Hocam merhaba ilgili Published ve Recived tablolarını CAP otomatik mi oluşturuyor
Çözebildiysen yazabilir misin dostum. Bende de aynı sorular 🙂
Evet. MongoDb kullanıyorum kendisi oluşturuyor.
teşekkürler.