.net6使用gRPC
一、前言
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