查看原文
其他

.NET Core使用gRPC打造服务间通信基础设施

DotNet 2021-09-23

(给DotNet加星标,提升.Net技能


转自:溪源More
cnblogs.com/xiyuanMore/p/11694190.html

一、什么是RPC


rpc(远程过程调用)是一个古老而新颖的名词,他几乎与http协议同时或更早诞生,也是互联网数据传输过程中非常重要的传输机制。


利用这种传输机制,不同进程(或服务)间像调用本地进程中的方法一般进行交互,而无需关心实现细节。


rpc的主要实现流程为:




二、什么时候使用RPC?



三、什么是gRPC



其官网为https://grpc.io/;开源项目地址为https://github.com/grpc/grpc。


官网提供了详细的文档说明,几乎可以开箱即用,只需简单配置就能满足你的应用需求。在开源项目中也提供了完善的各种语言实现的sample示例代码,能极大的方便开发者的使用。


在gRPC中,使用的传输协议为HTTP/2,使用的数据传输的格式为Protobuf协议。


四、什么是Protobuf


Protobuf全称为Protocal Buffers,是一种序列化协议实现,与之类似的还有thrift。这是一种与语言中立、与实现无关、可扩展的序列化数据格式,不仅仅可以用于通信协议传输过程,也同样适用于数据存储过程。它灵活高效、性能优良、更加快速和简单。在使用Protobuf的实践中,只需定义要处理数据的数据结构,就能利用Protobuf生成相关的代码。只需使用Protobuf对数据结构进行描述(IDL),即可在各种不同的语言或不同的数据流中对结构化数据进行轻松读写。



五、Proto3协议简述


当我们使用Visual Studio 2019创建一个.NET Core下的gRPC项目时,可以看到,项目会自带一个Protos\Greet.proto文件,这便是gRPC使用的Protobuf的接口描述文件,通过定义这个描述文件,可以为生成对应的服务端、客户端方法存根,让方法调用过程更加简单。


1、基本的数据类型对应关系


目前最新版本的Protobuf协议为proto3协议,在这个新版的协议中,提供了以下数据类型,可以方便的对应到我们日常使用的数据类型。





图2 使用保留字段时,会提示错误


3、枚举


允许在消息中定义枚举类型。也可以将枚举类型嵌套在message中。当使用枚举类型时,需要注意:


  • 枚举为 0 的是作为零值,当不赋值的时候,就会是零值。


  • 为了和 proto2 兼容。在 proto2 中,零值必须是第一个值。


4、消息嵌套


在proto协议中,允许嵌套组合为更加复杂的消息。


message SearchResponse {
repeated Result results = 1;
}
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}


5、定义服务(Services)


在proto中,如果需要对外提供接口方法,则需要使用Services。定义好services之后,protocol buffer编译器将使用所选语言生成服务接口代码和客户端与服务端方法存根。例如,


service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply);
}


这里就定义了一个SayHello的方法存根。该方法将返回一个名称为 HelloReply 的消息。


如果需要定义无参数方法,或返回值为 void 的方法,需要使用* google.protobuf.Empty** 对象,并在头部的命名空间中,引用默认的协议文件 google/protobuf/empty.proto. *例如:


option csharp_namespace = "TestGRPC_Client";
import "google/protobuf/empty.proto";
package Greet;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply);
rpc Listen (google.protobuf.Empty) returns (google.protobuf.Empty);//启动监听
}


同样,也可以使用 import 引用其他协议文件。






2、项目的组成结构


当我们查看这个项目时,可以看到这是一个Asp.NET Core的项目,默认的项目模板中已经集成了Asp.NET Core和gRPC.AspNetCore的组件包。



  • Protos文件夹


后缀名为proto的是基于proto3的协议文件。



  • Services文件夹


项目模板创建的默认请求文件,实现了在proto文件中定义的SayHello方法,并以异步的形式返回了对象HelloReply。



  • 其他文件


Dockerfile:模板自动创建的Dockerfile文件,后期可以基于这个文件进行docker容器的构建。


Program: 程序运行的入口。


Startup: 程序启动项,定义AspNET Core项目启动所需的各种配置信息。


在UseRouting和UseEndPoints中间,加入UseAuthentication()和UseAuthorization()代码,以便为后期身份认证和授权。



3、创建Proto文件


在Protos文件夹右键单击,创建一个空的记事本文件(快捷键为Ctrl+Shift+A),命名为helloworld.proto。然后再里面键入以下内容:


syntax = "proto3"
import "google/protobuf/empty.proto"; //需要使用空参数和空返回值时,需要使用这个默认的协议文件
option csharp_namespace="HelloWorld";
package Account;
service Account{
rpc Login (LoginModel) returns (UserModel);
rpc Logout (google.protobuf.Empty) returns (google.protobuf.Empty);
}
message LoginModel{
string userName=1;
string userPsw=2;
}
message UserModel{
string NickName=1;
string Token=2;
Date LoginDate=3;
}
message Date{
int32 Year=1;
int32 Month=2;
int32 Day=3;
int32 Hour=4;
int32 Minute=5;
int32 Second=6;
int32 FFF=7;
}


4、创建 AccountService文件


选择 Services 文件夹,并创建一个文件名为 AccountService的CSharp代码文件。并分别重载 Login 和 Logout 方法。


public class AccountService : account.accountBase
{
public override Task<UserModel> Login(LoginModel request, ServerCallContext context)
{
return base.Login(request, context);
}
public override Task<Empty> Logout(Empty request, ServerCallContext context)
{
return base.Logout(request, context);
}
}


然后再进行代码的编写。这里我们将登录后,返回一个假的 UserModel 数据。除此之外,我们还返回了错误情况下的返回模型 BadRequest 。


public class AccountService : account.accountBase
{
public override Task<StringData> Login(LoginModel request, ServerCallContext context)
{
if (request.UserName == "1234" && request.UserPsd == "1234")
{
var userModel = new UserModel
{
NickName = "测试用户",
Token = Guid.NewGuid().ToString(),
};
return Task.FromResult(new StringData() { Data = Newtonsoft.Json.JsonConvert.SerializeObject(userModel) });
}
else
{
var BadRequest = new BadRequest { ErrorCode = 1, ErrorDescription = "用户名或密码错误" };
return Task.FromResult(new StringData() { Data = Newtonsoft.Json.JsonConvert.SerializeObject(BadRequest) });
}
}
public override Task<Empty> Logout(Empty request, ServerCallContext context)
{
return Task.FromResult(new Empty());
}
}
public class BadRequest
{
public int ErrorCode { get; set; }
public string ErrorDescription { get; set; }
}


八、客户端开发


客户端,是一个基于 .NET Core 的控制台程序。在这个控制台中,我们可以实现下面功能:


  • 通过输入命令 1 调用登录方法;


  • 输入命令 2 调用登出方法。


1、创建项目、引用依赖包


创建一个基于.NET Core的一个控制台程序,并使用 Nuget 安装组件包



2、创建协议文件


将在服务端开发中创建的 Protos 文件夹拷贝到客户端程序中。



并使用记事本对项目文件【HelloWorld.Client.csproj】进行编辑, 将Protobuf 文件的GrpcServices属性设置为 "Client"。


完成这些操作,编译完成,即可自动生成客户端与服务端连接的客户端方法存根。


3、编写客户端方法


创建一个单独的类文件,用来编写客户端调用方法。这个类文件名称为 AccountClientImpl。代码如下:


using Grpc.Net.Client;
using System;
using System.Collections.Generic;
using System.Text;
using static HelloWorld.Greeter;
using System.Threading.Tasks;
namespace HelloWorld.Client
{
public class AccountClientImpl
{
private readonly GrpcChannel _grpcChannel;
private readonly Account.AccountClient _accountClient;
public AccountClientImpl(GrpcChannel grpcChannel, Account.AccountClient accountClient)
{
_grpcChannel = grpcChannel;
_accountClient = accountClient;
}
public void Login()
{
var result = _accountClient.Login(new LoginModel() { UserName = "1234", UserPsd = "1234" });
Console.WriteLine(result.Data);
}
public void Logout()
{
var empty = new Google.Protobuf.WellKnownTypes.Empty();
_accountClient.Logout(empty);
}
}
}


然后再修改 Program.cs 文件,用来调用上述方法。在这个方法中,如果输入1,则执行登录方法;输入2,则执行退出方法。


class Program
{
static void Main(string[] args)
{
var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new Account.AccountClient(channel);
AccountClientImpl accountClientImpl = new AccountClientImpl(channel, client);
if (Console.ReadLine() == "1")
{
accountClientImpl.Login();
}
else if (Console.ReadLine() == "2")
{
accountClientImpl.Logout();
}
Console.ReadKey();
}
}


这样就完成了我们的代码编写。


将客户端与服务端运行起来,然后在客户端代码中输入数字 1 ;即可获得我们想要的结果。



九、协议与项目分离


在传统的开发过程中,由于客户端和服务端需要维护两套内容完全相同的 proto 协议文件,略显臃肿,因此我们可以通过相应的手段,将对应的文件进行分离,便于后期的维护。


1、移动文件


将服务端中的Protos文件移动到上一级目录。



2、修改项目文件中的Proto文件


服务端修改为:


<ItemGroup>
<Protobuf Include="..\Protos\*.proto" GrpcServices="Server" />
<Content Include="@(Protobuf)" LinkBase="" />
</ItemGroup>


客户端修改为:


<ItemGroup>
<Protobuf Include="..\Protos\*.proto" GrpcServices="Client" />
<Content Include="@(Protobuf)" LinkBase="" />
</ItemGroup>



如果觉得这样的展示效果不太美观,也可以将proto文件移动到Protos目录下。


3、重新编译


完成协议文件分离,即可对项目进行编译。


总结


在这个教程中,我们从PRC开始讲起,简单介绍了与gRPC相关的技术栈,练习了使用 gRPC 进行服务端和客户端程序开发的全过程,希望大家能获得收获。


推荐阅读

(点击标题可跳转阅读)

ASP.NET Core 3.0 gRPC 双向流

ASP.NET Core 3.0 使用gRPC

GRPC与.NET Core


看完本文有收获?请转发分享给更多人

关注「DotNet」加星标,提升.Net技能 

好文章,我在看❤️

: . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存