一、截止时间概述
- 截止时间功能让 gRPC 客户端可以指定等待调用完成的时间。
- 超过截止时间时,将取消调用。
- 设定一个截止时间非常重要,因为它将提供调用可运行的最长时间。
- 它能阻止异常运行的服务持续运行并耗尽服务器资源。
- 截止时间对于构建可靠应用非常有效,应该进行配置。
二、取消概述
- 客户端主动取消不再需要长期运行的调用
- 线程故障自动取消
- 超出截止时间触发取消操作
三、实战案例
- 首先准备一个grpc后端服务
- 其次准备一个webapi服务作为客户端,方便HttpContext传递
- 客户端工厂配置EnableCallContextPropagation 用于上下文传递截止时间
- 传递CancellationTokenSource
- 话不多说,通过代码可以更好的看出程序的运行轨迹
// 引入proto文件
// 公共messages.protosyntax = "proto3";option csharp_namespace = "GrpcProject";package grpc.serviceing;// 请求体
message ServerRequest{string name = 1;double height = 2;int32 age = 3;bool flag = 4;float x = 5;float y = 6;float z= 7;repeated string departments = 8;
}message ServerFileRequest{bytes fileBytes = 1;
}// 响应体
message ServerResponse{bool result = 1;
}// 服务dollar.proto文件syntax = "proto3";import "google/protobuf/empty.proto";
import "Protos/messages.proto";option csharp_namespace = "GrpcProject";package grpc.serviceing;service DollarRpc{rpc ServerOne (ServerRequest) returns (ServerResponse);rpc ServerTwo (ServerRequest) returns (google.protobuf.Empty);
}
服务端接口实现:
public class DollarService : DollarRpc.DollarRpcBase{public override async Task<ServerResponse> ServerOne(ServerRequest request, ServerCallContext context){await Console.Out.WriteLineAsync("-------------------------ServerOne------------------------------\r\n");await Task.Delay(TimeSpan.FromSeconds(8), context.CancellationToken);foreach (var prop in request.GetType().GetProperties()){await Console.Out.WriteLineAsync($"property name:{prop.Name};value:{prop.GetValue(request)}");}return GetResponse();}public override async Task<Empty> ServerTwo(ServerRequest request, ServerCallContext context){await Console.Out.WriteLineAsync("-------------------------ServerTwo------------------------------\r\n");await Task.Delay(TimeSpan.FromSeconds(8), context.CancellationToken);foreach (var prop in request.GetType().GetProperties()){await Console.Out.WriteLineAsync($"property name:{prop.Name};value:{prop.GetValue(request)}");}return new();}private ServerResponse GetResponse() => new() { Result = true };}
客户端实现重点:
- program注入客户端工厂并启用截止时间配置
- 增加拦截器统一设定超时时间
- 调用查看结果
// program.csbuilder.Services.AddGrpcClient<DollarRpc.DollarRpcClient>(options =>
{options.Address = new Uri("https://localhost:7188");
}).EnableCallContextPropagation();
//拦截器过滤截止时间[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]public class GrpcFilterAttribute : Attribute, IActionFilter{public void OnActionExecuted(ActionExecutedContext context){}public void OnActionExecuting(ActionExecutingContext context){CancellationTokenSource tokenSource = new();GrpcServerCallContextFeature callContext = new(DateTime.UtcNow.AddSeconds(5), tokenSource.Token);context.HttpContext.Features.Set<IServerCallContextFeature>(callContext);}}public class GrpcServerCallContextFeature : ServerCallContext, IServerCallContextFeature{/// <summary>/// 构造/// </summary>/// <param name="deadline"></param>/// <param name="cancellationToken"></param>public GrpcServerCallContextFeature(DateTime deadline, CancellationToken cancellationToken){DeadlineCore = deadline;CancellationTokenCore = cancellationToken;AuthContextCore = new AuthContext(null, new Dictionary<string, List<AuthProperty>>());}public ServerCallContext ServerCallContext => this;protected override string MethodCore { get; }protected override string HostCore { get; }protected override string PeerCore { get; }protected override DateTime DeadlineCore { get; }protected override Metadata RequestHeadersCore { get; }protected override CancellationToken CancellationTokenCore { get; }protected override Metadata ResponseTrailersCore { get; }protected override Status StatusCore { get; set; }protected override WriteOptions? WriteOptionsCore { get; set; }protected override AuthContext AuthContextCore { get; }protected override ContextPropagationToken CreatePropagationTokenCore(ContextPropagationOptions? options){return base.CreatePropagationToken(options);}protected override Task WriteResponseHeadersAsyncCore(Metadata responseHeaders){throw new NotImplementedException();}}
//调用
// 在相应的类上打上标记 [GrpcFilter][Route("api/[controller]")][ApiController][GrpcFilter] // 设定截止时间过滤public class GrpcTestController : ControllerBase{private readonly DollarRpc.DollarRpcClient _dollarRpcClient;public GrpcTestController(DollarRpc.DollarRpcClient dollarRpcClient) => _dollarRpcClient = dollarRpcClient;[HttpGet("one")]public async Task<string> GetOneResult(){ServerRequest request = new ServerRequest(){Departments = {"one","two","three","four","five"},Age = 10,Flag = true,Height = 10,Name = "zhangsan",X = 10F,Y = 11F,Z = 12F};try{var response = await _dollarRpcClient.ServerOneAsync(request);if (response.Result){return "Success";}return "Fail";}catch (RpcException ex) when (ex.StatusCode == Grpc.Core.StatusCode.DeadlineExceeded){return "dealine timeout.";}catch (RpcException ex){return ($"NoResult:{ex.Message}");}}[HttpGet("two")]public async Task<string> GetTwoResult(){ServerRequest request = new ServerRequest(){Departments = {"one","two","three","four","five"},Age = 10,Flag = true,Height = 10,Name = "zhangsan",X = 10F,Y = 11F,Z = 12F};try{var response = await _dollarRpcClient.ServerTwoAsync(request);return "Success";}catch (RpcException ex) when (ex.StatusCode == Grpc.Core.StatusCode.DeadlineExceeded){return "dealine timeout.";}catch (RpcException ex){return ($"NoResult:{ex.Message}");}}}
四、查看执行结果
服务端One:
客户端One:
swagger:
另一个Two效果类似。同时服务端任务取消错误,也在截图上有显示。
五、源码地址
链接:https://pan.baidu.com/s/1vleChFc3F6ILs-5ad8xQCA
提取码:mud0