主要参考文章微软官方文档: 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> 元素的项组:

XML

<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入门指南的更多相关文章

  1. ASP.NET Core 3.0 使用gRPC

    一.简介 gRPC 是一个由Google开源的,跨语言的,高性能的远程过程调用(RPC)框架. gRPC使客户端和服务端应用程序可以透明地进行通信,并简化了连接系统的构建.它使用HTTP/2作为通信协 ...

  2. ASP.NET Core消息队列RabbitMQ基础入门实战演练

    一.课程介绍 人生苦短,我用.NET Core!消息队列RabbitMQ大家相比都不陌生,本次分享课程阿笨将给大家分享一下在一般项目中99%都会用到的消息队列MQ的一个实战业务运用场景.本次分享课程不 ...

  3. ASP.NET Core 中文文档 第二章 指南(3)用 Visual Studio 发布一个 Azure 云 Web 应用程序

    原文:Getting Started 作者:Rick Anderson 翻译:谢炀(Kiler) 校对:孟帅洋(书缘).刘怡(AlexLEWIS).何镇汐 设置开发环境 安装最新版本的 Azure S ...

  4. 基于 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 时代,包括其插件在需要时就引 ...

  5. ASP.NET Core 3.0 使用 gRPC无法编译问题

    一.问题 创建了gRPC项目后,编译发现报错: 二.解决 1.检查项目路径是否存在中文 2.检查当前Windows用户目录是否为非英文字符,如果是则必须改为英文 修改方法: https://jingy ...

  6. 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 ...

  7. ASP.NET Core MVC和Visual Studio入门

    本教程将教你使用Visual Studio 2017创建 ASP.NET Core MVC web应用程序的基础知识. 安装Visual Studio 2017 和.Net Core 安装Visual ...

  8. ASP.NET Core 中文文档 第二章 指南(8) 使用 dotnet watch 开发 ASP.NET Core 应用程序

    原文:Developing ASP.NET Core applications using dotnet watch 作者:Victor Hurdugaci 翻译:谢炀(Kiler) 校对:刘怡(Al ...

  9. ASP.NET Core 中文文档 第二章 指南 (09) 使用 Swagger 生成 ASP.NET Web API 在线帮助测试文档

    原文:ASP.NET Web API Help Pages using Swagger 作者:Shayne Boyer 翻译:谢炀(kiler) 翻译:许登洋(Seay) 对于开发人员来说,构建一个消 ...

随机推荐

  1. yython爬虫基础知识入门

    Python爬虫 关注公众号"轻松学编程"了解更多. 大纲: 1.获取响应 urllib(python3)/urllib2-urllib(python2) requests(url ...

  2. 面试小问题——Object中有哪些常用方法?

    一.equals方法 Object类中的equals方法用于检测一个对象是否等于另外一个对象.Java语言规范要求equals方法具有下面的特性: (1)自反性:对于任何非空引用x,x.equals( ...

  3. VSCode--HTML代码片段(基础版,react、vue、jquery)

    起因是最近在学习前端,看的网上的demo也是在react.vue.jquery之间穿插,为了方便一键生成html模板(懒)写demo,有了以下折腾. 本人使用的前端编辑工具是vscode(方便.懒), ...

  4. php 断点续传以及100% 后台zip解压

    前台部分 <div class="col-md-12"> <div class="form-group"> <label clas ...

  5. 服务网格istio概念应知应会

    一.背景 最近架构组基于istio开发了服务网格(Service Mesh)平台,借此机会把相关的背景知识做一次学习和记录,方便回头查看. 初版的效果: 二.istio 官方手册:https://is ...

  6. 基于gin的golang web开发:使用数据库事务

    在前文介绍访问数据库时介绍了github.com/jmoiron/sqlx包,本文基于这个包使用数据库事务. defer 在使用数据库事务之前,首先需要了解go语言的defer关键字.defer是go ...

  7. 百度开源插件echarts介绍及如何使用

    前言 如果你想要用较少的代码实现比较酷炫的数据统计表,echarts是值得你考虑的一种实现方式.官网提供了很多实例供参考:http://echarts.baidu.com/examples.html. ...

  8. nginx&http 第五章 https non-fd 读写检测

    EPOLL的LT/ET 模式下的读写 从一个非阻塞的socket上调用recv/send函数, 返回EAGAIN或者EWOULDBLOCK(注: EAGAIN就是EWOULDBLOCK)从字面上看, ...

  9. Tarjan算法求割点

    (声明:以下图片来源于网络) Tarjan算法求出割点个数 首先来了解什么是连通图 在图论中,连通图基于连通的概念.在一个无向图 G 中,若从顶点i到顶点j有路径相连(当然从j到i也一定有路径),则称 ...

  10. abp(net core)+easyui+efcore实现仓储管理系统——出库管理之六(五十五)

    abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统--ABP总体介绍(一) abp(net core)+ ...