一、截止时间概述
- 截止时间功能让 gRPC 客户端可以指定等待调用完成的时间。
- 超过截止时间时,将取消调用。
- 设定一个截止时间非常重要,因为它将提供调用可运行的最长时间。
- 它能阻止异常运行的服务持续运行并耗尽服务器资源。
- 截止时间对于构建可靠应用非常有效,应该进行配置。
二、取消概述
- 客户端主动取消不再需要长期运行的调用
- 线程故障自动取消
- 超出截止时间触发取消操作
三、实战案例
- 首先准备一个grpc后端服务
- 其次准备一个webapi服务作为客户端,方便HttpContext传递
- 客户端工厂配置EnableCallContextPropagation 用于上下文传递截止时间
- 传递CancellationTokenSource
- 话不多说,通过代码可以更好的看出程序的运行轨迹
cs
// 引入proto文件
// 公共messages.proto
syntax = "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);
}
服务端接口实现:
cs
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注入客户端工厂并启用截止时间配置
- 增加拦截器统一设定超时时间
- 调用查看结果
cs
// program.cs
builder.Services.AddGrpcClient<DollarRpc.DollarRpcClient>(options =>
{
options.Address = new Uri("https://localhost:7188");
}).EnableCallContextPropagation();
cs
//拦截器过滤截止时间
[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();
}
}
cs
//调用
// 在相应的类上打上标记 [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