Dịch vụ gRPC đáng tin cậy với deadlines và cancellation
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:
- Deadline được cấu hình bằng
CallOptions.Deadlinekhi thực hiện lời gọi. - Không có giá trị deadline mặc định. Lời gọi gRPC không bị giới hạn thời gian trừ khi deadline được chỉ định.
- Deadline là thời gian UTC khi deadline bị vượt quá. Ví dụ,
DateTime.UtcNow.AddSeconds(5)là deadline 5 giây kể từ bây giờ. - Nếu thời gian đã qua hoặc hiện tại được sử dụng thì lời gọi ngay lập tức vượt quá deadline.
- Deadline được gửi cùng với lời gọi gRPC đến dịch vụ và được theo dõi độc lập bởi cả client và dịch vụ. Có thể xảy ra trường hợp lời gọi gRPC hoàn thành trên một máy, nhưng vào lúc response trả về client thì deadline đã bị vượt quá.
Nếu deadline bị vượt quá, client và dịch vụ có hành vi khác nhau:
- Client ngay lập tức hủy bỏ HTTP request bên dưới và ném lỗi
DeadlineExceeded. Ứng dụng client có thể chọn bắt lỗi và hiển thị thông báo timeout cho người dùng. - Trên server, HTTP request đang thực hiện bị hủy bỏ và ServerCallContext.CancellationToken được kích hoạt. Mặc dù HTTP request bị hủy bỏ, lời gọi gRPC tiếp tục chạy trên server cho đến khi phương thức hoàn thành. Điều quan trọng là cancellation token được truyền cho các phương thức async để chúng bị hủy cùng với lời gọi. Ví dụ, truyền cancellation token cho các truy vấn cơ sở dữ liệu async và HTTP request. Việc truyền cancellation token cho phép lời gọi bị hủy hoàn thành nhanh chóng trên server và giải phóng tài nguyên cho các lời gọi khác.
Cấu hình CallOptions.Deadline để thiết lập deadline cho lời gọi 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.");
}Sử dụng ServerCallContext.CancellationToken trong dịch vụ 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 };
}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ụ:
- Ứng dụng client gọi
FrontendService.GetUservới deadline. FrontendServicegọiUserService.GetUser. Deadline được chỉ định bởi client nên được chỉ định với lời gọi gRPC mới.UserService.GetUsernhậ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:
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:
- Tự động propagate deadline và cancellation token đến các lời gọi con.
- Không propagate deadline nếu lời gọi con chỉ định deadline nhỏ hơn. Ví dụ, deadline propagated là 10 giây không được sử dụng nếu lời gọi con chỉ định deadline mới là 5 giây bằng
CallOptions.Deadline. Khi có nhiều deadline, deadline nhỏ nhất được sử dụng. - Là cách tuyệt vời để đảm bảo rằng các kịch bản gRPC phức tạp, lồng nhau luôn propagate deadline và cancellation.
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.
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:
- Truyền
ServerCallContext.CancellationTokencho các phương thức async. Việc hủy các phương thức async cho phép lời gọi trên server hoàn thành nhanh chóng. - Propagate cancellation token đến các lời gọi con. Việc propagate cancellation token đảm bảo rằng các lời gọi con bị hủy cùng với lời gọi cha. gRPC client factory và
EnableCallContextPropagation()tự động propagate cancellation token.