Tworzenie aplikacji biznesowych opartych na komunikatach przy użyciu sieci NServiceBus i Azure Service Bus
NServiceBus to komercyjna platforma obsługi komunikatów dostarczana przez określone oprogramowanie. Jest ona oparta na 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 witryny dokumentacji oprogramowania.
Wymagania wstępne
W przykładzie założono, że utworzono Azure Service Bus przestrzeni nazw.
Ważne
NServiceBus wymaga co najmniej warstwy Standardowa. Warstwa Podstawowa nie będzie działać.
Pobieranie i przygotowywanie rozwiązania
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:
Otwórz plik
SendReceiveWithNservicebus.sln
w ulubionym edytorze kodu (na przykład Visual Studio 2019).Otwórz
appsettings.json
plik zarówno w projektach Receiver, jak i Sender, a następnie ustawAzureServiceBusConnectionString
parametry połączenia dla przestrzeni nazw Azure Service Bus.
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 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 zapewniają 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.ICommand
interfejs 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#, chociaż ten obiekt implementuje NServiceBus.IMessage
element . 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.
Elementy Ping
i Pong
to dwa typy komunikatów, których będziesz używać. Następnym krokiem jest skonfigurowanie nadawcy do używania Azure Service Bus i wysyłania Ping
wiadomości.
Konfigurowanie nadawcy
Nadawca jest punktem końcowym, który wysyła wiadomość Ping
. W tym miejscu skonfigurujesz nadawcę do używania Azure Service Bus jako mechanizmu transportu, a następnie skonstruujesz Ping
wystąpienie i wyślesz je.
W metodzie w metodzie Main
Program.cs
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 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();
Jest tu wiele do rozpakowania, 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ć tak, aby był uruchamiany w 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, aby używał 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 parametry połączenia z appsettings.json
. Następnie skonfigurujesz routing, aby komunikaty typu Ping
zostały wysłane do punktu końcowego o nazwie "Receiver". Umożliwia NServiceBus zautomatyzowanie procesu wysyłania komunikatu do miejsca docelowego bez wymagania adresu odbiorcy.
Wywołanie metody EnableInstallers
spowoduje skonfigurowanie naszej topologii w Azure Service Bus przestrzeni nazw po uruchomieniu punktu końcowego, tworząc wymagane kolejki w razie potrzeby. W środowiskach produkcyjnych skrypty operacyjne to inna opcja tworzenia topologii.
Konfigurowanie usługi w tle do wysyłania komunikatów
Ostatnim elementem nadawcy jest SenderWorker
usł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++ })
.ConfigureAwait(false);
logger.LogInformation($"Message #{round}");
await Task.Delay(1_000, stoppingToken)
.ConfigureAwait(false);
}
}
catch (OperationCanceledException)
{
// graceful shutdown
}
}
}
Używany IMessageSession
element jest ExecuteAsync
wstrzykiwany do SenderWorker
elementu i umożliwia nam wysyłanie komunikatów przy użyciu NServiceBus poza procedurą obsługi komunikatów. Routing skonfigurowany w programie Sender
określa miejsce docelowe komunikatów Ping
. Utrzymuje topologię systemu (do którego są kierowane komunikaty) jako osobny problem 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 przejrzymy 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 ona tej samej konfiguracji rejestrowania i punktu końcowego (z Azure Service Bus co transport komunikatów), ale z inną nazwą, aby odróżnić ją od nadawcy:
var endpointConfiguration = new EndpointConfiguration("Receiver");
Ponieważ ten punkt końcowy odpowiada tylko jego inicjatorowi 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 program 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);
}
}
Teraz zignorujmy kod skomentowany; 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ń.
Nasze PingHandler
jest proste: po odebraniu Ping
komunikatu zarejestruj szczegóły wiadomości i odpowiedz nadawcy przy użyciu nowej Pong
wiadomości.
Uwaga
W konfiguracji nadawcy określono, że Ping
komunikaty powinny być kierowane do odbiornika. 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: Nadawca.
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 Visual Studio Code, uruchom konfigurację "Debuguj wszystko". Jeśli używasz programu Visual Studio, skonfiguruj rozwiązanie tak, aby uruchamiało projekty Nadawca i Odbiorca:
- Kliknij prawym przyciskiem myszy rozwiązanie w Eksplorator rozwiązań
- Wybierz pozycję "Ustaw projekty startowe..."
- Wybieranie wielu projektów startowych
- 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. Jest nieuniknione, że kod ulegnie awarii 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 na potrzeby 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 prób do punktu końcowego odbiornika:
- Otwórz
Program.cs
w projekcie Nadawca .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, jak działają te zasady, zobaczmy ją w działaniu. Przed przetestowaniem zasad możliwości odzyskiwania należy zasymulować błąd. PingHandler
Otwórz kod w projekcie Odbiornik 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 naszemu mniej niezawodnemu PingHandler
programowi wszystkie komunikaty kończą się niepowodzeniem. Zasady ponawiania prób są widoczne dla tych komunikatów. Gdy komunikat po raz pierwszy zakończy się niepowodzeniem, zostanie on natychmiast ponowiony do trzech razy:
Oczywiście nadal nie powiedzie się, więc gdy są używane trzy natychmiastowe ponawianie prób, opóźnione zasady ponawiania są uruchamiane, a komunikat jest opóźniony przez 5 sekund:
Po upływie tych 5 sekund komunikat zostanie ponowiony ponownie trzy razy (czyli kolejna iteracji natychmiastowych zasad ponawiania). Spowoduje to również niepowodzenie, a usługa NServiceBus ponownie opóźni komunikat, tym razem przez 10 sekund, zanim spróbuje ponownie.
Jeśli PingHandler
nadal nie powiedzie się po wykonaniu pełnych zasad ponawiania, komunikat zostanie umieszczony w scentralizowanej kolejce błędów o nazwie , zgodnie error
z definicją wywołania metody SendFailedMessagesTo
.
Koncepcja scentralizowanej kolejki błędów różni się od mechanizmu martwych listów w Azure Service Bus, który ma kolejkę utraconych komunikatów dla każdej kolejki przetwarzania. W przypadku NServiceBus kolejki utraconych wiadomości w Azure Service Bus działają jako prawdziwe kolejki komunikatów otruciach, 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 odejdą, jeśli komunikat jest po prostu ponownie przetworzony po krótkim opóźnieniu. Przykłady obejmują awarie 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ę niepowodzenia:
Po sprawdzeniu szczegółów możesz wysłać wiadomość 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 aplikacji konsolowych. Mogą być one również hostowane w różnych usługach platformy Azure, takich jak Azure Functions, usługi aplikacja systemu Azure, Azure Container Instances, usługi 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: 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;
});
}
}
Aby uzyskać więcej informacji na temat korzystania z NServiceBus z usługą Functions, zobacz Azure Functions z 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: