Derinlemesine yazılım eğitimleri için kanalımı takip edebilirsiniz...

.NET Aspire & Dapr | Distributed Uygulamalarda Bu Araçlar Birbirlerini Nasıl Tamamlarlar?

Merhaba,

Bu içeriğimizde en son klavyeye aldığımız .NET Aspire aracıyla birlikte Distributed Application Runtime(Dapr) aracını masaya yatıracak ve bu ikisinin çözdüğü sorunlarla birlikte aralarındaki farklara ve özellikle neden bu iki aracın birlikte kullanılması gerektiğine dair bir istişarede bulunuyor olacağız.

.NET Aspire’ın öğrenim sürecinde birçoğunuzun aklına Dapr’ın geldiğini biliyorum. Ve davranışsal olarak .NET Aspire’ın Dapr’ın yerine geçtiğini düşündüğünüzü de… Ama öyle değil. Dapr, uygulama geliştiricilerine, özellikle microservice mimarilerinde, servisler arası sık kullanılan işlevsellikleri basit ve taşınabilir bir şekilde sunan building block dediğimiz modüler ve standart API’ler üzerinden çalışan özelliklere sahip olan ve hem local development sırasında hem de production ortamında çalışan bir runtime’dır. .NET Aspire ise sadece local development için bir araç setidir. Dolayısıyla .NET Aspire’ın, Dapr’ın yerini alamadığı bu satırlarca aşikarken, bu satırlardan sonra Dapr ile birlikte kullanılmasının ne kadar güçlü bir kombinasyon yaratacağı bizler için eğlenceli bir keşif olacaktır. Hadi başlayalım…

.NET Aspire’ın Çözüm Getirdiği Sorunlar Nelerdir?

.NET Aspire, distributed sistemlerde birden fazla cloud native uygulamayı local olarak çalıştırmak ve debug etmek için oluşturulmuş bir araç setidir. Bu araç setiyle, .NET geliştiricilerinin hakim oldukları C# diliyle micro service’leri ve bunların kullanacakları altyapı bağımlılıklarını merkezi bir noktadan yapılandırabilmek ve bu yapılandırma neticesinde oldukça hızlı bir şekilde sistemi ayağa kaldırarak, debug edebilmek mümkündür.

.NET Aspire, bu çalışmayı aşağıdaki mantıkla gerçekleştirmektedir;

Yukarıdaki görselden de anlaşılacağı üzere birden fazla .NET uygulamasını local olarak çalıştırmak ve debug edebilmek için geliştiricilerin tüm uygulamaları/servisleri ve bu uygulamaların kullandıkları altyapı bağımlılıklarını yapılandırmanın bir yoluna ihtiyaçları vardır. .NET Aspire’da bu, AppHost adı verilen ve tek görevi tüm kaynakları(resource) düzenlemekten ibaret olan bir merkezi proje üzerinden gerçekleştirilir.

Dapr(Distributed Application Runtime)’ın Çözüm Getirdiği Sorunlar Nelerdir?

Dapr ise, geliştiricilerin distributed uygulamaları hızla oluşturması için tasarlanmış bir API kümesi sağlayan runtime’dır. Bu API kümesi sayesinde, geliştiricilerin uygulamaların kaynak kodlarına müdahale etmelerine gerek kalmaksızın farklı altyapılar arasında geçişler yapabilmesi sağlanmaktadır.

Ayrıca Dapr, uygulamanın yanında sidecar mimarisi ile çalışan bir geliştirme framework’ü olarak davranış sergilemektedir. Yani bu uygulamadaki her bir servis için ayrı bir Dapr sidecar başlatılır anlamına gelmektedir. Bu sidecar’lar, servislerin Dapr’ın sunduğu özellikleri kullanmasını sağlarlar. Misal olarak; 4 servisin olduğu bir uygulamada her biri için birer Dapr sidecar ayağa kalkacaktır. Yani toplamda 4 servise ve 4 Dapr sidecar eşlik edecektir. Bunlar aynı ağa dahil oldukları için birbirleriyle haberleşebilecektirler. Burada özellikle vurgulanması gereken nokta şudur ki; Dapr’ın bir merkezi sunucu yahut mikro servis olmadığı, bilakis her servisle birlikte ayağa kalkan, çalışan ve onun işlevselliğini artıran bir yapı olduğudur.

Dapr’ın çözüm getirdiği sorunlara gelirsek eğer; kullanılan programlama dili ve cloud provider’ından bağımsız olarak güvenli ve güvenilir distributed uygulamalar geliştirmeyi kolaylaştırmaktadır. Geliştiriciler Dapr sayesinde hızla microservice mimarileri oluşturabilmekte ve HTTP/gRPC veya birçok farklı dil SDK aracılığıyla Dapr API paketini kullanabilmektedirler. Ayrıca Dapr yalnızca geliştirme sırasında kullanılmamakta, production ortamında da çalışmakta ve herhangi bir cloud yahut şirket içi süreçlerde servisler ve kaynaklar arasında güvenli iletişim amaçlı da kullanılmaktadır.

Geliştiricilerden alınan geri bildirimlere göre, Dapr sayesinde geliştirme süreçlerindeki olası kodsal ve mimarisel maliyetlerden %30 tasarruf edildiği iddia edilmektedir.

Bunun nedeni, built-in cross-cutting concerns dediğimiz belli durumlardaki kodsal endişelerin yönetilmesi ve sistemi inşa ederken kurgulanan altyapının yapılandırılma sürecindeki olası değişikliklerin yaratacağı maliyetlerin bir YAML dosyasıyla soyutlanmasıdır.

İçeriğimizin mahiyetini tam yansıtabilmek için Dapr’ın nasıl çalıştığı ve kullanıldığına dair küçük bir örnek dokunuşu faydalı görmekteyim… Tabi öncelikle Dapr CLI’ın yüklenmesi ve ayağa kaldırılması gerekmektedir. Bunun için Distributed Application Runtime(Dapr) Nedir? Teorik ve Pratikte İnceleyelim başlıklı makalemizdeki ‘Dapr Nasıl Yüklenir?’ kısmına göz atmanızı tavsiye ederim.

Şimdi, diyelim ki iki servis kendi arasında Redis ve RabbitMQ teknolojilerini kullanarak haberleşme sağlayacaklar. Bunun için öncelikle Dapr’ın Pub/Sub modelini tanımlayacak olan aşağıdaki component dosyalarının oluşturulması gerekmektedir;

rabbitmq-pubsub.yaml;

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: rabbitmq-pubsub
spec:
  type: pubsub.rabbitmq
  version: v1
  metadata:
  - name: host
    value: "amqps://xearmc***Z@beaver.rmq.cloudamqp.com/xearmcdi"
  - name: durable
    value: "true"
  - name: deletedWhenUnused
    value: "false"

redis-pubsub.yaml;

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: redis-pubsub
spec:
  type: pubsub.redis
  version: v1
  metadata:
  - name: redisHost
    value: localhost:6379
  - name: redisPassword
    value: ""

(Dikkat! Bu dosyaların hangi dizinde oluşturulduğu vs. gibi teferruatlı bilgilere Dapr’a dair referans ettiğim ana makalede değindiğim için burada temas etmeksizin hızlıca geçiyorum)

Ardından servislerin aşağıdaki gibi yapılandırılmaları gerekmektedir;
ServiceA;

using Dapr.Client;
using Shared;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDaprClient();

var app = builder.Build();

app.MapGet("/publish/rabbitmq", async (DaprClient daprClient) =>
{
    await daprClient.PublishEventAsync(
        pubsubName: "rabbitmq-pubsub",
        topicName: "example.queue",
        new Message("Message..."));

    Console.WriteLine("Sent message...");
});

app.MapGet("/publish/redis", async (DaprClient daprClient) =>
{
    await daprClient.PublishEventAsync(
        pubsubName: "redis-pubsub",
        topicName: "example.queue",
        new Message("Message..."));

    Console.WriteLine("Sent message...");
});

app.Run();

ServiceB;

using Shared;

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();
app.MapSubscribeHandler();

app.MapPost("/subscribe/rabbitmq", (DaprMessage<Message> message) =>
{
    Console.WriteLine("RabbitMQ subscriber received : " + message);
    return Results.Ok(message);
}).WithTopic("rabbitmq-pubsub", "example.queue");

app.MapPost("/subscribe/redis", (DaprMessage<Message> message) =>
{
    Console.WriteLine("Redis subscriber received : " + message);
    return Results.Ok(message);
}).WithTopic("redis-pubsub", "example.queue");

app.Run();

Ve nihai olarak da aşağıdaki dapr talimatlarıyla servisler ayağa kaldırılıp, kullanılabilir hale getirilmelidir;

dapr run –app-id servicea –app-port 1000 — dotnet run
dapr run –app-id serviceb –app-port 2000 — dotnet run

Ve üstüne aşağıdaki görselde olduğu gibi ‘ServiceA’dan ilgili endpoint’leri tetikleyerek subscriber olan ‘ServiceB’ye mesajlar iletilebilir..NET Aspire & Dapr | Distributed Uygulamalarda Bu Araçlar Birbirlerini Nasıl Tamamlarlar?Burada yapılan örnek çalışmanın kaynak kodlarına içeriğimizin sonlarındaki github adresinden gözlemde bulunabilirsiniz. Şimdi bizler bu kadarıyla yetinip Dapr’ın kullanımını (bilenlerine ve meraklısına) anımsatabildiysek artık .NET Aspire ile Dapr arasındaki benzerliklerle birlikte farkları masaya yatırarak farkındalık doğurmaya devam edelim ve günün sonunda birlikte nasıl kullanılabileceklerini değerlendirmeye doğru zemini genişletelim.

.NET Aspire ve Dapr Arasındaki Benzerlikler & Farklılıklar Nelerdir?

Esasında .NET Aspire ile Dapr’ın amacı oldukça benzerdir. Evet, her ikisi de geliştiriciler açısından cloud native distributed uygulamaları oluşturma sürecini kolaylaştırmak ve hızlandırmak derdindedir. Tabi ki de her iki aracın bu vizyonu gerçekleştirme sürecindeki yol ve yordamı farklılık arz etse de esasında bu perspektifte Dapr’ın kapsamı .NET Aspire’a nazaran daha geniştir diyebilir. .NET Aspire, local development sürecindeki deneyime odaklanırken, Dapr ise hem local development hem de production’a deploy edilmiş uygulamaların çalışma süreçlerine odaklanmaktadır. Şimdi gelin, kısmen .NET Aspire ile Dapr arasında bir karşılaştırma yaparak kapsamları doğrultusunda benzerlikleri ve teknik açıdan farklarını masaya yatıralım. Tabi bunu yaparken de .NET Aspire’ın bir yerel geliştirme aracı, Dapr’ın da bir runtime olduğunu tekrar hatırlayarak ve esasında bu ikisini karşılaştırmanın hiçte adil olmadığını unutmayacak şekilde vurgulayarak başlayalım…

Benzerlikleri

Service Discovery;
.NET Aspire, AppHost projesine referans edilmiş servislere doğal olarak service discovery hizmeti sağlamaktadır. Dapr’da aynı şekilde microservice’ler arasındaki iletişimi kolaylaştırmak için benzersiz servis isimleri üzerinden service discovery hizmeti sunmaktadır.

Observability;
Biliyorsunuz ki; .NET Aspire, uygulamalarımız için OpenTelemetry SDK’sını kullandığı çok ciddi bir observability altyapısı sunmaktadır. Dapr’da ise ister OpenTelemetry isterseniz de Zipkin gibi izleme sistemleriyle entegrasyon hızlıca sağlanıp, yapılandırılabilir.

Resiliency;
Resiliency’de ise .NET Aspire ve Dapr aynı gayeyle planlanmış araçlardır, ancak her ikisinde uygulama oldukça farklıdır. .NET Aspire’da Microsoft.Extensions.Http.Resilience NuGet paketi üzerinden yapılandırılırken, Dapr’da ise Dapr sidecar üzerinden doğal bir şekilde resiliency sağlanmaktadır. Yani uygulamanın esnekliği için varsayılan bir davranıştır diyebiliriz.

Tüm bunların dışında her iki araçta da teknik yaklaşımlar ve kapsamlar tamamen farklı olsa da, component’lerin yönetim mantığı birbirine benzemektedir. Yani demek istediğim .NET Aspire ile NuGet paketleri üzerinden bir yapılandırma gerçekleştirilirken, Dapr’da ise .yaml dosyaları üzerinden gerçekleştirilmektedir. Tabi burada küçük bir nüansta söz konusudur, o da; .NET Aspire’ın component’leri Dapr’da olduğu gibi kullanılabilir API sunmazlar. Çünkü NuGet kütüphaneleridirler. İşte bu nedenle, .NET Aspire’da kaynağa özel SDK’lerin(resource-specific SDK) kullanılması gerekmektedir. Bu durumdan dolayı da .NET Aspire’da bir component değişimi uygulamanın kaynak koduna müdahale gerektirmekte, aksi taktirde değiştirilememektedir.

Farklılıkları

API’ler;
Dapr, altyapıdan bağımsız olarak birçok building blocks API’si sunmaktadır. Bu API’ler sayesinde bizler; RabbitMQ veya Redis gibi teknolojilere işlevsel arayüzler ve entegrasyon noktaları aracılığıyla hızlıca yapılandırma sağlayabilmekte ve kullanım gerçekleştirebilmekteyiz. Ayrıca geliştiriciler açısından da bu API’lerin distributed uygulamaları hızlı bir şekilde oluşturmak için de kullanıldığını söyleyebiliriz. İşte bu özellik .NET Aspire tarafından sunulmamaktadır. .NET Aspire’da, adı geçen teknolojilerle uygulama üzerinden bir etkileşim kurabilmek için kaynak kodu üzerinden SDK’lerin kullanılmasını gerekmektedir.

Playform & Dil;
Bir diğer önemli fark ise Dapr’ın bir sidecar olarak API’leri HTTP/gRPC interface’i üzerinden sunması nedeniyle herhangi bir programlama diliyle kullanılabilmesidir. .NET Aspire ise adından da anlaşılacağı üzere gerçekten .NET’e odaklı bir yapı sunmaktadır. Ha React, Vue ya da Angular frontend yapılanmaları ile .NET Aspire uygulamalarını çalıştırmak mümkündür ancak her daim bir AppHost .NET projesi gerektireceğinden dolayı dil ve platform zenginliği açısından daha kısıtlıdır.

Güvenlik Politikaları;
Dapr, yaml dosyaları aracılığıyla özelleştirilebilen yerleşik güvenlik politikalarına sahiptir. Ancak .NET Aspire’da service-to-service ya da service-to-resource iletişimini kontrol etmek için herhangi bir güvenlik politikası sağlanamamaktadır.

Deploy;
.NET Aspire, Visual Studio üzerinden yahut Azure Developer CLI(azd) aracılığıyla hızlı bir deploy süreci sunmaktadır. Bu süreçte, sistemde kullanılan tüm kaynakları tanımlayan json formatındaki manifest/bildiri dosyaları oluşturulmaktadır. Dapr’da ise herhangi bir IDE ile entegre uygulama deploy seçeneği söz konusu değildir.

Dashboard;
Hem .NET Aspire hem de Dapr dashboard servisi sunmaktadır. Ancak .NET Aspire’ın dashboard’u Dapr’ınkine nazaran kanımca bin basmaktadır. Panonun kapsamı ve şıklığı açısından tartışmasız .NET Aspire ön plandadır.

.NET Aspire İle Birlikte Neden Dapr’ı Kullanmalıyız?

Evet, artık .NET Aspire ile Dapr arasındaki benzerlikleri ve farkları ele aldığımıza göre artık bu iki aracı neden birlikte kullanmamız gerektiğinin istişaresine gelebiliriz.

.NET Aspire ile Dapr’ın birlikte kullanılmasının ana gerekçesi Dapr’ı yeni tecrübe edecek olan geliştiricilerin işini kolaylaştırması olabilir. Şöyle ki; Dapr’a yeni başlayacak biri öncelikle temel kavramlara odaklanacak, ardından bu kavramlar çerçevesinde görsel ya da metinsel kaynaklardan gerekli araştırmalar doğrultusunda bilgiler edinmeye çalışacaktır. SDK’yi uygulamalarına hızla entegre etmeyi umacak ve bunun için CLI ve gerekli dependency’leri yükleyecektir. Tabi ki de tüm bunlar biraz zaman alacaktır. Belki farkına varmadan günlerinin çoğunu buna harcayacak, hatta çoğu rehberde olduğu gibi tüm adımları yaptığı halde onlarca deneme neticesinde uygulamayı çalıştıramayıp gerekirse sayısız kere ağzını bozacaktır.

Evet, siz çok zeki ve hızlı ilerliyor olabilirsiniz belki ama çoğu kişi süreci aşağı yukarı bu şekilde geçirecektir. Ancak biliyorsunuz ki zaman sınırlıdır ve daha ilk adımların bu kadar yoğun zaman alması israf olmasa da gerçek bir maliyettir diyebiliriz. Dikkat ederseniz bu maliyete daha Dapr tecrübe edilmemiş, ancak ilk adımlar atılmış olunacaktır. Daha bunun local development deneyimi üzerindeki sorun gidermelere, debuginge vs. gibi süreçlere dair henüz bilgi edinimi zerre kadar söz konusu olmayacaktır. Daha gidilecek yol çoktur ve belki de kimi meslektaşlar tarafından ‘bu ne lan’ şeklinde isteksiz davranışlar söz konusu olacaktır ve Dapr’ın daha ne olduğu anlaşılmaksızın, işleri olduğundan daha karmaşık hale getiren bi zımbırtılar yığını olduğuna inanılacaktır.

İşte Dapr’a atılacak ilk kazmayı .NET Aspire ile vurmak, bahsedilen tüm bu hantal durumları süreçten soyutlayacak ve yeni geliştirici tayfanın ilgili teknolojiye katılım süresini ve vakıf olmasını oldukça azaltacaktır. Şimdi gelin bunun nasıl yapılacağını incelemeye başlayalım…

.NET Aspire İle Birlikte Dapr’ın Kullanımı

İlk olarak yukarıdaki satırlarda Dapr için sunulan örnekteki servislere .NET Aspire desteği oluşturulmalı ve aşağıdaki referans çalışmaları gerçekleştirilmelidir.

var builder = DistributedApplication.CreateBuilder(args);

var _servicea = builder.AddProject<Projects.ServiceA>("servicea");

var _serviceb = builder.AddProject<Projects.ServiceB>("serviceb");

var _stateStore = builder.AddDaprStateStore("statestore");

var _rabbitmqPubsub = builder.AddDaprPubSub("rabbitmq-pubsub", new Aspire.Hosting.Dapr.DaprComponentOptions { LocalPath = "../rabbitmq-pubsub.yaml" });

var _redisPubsub = builder.AddDaprPubSub("redis-pubsub", new Aspire.Hosting.Dapr.DaprComponentOptions { LocalPath = "../redis-pubsub.yaml" });

_servicea.WithDaprSidecar()
         .WithReference(_stateStore)
         .WithReference(_rabbitmqPubsub);

_serviceb.WithDaprSidecar()
         .WithReference(_stateStore)
         .WithReference(_redisPubsub);

builder.Build().Run();

Görüldüğü üzere solution’a eklenen AppHost uygulaması üzerinde her iki servis referans edilmekte ve bir yandan da AddDaprPubSub metodu ile Dapr Pub/Sub component’leri sürece dahil edilmektedir. Tabi bunun için AppHost uygulamasına Aspire.Hosting.Dapr NuGet paketiyle hosting entegrasyonunda bulunulması gerekmektedir. Ayrıca servis referansları da WithDaprSidecar metoduyla Dapr sidecar’ına eklenmektedirler.

Burada özellikle Dapr component’leri için yaml dosyalarının doğru dizinle referans edilmesi gerektiğinin önemini vurgulamak istiyorum. Evet, her ne kadar .NET Aspire ile Dapr’ı daha efektif kullanacak olsak da, Dapr’ın doğası gereği ihtiyaç duyacağı yaml dosyalarını da yine fiziksel olarak oluşturmak ve bunu .NET Aspire’a sunmak biz geliştiricilerin görevidir.

Ha tabi sizler isterseniz aşağıdaki gibi de herhangi bir yaml yapılandırması kullanmaksızın da direkt AddDaprPubSub metodunu kullanabilir ve operasyonlarınızı yürütebilirsiniz;

.
.
.
var _rabbitmqPubsub = builder.AddDaprPubSub("rabbitmq-pubsub");
ya da
var _redisPubsub = builder.AddDaprPubSub("redis-pubsub");
.
.
.

Böyle bir durumda gerekli olan yaml kodu geçici bir klasörde otomatik olarak oluşturulacaktır.

Velhasıl…

İşte bu çalışma neticesinde .NET Aspire ile herhangi bir yapılandırma gerekmeksizin Dapr rahatlıkla kullanılabilmektedir. Uygulama derlenip çalıştırıldığında aşağıdaki görseldeki gibi .NET Aspire’ın dashboard’u açılacaktır..NET Aspire & Dapr | Distributed Uygulamalarda Bu Araçlar Birbirlerini Nasıl Tamamlarlar?Görüldüğü üzere servislere dair Dapr CLI’ler ayağa kaldırılmış ve port değerleri otomatik atanmış vaziyettedir. Böylece .NET Aspire ile birlikte Dapr entegrasyonu başarıyla sağlanmıştır ve uygulamamız kullanılabilir vaziyette ayaktadır.

Daha Karmaşık Dapr Senaryolarını Ele Alırsak

.NET Aspire ile varsayılan olarak desteklenen Dapr component’lerinin dışında(state store için AddDaprStateStore,pub/sub için AddDaprPubSub) yapılandırılmış component’leri sisteme yüklemek istiyorsak AddDaprComponent metodu aşağıdaki gibi kullanılabilir;

var _localsecretstore = builder.AddDaprComponent("localsecretstore", "secretstores.local.file", new DaprComponentOptions
{
    LocalPath = "/path/to/component-config.yaml"
});

Benzer mantıkla varsa resilience politikaları gibi kaynaklar bunlarında aşağıdaki gibi sidecar’a atanması mümkündür;

_servicea.WithDaprSidecar(new DaprSidecarOptions
{
    ResourcesPaths = ["/path/to/resources-directory"]
})
         .WithReference(_stateStore)
         .WithReference(_rabbitmqPubsub)
         .WithReference(_redisPubsub);

Nihai olarak;
.NET ile distributed uygulamalar geliştirirken .NET Aspire ile local development için gerçekten akıcı bir deneyim söz konusu olmaktadır. Ve içeriğimiz sürecinde deneyimlendiği üzere .NET Aspire ile Dapr birbirlerini tamamlamakta ve biz geliştiricilere daha az eforla ciddi yol katetme imkanı sağlamaktadır.

İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…

Not : Örnek çalışmaya aşağıdaki github adresinden erişebilirsiniz.
https://github.com/gncyyldz/NetAspire.vs.Dapr.Example

Bunlar da hoşunuza gidebilir...

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir