Bagikan melalui


Membangun aplikasi bisnis berbasis pesan dengan NServiceBus dan Azure Service Bus

NServiceBus adalah kerangka kerja olahpesan komersial yang disediakan oleh Particular Software. NServiceBus dibangun di atas Azure Service Bus dan membantu pengembang berfokus pada logika bisnis dengan mengabstraksikan masalah infrastruktur. Dalam panduan ini, kami akan membangun solusi yang bertukar pesan antara dua layanan. Kami juga akan menunjukkan cara mencoba ulang pesan yang gagal secara otomatis dan meninjau opsi untuk hosting layanan ini di Azure.

Catatan

Kode untuk tutorial ini tersedia pada situs web Dokumentasi Particular Software.

Prasyarat

Sampel berasumsi bahwa Anda telah membuat namespace layanan Azure Service Bus.

Penting

NServiceBus membutuhkan setidaknya tingkat Standar. Tingkat dasar tidak akan berfungsi.

Mengunduh dan menyiapkan solusi

  1. Unduh kode dari Situs web Dokumentasi Particular Software. Solusi SendReceiveWithNservicebus.sln berisi tiga proyek:

    • Pengirim: aplikasi konsol yang mengirim pesan
    • Penerima: aplikasi konsol yang menerima pesan dari pengirim dan membalas pesan tersebut
    • Dibagikan: pustaka kelas berisi kontrak pesan yang dibagikan antara pengirim dan penerima

    Diagram berikut yang dihasilkan oleh ServiceInsight, suatu visualisasi dan alat penelusuran kesalahan dari Perangkat Lunak Tertentu, menunjukkan alur pesan:

    Citra yang memperlihatkan diagram urutan

  2. Buka SendReceiveWithNservicebus.sln di editor kode favorit Anda (Misalnya, Visual Studio 2022).

  3. Buka appsettings.json dalam proyek Penerima dan Pengirim kemudian atur AzureServiceBusConnectionString ke string koneksi untuk namespace layanan Azure Service Bus Anda.

    • Ini dapat ditemukan di portal Azure di bawah Bus Layanan Pengaturan Namespace>Layanan>Kebijakan akses bersama RootManageSharedAccessKey>>String Koneksi Utama .
    • juga AzureServiceBusTransport memiliki konstruktor yang menerima namespace layanan dan kredensial token, yang dalam lingkungan produksi akan lebih aman, namun untuk tujuan tutorial ini kunci akses bersama string koneksi akan digunakan.

Menentukan kontrak pesan bersama

Pustaka kelas Bersama adalah tempat Anda menentukan kontrak yang digunakan untuk mengirim pesan kita. Tempat ini mencakup referensi ke paket NuGet NServiceBus yang berisi antarmuka yang dapat Anda gunakan untuk mengidentifikasi pesan kita. Antarmuka tidak diperlukan, tetapi ia memberi kita beberapa validasi tambahan dari NServiceBus dan memungkinkan kode untuk melakukan dokumentasi secara mandiri.

Pertama, kita akan mengulas kelas Ping.cs

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

Kelas Ping menentukan pesan yang dikirim oleh Pengirim kepada Penerima. Kelas ini merupakan kelas C # sederhana yang menerapkan NServiceBus.ICommand, antarmuka paket NServiceBus. Pesan ini merupakan sinyal kepada pembaca dan NServiceBus bahwa pesan ini adalah perintah, meskipun ada cara lain untuk mengidentifikasi pesan tanpa menggunakan antarmuka.

Kelas pesan lainnya dalam proyek Bersama adalah Pong.cs:

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

Pong juga merupakan objek C# meskipun ia mengimplementasikan NServiceBus.IMessage. Antarmuka IMessage mewakili pesan generik yang bukan merupakan perintah maupun kejadian, dan secara umum digunakan untuk balasan. Dalam sampel kita, ini adalah balasan yang dikirim kembali oleh Penerima ke Pengirim untuk menunjukkan bahwa pesan telah diterima.

Ping dan Pong adalah dua jenis pesan yang akan Anda gunakan. Langkah selanjutnya adalah mengonfigurasi Pengirim untuk menggunakan Azure Service Bus dan mengirim pesan Ping.

Menyiapkan pengirim

Pengirim adalah titik akhir yang mengirim pesan Ping kita. Di sini, Anda mengonfigurasi Pengirim untuk menggunakan Azure Service Bus sebagai mekanisme transportasi, kemudian membangun instans Ping dan mengirimnya.

Dalam metode Main Program.cs, Anda mengonfigurasi titik akhir Pengirim:

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();

Banyak hal harus dibahas di sini, kami akan meninjaunya satu per satu.

Mengonfigurasi host untuk titik akhir

Hosting dan pengelogan dikonfigurasi menggunakan opsi Microsoft Generic Host standar. Untuk saat ini, titik akhir dikonfigurasi untuk berjalan sebagai aplikasi konsol tetapi dapat diubah untuk berjalan di Azure Functions dengan perubahan minimal, yang akan kita bahas nanti di artikel ini.

Mengonfigurasi titik akhir NServiceBus

Selanjutnya, Anda meminta host untuk menggunakan NServiceBus dengan metode ekstensi .UseNServiceBus(…). Metode ini mengambil fungsi panggilan balik yang mengembalikan titik akhir yang akan dimulai saat host berjalan.

Dalam konfigurasi titik akhir, Anda menentukan AzureServiceBus untuk transportasi kita, yang menyediakan string koneksi dari appsettings.json. Selanjutnya, Anda akan mengatur perutean sehingga pesan jenis Ping dikirim ke titik akhir bernama "Receiver". Pengaturan ini memungkinkan NServiceBus mengotomatiskan proses pengiriman pesan ke tujuan tanpa memerlukan alamat penerima.

Panggilan untuk EnableInstallers akan menyiapkan topologi kita di namespace layanan Azure Service Bus ketika titik akhir diluncurkan, yang menciptakan antrean yang diperlukan jika diperlukan. Dalam lingkungan produksi, pembuatan skrip operasional adalah opsi lain untuk membuat topologi.

Menyiapkan layanan latar belakang untuk mengirim pesan

Bagian terakhir dari pengirim adalah SenderWorker, yakni layanan latar belakang yang dikonfigurasi untuk mengirim pesan Ping setiap detik.

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

IMessageSession yang digunakan dalam ExecuteAsync diinjeksi ke SenderWorker dan memungkinkan kita mengirim pesan menggunakan NServiceBus di luar penangan pesan. Perutean yang Anda konfigurasi di Sender menentukan tujuan pesan Ping. Penentuan ini menjaga topologi sistem (pesan dirutekan ke alamat tertentu) sebagai masalah yang terpisah dari kode bisnis.

Aplikasi Pengirim juga berisi PongHandler. Anda akan kembali setelah kita berdiskusi mengenai Penerima, yang akan kita lakukan selanjutnya.

Penyiapan penerima

Penerima adalah titik akhir yang mendengarkan pesan Ping, akan mencatat ketika pesan diterima, dan membalas pesan kepada pengirim. Di bagian ini, kita akan dengan cepat meninjau konfigurasi titik akhir, yang serupa dengan Pengirim, kemudian mengalihkan perhatian kita ke penangan pesan.

Seperti pengirim, siapkan penerima sebagai aplikasi konsol menggunakan Microsoft Generic Host. Penyiapan ini menggunakan konfigurasi pengelogan dan titik akhir yang sama (dengan Azure Service Bus sebagai transportasi pesan) tetapi dengan nama yang berbeda, untuk membedakannya dari pengirim:

var endpointConfiguration = new EndpointConfiguration("Receiver");

Karena titik akhir ini hanya membalas pembuatnya dan tidak memulai percakapan baru, maka konfigurasi perutean tidak diperlukan. Konfigurasi ini juga tidak memerlukan pekerja latar belakang seperti yang diperlukan Pengirim, karena penerima hanya membalas ketika menerima pesan.

Penangan pesan Ping

Proyek Penerima berisi penangan pesan bernama 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);
    }
}

Untuk saat ini, mari abaikan kode yang dikomentari; kita akan kembali saat membicarakan pulih dari kegagalan.

Kelas menerapkan IHandleMessages<Ping> yang menentukan metode: Handle. Antarmuka ini memberi tahu NServiceBus bahwa ketika titik akhir menerima pesan jenis Ping, maka pesan tersebut harus diproses dengan metode Handle dalam penangan ini. Metode Handle menjadikan pesan tersebut parameter, dan IMessageHandlerContext, yang memungkinkan operasi olahpesan lebih lanjut, seperti membalas, mengirim perintah, atau menerbitkan kejadian.

Kami PingHandler mudah: ketika pesan Ping diterima, catat detail pesan dan balas kembali ke pengirim dengan pesan baru Pong , yang kemudian ditangani di Pengirim PongHandler.

Catatan

Dalam konfigurasi Pengirim, Anda menentukan bahwa pesan Ping harus dirutekan ke Penerima. NServiceBus menambahkan metadata ke pesan yang menunjukkan, antara lain, asal pesan. Inilah sebabnya Anda tidak perlu menentukan data perutean apa pun untuk pesan balasan Pong; pesan akan secara otomatis diarahkan kembali ke asalnya: Pengirim.

Setelah Pengirim dan Penerima dikonfigurasi dengan benar, Anda dapat menjalankan solusinya.

Menjalankan solusi

Untuk meluncurkan solusi, Anda perlu menjalankan Pengirim dan Penerima. Jika Anda menggunakan Visual Studio Code, luncurkan konfigurasi "Debug Semua". Jika Anda menggunakan Visual Studio, konfigurasikan solusi untuk meluncurkan proyek Pengirim dan Penerima:

  1. Klik kanan solusi di Penjelajah Solusi
  2. Pilih "Atur Proyek Startup..."
  3. Pilih Beberapa proyek startup
  4. Untuk Pengirim dan Penerima, pilih "Mulai" dalam daftar dropdown

Luncurkan solusi. Dua aplikasi konsol akan muncul, satu untuk Pengirim dan satu untuk Penerima.

Dalam Pengirim, perhatikan bahwa pesan Ping dikirim setiap detik berkat pekerjaan latar belakang SenderWorker. Penerima menampilkan detail pesan Ping yang ia terima dan Pengirim mencatat detail setiap pesan Pong yang ia terima sebagai balasan.

Kini semuanya telah berjalan baik, mari kita hancurkan.

Ketahanan dalam tindakan

Kesalahan merupakan hal yang biasa dalam sistem perangkat lunak. Kegagalan kode merupakan hal yang tidak dapat dihindari dan bisa disebabkan oleh berbagai alasan, seperti kegagalan jaringan, kunci database, perubahan dalam API pihak ke-tiga, dan kesalahan pengodean lama biasa.

NServiceBus memiliki fitur pemulihan yang kuat untuk menangani kegagalan. Saat handler pesan gagal, pesan secara otomatis dicoba ulang berdasarkan kebijakan yang telah ditentukan sebelumnya. Ada dua jenis kebijakan percobaan kembali: pengulangan kembali segera dan pengulangan kembali yang tertunda. Cara terbaik untuk menggambarkan cara kerja kebijakan percobaan kembali adalah dengan melihat tindakannya. Mari tambahkan kebijakan percobaan kembali ke titik akhir Penerima kami:

  1. Buka Program.cs dalam proyek Pengirim
  2. Setelah baris .EnableInstallers, tambahkan kode berikut:
endpointConfiguration.SendFailedMessagesTo("error");
var recoverability = endpointConfiguration.Recoverability();
recoverability.Immediate(
    immediate =>
    {
        immediate.NumberOfRetries(3);
    });
recoverability.Delayed(
    delayed =>
    {
        delayed.NumberOfRetries(2);
        delayed.TimeIncrease(TimeSpan.FromSeconds(5));
    });

Sebelum kita membahas cara kerja kebijakan, mari kita lihat dalam tindakan. Sebelum Anda menguji kebijakan pemulihan, Anda perlu menyimulasikan kesalahan. Buka kode PingHandler dalam proyek Penerima dan batalkan komentar baris ini:

throw new Exception("BOOM");

Sekarang, ketika Penerima menangani pesan Ping, operasi ini akan gagal. Luncurkan solusi kembali dan mari kita lihat apa yang terjadi di Penerima.

Dengan PingHandler yang kurang dapat diandalkan, semua pesan kita akan gagal. Anda dapat melihat kebijakan percobaan kembali yang mengaktifkan pesan-pesan tersebut. Saat pesan gagal untuk pertama kali, maka pesan tersebut akan dicoba kembali hingga tiga kali:

Citra yang menampilkan kebijakan percobaan kembali segera yang mencoba kembali pesan hingga 3 kali

Tentu saja, itu akan terus gagal sehingga ketika tiga percobaan kembali telah mencapai batas, kebijakan percobaan kembali yang tertunda dimulai dan pesan tertunda selama 5 detik:

Citra yang menunjukkan kebijakan percobaan kembali tertunda yang menunda pesan dalam peningkatan 5 detik sebelum mencoba percobaan ulang lainnya

Setelah 5 detik berlalu, pesan tersebut dicoba kembali sebanyak tiga kali lagi (yaitu, perulangan lain dari kebijakan percobaan kembali segera). Percobaan ini juga akan gagal dan NServiceBus akan menunda pesan lagi, kali ini selama 10 detik, sebelum mencoba lagi.

Jika PingHandler masih tidak berhasil setelah menjalankan kebijakan percobaan kembali secara penuh, pesan ditempatkan dalam antrean kesalahan terpusat, yang bernama error, seperti yang ditentukan oleh panggilan ke SendFailedMessagesTo.

Citra yang menampilkan pesan yang gagal

Konsep antrean kesalahan terpusat berbeda dari mekanisme surat gagal di Azure Service Bus, yang memiliki antrean surat gagal untuk setiap antrean pemrosesan. Dengan NServiceBus, antrean surat gagal di Azure Service Bus bertindak sebagai antrean pesan berbahaya sejati, sedangkan pesan yang berakhir di antrean kesalahan terpusat dapat diproses kembali di lain waktu, apabila perlu.

Kebijakan percobaan kembali membantu menyelesaikan beberapa jenis kesalahan yang sering kali bersifat sementara atau semi-sementara. Artinya, kesalahan yang bersifat sementara dan sering teratasi jika pesan hanya diproses kembali setelah penundaan singkat. Contohnya termasuk kegagalan jaringan, kunci database, dan ketidaktersediaan API pihak ke-tiga.

Setelah pesan berada dalam antrean kesalahan, Anda dapat memeriksa detail pesan dalam alat pilihan Anda, lalu memutuskan apa yang harus dilakukan dengan itu. Misalnya, menggunakan ServicePulse, yaitu alat pemantauan oleh Particular Software, kita dapat melihat detail pesan dan alasan dibalik kegagalan tersebut:

Citra yang menampilkan ServicePulse, dari Particular Software

Setelah memeriksa detail, Anda dapat mengirim pesan kembali ke antrean aslinya untuk diproses. Anda juga dapat mengedit pesan sebelum melakukannya. Jika terdapat beberapa pesan dalam antrean kesalahan, yang gagal karena alasan yang sama, semuanya dapat dikirim kembali ke tujuan semula sebagai batch.

Selanjutnya, saatnya mencari tahu tempat untuk menyebarkan solusi kita di Azure.

Tempat untuk menjadi host layanan di Azure

Dalam sampel ini, titik akhir Pengirim dan Penerima dikonfigurasi untuk dijalankan sebagai aplikasi konsol. Titik akhir tersebut juga dapat dihosting di berbagai layanan Azure termasuk Azure Functions, Azure App Services, Azure Container Instances, Azure Kubernetes Services, dan Azure VM. Misalnya, berikut adalah cara titik akhir Pengirim dapat dikonfigurasi untuk dijalankan sebagai Fungsi 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();
    }
}

Untuk informasi lebih lanjut penggunaan NServiceBus dengan Functions, lihat Azure Functions dengan Azure Service Bus dalam dokumentasi NServiceBus.

Langkah berikutnya

Untuk informasi lebih lanjut penggunaan dengan NServiceBus dengan layanan Azure, lihat artikel berikut: