.netcore grpc截止时间和取消详解

一、截止时间概述

  1. 截止时间功能让 gRPC 客户端可以指定等待调用完成的时间。
  2. 超过截止时间时,将取消调用。
  3. 设定一个截止时间非常重要,因为它将提供调用可运行的最长时间。
  4. 它能阻止异常运行的服务持续运行并耗尽服务器资源。
  5. 截止时间对于构建可靠应用非常有效,应该进行配置。

二、取消概述

  1. 客户端主动取消不再需要长期运行的调用
  2. 线程故障自动取消
  3. 超出截止时间触发取消操作

三、实战案例

  1. 首先准备一个grpc后端服务
  2. 其次准备一个webapi服务作为客户端,方便HttpContext传递
  3. 客户端工厂配置EnableCallContextPropagation 用于上下文传递截止时间
  4. 传递CancellationTokenSource
  5. 话不多说,通过代码可以更好的看出程序的运行轨迹
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 };
    }

客户端实现重点:

  1. program注入客户端工厂并启用截止时间配置
  2. 增加拦截器统一设定超时时间
  3. 调用查看结果
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

相关推荐
鑫 源17 分钟前
C#源码开源, 获取macOS 电池数据,包括iOS (xamarin开发)
c#·.net·xamarin
爱吃奥利奥_wen11 小时前
面向对象三板斧:封装、继承、多态,到底在“装“什么、“承“什么、“变“什么?
开发语言·经验分享·c#
wflynn1 天前
GitHub 今日推荐|SCSKiller:游戏着色器预编译消除卡顿
开源·c#·github·nvidia·amd·directx12
疯狂的维修2 天前
关于用C#写电机起保停控制
开发语言·c#
用户788477316342 天前
门店核销闭环:小程序扫码 + .NET 幂等接口 + WPF 看板,一天能跑通
微信小程序·c#
samble2 天前
从零搭建灌装监控系统(十三):报警系统设计,生命周期与通知
c#·wpf·mvvm·modbus·工业控制
miofly2 天前
GitHub 今日推荐|SCSKiller:游戏着色器预编译消除卡顿
开源·c#·github
周杰伦fans3 天前
8GB显存下模型量化实战指南
人工智能·后端·c#
samble3 天前
从零搭建灌装监控系统(十二):配置系统,原子写入与容错
c#·wpf·mvvm·modbus·工业控制
晚安code3 天前
RPC 到底是什么:微服务内部调用的首选,以及它藏起来的三个坑
微服务·rpc