NServiceBus ve Azure Service Bus ile ileti temelli iş uygulamaları oluşturma
NServiceBus, Belirli Yazılımlar tarafından sağlanan ticari bir mesajlaşma çerçevesidir. Azure Service Bus üzerine kurulmuştur ve geliştiricilerin altyapı sorunlarını soyutlayarak iş mantığına odaklanmalarına yardımcı olur. Bu kılavuzda, iki hizmet arasında ileti alışverişi sağlayan bir çözüm oluşturacağız. Ayrıca başarısız iletileri otomatik olarak yeniden denemeyi ve bu hizmetleri Azure'da barındırma seçeneklerini gözden geçirmeyi göstereceğiz.
Not
Bu öğreticinin kodu , Belirli Yazılım Belgeleri web sitesinde bulunabilir.
Ön koşullar
Örnekte bir Azure Service Bus ad alanı oluşturduğunuz varsayılır.
Önemli
NServiceBus için en az Standart katman gerekir. Temel katman çalışmaz.
Çözümü indirme ve hazırlama
Kodu Belirli Yazılım Belgeleri web sitesinden indirin. Çözüm
SendReceiveWithNservicebus.sln
üç projeden oluşur:- Gönderen: İleti gönderen bir konsol uygulaması
- Alıcı: Gönderenden ileti alan ve geri yanıtlayan bir konsol uygulaması
- Paylaşılan: Gönderen ve alıcı arasında paylaşılan ileti sözleşmelerini içeren bir sınıf kitaplığı
Belirli Bir Yazılımdan alınan görselleştirme ve hata ayıklama aracı ServiceInsight tarafından oluşturulan aşağıdaki diyagramda ileti akışı gösterilir:
Sık kullandığınız kod düzenleyicisinde açın
SendReceiveWithNservicebus.sln
(Örneğin, Visual Studio 2019).Hem Alıcı hem de Gönderen projelerinde açın
appsettings.json
ve Azure Service Bus ad alanınızın bağlantı dizesine ayarlayınAzureServiceBusConnectionString
.
Paylaşılan ileti sözleşmelerini tanımlama
Paylaşılan sınıf kitaplığı, iletilerimizi göndermek için kullanılan sözleşmeleri tanımladığınız yerdir. İletilerimizi tanımlamak için NServiceBus
kullanabileceğiniz arabirimleri içeren NuGet paketine bir başvuru içerir. Arabirimler gerekli değildir, ancak bize NServiceBus'tan fazladan doğrulama sağlar ve kodun kendi kendine belgelenmesine izin verir.
İlk olarak sınıfı gözden geçireceğiz Ping.cs
public class Ping : NServiceBus.ICommand
{
public int Round { get; set; }
}
sınıfı, Ping
Gönderen'in Alıcıya gönderdiği bir iletiyi tanımlar. NServiceBus paketinden arabirimini uygulayan NServiceBus.ICommand
basit bir C# sınıfıdır. Bu ileti, okuyucuya ve NServiceBus'a bunun bir komut olduğunu belirten bir sinyaldir, ancak arabirimleri kullanmadan iletileri tanımlamanın başka yolları da vardır.
Paylaşılan projelerdeki diğer ileti sınıfı:Pong.cs
public class Pong : NServiceBus.IMessage
{
public string Acknowledgement { get; set; }
}
Pong
aynı zamanda basit bir C# nesnesidir ancak bunu uygular NServiceBus.IMessage
. IMessage
Arabirim, ne komut ne de olay olan genel bir iletiyi temsil eder ve yanıtlar için yaygın olarak kullanılır. Örneğimizde, Alıcının bir iletinin alındığını belirtmek için Gönderen'e geri gönderdiği bir yanıttır.
Ping
vePong
, kullanacağınız iki ileti türü olur. Sonraki adım, Gönderen'i Azure Service Bus kullanacak ve ileti Ping
gönderecek şekilde yapılandırmaktır.
Göndereni ayarlama
Gönderen, iletimizi Ping
gönderen bir uç noktadır. Burada Gönderen'i aktarım mekanizması olarak Azure Service Bus kullanacak şekilde yapılandırır, ardından bir Ping
örnek oluşturup gönderirsiniz.
yönteminde Main
Program.cs
Gönderen uç noktasını yapılandıracaksınız:
var host = Host.CreateDefaultBuilder(args)
// Configure a host for the endpoint
.ConfigureLogging((context, logging) =>
{
logging.AddConfiguration(context.Configuration.GetSection("Logging"));
logging.AddConsole();
})
.UseConsoleLifetime()
.UseNServiceBus(context =>
{
// Configure the NServiceBus endpoint
var endpointConfiguration = new EndpointConfiguration("Sender");
var transport = endpointConfiguration.UseTransport<AzureServiceBusTransport>();
var connectionString = context.Configuration.GetConnectionString("AzureServiceBusConnectionString");
transport.ConnectionString(connectionString);
transport.Routing().RouteToEndpoint(typeof(Ping), "Receiver");
endpointConfiguration.EnableInstallers();
endpointConfiguration.AuditProcessedMessagesTo("audit");
return endpointConfiguration;
})
.ConfigureServices(services => services.AddHostedService<SenderWorker>())
.Build();
await host.RunAsync();
Burada paketten çıkarmamız gereken çok şey var, bu nedenle bunu adım adım inceleyeceğiz.
Uç nokta için konak yapılandırma
Barındırma ve günlüğe kaydetme, standart Microsoft Genel Ana Bilgisayar seçenekleri kullanılarak yapılandırılır. Şimdilik uç nokta bir konsol uygulaması olarak çalışacak şekilde yapılandırılmıştır, ancak bu makalenin devamında ele alacağımız en az değişiklikle Azure İşlevleri çalıştırılacak şekilde değiştirilebilir.
NServiceBus uç noktasını yapılandırma
Ardından, konağa uzantı yöntemiyle .UseNServiceBus(…)
NServiceBus kullanmasını söylersiniz. yöntemi, konak çalıştırıldığında başlatılacak bir uç nokta döndüren bir geri çağırma işlevi alır.
Uç nokta yapılandırmasında, aktarım için öğesinin bağlantı dizesini appsettings.json
sağlayarak belirtirsinizAzureServiceBus
. Ardından, yönlendirmeyi ayarlayarak türdeki Ping
iletilerin "Alıcı" adlı bir uç noktaya gönderilmesini sağlarsınız. NServiceBus'un alıcının adresine gerek kalmadan iletiyi hedefe gönderme işlemini otomatikleştirmesine olanak tanır.
çağrısıEnableInstallers
, uç nokta başlatıldığında Azure Service Bus ad alanında topolojimizi ayarlar ve gerektiğinde gerekli kuyrukları oluşturur. Üretim ortamlarında işlem betiği , topolojiyi oluşturmak için bir diğer seçenektir.
İleti göndermek için arka plan hizmetini ayarlama
Gönderenin son parçası, SenderWorker
saniyede bir ileti gönderecek şekilde yapılandırılmış bir Ping
arka plan hizmetidir.
public class SenderWorker : BackgroundService
{
private readonly IMessageSession messageSession;
private readonly ILogger<SenderWorker> logger;
public SenderWorker(IMessageSession messageSession, ILogger<SenderWorker> logger)
{
this.messageSession = messageSession;
this.logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
var round = 0;
while (!stoppingToken.IsCancellationRequested)
{
await messageSession.Send(new Ping { Round = round++ })
.ConfigureAwait(false);
logger.LogInformation($"Message #{round}");
await Task.Delay(1_000, stoppingToken)
.ConfigureAwait(false);
}
}
catch (OperationCanceledException)
{
// graceful shutdown
}
}
}
içinde IMessageSession
ExecuteAsync
kullanılan içine eklenir SenderWorker
ve bir ileti işleyicisi dışında NServiceBus kullanarak ileti göndermemize olanak tanır. içinde Sender
yapılandırdığınız yönlendirme, iletilerin hedefini Ping
belirtir. Sistemin topolojisini (hangi iletilerin hangi adreslere yönlendirildiği) iş kodundan ayrı bir endişe olarak tutar.
Sender uygulaması da içerir PongHandler
. Alıcı'yı tartıştıktan sonra buna geri dönersiniz. Bundan sonra bunu yapacağız.
Alıcıyı ayarlama
Alıcı, bir iletiyi dinleyen, ileti Ping
alındığında günlüğe kaydeden ve gönderene geri yanıt veren bir uç noktadır. Bu bölümde, Gönderen'e benzer olan uç nokta yapılandırmasını hızla gözden geçirecek ve ardından dikkatimizi ileti işleyicisine çevireceğiz.
Gönderen gibi, Microsoft Genel Ana Bilgisayarı'nı kullanarak alıcıyı bir konsol uygulaması olarak ayarlayın. Gönderenden ayırt etmek için aynı günlük ve uç nokta yapılandırmasını (ileti aktarımı olarak Azure Service Bus ile) ama farklı bir adla kullanır:
var endpointConfiguration = new EndpointConfiguration("Receiver");
Bu uç nokta yalnızca gönderene yanıt verdiği ve yeni konuşmalar başlatmadığı için yönlendirme yapılandırması gerekmez. Ayrıca, gönderenin yaptığı gibi bir arka plan çalışanına da gerek yoktur, çünkü yalnızca bir ileti aldığında yanıt verir.
Ping iletisi işleyicisi
Alıcı projesi adlı PingHandler
bir ileti işleyicisi içerir:
public class PingHandler : NServiceBus.IHandleMessages<Ping>
{
private readonly ILogger<PingHandler> logger;
public PingHandler(ILogger<PingHandler> logger)
{
this.logger = logger;
}
public async Task Handle(Ping message, IMessageHandlerContext context)
{
logger.LogInformation($"Processing Ping message #{message.Round}");
// throw new Exception("BOOM");
var reply = new Pong { Acknowledgement = $"Ping #{message.Round} processed at {DateTimeOffset.UtcNow:s}" };
await context.Reply(reply);
}
}
Açıklamaya alınan kodu şimdilik yoksayalım; hatadan kurtarma hakkında daha sonra söz ettiğimizde bu hataya geri döneceğiz.
sınıfı, bir yöntemi tanımlayan öğesini uygular IHandleMessages<Ping>
: Handle
. Bu arabirim NServiceBus'a uç nokta türünde Ping
bir ileti aldığında bu işleyicideki Handle
yöntemi tarafından işlenmesi gerektiğini bildirir. yöntemi, Handle
iletinin kendisini bir parametre olarak alır ve IMessageHandlerContext
bu da yanıtlama, komut gönderme veya olayları yayımlama gibi daha fazla mesajlaşma işlemine olanak tanır.
Bizim PingHandler
işimiz basittir: bir Ping
ileti alındığında, ileti ayrıntılarını günlüğe kaydedip gönderene yeni Pong
bir iletiyle yanıt verin.
Not
Gönderenin yapılandırmasında, iletilerin Ping
Alıcı'ya yönlendirilmesi gerektiğini belirttiniz. NServiceBus iletilere, iletinin kaynağını gösteren meta veriler ekler. Bu nedenle yanıt iletisi için Pong
yönlendirme verileri belirtmeniz gerekmez; otomatik olarak kaynağı olan Gönderen'e yönlendirilir.
Hem Gönderen hem de Alıcı düzgün yapılandırıldığında, artık çözümü çalıştırabilirsiniz.
Çözümü çalıştırın
Çözümü başlatmak için hem Gönderen'i hem de Alıcı'yı çalıştırmanız gerekir. Visual Studio Code kullanıyorsanız "Tümünde Hata Ayıkla" yapılandırmasını başlatın. Visual Studio kullanıyorsanız, çözümü hem Gönderen hem de Alıcı projelerini başlatacak şekilde yapılandırın:
- Çözüm Gezgini'de çözüme sağ tıklayın
- "Başlangıç Projelerini Ayarla..." öğesini seçin
- Birden çok başlangıç projesi seçin
- Hem Gönderen hem de Alıcı için açılan listeden "Başlat"ı seçin
Çözümü başlatın. Biri Gönderen, diğeri Alıcı için iki konsol uygulaması görüntülenir.
Gönderen'de, arka plan işi sayesinde her saniye bir Ping
ileti gönderildiğine SenderWorker
dikkat edin. Alıcı aldığı her Ping
iletinin ayrıntılarını görüntüler ve Gönderen yanıtta aldığı her Pong
iletinin ayrıntılarını günlüğe kaydeder.
Artık her şeyi çalıştırdığınıza göre, bunu kıralım.
Dayanıklılık çalışır durumda
Hatalar, yazılım sistemlerindeki hayatın bir gerçeğidir. Kodun başarısız olması kaçınılmazdır ve ağ hataları, veritabanı kilitleri, üçüncü taraf API'deki değişiklikler ve düz eski kodlama hataları gibi çeşitli nedenlerle bunu yapabilir.
NServiceBus, hataları işlemek için güçlü kurtarılabilirlik özelliklerine sahiptir. İleti işleyicisi başarısız olduğunda, iletiler önceden tanımlanmış bir ilkeye göre otomatik olarak yeniden deneniyor. İki tür yeniden deneme ilkesi vardır: anında yeniden denemeler ve gecikmeli yeniden denemeler. Nasıl çalıştıklarını açıklamanın en iyi yolu, onları çalışırken görmektir. Alıcı uç noktamıza bir yeniden deneme ilkesi ekleyelim:
- Gönderen projesinde aç
Program.cs
- Satırından
.EnableInstallers
sonra aşağıdaki kodu ekleyin:
endpointConfiguration.SendFailedMessagesTo("error");
var recoverability = endpointConfiguration.Recoverability();
recoverability.Immediate(
immediate =>
{
immediate.NumberOfRetries(3);
});
recoverability.Delayed(
delayed =>
{
delayed.NumberOfRetries(2);
delayed.TimeIncrease(TimeSpan.FromSeconds(5));
});
Bu ilkenin nasıl çalıştığını tartışmadan önce nasıl çalıştığını görelim. Kurtarılabilirlik ilkesini test etmeden önce bir hata benzetimi yapmanız gerekir. PingHandler
Alıcı projesinde kodu açın ve şu satırın açıklamasını kaldırın:
throw new Exception("BOOM");
Artık Alıcı bir Ping
iletiyi işlediğinde başarısız olur. Çözümü yeniden başlatın ve Alıcı'da neler olduğunu görelim.
Daha az güvenilir PingHandler
olan ile tüm iletilerimiz başarısız olur. Yeniden deneme ilkesinin bu iletiler için devreye giriyor olduğunu görebilirsiniz. bir ileti ilk kez başarısız olduğunda hemen üç kez yeniden denenecek:
Tabii ki, üç acil yeniden deneme kullanıldığında gecikmeli yeniden deneme ilkesi başlatıldığında ve ileti 5 saniye gecikmeli olduğunda başarısız olacak:
Bu 5 saniye geçtikten sonra, ileti üç kez daha yeniden denenecektir (yani, hemen yeniden deneme ilkesinin başka bir yinelemesi). Bunlar da başarısız olur ve NServiceBus yeniden denemeden önce iletiyi 10 saniye boyunca yeniden geciktirecektir.
PingHandler
Tam yeniden deneme ilkesi çalıştırıldıktan sonra yine de başarılı olmazsa, iletisi çağrısı tarafından tanımlandığı gibi adlı error
merkezi bir hata kuyruğuna SendFailedMessagesTo
yerleştirilir.
Merkezi hata kuyruğu kavramı, her işleme kuyruğu için teslim edilemeyen ileti kuyruğu olan Azure Service Bus'daki teslim edilemeyen ileti mekanizmasından farklıdır. NServiceBus ile, Azure Service Bus içindeki teslim edilemeyen ileti kuyrukları gerçek zehirli ileti kuyrukları gibi davranırken, merkezi hata kuyruğuna giden iletiler gerekirse daha sonra yeniden işlenebilir.
Yeniden deneme ilkesi, genellikle geçici veya yarı geçici olan çeşitli hata türlerinin giderilmesine yardımcı olur. Başka bir ifadeyle, geçici olan ve ileti kısa bir gecikmeden sonra yeniden işlendiğinde genellikle ortadan kaldırılan hatalar. Örnek olarak ağ hataları, veritabanı kilitleri ve üçüncü taraf API kesintileri verilebilir.
bir ileti hata kuyruğuna girdikten sonra, istediğiniz araçta ileti ayrıntılarını inceleyebilir ve ardından iletiyle ne yapacağınıza karar vekleyebilirsiniz. Örneğin, Belirli Yazılım tarafından kullanılan bir izleme aracı olan ServicePulse'ı kullanarak ileti ayrıntılarını ve hatanın nedenini görüntüleyebiliriz:
Ayrıntıları inceledikten sonra, iletiyi işlenmek üzere özgün kuyruğuna geri gönderebilirsiniz. Ayrıca, iletiyi düzenlemeden önce de düzenleyebilirsiniz. Hata kuyruğunda aynı nedenle başarısız olan birden çok ileti varsa, hepsi toplu iş olarak özgün hedeflerine geri gönderilebilir.
Şimdi çözümümüzün Azure'da nereye dağıtılacağına bakalım.
Azure'da hizmetlerin barındırıldığı yer
Bu örnekte, Gönderen ve Alıcı uç noktaları konsol uygulamaları olarak çalışacak şekilde yapılandırılır. Bunlar Azure İşlevleri, Azure Uygulaması Hizmetleri, Azure Container Instances, Azure Kubernetes Services ve Azure VM'leri gibi çeşitli Azure hizmetlerinde de barındırılabilir. Örneğin, Gönderen uç noktası bir Azure İşlevi olarak çalışacak şekilde şu şekilde yapılandırılabilir:
[assembly: FunctionsStartup(typeof(Startup))]
[assembly: NServiceBusEndpointName("Sender")]
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.UseNServiceBus(() =>
{
var configuration = new ServiceBusTriggeredEndpointConfiguration("Sender");
var transport = configuration.AdvancedConfiguration.Transport;
transport.Routing().RouteToEndpoint(typeof(Ping), "Receiver");
return configuration;
});
}
}
NServiceBus'ı İşlevler ile kullanma hakkında daha fazla bilgi için NServiceBus belgelerindeki Azure Service Bus ile Azure İşlevleri bölümüne bakın.
Sonraki adımlar
NServiceBus'ı Azure hizmetleriyle kullanma hakkında daha fazla bilgi için aşağıdaki makalelere bakın: