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

  1. 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ıralı diyagramı gösteren resim

  2. Sık kullandığınız kod düzenleyicisinde açın SendReceiveWithNservicebus.sln (Örneğin, Visual Studio 2019).

  3. 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.ICommandbasit 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 MainProgram.csGö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.jsonsağ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ı, SenderWorkersaniyede 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 IMessageSessionExecuteAsync 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ı PingHandlerbir 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 Pingbir 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 IMessageHandlerContextbu 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:

  1. Çözüm Gezgini'de çözüme sağ tıklayın
  2. "Başlangıç Projelerini Ayarla..." öğesini seçin
  3. Birden çok başlangıç projesi seçin
  4. 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:

  1. Gönderen projesinde aç Program.cs
  2. 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 PingHandlerolan 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:

İletileri en fazla 3 kez yeniden deneyen hemen yeniden deneme ilkesini gösteren görüntü

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:

Bir kez daha yeniden deneme denemeden önce iletileri 5 saniyelik artışlarla geciktiren gecikmeli yeniden deneme ilkesini gösteren görüntü

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ı errormerkezi bir hata kuyruğuna SendFailedMessagesToyerleştirilir.

Başarısız iletiyi gösteren resim

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:

Belirli Bir Yazılımdan ServicePulse'ı gösteren görüntü

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: