Bagikan melalui


Host Umum .NET

Dalam artikel ini, Anda mempelajari tentang berbagai pola untuk mengonfigurasi dan membangun Host Generik .NET yang tersedia dalam paket NuGet Microsoft.Extensions.Hosting . Host Generik .NET bertanggung jawab atas startup aplikasi dan manajemen seumur hidup. Template Layanan Pekerja membuat Host Umum .NET, HostApplicationBuilder. Host Umum dapat digunakan dengan jenis aplikasi .NET lain, seperti aplikasi Konsol.

Host adalah objek yang mencakup sumber daya dan fungsionalitas masa pakai aplikasi, seperti:

  • Injeksi dependensi (DI)
  • Pencatatan
  • Konfigurasi
  • Penonaktifan aplikasi
  • Implementasi IHostedService

Saat dimulai, host memanggil IHostedService.StartAsync pada setiap implementasi IHostedService yang terdaftar dalam kumpulan layanan yang dihosting kontainer layanan. Dalam aplikasi layanan pekerja, semua implementasi IHostedService yang berisi instans BackgroundService mendapati metode BackgroundService.ExecuteAsync-nya dipanggil.

Alasan utama untuk menyertakan semua sumber daya aplikasi yang saling bergantung dalam satu objek adalah pengelolaan masa pakai: kontrol atas pengaktifan dan penonaktifan aplikasi dengan baik.

Menyiapkan host

Host biasanya dikonfigurasi, dibangun, dan dijalankan oleh kode di kelas Program. Metode Main:

Template Layanan Pekerja .NET menghasilkan kode berikut untuk membuat Host Umum:

using Example.WorkerService;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<Worker>();

IHost host = builder.Build();
host.Run();

Untuk informasi selengkapnya tentang Layanan Pekerja, lihat Layanan Pekerja di .NET.

Pengaturan penyusun host

Metode CreateApplicationBuilder:

  • Mengatur akar konten ke jalur yang ditampilkan oleh GetCurrentDirectory().
  • Memuat konfigurasi host dari:
    • Variabel lingkungan yang diawali dengan DOTNET_.
    • Argumen baris perintah.
  • Memuat konfigurasi aplikasi dari:
    • appsettings.json.
    • appsettings.{Environment}.json.
    • Pengelola Rahasia saat aplikasi berjalan di lingkungan Development.
    • Variabel lingkungan.
    • Argumen baris perintah.
  • Menambahkan penyedia pengelogan berikut:
    • Konsol
    • Debug
    • EventSource
    • EventLog (hanya saat menjalankan di Windows)
  • Mengaktifkan validasi cakupan dan validasi dependensi saat lingkungan Development.

HostApplicationBuilder.Services adalah Microsoft.Extensions.DependencyInjection.IServiceCollection instans. Layanan ini digunakan untuk membangun yang IServiceProvider digunakan dengan injeksi dependensi untuk menyelesaikan layanan terdaftar.

Layanan yang disediakan kerangka kerja

Saat Anda memanggil IHostBuilder.Build() atau HostApplicationBuilder.Build(), layanan berikut didaftarkan secara otomatis:

Pembangun host berbasis skenario tambahan

Jika Anda membangun untuk web atau menulis aplikasi terdistribusi, Anda mungkin perlu menggunakan pembuat host yang berbeda. Pertimbangkan daftar penyusun host tambahan berikut:

IHostApplicationLifetime

Injeksikan layanan IHostApplicationLifetime ke kelas mana pun untuk menangani tugas pasca-pengaktifan dan penonaktifan dengan baik. Tiga properti pada antarmuka adalah token pembatalan yang digunakan untuk mendaftarkan metode penanganan aktivitas mulai aplikasi dan penghentian aplikasi. Antarmuka juga mencakup metode StopApplication().

Contoh berikut adalah implementasi IHostedService dan IHostedLifecycleService yang mendaftarkan IHostApplicationLifetime peristiwa:

using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace AppLifetime.Example;

public sealed class ExampleHostedService : IHostedService, IHostedLifecycleService
{
    private readonly ILogger _logger;

    public ExampleHostedService(
        ILogger<ExampleHostedService> logger,
        IHostApplicationLifetime appLifetime)
    {
        _logger = logger;

        appLifetime.ApplicationStarted.Register(OnStarted);
        appLifetime.ApplicationStopping.Register(OnStopping);
        appLifetime.ApplicationStopped.Register(OnStopped);
    }

    Task IHostedLifecycleService.StartingAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("1. StartingAsync has been called.");

        return Task.CompletedTask;
    }

    Task IHostedService.StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("2. StartAsync has been called.");

        return Task.CompletedTask;
    }

    Task IHostedLifecycleService.StartedAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("3. StartedAsync has been called.");

        return Task.CompletedTask;
    }

    private void OnStarted()
    {
        _logger.LogInformation("4. OnStarted has been called.");
    }

    private void OnStopping()
    {
        _logger.LogInformation("5. OnStopping has been called.");
    }

    Task IHostedLifecycleService.StoppingAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("6. StoppingAsync has been called.");

        return Task.CompletedTask;
    }

    Task IHostedService.StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("7. StopAsync has been called.");

        return Task.CompletedTask;
    }

    Task IHostedLifecycleService.StoppedAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("8. StoppedAsync has been called.");

        return Task.CompletedTask;
    }

    private void OnStopped()
    {
        _logger.LogInformation("9. OnStopped has been called.");
    }
}

Template Layanan Pekerja dapat dimodifikasi untuk menambahkan implementasi ExampleHostedService:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using AppLifetime.Example;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddHostedService<ExampleHostedService>();
using IHost host = builder.Build();

await host.RunAsync();

Aplikasi akan menulis output sampel berikut:

// Sample output:
//     info: AppLifetime.Example.ExampleHostedService[0]
//           1.StartingAsync has been called.
//     info: AppLifetime.Example.ExampleHostedService[0]
//           2.StartAsync has been called.
//     info: AppLifetime.Example.ExampleHostedService[0]
//           3.StartedAsync has been called.
//     info: AppLifetime.Example.ExampleHostedService[0]
//           4.OnStarted has been called.
//     info: Microsoft.Hosting.Lifetime[0]
//           Application started. Press Ctrl+C to shut down.
//     info: Microsoft.Hosting.Lifetime[0]
//           Hosting environment: Production
//     info: Microsoft.Hosting.Lifetime[0]
//           Content root path: ..\app-lifetime\bin\Debug\net8.0
//     info: AppLifetime.Example.ExampleHostedService[0]
//           5.OnStopping has been called.
//     info: Microsoft.Hosting.Lifetime[0]
//           Application is shutting down...
//     info: AppLifetime.Example.ExampleHostedService[0]
//           6.StoppingAsync has been called.
//     info: AppLifetime.Example.ExampleHostedService[0]
//           7.StopAsync has been called.
//     info: AppLifetime.Example.ExampleHostedService[0]
//           8.StoppedAsync has been called.
//     info: AppLifetime.Example.ExampleHostedService[0]
//           9.OnStopped has been called.

Output menunjukkan urutan semua dari berbagai peristiwa siklus hidup:

  1. IHostedLifecycleService.StartingAsync
  2. IHostedService.StartAsync
  3. IHostedLifecycleService.StartedAsync
  4. IHostApplicationLifetime.ApplicationStarted

Ketika aplikasi dihentikan, misalnya dengan Ctrl+C, peristiwa berikut dinaikkan:

  1. IHostApplicationLifetime.ApplicationStopping
  2. IHostedLifecycleService.StoppingAsync
  3. IHostedService.StopAsync
  4. IHostedLifecycleService.StoppedAsync
  5. IHostApplicationLifetime.ApplicationStopped

IHostLifetime

Implementasi IHostLifetime mengontrol waktu host dimulai dan dihentikan. Implementasi terakhir yang terdaftar akan digunakan. Microsoft.Extensions.Hosting.Internal.ConsoleLifetime adalah implementasi IHostLifetime default. Untuk informasi selengkapnya tentang mekanisme masa pakai penonaktifan, lihat Penonaktifan host.

Antarmuka IHostLifetime mengekspos IHostLifetime.WaitForStartAsync metode, yang disebut pada awalnya IHost.StartAsync akan menunggu sampai selesai sebelum melanjutkan. Ini dapat digunakan untuk menunda startup hingga disinyalir oleh peristiwa eksternal.

Selain itu, IHostLifetime antarmuka mengekspos IHostLifetime.StopAsync metode, yang dipanggil dari IHost.StopAsync untuk menunjukkan bahwa host berhenti dan saatnya untuk mematikan.

IHostEnvironment

Injeksikan layanan IHostEnvironment ke dalam kelas untuk mendapatkan informasi tentang pengaturan berikut:

Selain itu, layanan ini IHostEnvironment memaparkan kemampuan untuk mengevaluasi lingkungan dengan bantuan metode ekstensi ini:

Konfigurasi host

Konfigurasi host digunakan untuk mengonfigurasi properti implementasi IHostEnvironment.

Konfigurasi host tersedia di IHostApplicationBuilder.Configuration properti dan implementasi lingkungan tersedia di IHostApplicationBuilder.Environment properti. Untuk mengonfigurasi host, akses Configuration properti dan panggil salah satu metode ekstensi yang tersedia.

Untuk menambahkan konfigurasi host, pertimbangkan contoh berikut:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Environment.ContentRootPath = Directory.GetCurrentDirectory();
builder.Configuration.AddJsonFile("hostsettings.json", optional: true);
builder.Configuration.AddEnvironmentVariables(prefix: "PREFIX_");
builder.Configuration.AddCommandLine(args);

using IHost host = builder.Build();

// Application code should start here.

await host.RunAsync();

Kode sebelumnya:

  • Mengatur akar konten ke jalur yang ditampilkan oleh GetCurrentDirectory().
  • Memuat konfigurasi host dari:
    • hostsettings.json.
    • Variabel lingkungan yang diawali dengan PREFIX_.
    • Argumen baris perintah.

Konfigurasi aplikasi

Konfigurasi aplikasi dibuat dengan memanggil ConfigureAppConfiguration pada IHostApplicationBuilder. Properti publikIHostApplicationBuilder.Configuration memungkinkan konsumen untuk membaca dari atau membuat perubahan pada konfigurasi yang ada menggunakan metode ekstensi yang tersedia.

Untuk informasi selengkapnya, lihat Konfigurasi di .NET.

Penonaktifan host

Ada beberapa cara di mana proses yang dihosting dihentikan. Paling umum, proses yang dihosting dapat dihentikan dengan cara berikut:

Kode hosting tidak bertanggung jawab untuk menangani skenario ini. Pemilik proses perlu menanganinya sama dengan aplikasi lain. Ada beberapa cara lain di mana proses layanan yang dihosting dapat dihentikan:

  • Jika ConsoleLifetime digunakan (UseConsoleLifetime), ia mendengarkan sinyal berikut dan mencoba menghentikan host dengan anggun.
    • SIGINT (atau CTRL+C).
    • SIGQUIT (atau CTRL+BREAK di Windows, CTRL+\ di Unix).
    • SIGTERM (dikirim oleh aplikasi lain, seperti docker stop).
  • Jika aplikasi memanggil Environment.Exit.

Logika hosting bawaan menangani skenario ini, khususnya ConsoleLifetime kelas . ConsoleLifetime mencoba menangani sinyal "matikan" SIGINT, SIGQUIT, dan SIGTERM untuk memungkinkan keluar dengan baik ke aplikasi.

Sebelum .NET 6, tidak ada cara bagi kode .NET untuk menangani SIGTERM dengan baik. Untuk mengatasi batasan ini, ConsoleLifetime akan berlangganan ke System.AppDomain.ProcessExit. Saat ProcessExit dimunculkan, ConsoleLifetime akan memberi sinyal ke host untuk berhenti dan memblokir alur ProcessExit, yang menunggu host berhenti.

Handler keluar proses akan memungkinkan kode pembersihan dalam aplikasi berjalan—misalnya, IHost.StopAsync dan kode setelah HostingAbstractionsHostExtensions.Run dalam Main metode .

Namun, ada masalah lain dengan pendekatan ini karena SIGTERM bukan satu-satunya cara ProcessExit yang diangkat. SIGTERM juga dinaikkan saat kode aplikasi memanggil Environment.Exit. Environment.Exit bukanlah cara yang baik untuk mematikan proses dalam model aplikasi Microsoft.Extensions.Hosting. Hal ini memunculkan peristiwa ProcessExit dan kemudian keluar dari proses. Akhir metode Main tidak dijalankan. Utas latar belakang dan latar depan dihentikan, dan finally blok tidak dijalankan.

Karena ConsoleLifetime memblokir ProcessExit saat menunggu host dimatikan, perilaku ini menyebabkan kebuntuan dari Environment.Exit yang juga memblokir menunggu panggilan ke ProcessExit. Selain itu, karena penanganan SIGTERM mencoba mematikan proses dengan baik, ConsoleLifetime akan mengatur ExitCode ke 0, yang mengacaukan kode keluar pengguna yang diteruskan ke Environment.Exit.

Di .NET 6, sinyal POSIX didukung dan ditangani. Menangani ConsoleLifetime SIGTERM dengan anggun, dan tidak lagi terlibat ketika Environment.Exit dipanggil.

Tip

Untuk .NET 6+, ConsoleLifetime tidak lagi memiliki logika untuk menangani skenario Environment.Exit. Aplikasi yang memanggil Environment.Exit dan perlu menjalankan logika pembersihan dapat berlangganan ke ProcessExit dengan sendirinya. Hosting tidak akan lagi mencoba menghentikan host dengan anggun dalam skenario ini.

Jika aplikasi Anda menggunakan hosting, dan Anda ingin menghentikan host dengan baik, Anda dapat memanggil IHostApplicationLifetime.StopApplication dan bukan Environment.Exit.

Proses penonaktifan hosting

Diagram urutan berikut menunjukkan cara sinyal ditangani secara internal dalam kode hosting. Sebagian besar pengguna tidak perlu memahami proses ini. Tetapi bagi pengembang yang membutuhkan pemahaman mendalam, visual yang baik dapat membantu Anda memulai.

Setelah host dimulai, saat pengguna memanggil Run atau WaitForShutdown, penanganan akan terdaftar untuk IApplicationLifetime.ApplicationStopping. Eksekusi dijeda di WaitForShutdown, yang menunggu peristiwa ApplicationStopping dimunculkan. Metode Main ini tidak segera kembali, dan aplikasi tetap berjalan sampai Run atau WaitForShutdown kembali.

Saat dikirim ke proses, sinyal akan memulai urutan berikut:

Diagram urutan penonaktifan hosting.

  1. Kontrol mengalir dari ConsoleLifetime ke ApplicationLifetime untuk memunculkan peristiwa ApplicationStopping. Hal ini memberi sinyal WaitForShutdownAsync untuk membuka blokir kode eksekusi Main. Sementara itu, handler sinyal POSIX kembali dengan Cancel = true karena sinyal POSIX telah ditangani.
  2. Kode eksekusi Main mulai dijalankan lagi dan memberi tahu host ke StopAsync(), yang pada gilirannya menghentikan semua layanan yang dihosting, dan memunculkan peristiwa lain yang dihentikan.
  3. Akhirnya, WaitForShutdown keluar, memungkinkan kode pembersihan aplikasi apa pun dijalankan, dan agar Main metode keluar dengan anggun.

Penonaktifan host dalam skenario server web

Ada berbagai skenario umum lainnya di mana penonaktifan anggun berfungsi di Kestrel untuk protokol HTTP/1.1 dan HTTP/2, dan bagaimana Anda dapat mengonfigurasinya di lingkungan yang berbeda dengan load balancer untuk menguras lalu lintas dengan lancar. Meskipun konfigurasi server web berada di luar cakupan artikel ini, Anda dapat menemukan informasi selengkapnya tentang Mengonfigurasi opsi untuk dokumentasi server web ASP.NET Core Kestrel.

Ketika Host menerima sinyal matikan (misalnya, CTL+C atau StopAsync), host akan memberi tahu aplikasi dengan memberi ApplicationStoppingsinyal . Anda harus berlangganan acara ini jika Anda memiliki operasi jangka panjang yang perlu diselesaikan dengan baik.

Selanjutnya, Host memanggil IServer.StopAsync dengan batas waktu mati yang dapat Anda konfigurasi (default 30s). Kestrel (dan Http.Sys) menutup pengikatan port mereka dan berhenti menerima koneksi baru. Mereka juga memberi tahu koneksi saat ini untuk berhenti memproses permintaan baru. Untuk HTTP/2 dan HTTP/3, pesan awal GOAWAY dikirim ke klien. Untuk HTTP/1.1, mereka menghentikan perulangan koneksi karena permintaan diproses secara berurutan. IIS berperilaku berbeda, dengan menolak permintaan baru dengan kode status 503.

Permintaan aktif memiliki waktu hingga batas waktu mati selesai. Jika semuanya selesai sebelum waktu habis, server mengembalikan kontrol ke host lebih cepat. Jika waktu habis berakhir, koneksi dan permintaan yang tertunda dibatalkan secara paksa, yang dapat menyebabkan kesalahan dalam log dan ke klien.

Pertimbangan load balancer

Untuk memastikan transisi klien yang lancar ke tujuan baru saat bekerja dengan load balancer, Anda dapat mengikuti langkah-langkah berikut:

  • Munculkan instans baru dan mulai seimbangkan lalu lintas ke instans tersebut (Anda mungkin sudah memiliki beberapa instans untuk tujuan penskalaan).
  • Nonaktifkan atau hapus instans lama dalam konfigurasi load balancer sehingga berhenti menerima lalu lintas baru.
  • Beri sinyal instans lama untuk dimatikan.
  • Tunggu hingga habis atau waktu habis.

Lihat juga