ASP.Net Core 3.1 使用gRPC入门指南
主要参考文章微软官方文档: https://docs.microsoft.com/zh-cn/aspnet/core/grpc/client?view=aspnetcore-3.1
此外还参考了文章 https://www.cnblogs.com/stulzq/p/11581967.html并写了一个demo: https://files.cnblogs.com/files/hudean/GrpcDemo.zip
一、简介
gRPC 是一种与语言无关的高性能远程过程调用 (RPC) 框架。
gRPC 的主要优点是:
- 现代高性能轻量级 RPC 框架。
- 协定优先 API 开发,默认使用协议缓冲区,允许与语言无关的实现。
- 可用于多种语言的工具,以生成强类型服务器和客户端。
- 支持客户端、服务器和双向流式处理调用。
- 使用 Protobuf 二进制序列化减少对网络的使用。
这些优点使 gRPC 适用于:
- 效率至关重要的轻量级微服务。
- 需要多种语言用于开发的 Polyglot 系统。
- 需要处理流式处理请求或响应的点对点实时服务。
二、创建 gRPC 服务
启动 Visual Studio 并选择“创建新项目”。 或者,从 Visual Studio“文件”菜单中选择“新建” > “项目” 。
在“创建新项目”对话框中,选择“gRPC 服务”,然后选择“下一步” :
将项目命名为 GrpcGreeter。 将项目命名为“GrpcGreeter”非常重要,这样在复制和粘贴代码时命名空间就会匹配。
选择“创建”。
在“创建新 gRPC 服务”对话框中:
- 选择“gRPC 服务”模板。
- 选择“创建”。
运行服务
按 Ctrl+F5 以在不使用调试程序的情况下运行。
Visual Studio 会显示以下对话框:
如果信任 IIS Express SSL 证书,请选择“是” 。
将显示以下对话框:
如果你同意信任开发证书,请选择“是”。
日志显示该服务正在侦听 https://localhost:5001
。
控制台显示如下:
info: Microsoft.Hosting.Lifetime[0]
Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Development
备注
gRPC 模板配置为使用传输层安全性 (TLS)。 gRPC 客户端需要使用 HTTPS 调用服务器。
macOS 不支持 ASP.NET Core gRPC 及 TLS。 在 macOS 上成功运行 gRPC 服务需要其他配置。
检查项目文件
GrpcGreeter 项目文件:
- greet.proto : Protos/greet.proto 文件定义
Greeter
gRPC,且用于生成 gRPC 服务器资产。 - Services 文件夹:包含
Greeter
服务的实现。 - appSettings.json :包含配置数据,例如 Kestrel 使用的协议。
- Program.cs:包含 gRPC 服务的入口点。
Startup.cs :包含配置应用行为的代码。
上述准备工作完成,开始写gRPC服务端代码!
example.proto文件内容如下


syntax = "proto3"; option csharp_namespace = "GrpcGreeter"; package example; service exampler {
// Unarys
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 id = 1;
string name = 2;
} message ExampleResponse {
string msg = 1;
}
example.proto
其中:
syntax = "proto3";是使用 proto3 语法,protocol buffer 编译器默认使用的是 proto2 。 这必须是文件的非空、非注释的第一行。
对于 C#语言,编译器会为每一个.proto 文件创建一个.cs 文件,为每一个消息类型都创建一个类来操作。
option csharp_namespace = "GrpcGreeter";是c#代码的命名空间
package example;包的命名空间
service exampler 是服务的名字
rpc UnaryCall (ExampleRequest) returns (ExampleResponse); 意思是rpc调用方法 UnaryCall 方法参数是ExampleRequest类型 返回值是ExampleResponse 类型
message ExampleRequest {
int32 id = 1;
string name = 2;
}
指定字段类型 在上面的例子中,所有字段都是标量类型:一个整型(id),一个string类型(name)。当然,你也可以为字段指定其他的合成类型,包括枚举(enumerations)或其他消息类型。
分配标识号 我们可以看到在上面定义的消息中,给每个字段都定义了唯一的数字值。这些数字是用来在消息的二进制格式中识别各个字段的,一旦开始使用就不能够再改变。注:[1,15]之内的标识号在编码的时候会占用一个字节。[16,2047]之内的标识号则占用2个字节。所以应该为那些频繁出现的消息元素保留
[1,15]之内的标识号。切记:要为将来有可能添加的、频繁出现的标识号预留一些标识号。 最小的标识号可以从1开始,最大到2^29 - 1, or 536,870,911。不可以使用其中的[19000-19999]的标识号, Protobuf协议实现中对这些进行了预留。如果非要在.proto文件中使用这些预留标识号,编译时就会报警。类似地,你不能使用之前保留的任何标识符。 指定字段规则 消息的字段可以是一下情况之一: singular(默认):一个格式良好的消息可以包含该段可以出现 0 或 1 次(不能大于 1 次)。
repeated:在一个格式良好的消息中,这种字段可以重复任意多次(包括0次)。重复的值的顺序会被保留。
默认情况下,标量数值类型的repeated字段使用packed的编码方式。
在GrpcGreeter.csproj文件添加:
<ItemGroup>
<Protobuf Include="Protos\example.proto" GrpcServices="Server" />
</ItemGroup>
点击保存
在Services文件夹下添加ExampleService类,代码如下:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Grpc.Core; namespace GrpcGreeter
{
public class ExampleService :exampler.examplerBase
{
/// <summary>
/// 一元方法以参数的形式获取请求消息,并返回响应。 返回响应时,一元调用完成。
/// </summary>
/// <param name="request"></param>
/// <param name="context"></param>
/// <returns></returns>
public override Task<ExampleResponse> UnaryCall(ExampleRequest request, ServerCallContext context)
{
// return base.UnaryCall(request, context);
return Task.FromResult(new ExampleResponse
{
Msg = "id :" + request.Id + "name : " + request.Name + " hello" }) ;
} /// <summary>
/// 服务器流式处理方法
/// 服务器流式处理方法以参数的形式获取请求消息。 由于可以将多个消息流式传输回调用方,因此可使用 responseStream.WriteAsync 发送响应
/// 消息。 当方法返回时,服务器流式处理调用完成。
/// </summary>
/// <param name="request"></param>
/// <param name="responseStream"></param>
/// <param name="context"></param>
/// <returns></returns>
public override async Task StreamingFromServer(ExampleRequest request, IServerStreamWriter<ExampleResponse> responseStream, ServerCallContext context)
{ /**
* 服务器流式处理方法启动后,客户端无法发送其他消息或数据。 某些流式处理方法设计为永久运行。 对于连续流式处理方法,客户端可以在*再需要调用时将其取消。 当发生取消时,客户端会将信号发送到服务器,并引发 ServerCallContext.CancellationToken。 应在服务器上通过异步方法使用 CancellationToken 标记,以实现以下目的:
所有异步工作都与流式处理调用一起取消。
该方法快速退出。
**/
//return base.StreamingFromServer(request, responseStream, context); //for (int i = 0; i < 5; i++)
//{
// await responseStream.WriteAsync(new ExampleResponse { Msg = "我是服务端流for:" + i });
// await Task.Delay(TimeSpan.FromSeconds(1));
//}
int index = 0;
while (!context.CancellationToken.IsCancellationRequested)
{
index++;
await responseStream.WriteAsync(new ExampleResponse { Msg = "我是服务端流while" + index+" "+request.Id+" "+request.Name });
await Task.Delay(TimeSpan.FromSeconds(1), context.CancellationToken); } } /// <summary>
/// 客户端流式处理方法
/// 客户端流式处理方法在该方法没有接收消息的情况下启动。 requestStream 参数用于从客户端读取消息。 返回响应消息时,客户端流式处理调用
/// 完成:
/// </summary>
/// <param name="requestStream"></param>
/// <param name="context"></param>
/// <returns></returns>
public override async Task<ExampleResponse> StreamingFromClient(IAsyncStreamReader<ExampleRequest> requestStream, ServerCallContext context)
{
// return base.StreamingFromClient(requestStream, context);
List<string> list = new List<string>();
while (await requestStream.MoveNext())
{
//var message = requestStream.Current;
var id = requestStream.Current.Id;
var name = requestStream.Current.Name; list.Add($"{id}-{name}");
// ...
}
return new ExampleResponse() { Msg = "我是客户端流while"+string.Join(',',list) }; //await foreach (var message in requestStream.ReadAllAsync())
//{
// // ...
//}
// return new ExampleResponse() { Msg= "我是客户端流foreach" };
} /// <summary>
/// 双向流式处理方法
/// 双向流式处理方法在该方法没有接收到消息的情况下启动。 requestStream 参数用于从客户端读取消息。
/// 该方法可选择使用 responseStream.WriteAsync 发送消息。 当方法返回时,双向流式处理调用完成:
/// </summary>
/// <param name="requestStream"></param>
/// <param name="responseStream"></param>
/// <param name="context"></param>
/// <returns></returns>
public override async Task StreamingBothWays(IAsyncStreamReader<ExampleRequest> requestStream, IServerStreamWriter<ExampleResponse> responseStream, ServerCallContext context)
{
//return base.StreamingBothWays(requestStream, responseStream, context);
await foreach (var message in requestStream.ReadAllAsync())
{
string str= message.Id + " " + message.Name;
await responseStream.WriteAsync(new ExampleResponse() { Msg="我是双向流:"+ str }); } //// Read requests in a background task.
//var readTask = Task.Run(async () =>
//{
// await foreach (var message in requestStream.ReadAllAsync())
// {
// // Process request.
// string str = message.Id + " " + message.Name;
// }
//}); //// Send responses until the client signals that it is complete.
//while (!readTask.IsCompleted)
//{
// await responseStream.WriteAsync(new ExampleResponse());
// await Task.Delay(TimeSpan.FromSeconds(1), context.CancellationToken);
//}
} }
}
ExampleService
在Startup类里Configure中加入一个这个 endpoints.MapGrpcService<ExampleService>();


using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; namespace GrpcGreeter
{
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddGrpc();
} // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
} app.UseRouting(); app.UseEndpoints(endpoints =>
{
endpoints.MapGrpcService<GreeterService>();
endpoints.MapGrpcService<ExampleService>(); endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
});
});
}
}
}
Startup
就此 gRPC服务端代码完成了
在 .NET 控制台应用中创建 gRPC 客户端
- 打开 Visual Studio 的第二个实例并选择“创建新项目”。
- 在“创建新项目”对话框中,选择“控制台应用(.NET Core)”,然后选择“下一步” 。
- 在“项目名称”文本框中,输入“GrpcGreeterClient”,然后选择“创建” 。
添加所需的包
gRPC 客户端项目需要以下包:
- Grpc.Net.Client,其中包含 .NET Core 客户端。
- Google.Protobuf 包含适用于 C# 的 Protobuf 消息。
- Grpc.Tools 包含适用于 Protobuf 文件的 C# 工具支持。 运行时不需要工具包,因此依赖项标记为
PrivateAssets="All"
。
通过包管理器控制台 (PMC) 或管理 NuGet 包来安装包。
用于安装包的 PMC 选项
从 Visual Studio 中,依次选择“工具” > “NuGet 包管理器” > “包管理器控制台”
从“包管理器控制台”窗口中,运行
cd GrpcGreeterClient
以将目录更改为包含 GrpcGreeterClient.csproj 文件的文件夹。
运行以下命令:
PowerShell
Install-Package Grpc.Net.Client
Install-Package Google.Protobuf
Install-Package Grpc.Tools
管理 NuGet 包选项以安装包
- 右键单击“解决方案资源管理器” > “管理 NuGet 包”中的项目 。
- 选择“浏览”选项卡。
- 在搜索框中输入 Grpc.Net.Client。
- 从“浏览”选项卡中选择“Grpc.Net.Client”包,然后选择“安装” 。
- 为
Google.Protobuf
和Grpc.Tools
重复这些步骤。
添加 greet.proto
在 gRPC 客户端项目中创建 Protos 文件夹。
从 gRPC Greeter 服务将 Protos\greet.proto 文件复制到 gRPC 客户端项目。
将
greet.proto
文件中的命名空间更新为项目的命名空间:option csharp_namespace = "GrpcGreeterClient";
编辑 GrpcGreeterClient.csproj 项目文件:
右键单击项目,并选择“编辑项目文件”。
添加具有引用 greet.proto 文件的 <Protobuf>
元素的项组:
<ItemGroup>
<Protobuf Include="Protos\greet.proto" GrpcServices="Client" />
</ItemGroup>
创建 Greeter 客户端
构建客户端项目,以在 GrpcGreeter
命名空间中创建类型。 GrpcGreeter
类型是由生成进程自动生成的。
使用以下代码更新 gRPC 客户端的 Program.cs 文件:
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Grpc.Net.Client; namespace GrpcGreeterClient
{
class Program
{
static async Task Main(string[] args)
{
// The port number(5001) must match the port of the gRPC server.
using var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new Greeter.GreeterClient(channel);
var reply = await client.SayHelloAsync(
new HelloRequest { Name = "GreeterClient" });
Console.WriteLine("Greeting: " + reply.Message);
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
}
添加内容如下:
<ItemGroup>
<Protobuf Include="Protos\example.proto" GrpcServices="Server" />
<Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
</ItemGroup>
<ItemGroup>
<Protobuf Include="Protos\greet.proto" GrpcServices="Client" />
</ItemGroup>
<ItemGroup>
<Protobuf Include="Protos\example.proto" GrpcServices="Client" />
</ItemGroup>
在gRPC客户端写调用服务端代码,代码如下:


using Grpc.Core;
using Grpc.Net.Client;
using GrpcGreeter;
using System;
using System.Threading;
using System.Threading.Tasks; namespace GrpcGreeterClient
{
class Program
{
//static void Main(string[] args)
//{
// Console.WriteLine("Hello World!");
//}
//static async Task Main(string[] args)
//{
// // The port number(5001) must match the port of the gRPC server.
// using var channel = GrpcChannel.ForAddress("https://localhost:5001");
// var client = new Greeter.GreeterClient(channel);
// var reply = await client.SayHelloAsync(
// new HelloRequest { Name = "GreeterClient" });
// Console.WriteLine("Greeting: " + reply.Message);
// Console.WriteLine("Press any key to exit...");
// Console.ReadKey();
//} static async Task Main(string[] args)
{
// The port number(5001) must match the port of the gRPC server.
using var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new exampler.examplerClient(channel); #region 一元调用 //var reply = await client.UnaryCallAsync(new ExampleRequest { Id = 1, Name = "hda" });
//Console.WriteLine("Greeting: " + reply.Msg); #endregion 一元调用 #region 服务器流式处理调用 //using var call = client.StreamingFromServer(new ExampleRequest { Id = 1, Name = "hda" }); //while (await call.ResponseStream.MoveNext(CancellationToken.None))
//{
// Console.WriteLine("Greeting: " + call.ResponseStream.Current.Msg); //}
//如果使用 C# 8 或更高版本,则可使用 await foreach 语法来读取消息。 IAsyncStreamReader<T>.ReadAllAsync() 扩展方法读取响应数据流中的所有消息:
//await foreach (var response in call.ResponseStream.ReadAllAsync())
//{
// Console.WriteLine("Greeting: " + response.Msg);
// // "Greeting: Hello World" is written multiple times
//} #endregion 服务器流式处理调用 #region 客户端流式处理调用
//using var call = client.StreamingFromClient();
//for (int i = 0; i < 5; i++)
//{
// await call.RequestStream.WriteAsync(new ExampleRequest { Id = i, Name = "hda" + i });
//}
//await call.RequestStream.CompleteAsync();
//var response = await call;
//Console.WriteLine($"Count: {response.Msg}");
#endregion 客户端流式处理调用 #region 双向流式处理调用 //通过调用 EchoClient.Echo 启动新的双向流式调用。
//使用 ResponseStream.ReadAllAsync() 创建用于从服务中读取消息的后台任务。
//使用 RequestStream.WriteAsync 将消息发送到服务器。
//使用 RequestStream.CompleteAsync() 通知服务器它已发送消息。
//等待直到后台任务已读取所有传入消息。
//双向流式处理调用期间,客户端和服务可在任何时间互相发送消息。 与双向调用交互的最佳客户端逻辑因服务逻辑而异。
using var call = client.StreamingBothWays();
Console.WriteLine("Starting background task to receive messages");
var readTask = Task.Run(async () =>
{
await foreach (var response in call.ResponseStream.ReadAllAsync())
{
Console.WriteLine(response.Msg);
// Echo messages sent to the service
}
});
Console.WriteLine("Starting to send messages");
Console.WriteLine("Type a message to echo then press enter.");
while (true)
{
var result = Console.ReadLine();
if (string.IsNullOrEmpty(result))
{
break;
} await call.RequestStream.WriteAsync(new ExampleRequest { Id=1,Name= result });
} Console.WriteLine("Disconnecting");
await call.RequestStream.CompleteAsync();
await readTask;
#endregion 双向流式处理调用 Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
}
代码链接地址: https://files.cnblogs.com/files/hudean/GrpcGreeter.zip
ASP.Net Core 3.1 使用gRPC入门指南的更多相关文章
- ASP.NET Core 3.0 使用gRPC
一.简介 gRPC 是一个由Google开源的,跨语言的,高性能的远程过程调用(RPC)框架. gRPC使客户端和服务端应用程序可以透明地进行通信,并简化了连接系统的构建.它使用HTTP/2作为通信协 ...
- ASP.NET Core消息队列RabbitMQ基础入门实战演练
一.课程介绍 人生苦短,我用.NET Core!消息队列RabbitMQ大家相比都不陌生,本次分享课程阿笨将给大家分享一下在一般项目中99%都会用到的消息队列MQ的一个实战业务运用场景.本次分享课程不 ...
- ASP.NET Core 中文文档 第二章 指南(3)用 Visual Studio 发布一个 Azure 云 Web 应用程序
原文:Getting Started 作者:Rick Anderson 翻译:谢炀(Kiler) 校对:孟帅洋(书缘).刘怡(AlexLEWIS).何镇汐 设置开发环境 安装最新版本的 Azure S ...
- 基于 Vue.js 之 iView UI 框架非工程化实践记要 使用 Newtonsoft.Json 操作 JSON 字符串 基于.net core实现项目自动编译、并生成nuget包 webpack + vue 在dev和production模式下的小小区别 这样入门asp.net core 之 静态文件 这样入门asp.net core,如何
基于 Vue.js 之 iView UI 框架非工程化实践记要 像我们平日里做惯了 Java 或者 .NET 这种后端程序员,对于前端的认识还常常停留在 jQuery 时代,包括其插件在需要时就引 ...
- ASP.NET Core 3.0 使用 gRPC无法编译问题
一.问题 创建了gRPC项目后,编译发现报错: 二.解决 1.检查项目路径是否存在中文 2.检查当前Windows用户目录是否为非英文字符,如果是则必须改为英文 修改方法: https://jingy ...
- ASP.NET Core 中文文档 第二章 指南(4.1)ASP.NET Core MVC 与 Visual Studio 入门
原文:Getting started with ASP.NET Core MVC and Visual Studio 作者:Rick Anderson 翻译:娄宇(Lyrics) 校对:刘怡(Alex ...
- ASP.NET Core MVC和Visual Studio入门
本教程将教你使用Visual Studio 2017创建 ASP.NET Core MVC web应用程序的基础知识. 安装Visual Studio 2017 和.Net Core 安装Visual ...
- ASP.NET Core 中文文档 第二章 指南(8) 使用 dotnet watch 开发 ASP.NET Core 应用程序
原文:Developing ASP.NET Core applications using dotnet watch 作者:Victor Hurdugaci 翻译:谢炀(Kiler) 校对:刘怡(Al ...
- ASP.NET Core 中文文档 第二章 指南 (09) 使用 Swagger 生成 ASP.NET Web API 在线帮助测试文档
原文:ASP.NET Web API Help Pages using Swagger 作者:Shayne Boyer 翻译:谢炀(kiler) 翻译:许登洋(Seay) 对于开发人员来说,构建一个消 ...
随机推荐
- yython爬虫基础知识入门
Python爬虫 关注公众号"轻松学编程"了解更多. 大纲: 1.获取响应 urllib(python3)/urllib2-urllib(python2) requests(url ...
- 面试小问题——Object中有哪些常用方法?
一.equals方法 Object类中的equals方法用于检测一个对象是否等于另外一个对象.Java语言规范要求equals方法具有下面的特性: (1)自反性:对于任何非空引用x,x.equals( ...
- VSCode--HTML代码片段(基础版,react、vue、jquery)
起因是最近在学习前端,看的网上的demo也是在react.vue.jquery之间穿插,为了方便一键生成html模板(懒)写demo,有了以下折腾. 本人使用的前端编辑工具是vscode(方便.懒), ...
- php 断点续传以及100% 后台zip解压
前台部分 <div class="col-md-12"> <div class="form-group"> <label clas ...
- 服务网格istio概念应知应会
一.背景 最近架构组基于istio开发了服务网格(Service Mesh)平台,借此机会把相关的背景知识做一次学习和记录,方便回头查看. 初版的效果: 二.istio 官方手册:https://is ...
- 基于gin的golang web开发:使用数据库事务
在前文介绍访问数据库时介绍了github.com/jmoiron/sqlx包,本文基于这个包使用数据库事务. defer 在使用数据库事务之前,首先需要了解go语言的defer关键字.defer是go ...
- 百度开源插件echarts介绍及如何使用
前言 如果你想要用较少的代码实现比较酷炫的数据统计表,echarts是值得你考虑的一种实现方式.官网提供了很多实例供参考:http://echarts.baidu.com/examples.html. ...
- nginx&http 第五章 https non-fd 读写检测
EPOLL的LT/ET 模式下的读写 从一个非阻塞的socket上调用recv/send函数, 返回EAGAIN或者EWOULDBLOCK(注: EAGAIN就是EWOULDBLOCK)从字面上看, ...
- Tarjan算法求割点
(声明:以下图片来源于网络) Tarjan算法求出割点个数 首先来了解什么是连通图 在图论中,连通图基于连通的概念.在一个无向图 G 中,若从顶点i到顶点j有路径相连(当然从j到i也一定有路径),则称 ...
- abp(net core)+easyui+efcore实现仓储管理系统——出库管理之六(五十五)
abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统--ABP总体介绍(一) abp(net core)+ ...