gRPC in ASP.NET Core 3.x - gRPC 简介

gRPC的结构 

生 成 的 
Client 
代 码 
传 输 协 议 
Protocol Buffers 
生 成 的 
Se rve r 
代 码

在我们搭建gRPC通信系统之前,首先需要知道gRPC的结构组成。 

首先,需要一个server(服务器),它用来接收和处理请求,然后返回响应。 

既然有server,那么肯定有client(客户端),client的作用就是向server发送请求,具体就是生成一个请求,然后把它发送到server,然后等待server的响应。 

但是它们不必是一对一的关系,在整个系统里,可以有多个server,也可以有多个client。根据实际情况,一个应用程序可能是gRPC的server,也可能是gRPC的client,也可能两者都是。 

 

gRPC里面server和client并不是直接通信的,gRPC可以使用protocol buffer定义的消息来生成代码。 

当client发送请求的时候,它会和server端生成的代码进行交互;同样在client端也有生成的代码,client端生成的代码负责提供一个隧道,这个隧道被用来吧client端生成的消息发送给server。 

因为server和client两端都有生成的代码,所以如何序列化和反序列化,以及如何进行来回的传输等细节,我们都可以不了解。 

 

但是为了让server和client端来回传输通信,我们还需要一个协议,传输协议就负责把消息来回的传递。所以它并不需要懂得这些消息的内容,生成的代码会负责理解这些消息,但是传输协议需要负责把消息从一端传递到另一端。 

目前,好像gRPC只能使用Protocol Buffer这一个传输协议。但是gRPC在设计的时候,它的传输层是可插拔的,所以如果我们想把Protocol Buffer使用某种JSON或XML的协议替换掉,是可行的。如果你有特定的需求使用Protocol Buffer无法实现的话,那么你也可以创建自己的传输协议。 

 

设计步骤 

总共应该分三步。设计原则是从里到外(看上面结构图)。 

定 义 消 息 
开 发 
生 成 代 码 
Client/Server

所以: 

  1. 首先我们应该定义消息(message)。这些消息使用Protocol Buffer来进行定义 

  2. 定义完消息,我们使用Proto-c编译器来生成server和client端的代码,它们会负责把消息在两端之间来回传递 

  3. 现在,我们就可以写client和server了。 

 

 

gRPC 生命周期 

 

创 建 隧 道 
创 建 Client 
Client 发 送 请 求 
( 可 选 携 带 metadata) 
( 可 选 ) 
server 发 送 
Metadata 
发 送 / 接 收 
消 息

 

gRPC或者RPC的生命周期可以参考上图 

首先,需要创建一个隧道,该隧道会包装实际用来传输消息的线路协议。 

例如如果我们的server和client之间使用HTTP/2协议,那么这个隧道就会包装一个server和client之间的TCP连接。 

这些隧道的优点是,它们只需要创建一次。一旦隧道创建了,你就可以在你应用程序的生命周期之内持续的使该隧道来回发送消息。 

 

隧道建立好之后,就该创建client了。client也是可以复用的,不必没有个rpc调用都重建client。但是在调用之前,我们需要把client建立好。 

现在client进入隧道,这个client通常是提供给我们的,我们不需要自己实现任何代码。使用Proto-c编译消息定义生成的代码将会给我们提供client需要的一切。我们只需要提供隧道即可。 

 

client创建好之后,client就准备好给server发送请求了。这一步是必须的,gRPC无法让server端初始化请求发送给client端,请求都是client端初始化的。 

但是client初始化请求之后,server端是可以发送多个响应回来的,这个以后再说。这时,client可以随着请求发送一些metadata(元数据),这些metadata是关于请求的,但不是请求对象本身。 

 

请求被发送以后呢,server可以(但不是必须)把metadata返回。所以,你实际上可以在client和server之间进行这种“预约对话”。client可以发送一些metadata,然后server可以把一些metadata发送回来,这些都是发生在server开始处理请求之前。 

 

生命周期的最后一部分就是发送和接收消息。就以简单的情况为例,现在server就应该把响应发送回去了,因为client已经发送了请求,所以响应就是要返回。 

 

注意,关于metadata需要注意的是,gRPC内置的身份认证系统是用来做client和server的身份认证的。 

但是这个metadata也为你提供了检查实际用户身份的机制。所以,如果你需要认证或者授权实际用户,就需要在RPC请求这个级别来实现。也就是在这里。 

如果是client和server的身份认证,以后再写。。 

 

身份认证

这里指的不是用户的身份认证,而是指多个server和client之间,它们如何识别出来谁是谁,并且能安全的进行消息传输。
在身份认证这方面,gRPC一共有4种身份认证的 机制:
  • 不采取任何措施的连接,也就是不安全的连接。
  • TLS/SSL 连接。
  • 基于 Google Token 的身份认证。
  • 自定义的身份认证提供商。 
 
针对第一种不安全的连接,client和server默认将会采用HTTP/1,没有其他特殊的安全措施,也就是使用明文在网络上传输。所以尽量别用不安全连接,容易被截获。 但是不安全连接却不需要其他任何特殊的处理,不需要CA证书等等,所以适合于快速建立gRPC的情况,后期再添加其他安全措施也行。
 
如果采用了TLS/SSL,那么想截获传输的消息就比较困难了,而且默认也是使用HTTP/2。HTTP/2的很多实现根本就不支持不安全连接,所以gRPC也不会尝试使用这些不安全连接,但是如果gRPC发现它是在一个安全的连接上面,它就会尝试把这些连接升级到HTTP/2,这时你的消息的传输速度就会变得更快,因为HTTP/2协议的效率更高。
但是需要注意的是,client会对这些证书进行验证,所以不能因为这是一个安全连接,那么就啥也不用干。 client会去检查证书的授权来确保证书的真实性。所以如果你使用的是生成的证书,那么你还需要在client端做一些额外的工作来确保client能够识别出这些server证书的合理性。
 
当使用基于 Google Token 的身份认证方式时,需要注意的是它需要安全的连接,所以你可以把这种认证方式想象为在SSL/TLS上面的一层。所以你需要有安全连接,在此之上,你才能使用基于Google Token的认证方式。 
 
最后一个是自定义身份认证,你可能想要的是OAuth 2.0这种认证协议,但是gRPC并没有自带OAuth 2.0协议,但是还是有很多用于不同语言的插件可以支持OAuth 2.0的。所以如果有需要的话,可以去官网查一下。
你也可以自己实现一个身份认证协议,但是自己实现的肯定是和语言有关的,而且gRPC也会尽量配合这种语言。所以不是让你的认证协议像gRPC这样工作,而是让你尽量用该语言惯用的方式。所以使用C#开发一个身份认证提供商和使用Go语言可能会不太一样。这块的详细信息需要去官网查阅。
 

消息传输类型

gRPC的消息传输类型有4种。
  • 第一种是一元的消息,就是简单的请求--响应。
  • 第二种是server streaming(流),server会把数据streaming回给client。
  • 第三种是client streaming,也就是client会把数据streaming给server。
  • 最后是双向streaming。
 

 一元消息

这里有一个server,一个client。gRPC从client发送请求到server开始,然后server做一些处理,生成一个响应并返回。所以在这次远程调用里,有一个请求,一个响应。
这个Protocol Buffer的消息格式大约是这样:
rpc 方法名(请求类型) returns(响应类型) 
在这里,即使请求的时候不需要带有数据(参数),你仍然需要传递一个空的请求类型的对象。所以在gRPC里就必须有请求类型和响应类型,因为gRPC不知道你带没带数据,而且未来你有可能需要带上 数据。
 

Server Streaming