.Net Core gRPC 实战(二)
概述
gRPC 客户端必须使用与服务相同的连接级别安全性。 如调用服务时通道和服务的连接级别安全性不一致,gRPC 客户端就会抛出错误。
gRPC 配置使用HTTP
gRPC 客户端传输层安全性 (TLS) 是在创建 gRPC 通道时服务器地址以https开头配置的。若要配置为http协议做如下修改
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
GrpcChannel.ForAddress("http://localhost:5000")
关于通道和客户端的说明
- 创建通道成本高昂。 重用 gRPC 调用的通道可提高性能。
- gRPC 客户端是使用通道创建的。 gRPC 客户端是轻型对象,无需缓存或重用。
配置截止时间
建议配置 gRPC 调用的截止时间,因为它限制调用时间的上限,阻止异常运行的服务持续运行并耗尽服务器资源。 截止时间对于构建可靠应用非常有效。
进行调用时,使用 CallOptions.Deadline
配置截止时间。
如果超过了截止时间,客户端和服务将有不同的行为:
- 客户端将立即中止基础的 HTTP 请求并引发
DeadlineExceeded
错误。 客户端可以选择捕获错误并向用户显示超时消息。 - 服务器将中止正在执行的 HTTP 请求,并引发 ServerCallContext.CancellationToken。 尽管中止了 HTTP 请求,gRPC 调用仍将继续运行直到方法完成。 将取消令牌传递给异步方法,使其随调用一同被取消。 例如,向异步数据库查询和 HTTP 请求传递取消令牌。 传递取消令牌让取消的调用可以在服务器上快速完成,并为其他调用释放资源。
客户端代码示例:
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.");
}
服务器代码示例:
var response = await client.GetUserAsync(
new UserRequest { Id = request.Id },
deadline: context.Deadline);
注册 gRPC 客户端
在 Startup类的ConfigureServices方法中,使用 AddGrpcClient 扩展方法指定 gRPC客户端类和服务地址。
services.AddGrpcClient<Greeter.GreeterClient>(o =>
{
o.Address = new Uri("https://localhost:5001");
});
ASP.NET Core MVC 控制器和 gRPC 服务等通过构造函数等方式自动注入。
配置 HttpHandler
.ConfigurePrimaryHttpMessageHandler(() => new GrpcWebHandler(GrpcWebMode.GrpcWebText, new HttpClientHandler()));
配置通道和拦截器
通道
通道(Channel)是.Net Core 3.X引入的类型,Channel是线程安全的,Channel的预期用例是多线程场景,可以实现多线程之间通信。类似Golang的chan类型。
通道相关文章:https://webmote.blog.csdn.net/article/details/115361367
gRPC配置通道示例:
services
.AddGrpcClient<Greeter.GreeterClient>(o =>
{
o.Address = new Uri("https://localhost:5001");
})
.ConfigureChannel(o =>
{
var credentials = CallCredentials.FromInterceptor((context, metadata) =>
{
if (!string.IsNullOrEmpty(_token))
{
metadata.Add("Authorization", $"Bearer {_token}");
}
return Task.CompletedTask;
}); o.Credentials = ChannelCredentials.Create(new SslCredentials(), credentials);
});
拦截器
具有面向切面的思想,可以在调用服务的时候进行一些统一处理, 很适合在这里处理验证、日志等流程。
Interceptor类是gRPC服务拦截器的基类,是一个抽象类
各个方法作用如下:
方法名称 |
作用 |
---|---|
BlockingUnaryCall |
拦截阻塞调用 |
AsyncUnaryCall |
拦截异步调用 |
AsyncServerStreamingCall |
拦截异步服务端流调用 |
AsyncClientStreamingCall |
拦截异步客户端流调用 |
AsyncDuplexStreamingCall |
拦截异步双向流调用 |
UnaryServerHandler |
用于拦截和传入普通调用服务器端处理程序 |
ClientStreamingServerHandler |
用于拦截客户端流调用的服务器端处理程序 |
ServerStreamingServerHandler |
用于拦截服务端流调用的服务器端处理程序 |
DuplexStreamingServerHandler |
用于拦截双向流调用的服务器端处理程序 |
在实际使用中,可以根据自己的需要来使用对应的拦截方法。
本文示例为创建一个客户端拦截器ClientLoggerInterceptor,该类继承Interceptor。按需实现方法,这里我客户端调用的是SayHelloAsync方法则实现对应的AsyncUnaryCall方法。
注册拦截器:
运行效果图:
服务器端拦截器同理,继承Interceptor类实现对应方法。
服务器端注入方式:
services.AddGrpc(options =>
{
options.Interceptors.Add<ServerLoggerInterceptor>();
});
调用取消
可以使用 EnableCallContextPropagation() 对 gRPC 服务中工厂所创建的 gRPC 客户端进行配置,以自动将截止时间和取消令牌传播到子调用。
手动传播截止时间可能会很繁琐。 截止时间需要传递给每个调用,很容易不小心错过。 gRPC 客户端工厂提供自动解决方案。 指定 EnableCallContextPropagation
:
- 自动将截止时间和取消令牌传播到子调用。
- 这是确保复杂的嵌套 gRPC 场景始终传播截止时间和取消的一种极佳方式。
services
.AddGrpcClient<User.UserServiceClient>(o =>
{
o.Address = new Uri("https://localhost:5001");
})
.EnableCallContextPropagation();
如果客户端在 gRPC 调用的上下文之外使用,EnableCallContextPropagation
将引发错误。 此错误旨在提醒你没有要传播的调用上下文。 如果要在调用上下文之外使用客户端,请使用 SuppressContextNotFoundErrors
在配置客户端时禁止显示该错误:
services
.AddGrpcClient<Greeter.GreeterClient>(o =>
{
o.Address = new Uri("https://localhost:5001");
})
.EnableCallContextPropagation(o => o.SuppressContextNotFoundErrors = true);
配置 gRPC 重试策略
重试策略在创建 gRPC 通道时配置
var defaultMethodConfig = new MethodConfig
{
Names = { MethodName.Default },
RetryPolicy = new RetryPolicy
{
MaxAttempts = 5,
InitialBackoff = TimeSpan.FromSeconds(1),
MaxBackoff = TimeSpan.FromSeconds(5),
BackoffMultiplier = 1.5,
RetryableStatusCodes = { StatusCode.Unavailable }
}
}; var channel = GrpcChannel.ForAddress("http://localhost:5000",
new GrpcChannelOptions
{
LoggerFactory = loggerFactory,
ServiceConfig = new ServiceConfig
{
MethodConfigs = { defaultMethodConfig }
}
});
重试策略可以按方法配置,而方法可以使用
Names
属性进行匹配。 此方法配置有MethodName.Default
,因此它将应用于此通道调用的所有 gRPC 方法。
gRPC 重试选项
下表描述了用于配置 gRPC 重试策略的选项:
选项 | 描述 |
---|---|
MaxAttempts |
最大调用尝试次数,包括原始尝试。 此值受 GrpcChannelOptions.MaxRetryAttempts (默认值为 5)的限制。 必须为该选项提供值,且值必须大于 1。 |
InitialBackoff |
重试尝试之间的初始退避延迟。 介于 0 与当前退避之间的随机延迟确定何时进行下一次重试尝试。 每次尝试后,当前退避将乘以 BackoffMultiplier 。 必须为该选项提供值,且值必须大于 0。 |
MaxBackoff |
最大退避会限制指数退避增长的上限。 必须为该选项提供值,且值必须大于 0。 |
BackoffMultiplier |
每次重试尝试后,退避将乘以该值,并将在乘数大于 1 的情况下以指数方式增加。 必须为该选项提供值,且值必须大于 0。 |
RetryableStatusCodes |
状态代码的集合。 具有匹配状态的失败 gRPC 调用将自动重试。 有关状态代码的更多信息,请参阅状态代码及其在 gRPC 中的用法。 至少需要提供一个可重试的状态代码。 |
配置 gRPC hedging 策略
Hedging 是一种备选重试策略。 Hedged gRPC 调用可以在服务器上执行多次,并获取第一个成功的结果。
重要的是,务必仅针对可安全执行多次且不会造成负面影响的方法启用 hedging。且hedging 策略不能与重试策略结合使用。
Hedging 具有以下优缺点:
- Hedging 的优点是,它可能更快地返回成功的结果。 它允许同时进行多个 gRPC 调用,并在出现第一个成功的结果时完成。
- Hedging 的一个缺点是它可能会造成浪费。 进行了多个调用并且这些调用全部成功。 仅使用第一个结果放弃其余结果。
Hedging 策略的配置类似于重试策略:
var defaultMethodConfig = new MethodConfig
{
Names = { MethodName.Default },
HedgingPolicy = new HedgingPolicy
{
MaxAttempts = 5,
NonFatalStatusCodes = { StatusCode.Unavailable }
}
}; var channel = GrpcChannel.ForAddress("http://localhost:5000", new GrpcChannelOptions
{
LoggerFactory = loggerFactory,
ServiceConfig = new ServiceConfig { MethodConfigs = { defaultMethodConfig } }
});
gRPC hedging 选项
下表描述了用于配置 gRPC hedging 策略的选项:
选项 | 描述 |
---|---|
MaxAttempts |
Hedging 策略将发送的调用数量上限。 MaxAttempts 表示所有尝试的总数,包括原始尝试。 此值受 GrpcChannelOptions.MaxRetryAttempts (默认值为 5)的限制。 必须为该选项提供值,且值必须大于 2。 |
HedgingDelay |
第一次调用立即发送,但后续 hedging 调用将按该值延迟发送。 如果延迟设置为零或 null ,那么所有所有 hedged 调用都将立即发送。 默认值为 0。 |
NonFatalStatusCodes |
指示其他 hedge 调用仍可能会成功的状态代码集合。 如果服务器返回非致命状态代码,hedged 调用将继续。 否则,将取消未完成的请求,并将错误返回到应用。 有关状态代码的更多信息,请参阅状态代码及其在 gRPC 中的用法。 |
Github
本文示例代码:https://github.com/MayueCif/GrpcDemo
.Net Core gRPC 实战(二)的更多相关文章
- .Net Core gRPC 实战(一)
gRPC 是一种与语言无关的高性能远程过程调用 (RPC) 框架. gRPC 的主要优点是: 现代高性能轻量级 RPC 框架. 协定优先 API 开发,默认使用协议缓冲区,允许与语言无关的实现. 可用 ...
- 【无私分享:ASP.NET CORE 项目实战(第十二章)】添加对SqlServer、MySql、Oracle的支持
目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 增加对多数据库的支持,并不是意味着同时对多种数据库操作,当然,后面,我们会尝试同时对多种数据库操作,这可能需要多个上下文,暂且 ...
- 【.NET Core项目实战-统一认证平台】第十二章 授权篇-深入理解JWT生成及验证流程
[.NET Core项目实战-统一认证平台]开篇及目录索引 上篇文章介绍了基于Ids4密码授权模式,从使用场景.原理分析.自定义帐户体系集成完整的介绍了密码授权模式的内容,并最后给出了三个思考问题,本 ...
- .net core grpc consul 实现服务注册 服务发现 负载均衡(二)
在上一篇 .net core grpc 实现通信(一) 中,我们实现的grpc通信在.net core中的可行性,但要在微服务中真正使用,还缺少 服务注册,服务发现及负载均衡等,本篇我们将在 .net ...
- java版gRPC实战之二:服务发布和调用
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- 【无私分享:ASP.NET CORE 项目实战(第八章)】读取配置文件(二) 读取自定义配置文件
目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 我们在 读取配置文件(一) appsettings.json 中介绍了,如何读取appsettings.json. 但随之产生 ...
- ASP.NET Core gRPC 入门全家桶
一. 说明 本全家桶现在只包含了入门级别的资料,实战资料更新中. 二.官方文档 gRPC in Asp.Net Core :官方文档 gRPC 官网:点我跳转 三.入门全家桶 正片: ASP.NET ...
- 【无私分享:ASP.NET CORE 项目实战】目录索引
简介 首先,我们的 [无私分享:从入门到精通ASP.NET MVC] 系列已经接近尾声,希望大家在这个过程中学到了一些思路和方法,而不仅仅是源码. 因为是第一次写博客,我感觉还是比较混乱的,其中 ...
- 【无私分享:ASP.NET CORE 项目实战(第十四章)】图形验证码的实现
目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 很长时间没有来更新博客了,一是,最近有些忙,二是,Core也是一直在摸索中,其实已经完成了一个框架了,并且正在准备在生产环境中 ...
随机推荐
- docker搭建简单mysql主从
关于MySQL主从模式,如果我们直接在本机上搭建的话,是没法搭建的,只能借助于虚拟机,但有的时候我们又需要搭建一个主从集群,以便于进行一些功能性的测试.这个时候我们就可以尝试使用docker,借助于d ...
- jinja2的简单使用
后端代码 from jinja2 import Template def index(): with open('./index.html', 'r', encoding='utf-8') as fp ...
- maven自动化构建工具
目录结构: 一.Maven简介 二.Maven核心概念 三.Maven的使用 四.Maven在IDEA中的应用 五.依赖管理 六.Maven常用设置 ------------------------- ...
- 1.8.7- HTML值label标签
1.label直接进行包裹input就可以了.
- 【ShardingSphere】ShardingSphere学习(三)-数据分片-分片
分片键 分片算法 分片策略 SQL Hint 分片键 用于分片的数据库字段,是将数据库(表)水平拆分的关键字段.例:将订单表中的订单主键的尾数取模分片,则订单主键为分片字段. SQL中如果无分片字段, ...
- php 数学函数bc的使用(浮点数计算)
简介: 对于任意精度的数学,PHP提供了支持用字符串表示的任意大小和精度的数字的二进制计算,最多为2147483647-1(或0x7FFFFFFF-1). bcadd - 2个任意精度数字的加法计算 ...
- DVWA之CSRF(跨站请求伪造攻击)
目录 Low Middle High Impossible Low 源代码: <?php if( isset( $_GET[ 'Change' ] ) ) { // Get input $pas ...
- 解决GET http://localhost:8080/js/layui/layui.js net::ERR_ABORTED 404
用ssm+layui在写页面的时候,发现无法找到资源路径 <script src="js/layui/layui.js" charset="utf-8"& ...
- Windows子系统Ubuntu安装宝塔面板
首先你需要安装Ubuntu子系统,安装方法点击这里,安装完成后再进行下面的步骤. 安装宝塔面板 输入命令 wget -O install.sh http://download.bt.cn/instal ...
- 【c#】 使用Directory.GetFiles获取局域网中任意电脑指定文件夹下的文件
本文为老魏原创,如需转载请留言 格式如下: // 获取IP地址为10.172.10.167下D盘下railway下的所有文件 string[] picArray = Directory.GetFile ...