一、前言
gRPC 是一種與語言無關的高性能遠程過程調用 (RPC) 框架。
gRPC 的主要優點是:
- 現代高性能輕量級 RPC 框架。
- 協定優先 API 開發,默認使用協議緩沖區,允許與語言無關的實現。
- 可用於多種語言的工具,以生成強類型服務器和客戶端。
- 支持客戶端、服務器和雙向流式處理調用。
- 使用 Protobuf 二進制序列化減少對網絡的使用。
這些優點使 gRPC 適用於:
- 效率至關重要的輕量級微服務。
- 需要多種語言用於開發的 Polyglot 系統。
- 需要處理流式處理請求或響應的點對點實時服務。
引用自微軟官方文檔: https://docs.microsoft.com/zh-cn/aspnet/core/grpc/?view=aspnetcore-6.0
二、創建gRPC服務
本文使用VS2022+.net6進行演示。
1、通過模板創建gRPC服務
2、文件介紹
greet.proto:協議文件,定義通信的數據結構和服務接口。
GreeterService:服務類,繼承的 Greeter.GreeterBase
是根據proto文件自動生成的
Startup.cs:將 gRPC服務添加到了終結點路由中
csproj:項目文件,添加了proto文件引用
三、創建gRPC客戶端並調用
1、添加名稱為GrpcClient的控制台程序
2、添加Nuget包引用:Grpc.Net.Client、Google.Protobuf、Grpc.Tools
3、將服務的 proto 文件夾復制到客戶端。復制后會自動在工程文件中添加引用,注意將GrpcServices值改為Client
4、Program.cs中添加客戶端調用代碼
var channel = GrpcChannel.ForAddress("https://localhost:5001"); // 服務端地址
var client = new Greeter.GreeterClient(channel);
var reply = await client.SayHelloAsync(new HelloRequest { Name = "張三" });
Console.WriteLine("Greeter 服務返回數據: " + reply.Message);
Console.ReadKey();
5、運行服務端和客戶端,可以看到客戶端接受到了服務端返回的數據
四、創建自己的gRPC服務並調用
我們以注冊登錄為例。
1、在GrpcService > Protos文件夾新建user.proto文件
定義了注冊、登陸、退出、獲取所有用戶四個方法。
syntax = "proto3";
option csharp_namespace = "GrpcService";
package user;
// 需要使用空參數和空返回值時,需要使用這個協議文件
import "google/protobuf/empty.proto";
service User {
// 注冊
rpc Register (UserDTO) returns (CommonResult);
// 登陸
rpc Login (LoginDTO) returns (LoginResult);
// 退出
rpc Logout (LogoutDTO) returns (CommonResult);
// 獲取所有用戶
rpc GetAllUser (google.protobuf.Empty) returns (AllUser);
}
message UserDTO {
string account = 1;
string pwd = 2;
string name = 3;
int32 age = 4;
}
message LoginDTO{
string accont = 1;
string pwd = 2;
}
message LoginResult{
string token = 1;
}
message LogoutDTO{
string token = 1;
}
message CommonResult {
string code = 1;
string message = 2;
}
message AllUser{
// repeated 表示集合
repeated UserDTO userList = 1;
}
注意:Protobuf支持的類型與C#類型是有差異的,對照關系詳見:https://docs.microsoft.com/en-us/aspnet/core/grpc/protobuf?view=aspnetcore-6.0#scalar-value-types
2、在GrpcService > Services文件夾新建UserService.cs文件
實現proto中定義的方法
using Google.Protobuf.WellKnownTypes;
using Grpc.Core;
namespace GrpcService.Services
{
public class UserService : User.UserBase
{
private readonly ILogger<UserService> _logger;
public UserService(ILogger<UserService> logger)
{
_logger = logger;
}
// 存儲注冊用戶
private static IList<UserDTO> _userDTOs = new List<UserDTO>();
/// <summary>
/// 注冊方法實現
/// </summary>
/// <param name="userDTO"></param>
/// <param name="context"></param>
/// <returns></returns>
public override Task<CommonResult> Register(UserDTO userDTO, ServerCallContext context)
{
_userDTOs.Add(userDTO);
return Task.FromResult(new CommonResult
{
Code = "2000",
Message = "注冊成功。"
});
}
/// <summary>
/// 登陸方法實現
/// </summary>
/// <param name="userDTO"></param>
/// <param name="context"></param>
/// <returns></returns>
public override Task<LoginResult> Login(LoginDTO userDTO, ServerCallContext context)
{
// TODO...
return Task.FromResult(new LoginResult
{
Token = "TestToken"
});
}
/// <summary>
/// 退出方法實現
/// </summary>
/// <param name="userDTO"></param>
/// <param name="context"></param>
/// <returns></returns>
public override Task<CommonResult> Logout(LogoutDTO userDTO, ServerCallContext context)
{
// TODO...
return Task.FromResult(new CommonResult
{
Code = "2000",
Message = "退出成功。"
});
}
/// <summary>
/// 獲取所有用戶實現
/// </summary>
/// <param name="empty"></param>
/// <param name="context"></param>
/// <returns></returns>
public override Task<AllUser> GetAllUser(Empty empty, ServerCallContext context)
{
var allUser = new AllUser();
allUser.UserList.Add(_userDTOs);
return Task.FromResult(allUser);
}
}
}
3、將UserService添加到終結點路由
4、將GrpcService>Protos>user.proto文件拷貝到GrpcClient>Protos文件夾
5、在GrpcClient>Program.cs文件加入調用代碼
里面我用到了Newtonsoft.Json做序列化,需要安裝Newtonsoft.Json包。
var channel = GrpcChannel.ForAddress("https://localhost:7145"); // 服務端地址
var client = new User.UserClient(channel);
var reply = await client.RegisterAsync(new UserDTO { Account = "zhangsan", Pwd = "123", Name = "張三", Age = 18 });
Console.WriteLine("User 服務 RegisterAsync 方法返回數據: " + JsonConvert.SerializeObject(reply));
Console.ReadKey();
var reply1 = await client.RegisterAsync(new UserDTO { Account = "lisi", Pwd = "123", Name = "李四", Age = 20 });
Console.WriteLine("User 服務 RegisterAsync 返回數據: " + JsonConvert.SerializeObject(reply1));
Console.ReadKey();
var reply2 = await client.GetAllUserAsync(new Google.Protobuf.WellKnownTypes.Empty());
Console.WriteLine("User 服務 GetAllUserAsync 返回數據: " + JsonConvert.SerializeObject(reply2));
Console.ReadKey();
var reply3 = await client.LoginAsync(new LoginDTO() { Accont = "zhangsan", Pwd = "123" });
Console.WriteLine("User 服務 LoginAsync 返回數據: " + JsonConvert.SerializeObject(reply3));
Console.ReadKey();
var reply4 = await client.LogoutAsync(new LogoutDTO() { Token = "TestToken" });
Console.WriteLine("User 服務 LogoutAsync 返回數據: " + JsonConvert.SerializeObject(reply4));
Console.ReadKey();
至此服務和調用都已完畢,下面開始運行
6、先運行GrpcService,再運行GrpcClient
可以看到返回結果正常
六、總結
本文列出.net6環境下使用gRPC的簡單示例。demo地址:https://github.com/gaozejie/gRPCTest