Tworzenie aplikacji biznesowych opartych na komunikatach za pomocą sieci NServiceBus i usługi Azure Service Bus

NServiceBus to komercyjna platforma obsługi komunikatów dostarczana przez określone oprogramowanie. Jest ona oparta na usłudze Azure Service Bus i pomaga deweloperom skupić się na logice biznesowej przez abstrakcję problemów z infrastrukturą. W tym przewodniku utworzymy rozwiązanie, które wymienia komunikaty między dwiema usługami. Pokażemy również, jak automatycznie ponowić próbę niepowodzenia komunikatów i przejrzeć opcje hostowania tych usług na platformie Azure.

Uwaga

Kod tego samouczka jest dostępny w witrynie sieci Web Konkretnej witryny dokumentacji oprogramowania.

Wymagania wstępne

W przykładzie założono, że utworzono przestrzeń nazw usługi Azure Service Bus.

Ważne

NServiceBus wymaga co najmniej warstwy Standardowa. Warstwa Podstawowa nie będzie działać.

Pobieranie i przygotowywanie rozwiązania

  1. Pobierz kod z witryny sieci Web Docs określonego oprogramowania. Rozwiązanie SendReceiveWithNservicebus.sln składa się z trzech projektów:

    • Nadawca: aplikacja konsolowa, która wysyła komunikaty
    • Odbiornik: aplikacja konsolowa, która odbiera komunikaty od nadawcy i odpowiada z powrotem
    • Udostępnione: biblioteka klas zawierająca kontrakty komunikatów współużytkowane przez nadawcę i odbiorcę

    Na poniższym diagramie wygenerowanym przez usługę ServiceInsight, narzędziu do wizualizacji i debugowania z określonego oprogramowania przedstawiono przepływ komunikatów:

    Obraz przedstawiający diagram sekwencji

  2. Otwórz plik SendReceiveWithNservicebus.sln w ulubionym edytorze kodu (na przykład Visual Studio 2022).

  3. Otwórz appsettings.json zarówno w projektach Odbiornik, jak i Nadawca, a następnie ustaw AzureServiceBusConnectionString parametry połączenia dla przestrzeni nazw usługi Azure Service Bus.

    • Można to znaleźć w witrynie Azure Portal w obszarze Ustawienia przestrzeni nazw>>usługi Service Bus Zasady>dostępu współdzielonego RootManageSharedAccessKey>podstawowe parametry połączenia.
    • Ma AzureServiceBusTransport również konstruktor, który akceptuje przestrzeń nazw i poświadczenia tokenu, które w środowisku produkcyjnym będzie bezpieczniejsze, jednak na potrzeby tego samouczka zostanie użyty klucz dostępu współdzielonego parametry połączenia.

Definiowanie kontraktów komunikatów udostępnionych

Biblioteka klas udostępnionych służy do definiowania kontraktów używanych do wysyłania naszych komunikatów. Zawiera on odwołanie do NServiceBus pakietu NuGet, który zawiera interfejsy, których można użyć do identyfikowania naszych komunikatów. Interfejsy nie są wymagane, ale dają nam dodatkową walidację z NServiceBus i umożliwiają samodzielne dokumentowanie kodu.

Najpierw przejrzymy klasę Ping.cs

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

Klasa Ping definiuje komunikat, który nadawca wysyła do odbiorcy. Jest to prosta klasa języka C#, która implementuje NServiceBus.ICommandinterfejs z pakietu NServiceBus. Ten komunikat jest sygnałem dla czytnika i NServiceBus, że jest to polecenie, chociaż istnieją inne sposoby identyfikowania komunikatów bez używania interfejsów.

Druga klasa komunikatów w projektach udostępnionych to Pong.cs:

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

Pong jest również prostym obiektem języka C#, choć ten implementuje NServiceBus.IMessageelement . Interfejs IMessage reprezentuje ogólny komunikat, który nie jest ani poleceniem, ani zdarzeniem, i jest często używany do odpowiedzi. W naszym przykładzie jest to odpowiedź, którą odbiorca wysyła z powrotem do nadawcy, aby wskazać, że wiadomość została odebrana.

Wartości Ping i Pong to dwa typy komunikatów, których będziesz używać. Następnym krokiem jest skonfigurowanie nadawcy do używania usługi Azure Service Bus i wysłania komunikatu Ping .

Konfigurowanie nadawcy

Nadawca to punkt końcowy, który wysyła wiadomość Ping . W tym miejscu skonfigurujesz nadawcę do używania usługi Azure Service Bus jako mechanizmu transportu, a następnie skonstruujesz Ping wystąpienie i wyślesz je.

W metodzie Main Program.csprogramu należy skonfigurować punkt końcowy nadawcy:

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 connectionString = context.Configuration.GetConnectionString("AzureServiceBusConnectionString");
        // If token credentials are to be used, the overload constructor for AzureServiceBusTransport would be used here
        var routing = endpointConfiguration.UseTransport(new AzureServiceBusTransport(connectionString));
        endpointConfiguration.UseSerialization<SystemJsonSerializer>();

        endpointConfiguration.AuditProcessedMessagesTo("audit");
        routing.RouteToEndpoint(typeof(Ping), "Receiver");

        endpointConfiguration.EnableInstallers();

        return endpointConfiguration;
    })
    .ConfigureServices(services => services.AddHostedService<SenderWorker>())
    .Build();

await host.RunAsync();

Istnieje wiele do rozpakowania tutaj, więc zapoznamy się z nim krok po kroku.

Konfigurowanie hosta dla punktu końcowego

Hosting i rejestrowanie są konfigurowane przy użyciu standardowych opcji hosta ogólnego firmy Microsoft. Na razie punkt końcowy jest skonfigurowany do uruchamiania jako aplikacja konsolowa, ale można go zmodyfikować w celu uruchamiania w usłudze Azure Functions z minimalnymi zmianami, które omówimy w dalszej części tego artykułu.

Konfigurowanie punktu końcowego NServiceBus

Następnie należy poinformować hosta o użyciu narzędzia NServiceBus z .UseNServiceBus(…) metodą rozszerzenia. Metoda przyjmuje funkcję wywołania zwrotnego, która zwraca punkt końcowy, który zostanie uruchomiony po uruchomieniu hosta.

W konfiguracji punktu końcowego określisz AzureServiceBus dla naszego transportu, podając parametry połączenia z appsettings.json. Następnie skonfigurujesz routing, aby komunikaty typu Ping zostały wysłane do punktu końcowego o nazwie "Odbiornik". Dzięki niej NServiceBus może zautomatyzować proces wysyłania komunikatu do miejsca docelowego bez wymagania adresu odbiorcy.

Wywołanie metody , aby EnableInstallers skonfigurować naszą topologię w przestrzeni nazw usługi Azure Service Bus po uruchomieniu punktu końcowego, tworząc wymagane kolejki w razie potrzeby. W środowiskach produkcyjnych skrypty operacyjne to kolejna opcja tworzenia topologii.

Konfigurowanie usługi w tle w celu wysyłania komunikatów

Ostatnim elementem nadawcy jest SenderWorkerusługa w tle skonfigurowana do wysyłania komunikatu Ping co sekundę.

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

                logger.LogInformation($"Message #{round}");

                await Task.Delay(1_000, stoppingToken);
            }
        }
        catch (OperationCanceledException)
        {
            // graceful shutdown
        }
    }
}

Używany IMessageSession element jest ExecuteAsync wstrzykiwany do SenderWorker elementu i umożliwia wysyłanie komunikatów przy użyciu protokołu NServiceBus poza programem obsługi komunikatów. Routing skonfigurowany w programie Sender określa miejsce docelowe komunikatów Ping . Utrzymuje topologię systemu (które komunikaty są kierowane do których adresów) są oddzielone od kodu biznesowego.

Aplikacja Nadawca zawiera również element PongHandler. Wrócisz do niego po omówieniu odbiornika, który zrobimy dalej.

Konfigurowanie odbiornika

Odbiornik to punkt końcowy, który nasłuchuje komunikatu Ping , rejestruje po odebraniu komunikatu i odpowiada z powrotem do nadawcy. W tej sekcji szybko sprawdzimy konfigurację punktu końcowego, która jest podobna do nadawcy, a następnie zwrócimy uwagę na procedurę obsługi komunikatów.

Podobnie jak nadawca, skonfiguruj odbiornik jako aplikację konsolową przy użyciu hosta ogólnego firmy Microsoft. Używa tej samej konfiguracji rejestrowania i punktu końcowego (z usługą Azure Service Bus jako transportem komunikatów), ale z inną nazwą, aby odróżnić ją od nadawcy:

var endpointConfiguration = new EndpointConfiguration("Receiver");

Ponieważ ten punkt końcowy odpowiada tylko na jego inicjatora i nie uruchamia nowych konwersacji, nie jest wymagana żadna konfiguracja routingu. Nie potrzebuje również procesu roboczego w tle, takiego jak nadawca, ponieważ odpowiada tylko wtedy, gdy otrzymuje komunikat.

Procedura obsługi komunikatów ping

Projekt Receiver zawiera procedurę obsługi komunikatów o nazwie 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);
    }
}

Zignorujmy na razie skomentowany kod; Wrócimy do niego później, gdy będziemy mówić o odzyskiwaniu po awarii.

Klasa implementuje IHandleMessages<Ping>metodę , która definiuje jedną metodę: Handle. Ten interfejs informuje NServiceBus, że gdy punkt końcowy odbiera komunikat typu Ping, powinien zostać przetworzony przez metodę Handle w tej procedurze obsługi. Metoda Handle przyjmuje sam komunikat jako parametr i IMessageHandlerContext, który umożliwia dalsze operacje obsługi komunikatów, takie jak odpowiadanie, wysyłanie poleceń lub publikowanie zdarzeń.

Nasza PingHandler funkcja jest prosta: po odebraniu Ping komunikatu należy zarejestrować szczegóły wiadomości i odpowiedzieć z powrotem do nadawcy przy użyciu nowej Pong wiadomości, która jest następnie obsługiwana w elemecie Nadawca PongHandler.

Uwaga

W konfiguracji nadawcy określono, że Ping komunikaty powinny być kierowane do odbiorcy. NServiceBus dodaje metadane do komunikatów wskazujących między innymi źródło komunikatu. Dlatego nie trzeba określać żadnych danych routingu Pong dla wiadomości odpowiedzi— jest ona automatycznie kierowana z powrotem do źródła: nadawcy.

Po poprawnym skonfigurowaniu nadawcy i odbiorcy można teraz uruchomić rozwiązanie.

Uruchamianie rozwiązania

Aby uruchomić rozwiązanie, należy uruchomić zarówno nadawcę, jak i odbiorcę. Jeśli używasz programu Visual Studio Code, uruchom konfigurację "Debuguj wszystko". Jeśli używasz programu Visual Studio, skonfiguruj rozwiązanie tak, aby uruchamiało projekty nadawcy i odbiorcy:

  1. Kliknij rozwiązanie prawym przyciskiem myszy w Eksplorator rozwiązań
  2. Wybierz pozycję "Ustaw projekty startowe..."
  3. Wybieranie wielu projektów startowych
  4. W przypadku nadawcy i odbiorcy wybierz pozycję "Uruchom" na liście rozwijanej

Uruchom rozwiązanie. Zostaną wyświetlone dwie aplikacje konsolowe: jedna dla nadawcy i jedna dla odbiornika.

W nadawcy zwróć uwagę, że Ping komunikat jest wysyłany co sekundę dzięki SenderWorker zadaniu w tle. Odbiorca wyświetla szczegóły każdego Ping odbieranego komunikatu, a nadawca rejestruje szczegóły każdego Pong odbieranego komunikatu w odpowiedzi.

Teraz, gdy wszystko działa, przerwijmy go.

Odporność w działaniu

Błędy to fakt życia w systemach oprogramowania. Nieuniknione jest, że kod zakończy się niepowodzeniem i może to zrobić z różnych powodów, takich jak awarie sieci, blokady bazy danych, zmiany w interfejsie API innej firmy i zwykłe stare błędy kodowania.

NServiceBus ma niezawodne funkcje odzyskiwania do obsługi błędów. Gdy program obsługi komunikatów zakończy się niepowodzeniem, komunikaty są automatycznie ponawiane na podstawie wstępnie zdefiniowanych zasad. Istnieją dwa typy zasad ponawiania prób: natychmiastowe ponawianie prób i opóźnione próby. Najlepszym sposobem opisania sposobu ich działania jest sprawdzenie ich w działaniu. Dodajmy zasady ponawiania do punktu końcowego odbiorcy:

  1. Otwórz Program.cs w projekcie Nadawca
  2. .EnableInstallers Po wierszu dodaj następujący kod:
endpointConfiguration.SendFailedMessagesTo("error");
var recoverability = endpointConfiguration.Recoverability();
recoverability.Immediate(
    immediate =>
    {
        immediate.NumberOfRetries(3);
    });
recoverability.Delayed(
    delayed =>
    {
        delayed.NumberOfRetries(2);
        delayed.TimeIncrease(TimeSpan.FromSeconds(5));
    });

Zanim omówimy działanie tych zasad, zobaczmy ją w działaniu. Przed przetestowaniem zasad możliwości odzyskiwania należy zasymulować błąd. PingHandler Otwórz kod w projekcie Receiver i usuń komentarz w tym wierszu:

throw new Exception("BOOM");

Teraz, gdy odbiornik obsługuje komunikat, zakończy się niepowodzeniem Ping . Uruchom rozwiązanie ponownie i zobaczmy, co się dzieje w odbiorniku.

Dzięki naszym mniej niezawodnym PingHandlerkomunikatom wszystkie komunikaty kończą się niepowodzeniem. Możesz zobaczyć, że zasady ponawiania są uruchamiane dla tych komunikatów. Przy pierwszym niepomyślnym zakończeniu komunikatu następuje natychmiastowe ponowienia próby do trzech razy:

Obraz przedstawiający natychmiastowe zasady ponawiania próby, które ponawiają próby komunikatów do 3 razy

Oczywiście będzie ona nadal się nie powieść, więc gdy zostaną użyte trzy natychmiastowe ponawianie prób, opóźnione ponowne próby zostaną uruchomione, a komunikat zostanie opóźniony przez 5 sekund:

Obraz przedstawiający opóźnione zasady ponawiania, które opóźniają komunikaty w przyrostach o 5 sekund przed podjęciem kolejnej rundy natychmiastowych ponownych prób

Po upływie tych 5 sekund komunikat zostanie ponowiony ponownie trzy razy (oznacza to kolejną iterację natychmiastowych zasad ponawiania). Zakończy się to również niepowodzeniem, a usługa NServiceBus ponownie opóźni komunikat, tym razem przez 10 sekund, przed ponowną próbą.

Jeśli PingHandler nadal nie powiedzie się po uruchomieniu przez pełne zasady ponawiania prób, komunikat zostanie umieszczony w scentralizowanej kolejce błędów o nazwie error, zgodnie z definicją przez wywołanie metody .SendFailedMessagesTo

Obraz przedstawiający komunikat o błędzie

Koncepcja scentralizowanej kolejki błędów różni się od mechanizmu utraconych komunikatów w usłudze Azure Service Bus, który ma kolejkę utraconych komunikatów dla każdej kolejki przetwarzania. W przypadku magistrali NServiceBus kolejki utraconych komunikatów w usłudze Azure Service Bus działają jako prawdziwe kolejki komunikatów trucizny, podczas gdy komunikaty, które kończą się w scentralizowanej kolejce błędów, można ponownie przetworzyć w późniejszym czasie, w razie potrzeby.

Zasady ponawiania prób pomagają rozwiązać kilka typów błędów , które często są przejściowe lub częściowo przejściowe w naturze. Oznacza to, że błędy, które są tymczasowe i często odchodzą, jeśli komunikat jest po prostu ponownie przetworzony po krótkim opóźnieniu. Przykłady obejmują błędy sieci, blokady bazy danych i awarie interfejsu API innych firm.

Gdy komunikat znajduje się w kolejce błędów, możesz sprawdzić szczegóły komunikatu w wybranym narzędziu, a następnie zdecydować, co z nim zrobić. Na przykład przy użyciu usługi ServicePulse narzędzie do monitorowania według określonego oprogramowania możemy wyświetlić szczegóły komunikatu i przyczynę błędu:

Obraz przedstawiający usługę ServicePulse z określonego oprogramowania

Po zbadaniu szczegółów możesz wysłać komunikat z powrotem do oryginalnej kolejki na potrzeby przetwarzania. Przed wykonaniem tej czynności możesz również edytować komunikat. Jeśli w kolejce błędów znajduje się wiele komunikatów, które zakończyły się niepowodzeniem z tego samego powodu, wszystkie mogą być wysyłane z powrotem do oryginalnych miejsc docelowych jako partia.

Następnie nadszedł czas, aby dowiedzieć się, gdzie wdrożyć nasze rozwiązanie na platformie Azure.

Gdzie hostować usługi na platformie Azure

W tym przykładzie punkty końcowe nadawcy i odbiorcy są skonfigurowane do uruchamiania jako aplikacje konsolowe. Mogą one być również hostowane w różnych usługach platformy Azure, takich jak Azure Functions, aplikacja systemu Azure Services, Azure Container Instances, Azure Kubernetes Services i Maszyny wirtualne platformy Azure. Na przykład poniżej przedstawiono sposób konfigurowania punktu końcowego nadawcy do uruchamiania jako funkcji platformy Azure:

[assembly: NServiceBusTriggerFunction("Sender")]
public class Program
{
    public static async Task Main()
    {
        var host = new HostBuilder()
            .ConfigureFunctionsWorkerDefaults()
            .UseNServiceBus(configuration =>
            {
                configuration.Routing().RouteToEndpoint(typeof(Ping), "Receiver");
            })
            .Build();

        await host.RunAsync();
    }
}

Aby uzyskać więcej informacji na temat korzystania z usługi NServiceBus z usługą Functions, zobacz Azure Functions with Azure Service Bus (Usługa Azure Functions z usługą Azure Service Bus ) w dokumentacji NServiceBus.

Następne kroki

Aby uzyskać więcej informacji na temat korzystania z sieci NServiceBus z usługami platformy Azure, zobacz następujące artykuły: