Udostępnij za pośrednictwem


Implementowanie komunikacji opartej na zdarzeniach między mikrousługami (zdarzenia integracji)

Napiwek

Ta zawartość jest fragmentem książki eBook, architektury mikrousług platformy .NET dla konteneryzowanych aplikacji platformy .NET dostępnych na platformie .NET Docs lub jako bezpłatnego pliku PDF, który można odczytać w trybie offline.

.NET Microservices Architecture for Containerized .NET Applications eBook cover thumbnail.

Jak opisano wcześniej, w przypadku korzystania z komunikacji opartej na zdarzeniach mikrousługa publikuje zdarzenie, gdy coś istotnego się stanie, na przykład gdy aktualizuje jednostkę biznesową. Inne mikrousługi subskrybują te zdarzenia. Gdy mikrousługa odbiera zdarzenie, może zaktualizować własne jednostki biznesowe, co może prowadzić do opublikowania większej liczby zdarzeń. Jest to istota koncepcji spójności ostatecznej. Ten system publikowania/subskrybowania jest zwykle wykonywany przy użyciu implementacji magistrali zdarzeń. Magistrala zdarzeń może być zaprojektowana jako interfejs z interfejsem API wymaganym do subskrybowania i anulowania subskrypcji zdarzeń oraz publikowania zdarzeń. Może również mieć jedną lub więcej implementacji na podstawie dowolnej komunikacji między procesami lub komunikatami, takich jak kolejka obsługi komunikatów lub magistrala usług, która obsługuje asynchroniczną komunikację i model publikowania/subskrybowania.

Zdarzenia umożliwiają implementowanie transakcji biznesowych obejmujących wiele usług, co zapewnia spójność ostateczną między tymi usługami. Ostatecznie spójna transakcja składa się z serii akcji rozproszonych. W każdej akcji mikrousługa aktualizuje jednostkę biznesową i publikuje zdarzenie, które wyzwala następną akcję. Należy pamiętać, że transakcja nie obejmuje bazowej trwałości i magistrali zdarzeń, więc idempotencja musi być obsługiwana. Rysunek 6–18 poniżej przedstawia zdarzenie PriceUpdated opublikowane za pośrednictwem magistrali zdarzeń, więc aktualizacja cen jest propagowana do koszyka i innych mikrousług.

Diagram of asynchronous event-driven communication with an event bus.

Rysunek 6–18. Komunikacja sterowana zdarzeniami oparta na magistrali zdarzeń

W tej sekcji opisano sposób implementacji tego typu komunikacji z platformą .NET przy użyciu ogólnego interfejsu magistrali zdarzeń, jak pokazano na rysunku 6-18. Istnieje wiele potencjalnych implementacji, z których każda korzysta z innej technologii lub infrastruktury, takiej jak RabbitMQ, Azure Service Bus lub jakakolwiek inna firma typu open source lub komercyjna magistrala usług.

Używanie brokerów komunikatów i magistrali usług dla systemów produkcyjnych

Jak wspomniano w sekcji architektura, można wybrać spośród wielu technologii obsługi komunikatów na potrzeby implementowania abstrakcyjnej magistrali zdarzeń. Ale te technologie są na różnych poziomach. Na przykład RabbitMQ, transport brokera obsługi komunikatów, jest na niższym poziomie niż produkty komercyjne, takie jak Azure Service Bus, NServiceBus, MassTransit lub Brighter. Większość z tych produktów może pracować nad rozwiązaniem RabbitMQ lub Azure Service Bus. Wybór produktu zależy od liczby funkcji i skalowalności gotowej do użycia w aplikacji.

Aby zaimplementować tylko weryfikację koncepcji magistrali zdarzeń dla środowiska projektowego, podobnie jak w przykładzie eShopOnContainers, prosta implementacja na platformie RabbitMQ uruchomiona jako kontener może wystarczyć. Jednak w przypadku systemów o znaczeniu krytycznym i produkcyjnym, które wymagają wysokiej skalowalności, warto ocenić usługę Azure Service Bus i używać jej.

Jeśli potrzebujesz abstrakcji wysokiego poziomu i bogatszych funkcji, takich jak Sagas , dla długotrwałych procesów, które ułatwiają opracowywanie rozproszone, inne komercyjne i open source magistrale usług, takie jak NServiceBus, MassTransit i Brighter , warto ocenić. W tym przypadku abstrakcje i interfejs API do użycia zwykle byłyby bezpośrednio dostarczane przez te magistrale usług wysokiego poziomu zamiast własnych abstrakcji (na przykład proste abstrakcje magistrali zdarzeń udostępniane w eShopOnContainers). W tym celu możesz zbadać rozwidlenie eShopOnContainers przy użyciu NServiceBus (dodatkowe próbki pochodne zaimplementowane przez określone oprogramowanie).

Oczywiście zawsze można tworzyć własne funkcje magistrali usług na podstawie technologii niższego poziomu, takich jak RabbitMQ i Docker, ale praca wymagana do "ponownego wynalezienia koła" może być zbyt kosztowna dla niestandardowej aplikacji dla przedsiębiorstw.

Aby powtórzyć: przykładowe abstrakcje i implementacja magistrali zdarzeń zaprezentowane w przykładzie eShopOnContainers mają być używane tylko jako dowód koncepcji. Po podjęciu decyzji, że chcesz mieć komunikację asynchroniczną i opartą na zdarzeniach, zgodnie z opisem w bieżącej sekcji, należy wybrać produkt usługi Service Bus, który najlepiej odpowiada twoim potrzebom w środowisku produkcyjnym.

Zdarzenia integracji

Zdarzenia integracji służą do synchronizowania stanu domeny między wieloma mikrousługami lub systemami zewnętrznymi. Ta funkcja jest wykonywana przez publikowanie zdarzeń integracji poza mikrousługą. Gdy zdarzenie jest publikowane w wielu mikrousług odbiorcy (do tylu mikrousług, które są subskrybowane do zdarzenia integracji), odpowiedni program obsługi zdarzeń w każdej mikrousłudze odbiorcy obsługuje zdarzenie.

Zdarzenie integracji jest w zasadzie klasą gospodarstwa danych, jak w poniższym przykładzie:

public class ProductPriceChangedIntegrationEvent : IntegrationEvent
{
    public int ProductId { get; private set; }
    public decimal NewPrice { get; private set; }
    public decimal OldPrice { get; private set; }

    public ProductPriceChangedIntegrationEvent(int productId, decimal newPrice,
        decimal oldPrice)
    {
        ProductId = productId;
        NewPrice = newPrice;
        OldPrice = oldPrice;
    }
}

Zdarzenia integracji można zdefiniować na poziomie aplikacji każdej mikrousługi, więc są one oddzielone od innych mikrousług w sposób porównywalny z sposobem definiowania modelu ViewModels na serwerze i kliencie. Co nie jest zalecane, to udostępnianie wspólnej biblioteki zdarzeń integracji w wielu mikrousług; w ten sposób sprzężenie tych mikrousług z pojedynczą biblioteką danych definicji zdarzeń. Nie chcesz tego robić z tych samych powodów, dla których nie chcesz współużytkować wspólnego modelu domeny w wielu mikrousługach: mikrousługi muszą być całkowicie autonomiczne. Aby uzyskać więcej informacji, zobacz ten wpis w blogu dotyczący ilości danych do umieszczenia w zdarzeniach. Uważaj, aby nie robić tego zbyt daleko, ponieważ w tym innym wpisie w blogu opisano problemy z brakami komunikatów, które mogą powodować niedobór danych. Twój projekt wydarzeń powinien być "odpowiedni" dla potrzeb ich konsumentów.

Istnieje tylko kilka rodzajów bibliotek, które należy udostępniać między mikrousługami. Jedną z nich są biblioteki, które są końcowymi blokami aplikacji, takimi jak interfejs API klienta usługi Event Bus, jak w aplikacjach eShopOnContainers. Innym jest biblioteki, które stanowią narzędzia, które mogą być również udostępniane jako składniki NuGet, takie jak serializatory JSON.

Magistrala zdarzeń

Magistrala zdarzeń umożliwia komunikację w stylu publikowania/subskrybowania między mikrousługami bez konieczności jawnego informowania składników o sobie, jak pokazano na rysunku 6–19.

A diagram showing the basic publish/subscribe pattern.

Rysunek 6–19. Publikowanie/subskrybowanie podstaw za pomocą magistrali zdarzeń

Na powyższym diagramie pokazano, że mikrousługę A publikuje w usłudze Event Bus, która dystrybuuje do subskrybowania mikrousług B i C, bez konieczności znajomości subskrybentów przez wydawcę. Magistrala zdarzeń jest powiązana ze wzorcem obserwatora i wzorcem publikowania-subskrybowania.

Wzorzec obserwatora

We wzorcu obserwatora obiekt podstawowy (znany jako Obserwowalny) powiadamia inne zainteresowane obiekty (znane jako obserwatorzy) o odpowiednich informacjach (zdarzeniach).

Wzorzec publikowania/subskrybowania (pub/sub)

Cel wzorca Publikuj/Subskrybuj jest taki sam jak wzorzec obserwatora: chcesz powiadomić inne usługi o wystąpieniu niektórych zdarzeń. Istnieje jednak ważna różnica między wzorcami Obserwator i Pub/Sub. We wzorcu obserwatora emisja jest wykonywana bezpośrednio z obserwowanych do obserwatorów, więc "znają się" nawzajem. Jednak w przypadku korzystania ze wzorca Pub/Sub istnieje trzeci składnik nazywany brokerem lub brokerem komunikatów lub magistralą zdarzeń, który jest znany zarówno przez wydawcę, jak i subskrybenta. W związku z tym w przypadku korzystania ze wzorca Pub/Sub wydawca i subskrybentów są dokładnie oddzielone dzięki wymienionej magistrali zdarzeń lub brokerowi komunikatów.

Middleman lub event bus

Jak osiągnąć anonimowość między wydawcą a subskrybentem? Łatwym sposobem jest pozwolić pośrednicowi dbać o całą komunikację. Magistrala zdarzeń jest jednym z takich pośredników.

Magistrala zdarzeń składa się zazwyczaj z dwóch części:

  • Abstrakcja lub interfejs.

  • Co najmniej jedna implementacja.

Na rysunku 6–19 widać, jak z punktu widzenia aplikacji magistrala zdarzeń nie jest niczym więcej niż kanałem Pub/Sub. Sposób implementacji tej komunikacji asynchronicznej może się różnić. Może mieć wiele implementacji, dzięki czemu można je zamienić w zależności od wymagań środowiska (na przykład środowiska produkcyjnego i programistycznego).

Na rysunku 6–20 widać abstrakcję magistrali zdarzeń z wieloma implementacjami opartymi na technologiach obsługi komunikatów infrastruktury, takich jak RabbitMQ, Azure Service Bus lub inny broker zdarzeń/komunikatów.

Diagram showing the addition of an event bus abstraction layer.

Rysunek 6–20. Wiele implementacji magistrali zdarzeń

Dobrze jest mieć zdefiniowaną magistralę zdarzeń za pośrednictwem interfejsu, aby można ją było zaimplementować za pomocą kilku technologii, takich jak RabbitMQ, Azure Service Bus lub inne. Jednak i jak wspomniano wcześniej, korzystanie z własnych abstrakcji (interfejs magistrali zdarzeń) jest dobre tylko wtedy, gdy potrzebujesz podstawowych funkcji magistrali zdarzeń obsługiwanych przez abstrakcje. Jeśli potrzebujesz bogatszych funkcji usługi Service Bus, prawdopodobnie użyj interfejsu API i abstrakcji udostępnianych przez preferowaną komercyjną magistralę usług zamiast własnych abstrakcji.

Definiowanie interfejsu magistrali zdarzeń

Zacznijmy od kodu implementacji interfejsu magistrali zdarzeń i możliwych implementacji na potrzeby eksploracji. Interfejs powinien być ogólny i prosty, jak w poniższym interfejsie.

public interface IEventBus
{
    void Publish(IntegrationEvent @event);

    void Subscribe<T, TH>()
        where T : IntegrationEvent
        where TH : IIntegrationEventHandler<T>;

    void SubscribeDynamic<TH>(string eventName)
        where TH : IDynamicIntegrationEventHandler;

    void UnsubscribeDynamic<TH>(string eventName)
        where TH : IDynamicIntegrationEventHandler;

    void Unsubscribe<T, TH>()
        where TH : IIntegrationEventHandler<T>
        where T : IntegrationEvent;
}

Metoda Publish jest prosta. Magistrala zdarzeń będzie emitować zdarzenie integracji przekazane do niego do dowolnej mikrousługi, a nawet aplikacji zewnętrznej, subskrybowane do tego zdarzenia. Ta metoda jest używana przez mikrousługę publikującą zdarzenie.

Metody Subscribe (można mieć kilka implementacji w zależności od argumentów) są używane przez mikrousługi, które chcą odbierać zdarzenia. Ta metoda ma dwa argumenty. Pierwszy to zdarzenie integracji do subskrybowania (IntegrationEvent). Drugim argumentem jest procedura obsługi zdarzeń integracji (lub metoda wywołania zwrotnego), o nazwie IIntegrationEventHandler<T>, która ma być wykonywana, gdy mikrousługa odbiorcy pobiera ten komunikat zdarzenia integracji.

Dodatkowe zasoby

Niektóre rozwiązania do obsługi komunikatów gotowych do produkcji: