gRPC在 ASP.NET Core 中应用学习(二)
前言:
上一篇文章中简单的对gRPC进行了简单了解,并实现了gRPC在ASP.NET Core中服务实现、客户端调用;那么本篇继续对gRPC的4中服务方法定义、其他使用注意点进一步了解学习
一、gRPC的4类服务方法
- 简单 RPC(一元方法):客户端向服务器发送单个请求并获得单个响应,就像普通的函数调用一样。
示例:
rpc UnaryCall(ExampleRequest) returns (ExampleResponse) {}
- 服务器端流式 RPC: 客户端发送请求到服务器,拿到一个流去读取返回的消息序列。 客户端读取返回的流,直到里面没有任何消息。通过在 响应 类型前插入
stream关键字,可以指定一个服务器端的流方法
示例:
rpc StreamingFromServer(ExampleRequest) returns (stream ExampleResponse) {}
- 客户端流式 RPC:
示例:
rpc StreamingFromClient(stream ExampleRequest) returns (ExampleResponse) {}
- 双向流式 RPC:双方使用读写流去发送一个消息序列。两个流独立操作,因此客户端和服务器可以以任意喜欢的顺序读写:
比如, 服务器可以在写入响应前等待接收所有的客户端消息,或者可以交替的读取和写入消息,或者其他读写的组合。 每个流中的消息顺序被预留。你可以通过在请求和响应前加 stream 关键字去制定方法的类型。
示例:
rpc StreamingBothWays(stream ExampleRequest) returns (stream ExampleResponse) {}
完整protos如下:
syntax = "proto3";
option csharp_namespace = "GrpcServiceDemo";
//可添加版本号区分v1/v2
package serviceTypeDemo;
//服务类型Demo:用于展现gRpc四种服务方法
service ServiceTypeDemo {
// Unary:一元方法
rpc UnaryCall (ExampleRequest) returns (ExampleResponse);
// Server streaming:服务流
rpc StreamingFromServer (ExampleRequest) returns (stream ExampleResponse);
// Client streaming:客户端流
rpc StreamingFromClient (stream ExampleRequest) returns (ExampleResponse);
// Bi-directional streaming:双向流
rpc StreamingBothWays (stream ExampleRequest) returns (stream ExampleResponse);
}
//请求对象
message ExampleRequest {
int32 pageIndex = 1;
int32 pageSize = 2;
bool isDescending = 3;
}
//响应对象
message ExampleResponse{
object result = 1;
int32 total = 2;
}
服务实现:实现相对简单,主要展示读取流和写入流
public class ServerTypeService : ServiceTypeDemo.ServiceTypeDemoBase
{
public override Task<ExampleResponse> UnaryCall(ExampleRequest request, ServerCallContext context)
{
ExampleResponse resp = GetResp();
return Task.FromResult(resp);
}
private static ExampleResponse GetResp()
{
var resp = new ExampleResponse();
resp.Total = new Random().Next(1, 20);
resp.Result.Add("abc");
resp.Result.Add("efg");
return resp;
}
public async override Task StreamingFromServer(ExampleRequest request, IServerStreamWriter<ExampleResponse> responseStream, ServerCallContext context)
{
var responses = new List<ExampleResponse>();
responses.Add(GetResp());
responses.Add(GetResp());
responses.Add(GetResp());
foreach (var response in responses)
{
//写入流中
await responseStream.WriteAsync(response);
}
}
public async override Task<ExampleResponse> StreamingFromClient(IAsyncStreamReader<ExampleRequest> requestStream, ServerCallContext context)
{
int pointCount = 0;
//读取传入流
while (await requestStream.MoveNext())
{
var point = requestStream.Current;
pointCount++;
}
var info = GetResp();
info.Total = pointCount;
return info;
}
public async override Task StreamingBothWays(IAsyncStreamReader<ExampleRequest> requestStream, IServerStreamWriter<ExampleResponse> responseStream, ServerCallContext context)
{
//读取内容
while (await requestStream.MoveNext())
{
var note = requestStream.Current;
var responses = new List<ExampleResponse>();
responses.Add(GetResp());
responses.Add(GetResp());
//循环写入
foreach (var prevNote in responses){
await responseStream.WriteAsync(prevNote);
}
}
}
}
二、gRPC其他注意点
1、截止时间和取消处理
截止时间功能让 gRPC 客户端可以指定等待调用完成的时间。 超过截止时间时,将取消调用。
截止时间配置:
- 在进行调用时,使用 CallOptions.Deadline 配置截止时间。
- 没有截止时间默认值。 gRPC 调用没有时间限制,除非指定了截止时间。
- 截止时间指的是超过截止时间的 UTC 时间。 例如,DateTime.UtcNow.AddSeconds(5) 是从现在起 5 秒的截止时间。
- 如果使用的是过去或当前的时间,则调用将立即超过截止时间。
- 截止时间随 gRPC 调用发送到服务,并由客户端和服务独立跟踪。 gRPC 调用可能在一台计算机上完成,但当响应返回给客户端时,已超过了截止时间。
如果超过了截止时间,客户端和服务将有不同的行为:
- 客户端将立即中止基础的 HTTP 请求并引发 DeadlineExceeded 错误。 客户端应用可以选择捕获错误并向用户显示超时消息。
- 在服务器上,将中止正在执行的 HTTP 请求,并引发 ServerCallContext.CancellationToken。 尽管中止了 HTTP 请求,gRPC 调用仍将继续在服务器上运行,直到方法完成。 将取消令牌传递给异步方法,使其随调用一同被取消,这非常重要。例如,向异步数据库查询和 HTTP 请求传递取消令牌。 传递取消令牌让取消的调用可以在服务器上快速完成,并为其他调用释放资源。
使用方式如下:
var defaultMethodConfig = new MethodConfig
{
Names = { MethodName.Default },
RetryPolicy = new RetryPolicy
{
//最大重试5次
MaxAttempts = 5,
//重试尝试之间的初始退避延迟。
InitialBackoff = TimeSpan.FromSeconds(1),
//最大退避会限制指数退避增长的上限。
MaxBackoff = TimeSpan.FromSeconds(5),
//每次重试尝试后,退避将乘以该值,并将在乘数大于 1 的情况下以指数方式增加。
BackoffMultiplier = 1.5,
//状态代码的集合,在此集合中重试
RetryableStatusCodes = { StatusCode.Unavailable }
}
};
//初始化Grpc通道:参数为gRPC服务地址
using var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions()
{
ThrowOperationCanceledOnCancellation = true,
//设置重试:v2.36版本添加
ServiceConfig = new ServiceConfig() { MethodConfigs = { defaultMethodConfig } }
}); string token = string.Empty;
var client = new Greeter.GreeterClient(channel);
//设置超时5秒;headers:可传递头信息:如认证串
Metadata metadata = new Metadata();
metadata.Add("Authorization", $"Bearer {token}");
var reply = client.SayHello(new HelloRequest { Name = "GreeterClient" },
headers: metadata,
deadline: DateTime.UtcNow.AddSeconds(5));
2、gRPC-Web应用
gRPC-Web 允许浏览器 JavaScript 和 Blazor 应用调用 gRPC 服务。 无法从基于浏览器的应用中调用 HTTP/2 gRPC 服务。 可将托管于 ASP.NET Core 中的 gRPC 服务配置为随 HTTP/2 gRPC 一起支持 gRPC-Web。
有两种方式可将 gRPC-Web 添加到 ASP.NET Core 应用中:
- 在 ASP.NET Core 中同时支持 gRPC-Web 和 gRPC HTTP/2。 此选项会使用
Grpc.AspNetCore.Web包提供的中间件。 - 使用 Envoy 代理的 gRPC-Web 支持将 gRPC-Web 转换为 gRPC HTTP/2。 转换后的调用随后会转发给 ASP.NET Core 应用。
a)添加Grpc.AspNetCore.Web 包引用
b)修改Startup.cs文件如下:
public void ConfigureServices(IServiceCollection services){
services.AddGrpc();
}
public void Configure(IApplicationBuilder app){
app.UseRouting();
app.UseGrpcWeb();//启用gRPCWeb
app.UseEndpoints(endpoints =>
{
endpoints.MapGrpcService<GreeterService>().EnableGrpcWeb();
});
}
c)Cors跨域设置:
public void ConfigureServices(IServiceCollection services)
{
services.AddGrpc();
services.AddCors(o => o.AddPolicy("AllowAll", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.WithExposedHeaders("Grpc-Status", "Grpc-Message", "Grpc-Encoding", "Grpc-Accept-Encoding");
}));
}
public void Configure(IApplicationBuilder app)
{
app.UseRouting();
app.UseGrpcWeb();
app.UseCors();
app.UseEndpoints(endpoints =>{
endpoints.MapGrpcService<GreeterService>().EnableGrpcWeb()
.RequireCors("AllowAll");
});
}
3、gRPC进程间调用:需要.NET 5;
服务端设置:
public static readonly string SocketPath = Path.Combine(Path.GetTempPath(), "socket.tmp"); public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
webBuilder.ConfigureKestrel(options =>
{
if (File.Exists(SocketPath))
{
File.Delete(SocketPath);
}
options.ListenUnixSocket(SocketPath);
});
});
客户端设置:
public class UnixDomainSocketConnectionFactory
{
private readonly EndPoint _endPoint; public UnixDomainSocketConnectionFactory(EndPoint endPoint)
{
_endPoint = endPoint;
} public async ValueTask<Stream> ConnectAsync(SocketsHttpConnectionContext _,
CancellationToken cancellationToken = default)
{
var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified); try
{
await socket.ConnectAsync(_endPoint, cancellationToken).ConfigureAwait(false);
return new NetworkStream(socket, true);
}
catch
{
socket.Dispose();
throw;
}
}
}
在创建通道连接时:
public static readonly string SocketPath = Path.Combine(Path.GetTempPath(), "socket.tmp"); public static GrpcChannel CreateChannel()
{
var udsEndPoint = new UnixDomainSocketEndPoint(SocketPath);
var connectionFactory = new UnixDomainSocketConnectionFactory(udsEndPoint);
var socketsHttpHandler = new SocketsHttpHandler
{
ConnectCallback = connectionFactory.ConnectAsync
}; return GrpcChannel.ForAddress("http://localhost", new GrpcChannelOptions
{
HttpHandler = socketsHttpHandler
});
}
4、相关配置项:
服务端:
gRPC 服务在 Startup.cs 中使用 AddGrpc 进行配置。
| 选项 | 默认值 | 描述 |
|---|---|---|
| MaxSendMessageSize | null |
可以从服务器发送的最大消息大小(以字节为单位)。 尝试发送超过配置的最大消息大小的消息会导致异常。 设置为 null时,消息的大小不受限制。 |
| MaxReceiveMessageSize | 4 MB | 可以由服务器接收的最大消息大小(以字节为单位)。 如果服务器收到的消息超过此限制,则会引发异常。 增大此值可使服务器接收更大的消息,但可能会对内存消耗产生负面影响。 设置为 null时,消息的大小不受限制。 |
| EnableDetailedErrors | false |
如果为 true,则当服务方法中引发异常时,会将详细异常消息返回到客户端。 默认值为 false。 将 EnableDetailedErrors 设置为 true 可能会泄漏敏感信息。 |
| CompressionProviders | gzip | 用于压缩和解压缩消息的压缩提供程序的集合。 可以创建自定义压缩提供程序并将其添加到集合中。 默认已配置提供程序支持 gzip 压缩。 |
| ResponseCompressionAlgorithm | null |
压缩算法用于压缩从服务器发送的消息。 该算法必须与 CompressionProviders 中的压缩提供程序匹配。 若要使算法可压缩响应,客户端必须通过在 grpc-accept-encoding 标头中进行发送来指示它支持算法。 |
| ResponseCompressionLevel | null |
用于压缩从服务器发送的消息的压缩级别。 |
| Interceptors | None | 随每个 gRPC 调用一起运行的侦听器的集合。 侦听器按注册顺序运行。 全局配置的侦听器在为单个服务配置的侦听器之前运行。 有关 gRPC 侦听器的详细信息,请参阅 gRPC 侦听器与中间件。 |
| IgnoreUnknownServices | false |
如果为 true,则对未知服务和方法的调用不会返回 UNIMPLEMENTED 状态,并且请求会传递到 ASP.NET Core 中的下一个注册中间件。 |
客户端:
gRPC 客户端配置在 GrpcChannelOptions 中进行设置。 下表描述了用于配置 gRPC 通道的选项
| 选项 | 默认值 | 描述 |
|---|---|---|
| HttpHandler | 新实例 | 用于进行 gRPC 调用的 HttpMessageHandler。 可以将客户端设置为配置自定义 HttpClientHandler,或将附加处理程序添加到 gRPC 调用的 HTTP 管道。 如果未指定 HttpMessageHandler,则会通过自动处置为通道创建新 HttpClientHandler 实例。 |
| HttpClient | null |
用于进行 gRPC 调用的 HttpClient。 此设置是 HttpHandler 的替代项。 |
| DisposeHttpClient | false |
如果设置为 true 且指定了 HttpMessageHandler 或 HttpClient,则在处置 GrpcChannel 时,将分别处置 HttpHandler 或 HttpClient。 |
| LoggerFactory | null |
客户端用于记录有关 gRPC 调用的信息的 LoggerFactory。 可以通过依赖项注入来解析或使用 LoggerFactory.Create 来创建 LoggerFactory 实例。 有关配置日志记录的示例,请参阅 .NET 上 gRPC 中的日志记录和诊断。 |
| MaxSendMessageSize | null |
可以从客户端发送的最大消息大小(以字节为单位)。 尝试发送超过配置的最大消息大小的消息会导致异常。 设置为 null时,消息的大小不受限制。 |
| MaxReceiveMessageSize | 4 MB | 可以由客户端接收的最大消息大小(以字节为单位)。 如果客户端收到的消息超过此限制,则会引发异常。 增大此值可使客户端接收更大的消息,但可能会对内存消耗产生负面影响。 设置为 null时,消息的大小不受限制。 |
| Credentials | null |
一个 ChannelCredentials 实例。 凭据用于将身份验证元数据添加到 gRPC 调用。 |
| CompressionProviders | gzip | 用于压缩和解压缩消息的压缩提供程序的集合。 可以创建自定义压缩提供程序并将其添加到集合中。 默认已配置提供程序支持 gzip 压缩。 |
| ThrowOperationCanceledOnCancellation | false |
如果设置为 true,则在取消调用或超过其截止时间时,客户端将引发 OperationCanceledException。 |
| MaxRetryAttempts | 5 | 最大重试次数。 该值限制服务配置中指定的任何重试和 hedging 尝试值。单独设置该值不会启用重试。 重试在服务配置中启用,可以使用 ServiceConfig 来启用。 null 值会删除最大重试次数限制。 有关重试的更多详细信息,请参阅“暂时性故障处理与 gRPC 重试”。 |
| MaxRetryBufferSize | 16 MB | 在重试或 hedging 调用时,可用于存储发送的消息的最大缓冲区大小(以字节为单位)。 如果超出了缓冲区限制,则不会再进行重试,并且仅保留一个 hedging 调用,其他 hedging 调用将会取消。 此限制将应用于通过通道进行的所有调用。 值 null 移除最大重试缓冲区大小限制。 |
| MaxRetryBufferPerCallSize | 1 MB | 在重试或 hedging 调用时,可用于存储发送的消息的最大缓冲区大小(以字节为单位)。 如果超出了缓冲区限制,则不会再进行重试,并且仅保留一个 hedging 调用,其他 hedging 调用将会取消。 此限制将应用于一个调用。 值 null 移除每个调用的最大重试缓冲区大小限制。 |
| ServiceConfig | null |
gRPC 通道的服务配置。 服务配置可以用于配置 gRPC 重试。 |
参考:
官方说明文档:https://grpc.io/docs/what-is-grpc/
微软:https://docs.microsoft.com/zh-cn/aspnet/core/grpc/?view=aspnetcore-3.0
示例源码地址:https://github.com/cwsheng/GrpcDemo
gRPC在 ASP.NET Core 中应用学习(二)的更多相关文章
- gRPC在 ASP.NET Core 中应用学习
一.gRPC简介: gRPC 是一个由Google开源的,跨语言的,高性能的远程过程调用(RPC)框架. gRPC使客户端和服务端应用程序可以透明地进行通信,并简化了连接系统的构建.它使用HTTP/2 ...
- (12)ASP.NET Core 中的配置二(Configuration)
1.内存配置 MemoryConfigurationProvider使用内存中集合作为配置键值对.若要激活内存中集合配置,请在ConfigurationBuilder的实例上调用AddInMemory ...
- IdentityServer4在Asp.Net Core中的应用(二)
继续上次授权的内容,客户端模式后我们再说以下密码模式,先回顾下密码模式的流程: 我们还是使用上次的代码,在那基础上修改,在IdentityServer4里面有一个IdentityServer4.Tes ...
- (13)ASP.NET Core 中的选项模式(Options)
1.前言 选项(Options)模式是对配置(Configuration)的功能的延伸.在12章(ASP.NET Core中的配置二)Configuration中有介绍过该功能(绑定到实体类.绑定至对 ...
- C# 嵌入dll 动软代码生成器基础使用 系统缓存全解析 .NET开发中的事务处理大比拼 C#之数据类型学习 【基于EF Core的Code First模式的DotNetCore快速开发框架】完成对DB First代码生成的支持 基于EF Core的Code First模式的DotNetCore快速开发框架 【懒人有道】在asp.net core中实现程序集注入
C# 嵌入dll 在很多时候我们在生成C#exe文件时,如果在工程里调用了dll文件时,那么如果不加以处理的话在生成的exe文件运行时需要连同这个dll一起转移,相比于一个单独干净的exe,这种形 ...
- 3、带你一步一步学习ASP.NET Core中的配置之Configuration
如果你是刚接触ASP.NET Core的学习的话,你会注意到:在ASP.NET Core项目中,看不到.NET Fraemwork时代中的web.config文件和app.config文件了.那么你肯 ...
- 学习ASP.NET Core Blazor编程系列二——第一个Blazor应用程序(中)
学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应用程序(上) 四.创建一个Blazor应用程序 1. 第一种创 ...
- ASP.NET Core 中的那些认证中间件及一些重要知识点
前言 在读这篇文章之间,建议先看一下我的 ASP.NET Core 之 Identity 入门系列(一,二,三)奠定一下基础. 有关于 Authentication 的知识太广,所以本篇介绍几个在 A ...
- [转]ASP.NET Core 中的那些认证中间件及一些重要知识点
本文转自:http://www.qingruanit.net/c_all/article_6645.html 在读这篇文章之间,建议先看一下我的 ASP.NET Core 之 Identity 入门系 ...
随机推荐
- taro best practice
taro best practice 最佳实践 https://taro-docs.jd.com/taro/docs/best-practice.html#关于-jsx-支持程度补充说明 https: ...
- 线上 S1 故障是什么, 线上 S1 故障, 运维故障分级, 运维, 故障分级, P1 级别故障, 故障, P1 , S1
线上 S1 故障是什么 线上 S1 故障, 运维故障分级, 运维, 故障分级, P1 级别故障, 故障, P1 , S1 故障复盘 https://time.geekbang.org/column/a ...
- NGK.IO的智能合约是炒作还是未来商业的主流?
随着NGK主网的上线,NGK市场也备受关注.目前,NGK代币价格已经由初始价格0.0215美元涨到现在的0.86美元,代币价格上涨40倍!数字货币市场也已经将重点目光放到了NGK代币上,相信在不久的将 ...
- SPEC-RFC3261总述
最近学习VoLTE(Voice Vver LTE)相关知识,而学习VoLTE必须要学相关的协议,最基础的就是RFC3261,RFC3261的全称是:SIP: Session Initiation Pr ...
- Anno&Viper -分布式锁服务端怎么实现
1.Anno简介 Anno是一个微服务框架引擎.入门简单.安全.稳定.高可用.全平台可监控.依赖第三方框架少.底层通讯RPC(Remote Procedure Call)采用稳定可靠经过无数成功项目验 ...
- Svelte v2 已经过时了!
带你走马观花,细看新版变化. 注意:原文发表于2018-04-18,随着框架不断演进,部分内容可能已不适用. 大约是一年之前,我们首次在 Svelte 的 issue 跟踪器上讨论过 v2 版本,现在 ...
- javascript中的模块系统
目录 简介 CommonJS和Nodejs AMD异步模块加载 CMD ES modules和现代浏览器 在HTML中使用module和要注意的问题 简介 在很久以前,js只是简单的作为浏览器的交互操 ...
- PAT-1167(Cartesian Tree)根据中序遍历序列重建最小堆
Cartesian Tree PAT-1167 一开始我使用数组进行存储,但是这样可能会导致无法开足够大的数组,因为树如果是链表状的则无法开这么大的数组(虽然结点很少). 正确的解法还是需要建树,使用 ...
- Elasticsearch 模块 - Shard Allocation 机制
原文 1. 背景 shard allocation 意思是分片分配, 是一个将分片分配到节点的过程; 可能发生该操作的过程包括: 初始恢复(initial recovery) 副本分配(replica ...
- Activiti工作流学习笔记(三)——自动生成28张数据库表的底层原理分析
原创/朱季谦 我接触工作流引擎Activiti已有两年之久,但一直都只限于熟悉其各类API的使用,对底层的实现,则存在较大的盲区. Activiti这个开源框架在设计上,其实存在不少值得学习和思考的地 ...