.net core grpc 实现通信(一)
现在系统都服务化,.net core 实现服务化的方式有很多,我们通过grpc实现客户端、服务端通信。
grpc([https://grpc.io/](https://grpc.io/))是google发布的一个开源、高性能、通用RPC(Remote Procedure Call)框架,使用HTTP/2协议,支持多路复用,并用ProtoBuf作为序列化工具,提供跨语言、跨平台支持。下面以.net core演示如何使用grpc框架实现通信。
软件版本
.net core:2.0
grpc:1.11.0
#### 项目结构 ####
InstallGrpc .net framework类库 只为得到生成协议代码工具protoc.exe、grpc_csharp_plugin.exe,没有其他作用,如果已有工具,可以不用
Snai.GrpcClient 客户端 .net core 2.0控制台程序
Snai.GrpcService.Hosting 服务端宿主 .net core 2.0控制台程序
Snai.GrpcService.Impl 协议方法实现 .net standard 2.0类库
Snai.GrpcService.Protocol 生成协议方法 .net standard 2.0类库
![Image](/sitedata/image/dotnet_1_1.png)
#### 运行结果 ####
服务端
![Image](/sitedata/image/dotnet_1_2.png)
客户端
![Image](/sitedata/image/dotnet_1_3.png)
客户端调用服务端求和方法成功。
#### 项目实现 ####
**一、服务端**
新建Snai.GrpcService解决方案
1、编写协议
新建 Snai.GrpcService.Protocol协议类库项目,在 依赖项 右击 管理NuGet程序包 浏览 找到 Grpc.Core 版本1.11.0,Google.Protobuf 版本3.5.1 包下载安装
在项目根目录下新建一个 msg.proto 文件,打开 msg.proto 文件,在其中编写基于proto3语言的协议代码,用于自动生成到各语言协议,如果需要更深入的学习proto3语言可以打开该网站Proto3语言指南。msg.proto 代码如下
定义当前使用的是proto3语言并且包名(生成为C#则为命名空间):
```
syntax = "proto3";
package Snai.GrpcService.Protocol;
```
定义了1个服务,且有1个方法:
```
service MsgService{
rpc GetSum(GetMsgNumRequest) returns (GetMsgSumReply){}
}
```
方法的接收参数和返回参数
```
message GetMsgNumRequest {
int32 Num1 = 1;
int32 Num2 = 2;
}
message GetMsgSumReply {
int32 Sum = 1;
}
```
2、将协议生成C#代码
生成协议代码需 protoc.exe、grpc_csharp_plugin.exe工具,在.net framework 项目下引用安装 Grpc.Tools 组件程序包,会得到protoc.exe、grpc_csharp_plugin.exe,但.net core 项目引用安装是不会下载工具到项目目录的,所以我们需要建一个.net framework项目,我建了个 InstallGrpc .net framework类库 用于引用安装得到工具。
这里得到工具有个小插曲,引用Grpc.Tools版本1.11.0得到protoc.exe、grpc_csharp_plugin.exe 拷到 Snai.GrpcService.Protocol 目录下生成不了,我再引用Google.Protobuf.Tools版本3.5.1里面有 protoc.exe,用 Grpc.Tools下的 grpc_csharp_plugin.exe, Google.Protobuf.Tools下protoc.exe 根据当前系统选择,拷贝到 Snai.GrpcService.Protocol 目录下。
先用Grpc.Tools 下的,如果生成不了,再用 Grpc.Tools下的 grpc_csharp_plugin.exe, Google.Protobuf.Tools下protoc.exe
然后在项目中新建一个名为ProtocGenerate.cmd的文件,在其中输入以下指令:
```
protoc -I . --csharp_out . --grpc_out . --plugin=protoc-gen-grpc=grpc_csharp_plugin.exe msg.proto
```
然后直接双击运行,项目下生成了“Msg.cs”和“MsgGrpc.cs”两个文件,这样协议部分的所有工作就完成了,最终项目结构如下:
![Image](/sitedata/image/dotnet_1_4.png)
3、编写协议实现代码
新建Snai.GrpcService.Impl实现类库项目,在 依赖项 下载安装Grpc.Core 包,项目引用Snai.GrpcService.Protocol
在项目根目录下新建 MsgServiceImpl.cs 类文件,继承 MsgService.MsgServiceBase 协议类,实现服务方法,代码如下:
```
using Grpc.Core;
using Snai.GrpcService.Protocol;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace Snai.GrpcService.Impl
{
public class MsgServiceImpl: MsgService.MsgServiceBase
{
public MsgServiceImpl()
{
}
public override async Task<GetMsgSumReply> GetSum(GetMsgNumRequest request, ServerCallContext context)
{
var result = new GetMsgSumReply();
result.Sum = request.Num1 + request.Num2;
return result;
}
}
}
```
在项目根目录下新建 RpcConfig.cs 类文件,编写绑定服务到服务端,服务端 地址 端口 等信息,实现启动方法,代码如下:
```
using Grpc.Core;
using Snai.GrpcService.Protocol;
using System;
using System.Collections.Generic;
using System.Text;
namespace Snai.GrpcService.Impl
{
public static class RpcConfig
{
private static Server _server;
public static void Start()
{
_server = new Server
{
Services = { MsgService.BindService(new MsgServiceImpl()) },
Ports = { new ServerPort("localhost", 40001, ServerCredentials.Insecure) }
};
_server.Start();
Console.WriteLine("grpc ServerListening On Port 40001");
Console.WriteLine("任意键退出...");
Console.ReadKey();
_server?.ShutdownAsync().Wait();
}
}
}
```
最终项目结构如下:
![Image](/sitedata/image/dotnet_1_5.png)
4、编写服务端启动程序
新建Snai.GrpcService.Hosting 控制台程序,项目引用Snai.GrpcService.Impl
打开 Program.cs 文件,修改 Main 方法,加入服务启动,代码如下:
```
using Snai.GrpcService.Impl;
using System;
namespace Snai.GrpcService.Hosting
{
class Program
{
static void Main(string[] args)
{
RpcConfig.Start();
}
}
}
```
最终项目结构如下:
![Image](/sitedata/image/dotnet_1_6.png)
到此服务端所有代码已编写完成,下面开始编写客户端。
**二、客户端**
新建Snai.GrpcClient 控制台程序,在 依赖项 下载安装Grpc.Core 包,项目引用Snai.GrpcService.Protocol
在项目根目录下新建 MsgServiceClient.cs 类文件,编写与服务端通信的 地址 端口 等信息,并调用服务端方法,代码如下:
```
using Grpc.Core;
using Snai.GrpcService.Protocol;
using System;
using System.Collections.Generic;
using System.Text;
namespace Snai.GrpcClient
{
public static class MsgServiceClient
{
private static Channel _channel;
private static MsgService.MsgServiceClient _client;
static MsgServiceClient()
{
_channel = new Channel("127.0.0.1:40001", ChannelCredentials.Insecure);
_client = new MsgService.MsgServiceClient(_channel);
}
public static GetMsgSumReply GetSum(int num1, int num2)
{
return _client.GetSum(new GetMsgNumRequest
{
Num1 = num1,
Num2 = num2
});
}
}
}
```
打开 Program.cs 文件,修改 Main 方法,得到服务端返回结果,显示结果,代码如下:
```
using Snai.GrpcService.Protocol;
using System;
namespace Snai.GrpcClient
{
class Program
{
static void Main(string[] args)
{
GetMsgSumReply msgSum = MsgServiceClient.GetSum(10, 2);
Console.WriteLine("grpc Client Call GetSum():" + msgSum.Sum);
Console.WriteLine("任意键退出...");
Console.ReadKey();
}
}
}
```
最终项目结构如下:
![Image](/sitedata/image/dotnet_1_7.png)
到此所有代码都已编写完成。
**三、启动**
右击生成解决方案,生成完成后,先启动服务端,再启动客户端
命令行到服务端目录 Snai.GrpcService.Hosting\bin\Debug\netcoreapp2.0\,用命令 dotnet Snai.GrpcService.Hosting.dll 启动服务端
![Image](/sitedata/image/dotnet_1_8.png)
命令行到客户端目录 Snai.GrpcClient\bin\Debug\netcoreapp2.0\,用命令 dotnet Snai.GrpcClient.dll 启动客户端
![Image](/sitedata/image/dotnet_1_9.png)
客户端调用服务端方法成功,实现grpc
它们之间是通过Grpc.Core中的 Server 和 Channel 来通信
Github源码地址:[https://github.com/Liu-Alan/Snai.GrpcService](https://github.com/Liu-Alan/Snai.GrpcService)