ASP.NET Core 使用 gRPC 初探

(RPC通讯示意图)

为什么突然说到gRPC呢,其实以前就想说一说这个东西,也想尝试使用一下,一直没有机会,一直看我公众号的小伙伴肯定都知道,这几天一直在录制一个《eShopOnContainer微服务架构》系列,现在已经是8期了,里边涵盖了使用ASP.NETCore开发微服务的常用的基本的知识技能,具体的你可以看我的视频就行,B站也同步更新。

既然要说到了微服务,那肯定就离不开服务间调用,自然而然的就联系到了常用的一个框架——gRPC了,那今天就简单的说一说这个框架,也算是一个刚入门的,比较简单,后边我也会持续跟进讲解。

 划 

 重 

 点 

gRPC是什么?

用官网的一句话就是:A high-performance, open-source universal RPC framework。

要说gRPC,那就先说下什么的RPC框架,所谓RPC(remote procedure call 远程过程调用)框架实际是提供了一套机制,使得应用程序之间可以进行通信,而且也遵从server/client模型。使用的时候客户端调用server端提供的接口就像是调用本地的函数一样。

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

可能第一次看到这种通信框架比较陌生,介绍的也比较官方和抽象,这里说一下另一个常用的服务间通讯的方案,你可能就明白了,那就是RESTFul风格的API,想必每个人都用过RestfulAPI吧,这里就先简单说下RestfulAPI,如果这个还不是很理解的话,建议死记硬背。

1、REST,即Representational State Transfer的缩写。直接翻译的意思是"表现层状态转化"。
2、它是一种互联网应用程序的API设计理念:URL定位资源,用HTTP动词(GET,POST,DELETE,PUT,DETC)描述操作,比如只需要知道/api/blog,你就知道了他的常见的CURD多种操作。

3、简单来说就是url地址中只包含名词表示资源,使用http动词表示动作进行操作资源,软件和网络这两个领域一定程度上结合起来

4、之所以灵活,是因为他很少参与业务逻辑,只定义资源操作


看完了RestfulAPI,你应该就能明白gRPC是干什么的了吧。

那两者有什么区别呢,平时在前后端分离或者移动端需要后端api的场景下,经常使用Restful丰富的API,既然大家已经习惯并熟悉了Restful,为何还用gRPC呢?

PS:下边的内容我基本是摘抄于官网和网络,文末有参考连接,今天主要是介绍下如何操作代码,文字讲解不是重点。

为什么要使用gRPC?

问题:既然是server/client模型,那么我们直接用restful api不是也可以满足吗,为什么还需要RPC呢?

我这里简单说明下优缺点和比较,说说到底使用gRPC有什么好处。

gRPC 和 Restful API

gRPC和Restful API都提供了一套通信机制,用于server/client模型通信,而且它们都使用http作为底层的传输协议(严格地说, gRPC使用的http2.0,而Restful api则不一定)。

不过gRPC还是有些特有的优势,如下:

1、gRPC可以通过protobuf来定义接口,从而可以有更加严格的接口约束条件。

2、通过protobuf可以将数据序列化为二进制编码,这会大幅减少需要传输的数据量,从而大幅提高性能。

3、gRPC可以方便地支持流式通信.

场景与好处????

1、需要对接口进行严格约束的情况。

比如我们提供了一个公共的服务,很多人,甚至公司外部的人也可以访问这个服务,这时对于接口我们希望有更加严格的约束,我们不希望客户端给我们传递任意的数据,尤其是考虑到安全性的因素,我们通常需要对接口进行更加严格的约束。这时gRPC就可以通过protobuf来提供严格的接口约束。

2、对于性能有更高要求的轻量级微服务。

有时我们的服务需要传递大量的数据,而又不希望影响到我们的性能,这个时候也可以考虑gRPC服务,因为通过protobuf我们可以将数据压缩编码转化为二进制格式,通常传递的数据量要小得多,而且通过http2我们可以实现异步的请求,从而大大提高了通信效率。

同时,更适应于网络受限的环境,使用 Protocol Buffers二进制序列化消息,该序列化始终小于等效的JSON消息,对网络带宽需求比JSON小。

 

3、需要对接多种语言的微服务的情况。

比如我们公司的项目,有JAVA组,有Python组,或者.NETCore组别,每个组当然负责各自独立的子服务部分,那就需用用到不同语言之间的服务调用问题,不希望出现兼容性问题。这个时候就用到了gRPC了,它协定优先 API 开发,默认使用协议缓冲区,允许与语言无关的实现。可用于多种语言的工具,以生成强类型服务器和客户端。gRPC工具支持所有流行的开发语言,使gRPC成为多语言开发环境的理想选择。

4、需要处理流式处理请求或响应的点对点实时服务

gRPC用更小的网络带宽,又支持客户端、服务器和双向流式处理调用,更好的帮助处理流式请求。

(理论上通过http2.0就可以使用streaming模式, 但是通常web服务的Restful api似乎很少这么用,通常的流式数据应用如视频流,一般都会使用专门的协议如HLS,RTMP等,这些就不是我们通常web服务了,而是有专门的服务器应用。)

也并不是十全十美的????

任何开发工具或者项目框架都不是十全十美的,就算是K8s、微服务或者DDD这么火热的技术也并不是无脑就上的,gRPC框架也有一定的弊端,或者至少是某些场景下是不适合的:

1、浏览器可访问的API。

 浏览器不完全支持gRPC。虽然gRPC-Web可以提供浏览器支持,但是它有局限性,引入了服务器代理

2、广播实时通信

gRPC支持通过流进行实时通信,但不存在向已注册连接广播消息的概念

3、进程间通信

进程必须承载HTTP/2才能接受传入的gRPC调用,对于Windows,进程间通信管道是一种更快速的方法。

如何.NETCore上使用gRPC?

关于如何在ASP.NETCore上使用gRPC,这里有两种方法,第一是直接创建gRPC模板项目,第二个就是在在ASP.NETCore项目上创建gRPC服务。其实这两个原理和操作流程都是差不多的,我这里都说一下吧。

通过模板创建gRPC服务

打开VS2019(版本至少16.3+),新建项目,搜索"gRPC",就能看到一个选项,

点击下一步,填写好项目名称和项目地址以后,点击创建,

然后可以看到NetCore版本是3.1,然后不勾选Docker,点击创建。

等待新建好项目,就可以看到默认的文件是这样的,其实和我们创建ASP.NETCore项目是很相似的,如果说真的不一样,就是依赖包和多了一个Protos的文件夹,那下边我们来一一看看都是怎么作用的:

1、依赖包

Grpc.AspNetCore是gRPC结合ASP.NETCore封装的一个类库,其中很重要的是下边的两个依赖包,第一个就是Protobuf,第二个就是Tools,从名字上应该都能大概猜出来是干啥的,肯定有一个是解析protobuf文件的,一个是工具包,负责一些操作的。

2、Protos文件夹

在文章的开头我们已经说过了,gRPC很重要的一点,就是在请求和相应的的时候需要用到一个.proto的文件,用来定义服务和提供参数已经响应的参数。

默认的内容是这样的:

  // 语法结构,使用pb3
  syntax = "proto3";
  // 定义命名空间,一般是项目名或者解决方案名
  option csharp_namespace = "GrpcService1";
  // 定义服务的包
  package greet;


  // 定义具体的服务
  service Greeter {
    // 定义某一个方法API,格式是:rpc 方法名(请求参数对象名) returns(返回参数对象名)
    rpc SayHello (HelloRequest) returns (HelloReply);
  }


  // 定义请求的对象名
  message HelloRequest {
    // 有一个属性字段是name
    string name = 1;
  }


  // 定义返回的对象名
  message HelloReply {
    // 有一个返回的字段是message
    string message = 1;
  }


可以看到虽然是扩展名是.proto的文件,但是语法结构很像一个.cs文件,语法上也类似,当然只不过是类似,具体的意思我已经在上边注释了,你看看就能明白。

你可能会好奇,那我定义好了这一个文件,怎么来使用呢,别着急,分成两步,咱们先说第二步定义具体服务,第一步先卖个关子。

3、GreeterService服务

上边我们定义好了proto文件,下边就需要针对这个配置,设计服务了,因为proto仅仅是定义了服务,还没有具体的内容,那很简单,就直接看代码吧。

 /// <summary>
 /// 根据.proto定义具体的服务
 /// GreeterService可以任意定义
 /// Greeter.GreeterBase 根据.proto文件中定义的规则来
 /// </summary>
 public class GreeterService : Greeter.GreeterBase
 {
     // 和ASP.NETCore一样,可以使用依赖注入和服务
     private readonly ILogger<GreeterService> _logger;
     public GreeterService(ILogger<GreeterService> logger)
     {
         _logger = logger;
     }


     /// <summary>
     /// 重写 设计对应的多个接口
     /// 一般都是异步处理
     /// </summary>
     /// <param name="request"></param>
     /// <param name="context"></param>
     /// <returns></returns>
     public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
     {
         return Task.FromResult(new HelloReply
         {
             Message = "Hello " + request.Name
         });
     }
 }


我也同样的把注释都写上了,其实内容都是很简单的,我们都已经用了ASPNETCore这么久了,肯定都一样都能看明白,有两个问题肯定你会问:

第一、定义服务我明白,但是继承的父类Greeter.GreeterBase是如何处理的呢?

这个就是我第二步说完.proto文件的时候卖的那个关子,我们定义好了.proto文件后,系统会自动给我们创建生成服务、客户端和消息(表示传递的数据)的C# Class,但是需要一个操作:

右键项目,编辑项目文件,可以看到有一个配置:

  <ItemGroup>
    <Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
  </ItemGroup>

这个是官方默认模板已经创建好的,是一个Server类型的服务,Include对应的文件,然后项目就能自动给我们创建好了父类,你可以从obj文件夹看到:

这个时候,就可以继承了。

第二、如何重写对应的方法呢?

很简单,直接针对当前的类型,alt+enter,在智能提示里,找到重写,就可以看到要重写的接口了:

4、appsettings.json

注意这里别之前不一样的地方,就是定义了一个节点:

"Kestrel": {
"EndpointDefaults": {
"Protocols": "Http2"
    }
  }

在上边我们说过,gRPC 需要 HTTP/2。 适用于 ASP.NET Core 的 gRPC 验证 HttpRequest.Protocol 为 HTTP/2。

Kestrel 在大多数新式操作系统上支持 HTTP/2。默认情况下,Kestrel 终结点配置为支持 HTTP/1.1 和 HTTP/2 连接。

5、Startup.cs

其他的文件内容都类似,我就不多说了,我们都知道,要用一个服务,就需要注册这个服务,那就肯定需要是Startup里,

 // 注册grpc服务
 services.AddGrpc();


 // 在结点路由里配置指定的服务
 app.UseEndpoints(endpoints =>
 {
     endpoints.MapGrpcService<GreeterService>();


     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");
     });
 });


项目启动,

可以看到,只有https安全协议的,因为上边已经说过了使用gRPC必须是http/2的,同样也是需要https安全协议的。

到这里就没有问题了,说完了系统默认模板创建的方案,那现在我们不用这个方案,尝试一下,如果已经创建好了一个NetCore的API项目,比如我的Blog.Core,如何在这个基础上,创建gRPC服务呢?

基于ASP.NETCore项目创建

因为上边我们已经讲完了对应的内容和注意事项,为了篇幅不罗嗦,我就直接创建,看看是否真的可以:

还是在当然解决方案,创建一个netcore的api项目,然后添加三个nuget包:

<PackageReference Include="Google.Protobuf" Version="3.11.2" />
<PackageReference Include="Grpc.AspNetCore.Server" Version="2.25.0" />
<PackageReference Include="Grpc.Tools" Version="2.25.0" PrivateAssets="All" />  

接着添加helloworld.proto文件,配置.csproj项目配置,包含当前的.proto文件,创建HelloWorldservice.cs服务类,继承刚刚创建好的父类Hello.HelloBase,最后,注册服务,配置中间件,相应的操作可以看下边的视频:

现在我们已经定义了好了server端的服务,那如何发起调用呢,需要一个client客户端。

如何发起调用?

1、创建一个netcore的控制台

还是在该解决方案中,添加一个控制台项目

然后添加三个依赖包:

<ItemGroup>
  <PackageReference Include="Google.Protobuf" Version="3.13.0" />
  <PackageReference Include="Grpc.Net.Client" Version="2.32.0" />
  <PackageReference Include="Grpc.Tools" Version="2.33.1">
    <PrivateAssets>all</PrivateAssets>
    <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
  </PackageReference>
</ItemGroup>

注意,这里我们并没有添加其他的项目引用!

2、把Hello.proto拷贝到控制台

这个很简单,只需要直接把文件夹和文件直接拖动过去就行了。

然后配置下.csproj文件,修改下gprc的服务类型,一定是client:

<ItemGroup>
  <Protobuf Include="Proto\helloworld.proto" GrpcServices="Client" />
</ItemGroup>

3、发起调用

我这里就直接写代码了

 class Program
 {
     static async System.Threading.Tasks.Task Main(string[] args)
     {
         // 创建通道
         var channel = GrpcChannel.ForAddress("https://localhost:5001");
         // 发起客户端调用
         var client = new Hello.HelloClient(channel);
         // api请求,传递参数
         var response = await client.SayHelloAsync(new HelloRequest { Name = "World" });


         Console.WriteLine("Greeting: " + response.Message);


     }
 }

大概意思就是这样的,应该能够看懂的。

运行我们的gRPC服务,也就是运行core的webapi程序,然后运行客户端控制台:

看到没有,我们并没有在控制台去引用我们的gRPC服务端的代码,只需要一个.proto文件,就能够像调用方法一样,去调用其他服务端项目的服务,这就是很大的直观上的好处。

当然好处还有很多的,比如什么是流式,如何实现服务间调用,如何网关配置等等等等,咱们下次再见吧。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值