Menerapkan komunikasi berbasis peristiwa antara layanan mikro (peristiwa integrasi)

Tip

Konten ini adalah kutipan dari eBook, .NET Microservices Architecture for Containerized .NET Applications, tersedia di .NET Docs atau sebagai PDF yang dapat diunduh gratis dan dapat dibaca secara offline.

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

Seperti yang dijelaskan sebelumnya, saat Anda menggunakan komunikasi berbasis peristiwa, layanan mikro menerbitkan peristiwa ketika sesuatu yang penting terjadi, seperti ketika memperbarui entitas bisnis. Layanan mikro lainnya mengacu kepada peristiwa tersebut. Saat layanan mikro menerima peristiwa, layanan mikro dapat memperbarui entitas bisnisnya sendiri, yang mungkin menyebabkan lebih banyak peristiwa diterbitkan. Ini adalah inti dari konsep konsistensi akhirnya. Sistem penerbitan/berlangganan ini biasanya dilakukan dengan menggunakan implementasi bus peristiwa. Bus peristiwa dapat dirancang sebagai antarmuka dengan API yang diperlukan untuk berlangganan dan berhenti berlangganan peristiwa dan untuk menerbitkan peristiwa. Ini juga dapat memiliki satu atau beberapa implementasi berdasarkan komunikasi antar-proses atau olahpesan, seperti antrean olahpesan atau bus layanan yang mendukung komunikasi asinkron dan model terbitkan/berlangganan.

Anda dapat menggunakan peristiwa untuk menerapkan transaksi bisnis yang mencakup beberapa layanan, yang memberi Anda konsistensi akhir antara layanan tersebut. Transaksi yang akhirnya konsisten terdiri dari serangkaian tindakan terdistribusi. Pada setiap tindakan, layanan mikro memperbarui entitas bisnis dan menerbitkan peristiwa yang memicu tindakan berikutnya. Ketahuilah bahwa transaksi tidak mencakup persistensi yang mendasar dan bus peristiwa, sehingga idempotensi perlu ditangani. Gambar 6-18 di bawah ini, menunjukkan peristiwa PriceUpdated yang diterbitkan melalui bus peristiwa, sehingga pembaruan harga disebarkan ke basket dan layanan mikro lainnya.

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

Gambar 6-18.. Komunikasi berbasis peristiwa berdasarkan bus peristiwa

Bagian ini menjelaskan bagaimana Anda dapat menerapkan jenis komunikasi ini dengan .NET menggunakan antarmuka bus peristiwa generik, seperti yang ditunjukkan pada Gambar 6-18. Ada beberapa implementasi potensial, masing-masing menggunakan teknologi atau infrastruktur yang berbeda seperti RabbitMQ, Azure Service Bus, atau bus layanan sumber terbuka atau komersial pihak ketiga lainnya.

Menggunakan broker pesan dan bus layanan untuk sistem produksi

Seperti yang disebutkan di bagian arsitektur, Anda dapat memilih dari beberapa teknologi olahpesan untuk mengimplementasikan bus peristiwa abstrak Anda. Tetapi teknologi ini berada pada tingkat yang berbeda. Misalnya, RabbitMQ, transportasi broker olahpesan, berada pada tingkat yang lebih rendah daripada produk komersial seperti Azure Service Bus, NServiceBus, MassTransit, atau Brighter. Sebagian besar produk ini dapat bekerja di atas RabbitMQ atau Azure Service Bus. Pilihan produk Anda tergantung pada berapa banyak fitur dan berapa banyak skalabilitas out-of-the-box yang Anda butuhkan untuk aplikasi Anda.

Untuk menerapkan hanya bukti konsep bus peristiwa untuk lingkungan pengembangan Anda, seperti dalam sampel eShopOnContainers, implementasi sederhana di atas RabbitMQ yang berjalan sebagai kontainer mungkin sudah cukup. Tetapi untuk sistem misi penting dan produksi yang membutuhkan skalabilitas tinggi, Anda mungkin ingin mengevaluasi dan menggunakan Azure Bus Layanan.

Jika Anda memerlukan abstraksi tingkat tinggi dan fitur yang lebih kaya seperti Sagas untuk proses jangka panjang yang membuat pengembangan terdistribusi lebih mudah, bus layanan komersial dan sumber terbuka lainnya seperti NServiceBus, MassTransit, dan Brighter layak dievaluasi. Dalam hal ini, abstraksi dan API yang digunakan biasanya akan langsung menjadi yang disediakan oleh bus layanan tingkat tinggi tersebut alih-alih abstraksi Anda sendiri (seperti abstraksi bus peristiwa sederhana yang disediakan di eShopOnContainers). Untuk itu, Anda dapat meneliti eShopOnContainers fork menggunakan NServiceBus (sampel turunan tambahan yang diterapkan oleh Perangkat Lunak Tertentu).

Tentu saja, Anda selalu dapat membangun fitur bus layanan Anda sendiri di atas teknologi tingkat bawah seperti RabbitMQ dan Docker, tetapi pekerjaan yang diperlukan untuk "menciptakan kembali roda" mungkin terlalu mahal untuk aplikasi perusahaan kustom.

Untuk menegaskan kembali: sampel abstraksi dan implementasi bus peristiwa yang ditampilkan dalam sampel eShopOnContainers dimaksudkan untuk digunakan hanya sebagai bukti konsep. Setelah Anda memutuskan bahwa Anda ingin memiliki komunikasi asinkron dan berbasis peristiwa, seperti yang dijelaskan di bagian saat ini, Anda harus memilih produk bus layanan yang paling sesuai dengan kebutuhan Anda untuk produksi.

Peristiwa integrasi

Peristiwa integrasi digunakan untuk menyinkronkan status domain di beberapa layanan mikro atau sistem eksternal. Fungsionalitas ini dilakukan dengan menerbitkan peristiwa integrasi di luar layanan mikro. Saat peristiwa diterbitkan ke beberapa layanan mikro penerima (untuk layanan mikro sebanyak yang berlangganan peristiwa integrasi), penanganan aktivitas yang sesuai di setiap layanan mikro penerima menangani peristiwa tersebut.

Peristiwa integrasi pada dasarnya adalah kelas penampung data, seperti dalam contoh berikut:

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

Peristiwa integrasi dapat didefinisikan pada tingkat aplikasi setiap layanan mikro, sehingga dipisahkan dari layanan mikro lainnya, dengan cara yang sebanding dengan bagaimana ViewModels didefinisikan di server dan klien. Yang tidak disarankan adalah berbagi pustaka peristiwa integrasi umum di beberapa layanan mikro; melakukan itu akan mengkudeta layanan mikro tersebut dengan satu pustaka data definisi peristiwa. Anda tidak ingin melakukannya karena alasan yang sama seperti Anda tidak ingin berbagi model domain umum di beberapa layanan mikro: layanan mikro harus sepenuhnya otonom. Untuk informasi selengkapnya, lihat posting blog ini tentang jumlah data yang akan dimasukkan ke dalam peristiwa. Berhati-hatilah untuk tidak mengambil ini terlalu jauh, karena posting blog lain ini menjelaskan masalah yang dapat dihasilkan oleh pesan kekurangan data. Desain acara Anda harus bertujuan "tepat" untuk kebutuhan konsumen mereka.

Hanya ada beberapa jenis pustaka yang harus Anda bagikan di seluruh layanan mikro. Salah satunya adalah pustaka yang merupakan blok aplikasi akhir, seperti API klien Event Bus, seperti di eShopOnContainers. Yang lain adalah pustaka yang merupakan alat yang juga dapat dibagikan sebagai komponen NuGet, seperti serializer JSON.

Bus peristiwa

Bus peristiwa memungkinkan komunikasi gaya terbitkan/berlangganan antara layanan mikro tanpa mengharuskan komponen untuk secara eksplisit saling mengetahui, seperti yang ditunjukkan pada Gambar 6-19.

A diagram showing the basic publish/subscribe pattern.

Gambar 6-19.. Dasar-dasar menerbitkan/berlangganan dengan bus peristiwa

Diagram di atas menunjukkan bahwa layanan mikro A menerbitkan ke Event Bus, yang mendistribusikan ke langganan layanan mikro B dan C, tanpa penerbit perlu mengetahui pelanggan. Bus peristiwa terkait dengan pola Observer dan pola terbitkan-berlangganan.

Pola pengamat

Dalam Pola pengamat, objek utama Anda (dikenal sebagai Observable) memberi tahu objek lain yang tertarik (dikenal sebagai Observer) dengan informasi (peristiwa) yang relevan.

Pola Terbitkan/Berlangganan (Pub/Sub)

Tujuan pola Terbitkan/Berlangganan sama dengan pola Pengamat: Anda ingin memberi tahu layanan lain saat peristiwa tertentu terjadi. Tetapi ada perbedaan penting antara pola Observer dan Pub/Sub. Dalam pola pengamat, siaran dilakukan langsung dari yang dapat diamati kepada pengamat, sehingga mereka "tahu" satu sama lain. Tetapi saat menggunakan pola Pub/Sub, ada komponen ketiga, yang disebut broker, atau broker pesan atau bus peristiwa, yang dikenal oleh penerbit dan pelanggan. Oleh karena itu, saat menggunakan pola Pub/Sub, penerbit dan pelanggan diuraikan dengan tepat berkat bus peristiwa atau broker pesan yang disebutkan.

Middleman atau bus peristiwa

Bagaimana Anda mencapai anonimitas antara penerbit dan pelanggan? Cara mudah adalah membiarkan perantara mengurus semua komunikasi. Bus peristiwa adalah salah satu perantara seperti itu.

Bus peristiwa biasanya terdiri dari dua bagian:

  • Abstraksi atau antarmuka.

  • Satu atau beberapa implementasi.

Pada Gambar 6-19 Anda dapat melihat bagaimana, dari sudut pandang aplikasi, bus peristiwa tidak lebih dari saluran Pub/Sub. Cara Anda menerapkan komunikasi asinkron ini dapat bervariasi. Ini dapat memiliki beberapa implementasi sehingga Anda dapat bertukar di antara mereka, tergantung pada persyaratan lingkungan (misalnya, produksi versus lingkungan pengembangan).

Pada Gambar 6-20, Anda dapat melihat abstraksi bus peristiwa dengan beberapa implementasi berdasarkan teknologi olahpesan infrastruktur seperti RabbitMQ, Azure Service Bus, atau broker peristiwa/pesan lainnya.

Diagram showing the addition of an event bus abstraction layer.

Gambar 6- 20. Beberapa implementasi bus peristiwa

Ada baiknya memiliki bus peristiwa yang ditentukan melalui antarmuka sehingga dapat diimplementasikan dengan beberapa teknologi, seperti RabbitMQ, Azure Service bus atau lainnya. Namun, dan seperti disebutkan sebelumnya, menggunakan abstraksi Anda sendiri (antarmuka bus peristiwa) hanya baik jika Anda memerlukan fitur bus peristiwa dasar yang didukung oleh abstraksi Anda. Jika Anda membutuhkan fitur bus layanan yang lebih kaya, Anda mungkin harus menggunakan API dan abstraksi yang disediakan oleh bus layanan komersial pilihan Anda alih-alih abstraksi Anda sendiri.

Menentukan antarmuka bus peristiwa

Mari kita mulai dengan beberapa kode implementasi untuk antarmuka bus peristiwa dan kemungkinan implementasi untuk tujuan eksplorasi. Antarmuka harus generik dan mudah, seperti pada antarmuka berikut.

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

Metode Publish itu mudah dimengerti. Bus peristiwa akan menyiarkan peristiwa integrasi yang diteruskan ke layanan mikro apa pun, atau bahkan aplikasi eksternal, berlangganan peristiwa tersebut. Metode ini digunakan oleh layanan mikro yang menerbitkan peristiwa.

Metode Subscribe (Anda dapat memiliki beberapa implementasi tergantung pada argumen) digunakan oleh layanan mikro yang ingin menerima peristiwa. Metode ini memiliki dua kelebihan muatan. Yang pertama adalah peristiwa integrasi untuk berlangganan (IntegrationEvent). Argumen kedua adalah handler peristiwa integrasi (atau metode panggilan balik), bernama IIntegrationEventHandler<T>, untuk dijalankan saat layanan mikro penerima mendapatkan pesan peristiwa integrasi tersebut.

Sumber daya tambahan

Beberapa solusi olahpesan siap produksi: