Layanan gRPC yang andal dengan tenggat waktu dan pembatalan

Oleh James Newton-King

Tenggat waktu dan pembatalan adalah fitur yang digunakan oleh klien gRPC untuk membatalkan panggilan yang sedang berlangsung. Artikel ini membahas mengapa tenggat waktu dan pembatalan penting, dan cara menggunakannya di aplikasi .NET gRPC.

Tenggat waktu

Tenggat waktu memungkinkan klien gRPC menentukan berapa lama akan menunggu panggilan selesai. Ketika tenggat waktu terlampaui, panggilan dibatalkan. Mengatur tenggat waktu penting karena memberikan batas atas berapa lama panggilan dapat berjalan. Ini menghentikan layanan yang salah tingkah laku agar tidak berjalan selamanya dan menghabiskan sumber daya server. Tenggat waktu adalah alat yang berguna untuk membangun aplikasi yang andal dan harus dikonfigurasi.

Konfigurasi tenggat waktu:

  • Tenggat waktu dikonfigurasi menggunakan CallOptions.Deadline saat panggilan dilakukan.
  • Tidak ada nilai tenggat waktu default. Panggilan gRPC tidak terbatas waktu kecuali tenggat waktu ditentukan.
  • Tenggat waktu adalah waktu UTC ketika tenggat waktu terlampaui. Misalnya, DateTime.UtcNow.AddSeconds(5) adalah tenggat waktu 5 detik dari sekarang.
  • Jika waktu lalu atau saat ini digunakan, panggilan segera melebihi tenggat waktu.
  • Tenggat waktu dikirim dengan panggilan gRPC ke layanan dan dilacak secara independen oleh klien dan layanan. Ada kemungkinan bahwa panggilan gRPC selesai pada satu komputer, tetapi pada saat respons telah kembali ke klien, tenggat waktu telah terlampaui.

Jika tenggat waktu terlampaui, klien dan layanan memiliki perilaku yang berbeda:

  • Klien segera membatalkan permintaan HTTP yang mendasar dan melemparkan DeadlineExceeded kesalahan. Aplikasi klien dapat memilih untuk menangkap kesalahan dan menampilkan pesan batas waktu kepada pengguna.
  • Di server, permintaan HTTP yang dijalankan dibatalkan dan ServerCallContext.CancellationToken dinaikkan. Meskipun permintaan HTTP dibatalkan, panggilan gRPC terus berjalan di server hingga metode selesai. Penting bahwa token pembatalan diteruskan ke metode asinkron sehingga dibatalkan bersama dengan panggilan. Misalnya, meneruskan token pembatalan ke kueri database asinkron dan permintaan HTTP. Melewati token pembatalan memungkinkan panggilan yang dibatalkan selesai dengan cepat di server dan membebaskan sumber daya untuk panggilan lain.

Konfigurasikan CallOptions.Deadline untuk mengatur tenggat waktu untuk panggilan gRPC:

var client = new Greet.GreeterClient(channel);

try
{
    var response = await client.SayHelloAsync(
        new HelloRequest { Name = "World" },
        deadline: DateTime.UtcNow.AddSeconds(5));
    
    // Greeting: Hello World
    Console.WriteLine("Greeting: " + response.Message);
}
catch (RpcException ex) when (ex.StatusCode == StatusCode.DeadlineExceeded)
{
    Console.WriteLine("Greeting timeout.");
}

Menggunakan ServerCallContext.CancellationToken dalam layanan gRPC:

public override async Task<HelloReply> SayHello(HelloRequest request,
    ServerCallContext context)
{
    var user = await _databaseContext.GetUserAsync(request.Name,
        context.CancellationToken);

    return new HelloReply { Message = "Hello " + user.DisplayName };
}

Tenggat waktu dan percobaan ulang

Saat panggilan gRPC dikonfigurasi dengan penanganan kesalahan coba lagi dan tenggat waktu, tenggat waktu melacak waktu di semua percobaan ulang untuk panggilan gRPC. Jika tenggat waktu terlampaui, panggilan gRPC segera membatalkan permintaan HTTP yang mendasarinya, melewati percobaan ulang yang tersisa, dan melemparkan DeadlineExceeded kesalahan.

Menyebarkan tenggat waktu

Ketika panggilan gRPC dilakukan dari layanan gRPC yang mengeksekusi, tenggat waktu harus disebarluaskan. Contohnya:

  1. Panggilan FrontendService.GetUser aplikasi klien dengan tenggat waktu.
  2. FrontendServiceUserService.GetUsermemanggil . Tenggat waktu yang ditentukan oleh klien harus ditentukan dengan panggilan gRPC baru.
  3. UserService.GetUser menerima tenggat waktu. Waktu habis dengan benar jika tenggat waktu aplikasi klien terlampaui.

Konteks panggilan menyediakan tenggat waktu dengan ServerCallContext.Deadline:

public override async Task<UserResponse> GetUser(UserRequest request,
    ServerCallContext context)
{
    var client = new User.UserServiceClient(_channel);
    var response = await client.GetUserAsync(
        new UserRequest { Id = request.Id },
        deadline: context.Deadline);

    return response;
}

Menyebarkan tenggat waktu secara manual bisa rumit. Tenggat waktu perlu diteruskan ke setiap panggilan, dan mudah untuk melewatkan secara tidak sengaja. Solusi otomatis tersedia dengan pabrik klien gRPC. Menentukan EnableCallContextPropagation:

  • Secara otomatis menyebarluaskan tenggat waktu dan token pembatalan ke panggilan anak.
  • Tidak menyebarluaskan tenggat waktu jika panggilan anak menentukan tenggat waktu yang lebih kecil. Misalnya, tenggat waktu yang disebarkan 10 detik tidak digunakan jika panggilan anak menentukan tenggat waktu baru 5 detik menggunakan CallOptions.Deadline. Saat beberapa tenggat waktu tersedia, tenggat waktu terkecil digunakan.
  • Adalah cara yang sangat baik untuk memastikan bahwa skenario gRPC yang kompleks dan berlapis selalu menyebarluaskan tenggat waktu dan pembatalan.
services
    .AddGrpcClient<User.UserServiceClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .EnableCallContextPropagation();

Untuk informasi selengkapnya, lihat integrasi pabrik klien gRPC di .NET.

Pembatalan

Pembatalan memungkinkan klien gRPC untuk membatalkan panggilan jangka panjang yang tidak lagi diperlukan. Misalnya, panggilan gRPC yang melakukan streaming pembaruan realtime dimulai ketika pengguna mengunjungi halaman di situs web. Aliran harus dibatalkan saat pengguna menavigasi jauh dari halaman.

Panggilan gRPC dapat dibatalkan di klien dengan meneruskan token pembatalan dengan CallOptions.CancellationToken atau memanggil Dispose panggilan.

private AsyncServerStreamingCall<HelloReply> _call;

public void StartStream()
{
    _call = client.SayHellos(new HelloRequest { Name = "World" });

    // Read response in background task.
    _ = Task.Run(async () =>
    {
        await foreach (var response in _call.ResponseStream.ReadAllAsync())
        {
            Console.WriteLine("Greeting: " + response.Message);
        }
    });
}

public void StopStream()
{
    _call.Dispose();
}

Layanan gRPC yang dapat dibatalkan harus:

  • Teruskan ServerCallContext.CancellationToken ke metode asinkron. Membatalkan metode asinkron memungkinkan panggilan di server selesai dengan cepat.
  • Menyebarkan token pembatalan ke panggilan anak. Menyebarkan token pembatalan memastikan bahwa panggilan anak dibatalkan dengan induknya. pabrik klien gRPC dan EnableCallContextPropagation() secara otomatis menyebarkan token pembatalan.

Sumber Daya Tambahan: