Udostępnij za pośrednictwem


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

Wskazówka

Ta treść jest fragmentem eBooka "Architektura mikrousług .NET dla konteneryzowanych aplikacji .NET", dostępnego na .NET Docs lub jako bezpłatny plik PDF do pobrania i czytania w trybie offline.

Miniatura okładki eBooka „Architektura mikrousług platformy .NET dla konteneryzowanych aplikacji platformy .NET”.

Jak opisano wcześniej, w przypadku korzystania z komunikacji opartej na zdarzeniachmikrousł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 mieć także jedną lub więcej implementacji opartych na dowolnej komunikacji międzyprocesowej lub komunikacji opartej na przesyłaniu komunikatów, takich jak kolejka komunikatów lub magistrala usług, które obsługują asynchroniczną komunikację oraz model publikowania i subskrypcji.

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 podstawowej trwałości danych ani magistrali zdarzeń, więc idempotencja musi być obsługiwana. Rysunek 6-18 poniżej przedstawia zdarzenie PriceUpdated opublikowane za pośrednictwem magistrali zdarzeń, dzięki czemu aktualizacja cen jest przekazywana do koszyka i innych mikrousług.

Diagram asynchronicznej komunikacji sterowanej zdarzeniami z magistralą zdarzeń.

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 komunikatów brokerskich, jest na niższym poziomie niż komercyjne produkty, takie jak Azure Service Bus, NServiceBus, MassTransit czy Brighter. Większość z tych produktów może działać na platformie RabbitMQ lub Azure Service Bus. Wybór produktu zależy od tego, jak wielu funkcji i jak dużej skalowalności gotowej do użycia potrzebujesz dla swojej 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 wysokopoziomowych abstrakcji i bardziej zaawansowanych funkcji, takich jak Sagas, które ułatwiają tworzenie rozproszonych aplikacji w przypadku długotrwałych procesów, warto rozważyć ocenę innych komercyjnych i open-source magistrali usług, takich jak NServiceBus, MassTransit oraz Brighter. W tym przypadku, abstrakcje i interfejs API, które należy zastosować, zwykle będą bezpośrednio te dostarczane przez magistrale usług wysokiego poziomu, zamiast własnych abstrakcji (jak na przykład proste abstrakcje magistrali zdarzeń udostępniane w eShopOnContainers). W związku z tym możesz zbadać rozwidlenie eShopOnContainers przy użyciu NServiceBus (dodatkowy przykładowy projekt stworzony przez Particular Software).

Oczywiście, zawsze można tworzyć własne funkcje magistrali usług w oparciu o technologie niskiego poziomu, takie jak RabbitMQ i Docker, ale wysiłek potrzebny do "ponownego wynalezienia koła" może być zbyt kosztowny dla dedykowanej aplikacji dla przedsiębiorstwa.

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 do wielu mikrousług odbiorców (w zależności od liczby mikrousług subskrybujących zdarzenie integracyjne), odpowiedni program obsługi zdarzeń w każdej mikrousłudze odbiorcy obsługuje zdarzenie.

Zdarzenie integracji jest w zasadzie klasą przechowującą dane, 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. Nie zaleca się dzielenia wspólnej biblioteki zdarzeń integracji pomiędzy wieloma mikrousługami; w ten sposób mikrousługi byłyby sprzężone z jedną biblioteką 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 przesadzać, ponieważ w tym innym wpisie w blogu opisano problemy, które mogą powodować wiadomości z niedoborem 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 zdarzeniowa

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.

Diagram przedstawiający podstawowy wzorzec publikowania/subskrybowania.

Rysunek 6–19. Podstawy publikacji/subskrypcji za pomocą magistrali zdarzeń

Na powyższym diagramie pokazano, że mikrousługa A publikuje do Event Bus, który dystrybuuje wiadomości do mikrousług subskrybujących B i C, bez potrzeby 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. W wzorcu projektowym obserwatora emisja jest wykonywana bezpośrednio z obiektu obserwowanego do obserwatorów, więc "znają się". 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, podczas korzystania ze wzorca Pub/Sub, wydawca i subskrybenci są precyzyjnie oddzieleni 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ń to jeden 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 przedstawiający dodawanie warstwy abstrakcji magistrali zdarzeń.

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 bardziej rozwiniętych funkcji magistrali usług, prawdopodobnie powinieneś użyć interfejsu API i abstrakcji udostępnianych przez komercyjną magistralę usług, którą preferujesz, 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 wydarzeń będzie emitować zdarzenie integracji przekazane do niej do dowolnej mikrousługi, a nawet aplikacji zewnętrznej, subskrybujące to zdarzenie. 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. Pierwszym jest zdarzenie integracji, które należy zasubskrybować (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 gotowe do użycia w środowisku produkcyjnym: