Nguon: Microsoft Learn · .NET 8.0

Dịch vụ gRPC đáng tin cậy với deadlines và cancellation

Nguồn: Reliable gRPC services with deadlines and cancellation

Bởi James Newton-King

Deadlines (thời hạn) và cancellation (hủy bỏ) là các tính năng được gRPC client sử dụng để hủy bỏ các lời gọi đang thực hiện. Bài viết này thảo luận về lý do tại sao deadlines và cancellation quan trọng, và cách sử dụng chúng trong các ứng dụng .NET gRPC.

Deadlines (thời hạn)

Deadline cho phép gRPC client chỉ định thời gian nó sẽ đợi để một lời gọi hoàn thành. Khi deadline bị vượt quá, lời gọi bị hủy. Việc thiết lập deadline quan trọng vì nó cung cấp giới hạn trên về thời gian lời gọi có thể chạy. Nó ngăn các dịch vụ hoạt động sai chạy mãi mãi và làm cạn kiệt tài nguyên server. Deadlines là công cụ hữu ích để xây dựng ứng dụng đáng tin cậy và nên được cấu hình.

Cấu hình deadline:

Nếu deadline bị vượt quá, client và dịch vụ có hành vi khác nhau:

Cấu hình CallOptions.Deadline để thiết lập deadline cho lời gọi gRPC:

csharp
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.");
}

Sử dụng ServerCallContext.CancellationToken trong dịch vụ gRPC:

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

Deadlines và retries

Khi lời gọi gRPC được cấu hình với retry fault handling (xử lý lỗi retry) và deadline, deadline theo dõi thời gian qua tất cả các lần retry cho lời gọi gRPC. Nếu deadline bị vượt quá, lời gọi gRPC ngay lập tức hủy bỏ HTTP request bên dưới, bỏ qua các lần retry còn lại và ném lỗi DeadlineExceeded.

Propagating deadlines (truyền deadlines)

Khi lời gọi gRPC được thực hiện từ một dịch vụ gRPC đang thực thi, deadline nên được propagate. Ví dụ:

  1. Ứng dụng client gọi FrontendService.GetUser với deadline.
  2. FrontendService gọi UserService.GetUser. Deadline được chỉ định bởi client nên được chỉ định với lời gọi gRPC mới.
  3. UserService.GetUser nhận deadline. Nó timeout (hết thời gian) đúng cách nếu deadline của ứng dụng client bị vượt quá.

Call context cung cấp deadline với ServerCallContext.Deadline:

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

Việc propagate deadlines thủ công có thể cồng kềnh. Deadline cần được truyền đến mọi lời gọi và rất dễ vô tình bỏ qua. Giải pháp tự động có sẵn với gRPC client factory. Chỉ định EnableCallContextPropagation:

csharp
services
    .AddGrpcClient<User.UserServiceClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .EnableCallContextPropagation();

Để biết thêm thông tin, xem gRPC client factory integration in .NET.

Cancellation (hủy bỏ)

Cancellation cho phép gRPC client hủy bỏ các lời gọi chạy dài không còn cần thiết. Ví dụ, lời gọi gRPC stream các bản cập nhật theo thời gian thực được bắt đầu khi người dùng truy cập một trang trên website. Stream nên được hủy khi người dùng điều hướng khỏi trang.

Lời gọi gRPC có thể bị hủy trong client bằng cách truyền cancellation token với CallOptions.CancellationToken hoặc gọi Dispose trên lời gọi.

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

Các dịch vụ gRPC có thể bị hủy nên: