ASP.NET Core 使用 gRPC 初探

标签: 网络  java  编程语言  python  http

(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文件,就能够像调用方法一样,去调用其他服务端项目的服务,这就是很大的直观上的好处。

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

版权声明:本文为sD7O95O原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/sD7O95O/article/details/109375585

智能推荐

更改springboot启动拼成的字母

1.更改springboot启动拼成的字母 其实很好改,只需要在resources下新建一个txt文件就可以,命名为banner.txt,那这种字符该怎么拼出来呢,下面推荐一个网址,有这种工具 传送门 2.集成...

Node.js安装配置

好久都没更新博客了,今天心血来潮,决定是时候更新一篇了,首先我们来认识一下node.js。 什么是node.js? 简单的说 Node.js 就是运行在服务端的 JavaScript。 Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台。 Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的...

RocketMQ之双Master集群搭建笔记记录

一:RocketMQ双master集群部署 服务器环境(我采用的虚拟机,centos6 .5【特别注意:安装的虚拟机centos系统一定得是64位的,32位的会启动不起来。即便起来了也会有很多问题,深坑勿踩】)  ip       用户名    密码        角色     模式 192.168.197.101   root        nameServer1,brokerServer1  ...

蓝桥杯试题集-基础练习题-数列特征(Java)

//做题笔记,仅自己看得懂 题目: 正确姿势:...

多线程爬取4k超高清美图壁纸

多线程爬取4k美图壁纸 前言:看完此篇文章你可以更加深入的了解多线程的使用,并且最重要的你能够下载你自己想要的超高清4k壁纸 爬取结果: 1. 分析网站 要爬取的url :http://pic.netbian.com/ a) 判断网页是动态加载还是静态加载页面。右击查看网页源代码,按Ctrl + f在源代码中搜索网站的详情页地址,从而判断整个网页是静态加载的 b) 明确爬取的目标。我们要爬取的目标...

猜你喜欢

elementUI-添加自定义图标

elementui的小图标有限,跟UI给的不一样,这个时候咋办呢?百度走起。。。。参考了两篇博主分享的 自定义elementui中的图标 和 建立图标库,这里主要用到第一种 实际中: elementUI导航栏 具体代码: 汉字转换Unicode编码: 直接打开控制台: 汉字.chatCodeAt().toString(16); 然后回车; 至于三角形的图标,我直接把箭头的 unicode 值改成了...

[Linux]——文件缓冲区

文件缓冲区 提到文件缓冲区这个概念我们好像并不陌生,但是我们对于这个概念好像又是模糊的存在脑海中,之间我们在介绍c语言文件操作已经简单的提过这个概念,今天我们不妨深入理解什么是文件缓冲区。 为什么需要文件缓冲区 当我们在程序中写下一条printf语句时,我们希望将这条语句的内容打印到屏幕上。但是如果你将语句放在循环中,难道你执行一次循环那么操作系统就要打印一次这条数据么?答案当然不是 我们对于程序...

基于FPGA的IIC协议详解——EEPROM控制器(1)

IIC协议举例 常用IIC协议使用地方 常见IIC协议的注意点 24LC64芯片读写命令的时序图 eeprom控制器的系统框图 时序图设计 代码设计 EEPROM控制器测试模块的代码 结束语 常用IIC协议使用地方 熟悉一个协议一定要知道这个协议应该用到什么地方,IIC协议作为飞利浦公司定义的一个慢速传输协议,常用于: 1、芯片寄存器的配置; 2、eeprom的读写; 本次实验我们将使用eepro...

ssm 工程简易搭建(idea 工具下)

现在都流行springboot了,大部分都给你配合了了,可以快速开发。但是今天我们回顾一下ssm的搭建,其实也挺简单的,比刚入门那会觉得简单多了。 这个先讲几点: 1.开始搭建前,idea 自己要配置maven 和tomcat和jdk ,这里就不讲了。 2.为了简化配置,我们按照“约定优于配置”的原则(这种原则在sprigboot项目里面特别明显),启动项目时,项目有个文件...

oracle简单视图read only、check option和默认权限的异同(相对于DML语句)

视图分为俩类: 简单视图:   视图所代表的sql中如果没有group by语句,   没有组函数,查询的只有一张表,那么这样的视图就是简单视图. 复杂视图   视图所代表的sql中如果有group by语句,   或者有组函数,或者查询的是多张表,那么这样的视图就是复杂视图. s_emp表格属性 默认权限 1.可以插入不满足where条件的数据,原表也会更...