Bagikan melalui


Menerapkan tugas latar belakang di layanan mikro dengan IHostedService dan kelas BackgroundService

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.

Proses di latar belakang dan pekerjaan terjadwal adalah sesuatu yang mungkin perlu Anda gunakan dalam aplikasi apa pun, apakah itu mengikuti pola arsitektur layanan mikro atau tidak. Perbedaan saat menggunakan arsitektur layanan mikro adalah Anda dapat menerapkan proses di latar belakang dalam proses/kontainer terpisah untuk hosting sehingga Anda dapat menurunkan/meningkatkan skala berdasarkan kebutuhan Anda.

Dari sudut pandang generik, di .NET kami memanggil jenis tugas Layanan yang Dihosting ini, karena itu adalah layanan/logika yang Anda host dalam host/aplikasi/layanan mikro Anda. Perhatikan bahwa dalam hal ini, layanan yang dihosting hanya berarti kelas dengan logika proses di latar belakang.

Sejak .NET Core 2.0, kerangka kerja menyediakan antarmuka baru bernama IHostedService membantu Anda menerapkan layanan yang dihosting dengan mudah. Ide dasarnya adalah Anda dapat mendaftarkan beberapa proses di latar belakang (layanan yang dihosting) yang berjalan di latar belakang saat host web atau host Anda berjalan, seperti yang ditunjukkan pada gambar 6-26.

Diagram comparing ASP.NET Core IWebHost and .NET Core IHost.

Gambar 6-26. Menggunakan IHostedService di WebHost vs. Host

Dukungan IWebHost ASP.NET Core 1.x dan 2.x untuk proses latar belakang di aplikasi web. Dukungan IHost .NET Core 2.1 dan versi yang lebih baru untuk proses latar belakang dengan aplikasi konsol biasa. Perhatikan perbedaan yang dibuat antara WebHost dan Host.

WebHost (penerapan kelas dasar IWebHost) di ASP.NET Core 2.0 adalah artefak infrastruktur yang Anda gunakan untuk menyediakan fitur server HTTP untuk proses Anda, seperti saat Anda menerapkan aplikasi web MVC atau layanan API Web. Ini memberikan semua kebaikan infrastruktur baru di ASP.NET Core, memungkinkan Anda untuk menggunakan injeksi dependensi, menyisipkan middleware dalam alur permintaan, dan yang serupa. menggunakan WebHost ini sangat sama IHostedServices untuk tugas latar belakang.

Host (penerapan kelas dasar IHost) diperkenalkan di .NET Core 2.1. Pada dasarnya, memungkinkan Host Anda memiliki infrastruktur yang sama dengan apa yang Anda miliki WebHost (injeksi dependensi, layanan yang dihosting, dll.), tetapi dalam hal ini, Anda hanya ingin memiliki proses yang sederhana dan lebih ringan sebagai host, dengan tidak ada yang terkait dengan fitur MVC, Web API atau server HTTP.

Oleh karena itu, Anda dapat memilih dan membuat proses host khusus dengan IHost untuk menangani layanan yang dihosting dan tidak ada yang lain, seperti layanan mikro yang dibuat hanya untuk menghosting IHostedServices, atau Anda dapat memperluas ASP.NET Core WebHostyang ada, seperti ASP.NET Core Web API atau aplikasi MVC yang ada.

Setiap pendekatan memiliki pro dan kontra tergantung pada kebutuhan bisnis dan skalabilitas Anda. Garis bawah pada dasarnya adalah bahwa jika proses di latar belakang Anda tidak ada hubungannya dengan HTTP (IWebHost) Anda harus menggunakan IHost.

Mendaftarkan layanan yang dihosting di WebHost atau Host Anda

Mari kita telusuri lebih lanjut pada IHostedService antarmuka karena penggunaannya cukup mirip dalam WebHost atau di Host.

SignalR adalah salah satu contoh artefak menggunakan layanan yang dihosting, tetapi Anda juga dapat menggunakannya untuk hal-hal yang jauh lebih sederhana seperti:

  • Polling proses di latar belakang database mencari perubahan.
  • Tugas terjadwal memperbarui beberapa cache secara berkala.
  • Implementasi QueueBackgroundWorkItem yang memungkinkan tugas dijalankan pada alur latar belakang.
  • Memproses pesan dari antrean pesan di latar belakang aplikasi web sambil berbagi layanan umum seperti ILogger.
  • Proses di latar belakang dimulai dengan Task.Run().

Anda pada dasarnya dapat membongkar salah satu tindakan tersebut ke tugas latar belakang yang mengimplementasikan IHostedService.

Cara Anda menambahkan satu atau beberapa IHostedServices ke dalam Anda WebHost atau Host dengan mendaftarkannya melalui AddHostedService metode ekstensi dalam ASP.NET Core WebHost (atau dalam Host .NET Core 2.1 ke atas). Pada dasarnya, Anda harus mendaftarkan layanan yang dihosting dalam startup aplikasi di Program.cs.

//Other DI registrations;

// Register Hosted Services
builder.Services.AddHostedService<GracePeriodManagerService>();
builder.Services.AddHostedService<MyHostedServiceB>();
builder.Services.AddHostedService<MyHostedServiceC>();
//...

Dalam kode itu, layanan yang dihosting GracePeriodManagerService adalah kode nyata dari layanan mikro bisnis Pemesanan di eShopOnContainers, sementara dua lainnya hanya dua sampel tambahan.

Eksekusi IHostedService proses di latar belakang dikoordinasikan dengan masa pakai aplikasi (host atau layanan mikro, untuk hal ini). Anda mendaftarkan tugas ketika aplikasi dimulai dan Anda memiliki kesempatan untuk melakukan beberapa tindakan anggun atau pembersihan saat aplikasi dimatikan.

Tanpa menggunakan IHostedService, Anda selalu dapat memulai alur latar belakang untuk menjalankan tugas apa pun. Perbedaannya tepatnya pada waktu pematian aplikasi ketika alur itu hanya akan dimatikan tanpa memiliki kesempatan untuk menjalankan tindakan pembersihan dengan baik.

Menerapkan antarmuka IHostedService

Ketika Anda mendaftarkan IHostedService, .NET memanggil StartAsync() metode dan StopAsync() jenis Anda IHostedService selama aplikasi dimulai dan dihentikan masing-masing. Untuk detail selengkapnya, lihat Antarmuka IHostedService.

Seperti yang dapat Anda bayangkan, Anda dapat membuat beberapa implementasi IHostedService dan mendaftarkan masing-masing di Program.cs, seperti yang ditunjukkan sebelumnya. Semua layanan yang dihosting akan dimulai dan dihentikan bersama dengan aplikasi/layanan mikro.

Sebagai pengembang, Anda bertanggung jawab untuk menangani tindakan penghentian layanan Anda ketika StopAsync() metode dipicu oleh host.

Menerapkan IHostedService dengan kelas layanan yang dihosting kustom yang berasal dari kelas dasar BackgroundService

Anda dapat melanjutkan dan membuat kelas layanan kustom yang dihosting dari awal dan mengimplementasikan IHostedService, seperti yang perlu Anda lakukan saat menggunakan .NET Core 2.0 dan yang lebih baru.

Namun, karena sebagian besar proses di latar belakang akan memiliki kebutuhan yang sama sehubungan dengan manajemen token pembatalan dan operasi khas lainnya, ada kelas dasar abstrak yang nyaman yang dapat Anda dapatkan, bernama BackgroundService (tersedia sejak .NET Core 2.1).

Kelas tersebut menyediakan pekerjaan utama yang diperlukan untuk menyiapkan proses di latar belakang.

Kode berikutnya adalah kelas dasar BackgroundService abstrak seperti yang diimplementasikan dalam .NET.

// Copyright (c) .NET Foundation. Licensed under the Apache License, Version 2.0.
/// <summary>
/// Base class for implementing a long running <see cref="IHostedService"/>.
/// </summary>
public abstract class BackgroundService : IHostedService, IDisposable
{
    private Task _executingTask;
    private readonly CancellationTokenSource _stoppingCts =
                                                   new CancellationTokenSource();

    protected abstract Task ExecuteAsync(CancellationToken stoppingToken);

    public virtual Task StartAsync(CancellationToken cancellationToken)
    {
        // Store the task we're executing
        _executingTask = ExecuteAsync(_stoppingCts.Token);

        // If the task is completed then return it,
        // this will bubble cancellation and failure to the caller
        if (_executingTask.IsCompleted)
        {
            return _executingTask;
        }

        // Otherwise it's running
        return Task.CompletedTask;
    }

    public virtual async Task StopAsync(CancellationToken cancellationToken)
    {
        // Stop called without start
        if (_executingTask == null)
        {
            return;
        }

        try
        {
            // Signal cancellation to the executing method
            _stoppingCts.Cancel();
        }
        finally
        {
            // Wait until the task completes or the stop token triggers
            await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite,
                                                          cancellationToken));
        }

    }

    public virtual void Dispose()
    {
        _stoppingCts.Cancel();
    }
}

Saat berasal dari kelas dasar abstrak sebelumnya, berkat implementasi yang diwariskan, Anda hanya perlu menerapkan ExecuteAsync() metode di kelas layanan yang dihosting kustom Anda sendiri, seperti dalam kode yang disederhanakan berikut dari eShopOnContainers yang melakukan polling database dan menerbitkan peristiwa integrasi ke dalam Azure Event Bus saat diperlukan.

public class GracePeriodManagerService : BackgroundService
{
    private readonly ILogger<GracePeriodManagerService> _logger;
    private readonly OrderingBackgroundSettings _settings;

    private readonly IEventBus _eventBus;

    public GracePeriodManagerService(IOptions<OrderingBackgroundSettings> settings,
                                     IEventBus eventBus,
                                     ILogger<GracePeriodManagerService> logger)
    {
        // Constructor's parameters validations...
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogDebug($"GracePeriodManagerService is starting.");

        stoppingToken.Register(() =>
            _logger.LogDebug($" GracePeriod background task is stopping."));

        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogDebug($"GracePeriod task doing background work.");

            // This eShopOnContainers method is querying a database table
            // and publishing events into the Event Bus (RabbitMQ / ServiceBus)
            CheckConfirmedGracePeriodOrders();

            try {
                    await Task.Delay(_settings.CheckUpdateTime, stoppingToken);
                }
            catch (TaskCanceledException exception) {
                    _logger.LogCritical(exception, "TaskCanceledException Error", exception.Message);
                }
        }

        _logger.LogDebug($"GracePeriod background task is stopping.");
    }

    .../...
}

Dalam kasus khusus ini untuk eShopOnContainers, ini menjalankan metode aplikasi yang mengkueri tabel database yang mencari pesanan dengan status tertentu dan saat menerapkan perubahan, itu menerbitkan peristiwa integrasi melalui bus peristiwa (di bawahnya dapat menggunakan RabbitMQ atau Azure Service Bus).

Tentu saja, Anda dapat menjalankan proses di latar belakang bisnis lainnya, sebagai gantinya.

Secara default, token pembatalan diatur dengan batas waktu 5 detik, meskipun Anda dapat mengubah nilai tersebut saat membangun Anda WebHost menggunakan UseShutdownTimeout ekstensi IWebHostBuilder. Ini berarti bahwa layanan kami diperkirakan akan dibatalkan dalam waktu 5 detik jika tidak, layanan akan lebih tiba-tiba mati.

Kode berikut akan mengubah waktu tersebut menjadi 10 detik.

WebHost.CreateDefaultBuilder(args)
    .UseShutdownTimeout(TimeSpan.FromSeconds(10))
    ...

Diagram kelas ringkasan

Gambar berikut menunjukkan ringkasan visual kelas dan antarmuka yang terlibat saat menerapkan IHostedServices.

Diagram showing that IWebHost and IHost can host many services.

Gambar 6-27. Diagram kelas memperlihatkan beberapa kelas dan antarmuka yang terkait dengan IHostedService

Diagram kelas: IWebHost dan IHost dapat menghosting banyak layanan, yang mewarisi dari BackgroundService, yang mengimplementasikan IHostedService.

Pertimbangan dan takeaway penyebaran

Penting untuk dicatat bahwa cara Anda menyebarkan ASP.NET Core WebHost atau .NET Host dapat memengaruhi solusi akhir. Misalnya, jika Anda menyebarkan WebHost di IIS atau Azure App Service biasa, host Anda dapat dimatikan karena daur ulang kumpulan aplikasi. Tetapi jika Anda menyebarkan host sebagai kontainer ke dalam orkestrator seperti Kubernetes, Anda dapat mengontrol jumlah instans langsung host yang terjaga. Selain itu, Anda dapat mempertimbangkan pendekatan lain di cloud yang terutama dibuat untuk skenario ini, seperti Azure Functions. Terakhir, jika Anda memerlukan layanan untuk berjalan sepanjang waktu dan menyebarkan di Server Windows Anda dapat menggunakan Layanan Windows.

Tetapi bahkan untuk yang WebHost disebarkan ke dalam kumpulan aplikasi, ada skenario seperti mengisi ulang atau membersihkan cache dalam memori aplikasi yang masih akan berlaku.

Antarmuka IHostedService menyediakan cara mudah untuk memulai proses di latar belakang dalam aplikasi web ASP.NET Core (di .NET Core 2.0 dan versi yang lebih baru) atau dalam proses/host apa pun (dimulai di .NET Core 2.1 dengan IHost). Manfaat utamanya adalah kesempatan yang Anda dapatkan dengan pembatalan anggun untuk membersihkan kode proses di latar belakang Anda ketika host itu sendiri dimatikan.

Sumber daya tambahan