Vytváření obchodních aplikací řízených zprávami pomocí NServiceBus a Azure Service Bus

NServiceBus je komerční architektura zasílání zpráv poskytovaná konkrétním softwarem. Je postaven na Azure Service Bus a pomáhá vývojářům soustředit se na obchodní logiku tím, že abstrahuje aspekty infrastruktury. V této příručce vytvoříme řešení, které si vyměňuje zprávy mezi dvěma službami. Ukážeme si také, jak automaticky opakovat neúspěšné zprávy, a projdeme si možnosti hostování těchto služeb v Azure.

Poznámka

Kód pro tento kurz je k dispozici na webu Dokumentace ke konkrétnímu softwaru.

Požadavky

Ukázka předpokládá, že jste vytvořili obor názvů Azure Service Bus.

Důležité

NServiceBus vyžaduje alespoň úroveň Standard. Úroveň Basic nebude fungovat.

Stažení a příprava řešení

  1. Stáhněte si kód z webu Dokumentace ke konkrétnímu softwaru. Řešení SendReceiveWithNservicebus.sln se skládá ze tří projektů:

    • Odesílatel: konzolová aplikace, která odesílá zprávy.
    • Příjemce: konzolová aplikace, která přijímá zprávy od odesílatele a odpovídá zpět
    • Sdílené: Knihovna tříd obsahující kontrakty zpráv sdílené mezi odesílatelem a příjemcem

    Následující diagram, který vygeneruje ServiceInsight, vizualizační a ladicí nástroj od konkrétního softwaru, znázorňuje tok zpráv:

    Obrázek znázorňující sekvenční diagram

  2. Otevřete SendReceiveWithNservicebus.sln v oblíbeném editoru kódu (například Visual Studio 2019).

  3. Otevřete appsettings.json v projektech Receiver i Sender a nastavte AzureServiceBusConnectionString připojovací řetězec pro váš obor názvů Azure Service Bus.

Definování sdílených kontraktů zpráv

Knihovna sdílených tříd je místo, kde definujete kontrakty používané k odesílání našich zpráv. Obsahuje odkaz na NServiceBus balíček NuGet, který obsahuje rozhraní, která můžete použít k identifikaci našich zpráv. Rozhraní nejsou povinná, ale poskytují nám dodatečné ověření ze služby NServiceBus a umožňují, aby se kód sám zdokumentoval.

Nejprve si projdeme předmět.Ping.cs

public class Ping : NServiceBus.ICommand
{
    public int Round { get; set; }
}

Třída Ping definuje zprávu, kterou odesílatel odešle příjemci. Jedná se o jednoduchou třídu jazyka C#, která implementuje NServiceBus.ICommandrozhraní z balíčku NServiceBus. Tato zpráva je signálem pro čtenáře a pro NServiceBus, že se jedná o příkaz, i když existují i jiné způsoby, jak identifikovat zprávy bez použití rozhraní.

Druhá třída zpráv ve sdílených projektech je Pong.cs:

public class Pong : NServiceBus.IMessage
{
    public string Acknowledgement { get; set; }
}

Pong je také jednoduchý objekt jazyka C#, i když tento objekt implementuje NServiceBus.IMessage. Rozhraní IMessage představuje obecnou zprávu, která není příkazem ani událostí a běžně se používá pro odpovědi. V naší ukázce je to odpověď, kterou příjemce odešle zpět odesílateli, aby označil, že zpráva byla přijata.

Pong A Ping jsou dva typy zpráv, které budete používat. Dalším krokem je konfigurace odesílatele pro použití Azure Service Bus a odeslání Ping zprávy.

Nastavení odesílatele

Odesílatel je koncový bod, který odesílá naši Ping zprávu. Zde nakonfigurujete odesílatele tak, aby jako mechanismus přenosu používal Azure Service Bus, pak sestavíte Ping instanci a odešlete ji.

Main V metodě nakonfigurujete Program.cskoncový bod sender:

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();

Je toho hodně, co je potřeba rozbalit, takže si to projdeme krok za krokem.

Konfigurace hostitele pro koncový bod

Hostování a protokolování se konfiguruje pomocí standardních možností obecného hostitele Microsoftu. Prozatím je koncový bod nakonfigurovaný tak, aby běžel jako konzolová aplikace, ale dá se upravit tak, aby běžel v Azure Functions s minimálními změnami, o kterých se budeme zabývat dále v tomto článku.

Konfigurace koncového bodu NServiceBus

Dále řeknete hostiteli, aby použil NServiceBus s metodou .UseNServiceBus(…) rozšíření. Metoda přebírá funkci zpětného volání, která vrátí koncový bod, který se spustí při spuštění hostitele.

V konfiguraci koncového bodu zadáte AzureServiceBus pro náš přenos a poskytnete připojovací řetězec z appsettings.json. Dále nastavíte směrování tak, aby se zprávy typu Ping odesílaly do koncového bodu s názvem Příjemce. Umožňuje službě NServiceBus automatizovat proces odeslání zprávy do cíle, aniž by vyžadovala adresu příjemce.

Volání EnableInstallers nastaví topologii v oboru názvů Azure Service Bus při spuštění koncového bodu a v případě potřeby vytvoří požadované fronty. V produkčních prostředích je další možností vytvoření topologie provozní skriptování .

Nastavení služby na pozadí pro odesílání zpráv

Poslední částí odesílatele je SenderWorkerslužba na pozadí, která je nakonfigurovaná tak, aby odesílala zprávu každou sekundu Ping .

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
        }
    }
}

Použitý IMessageSession v ExecuteAsync souboru je vložen do SenderWorker a umožňuje odesílat zprávy pomocí NServiceBus mimo obslužnou rutinu zprávy. Směrování, které jste nakonfigurovali v Sender nástroji, určuje cíl Ping zpráv. Topologii systému (na které adresy se zprávy směrují) udržuje jako samostatnou záležitost od obchodního kódu.

Aplikace Sender obsahuje PongHandlertaké . Vrátíte se k němu, jakmile probereme příjemce, což uděláme příště.

Nastavení přijímače

Příjemce je koncový bod, který naslouchá zprávě, protokoluje Ping přijetí zprávy a odpovídá zpět odesílateli. V této části rychle zkontrolujeme konfiguraci koncového bodu, která se podobá odesílateli, a pak obrátíme pozornost na obslužnou rutinu zprávy.

Stejně jako odesílatel nastavte příjemce jako konzolovou aplikaci pomocí nástroje Microsoft Generic Host. Používá stejné protokolování a konfiguraci koncového bodu (s Azure Service Bus jako přenos zpráv), ale s jiným názvem, aby se odlišil od odesílatele:

var endpointConfiguration = new EndpointConfiguration("Receiver");

Vzhledem k tomu, že tento koncový bod pouze odpovídá svému původci a nezahajuje nové konverzace, nevyžaduje se žádná konfigurace směrování. Také nepotřebuje pracovníka na pozadí jako Odesílatel, protože odpoví pouze při přijetí zprávy.

Obslužná rutina zprávy Ping

Projekt Receiver obsahuje obslužnou rutinu zprávy s názvem PingHandler:

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);
    }
}

Prozatím komentovaný kód ignorujme. k tomu se vrátíme později, až budeme mluvit o zotavení po selhání.

Třída implementuje IHandleMessages<Ping>, který definuje jednu metodu: Handle. Toto rozhraní informuje NServiceBus, že když koncový bod obdrží zprávu typu Ping, měla by být zpracována metodou Handle v této obslužné rutině. Metoda Handle přebírá samotnou zprávu jako parametr a IMessageHandlerContext, který umožňuje další operace zasílání zpráv, jako je odpovídání, odesílání příkazů nebo publikování událostí.

Naše PingHandler je jednoduchá: při Ping přijetí zprávy protokolujte podrobnosti zprávy a odpovězte odesílateli novou Pong zprávou.

Poznámka

V konfiguraci odesílatele jste určili, že Ping zprávy se mají směrovat na příjemce. NServiceBus přidá do zpráv metadata, která mimo jiné označují původ zprávy. Proto nemusíte pro Pong zprávu odpovědi zadávat žádná data směrování. Automaticky se směruje zpět na její původ: odesílatele.

Když máte správně nakonfigurované možnosti Odesílatel i Příjemce, můžete řešení spustit.

Spuštění řešení

Pokud chcete řešení spustit, musíte spustit odesílatele i příjemce. Pokud používáte Visual Studio Code, spusťte konfiguraci "Debug All". Pokud používáte Visual Studio, nakonfigurujte řešení tak, aby spustilo projekty Sender i Receiver:

  1. Klikněte pravým tlačítkem na řešení v Průzkumník řešení
  2. Vyberte Nastavit projekty po spuštění.
  3. Výběr více projektů po spuštění
  4. Pro odesílatele i příjemce vyberte v rozevíracím seznamu "Start".

Spusťte řešení. Zobrazí se dvě konzolové aplikace, jedna pro odesílatele a jedna pro příjemce.

V části Odesílatel si všimněte, že Ping se zpráva odesílá každou sekundu díky úloze na SenderWorker pozadí. Příjemce zobrazí podrobnosti o každé Ping zprávě, kterou obdrží, a odesílatel zaznamená podrobnosti o každé Pong zprávě, kterou obdrží jako odpověď.

Teď, když všechno funguje, můžeme to zlomit.

Odolnost v akci

Chyby jsou faktem života v softwarových systémech. Je nevyhnutelné, že kód selže a může to udělat z různých důvodů, jako jsou selhání sítě, zámky databáze, změny v rozhraní API třetí strany a prosté staré chyby kódování.

NServiceBus má robustní funkce obnovitelnosti pro zpracování chyb. Pokud obslužná rutina zprávy selže, zprávy se automaticky zopakují na základě předdefinovaných zásad. Existují dva typy zásad opakování: okamžité opakování a zpožděné opakování. Nejlepší způsob, jak popsat, jak fungují, je vidět je v akci. Pojďme do koncového bodu příjemce přidat zásadu opakování:

  1. Otevřít Program.cs v projektu Sender
  2. .EnableInstallers Za řádek přidejte následující kód:
endpointConfiguration.SendFailedMessagesTo("error");
var recoverability = endpointConfiguration.Recoverability();
recoverability.Immediate(
    immediate =>
    {
        immediate.NumberOfRetries(3);
    });
recoverability.Delayed(
    delayed =>
    {
        delayed.NumberOfRetries(2);
        delayed.TimeIncrease(TimeSpan.FromSeconds(5));
    });

Než se podíváme na to, jak tato zásada funguje, podívejme se, jak funguje. Než otestujete zásady obnovitelnosti, musíte simulovat chybu. PingHandler Otevřete kód v projektu Receiver a odkomentujte tento řádek:

throw new Exception("BOOM");

Když příjemce zpracuje Ping zprávu, selže. Spusťte řešení znovu a podívejme se, co se stane v přijímači.

S méně spolehlivými PingHandlerzprávami selžou všechny naše zprávy. Uvidíte, jak se u těchto zpráv nastartují zásady opakování. Při prvním selhání zprávy se okamžitě až třikrát opakuje:

Obrázek znázorňující zásadu okamžitého opakování, která až 3krát opakuje zprávy

Samozřejmě bude dál selhávat, takže když se tři okamžité opakování využijí, spustí se zásada zpožděného opakování a zpráva se zpozdí o 5 sekund:

Obrázek znázorňující zásadu zpožděného opakování, která zpožďuje zprávy v přírůstcích po 5 sekundách před pokusem o další kolo okamžitého opakování

Po uplynutí těchto 5 sekund se zpráva opakuje ještě třikrát (to znamená další iteraci zásad okamžitého opakování). Tyto chyby také selžou a NServiceBus znovu zpozdí zprávu, tentokrát o 10 sekund, a pak to zkusí znovu.

Pokud PingHandler se ani po spuštění zásady úplného opakování nepodaří, zpráva se umístí do centralizované fronty chyb s názvem error, jak je definováno voláním SendFailedMessagesTo.

Obrázek znázorňující zprávu o selhání

Koncept centralizované fronty chyb se liší od mechanismu nedoručených zpráv v Azure Service Bus, který má frontu nedoručených zpráv pro každou frontu zpracování. Při použití NServiceBus fungují fronty nedoručených zpráv v Azure Service Bus jako fronty zpráv s true jedem, zatímco zprávy, které končí v centralizované frontě chyb, lze v případě potřeby později znovu zpracovat.

Zásady opakování pomáhají řešit několik typů chyb , které jsou často přechodné nebo polo přechodné povahy. To znamená, že chyby, které jsou dočasné a často zmizí, pokud se zpráva jednoduše po krátké prodlevě znovu zpracuje. Mezi příklady patří selhání sítě, zámky databází a výpadky rozhraní API třetích stran.

Jakmile je zpráva ve frontě chyb, můžete si prohlédnout podrobnosti o zprávě v nástroji podle svého výběru a pak se rozhodnout, co s ní dělat. Například pomocí nástroje pro monitorování ServicePulse od konkrétního softwaru můžeme zobrazit podrobnosti o zprávě a důvod selhání:

Obrázek znázorňující ServicePulse z konkrétního softwaru

Po prozkoumání podrobností můžete zprávu odeslat zpět do původní fronty ke zpracování. Před tím můžete zprávu také upravit. Pokud je ve frontě chyb více zpráv, které selhaly ze stejného důvodu, mohou být všechny odeslány zpět do původního cíle jako dávka.

V dalším kroku je čas zjistit, kam nasadit naše řešení v Azure.

Kde hostovat služby v Azure

V této ukázce jsou koncové body odesílatele a příjemce nakonfigurované tak, aby běžely jako konzolové aplikace. Můžete je také hostovat v různých službách Azure, včetně Azure Functions, Aplikace Azure Services, Azure Container Instances, Azure Kubernetes Services a virtuálních počítačů Azure. Tady je například postup konfigurace koncového bodu odesílatele pro spuštění jako funkce Azure Functions:

[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;
        });
    }
}

Další informace o používání služby NServiceBus s funkcemi najdete v tématu Azure Functions s Azure Service Bus v dokumentaci ke službě NServiceBus.

Další kroky

Další informace o používání NServiceBus se službami Azure najdete v následujících článcích: