主要记录下使用gRPC躺过的坑
1. GRPC c++版安装
- 在github上搜索grpc(https://github.com/grpc/grpc),我选择的版本是v1.22.1(需要在火狐浏览器中打开才能选择分支)
- 注意不要直接下载源码,这样无法获取grpc依赖的三方库,thirty_party文件夹会为空
- 下载完整的源码前需要做一些准备工作,可从BUILDING.md文件中查找,具体步骤如下:
- 下载并安装git
https://git-scm.com/ - 下载安装choco
https://blog.csdn.net/u010570551/article/details/72936663
3.安装ActivePerl,在cmd中执行
choco install activeperl - 安装golang,在cmd中执行
choco install golang - 安装yasm并设置环境变量,在cmd中执行
choco install yasm - 下载grpc源码,在cmd中执行以下命令
git clone --recursive -b master(v1.22.1) https://github.com/grpc/grpc(可以指定特定版本) - cd grpc/third_party/protobuf
Cmake GUI生成protobuf的解决方案,并用VS2017编译生成库 - cd grpc
Cmake GUI生成grpc的解决方案,并用VS2017编译生成库 - 利用protoc.exe和grpc_cpp_plugin.exe生成proto文件对应的.h和.cc文件
2. GRPC c#版安装
- 右键工程->管理NuGet程序包->浏览,分别搜索grpc,grpc.tools,Google.Protobuf并安装,注意针对每个工程右键,而不是直接在解决方案上右键
- 利用protoc.exe及grpc插件生成ProtocolGrpc.cs和Protocol.cs文件
//注意proto3中,若需要判断基本变量是否为空,需要封装基本变量,具体见wrappers.proto
protoc.exe --csharp_out=.\ mcsf_iron_man_protocol.proto --include_imports wrappers.proto
protoc.exe --grpc_out=.\ --plugin=protoc-gen-grpc=grpc_csharp_plugin.exe *protocol.proto
3.GRPC demo
定义协议
//定义服务的代码,放在刚创建的helloworld.proto中
syntax = "proto3";
package gRPCDemo;
service gRPC {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
C#版客户端代码
//客户端代码
using Grpc.Core;
using GRPCDemo;
using System;
namespace gRPCClient
{
class Program
{
static void Main(string[] args)
{
Channel channel = new Channel("127.0.0.1:9007", ChannelCredentials.Insecure);
var client = new gRPC.gRPCClient(channel);
var reply = client.SayHello(new HelloRequest { Name = "Zhang San" });
Console.WriteLine("来自" + reply.Message);
channel.ShutdownAsync().Wait();
Console.WriteLine("任意键退出...");
Console.ReadKey();
}
}
}
C#版服务端代码
//服务端代码
using Grpc.Core;
using GRPCDemo;
using System;
using System.Threading.Tasks;
namespace gRPCServer
{
class severProgram
{
const int Port = 9007;
public static void Main(string[] args)
{
Server server = new Server
{
Services = { gRPC.BindService(new gRPCImpl()) },
Ports = { new ServerPort("localhost", Port, ServerCredentials.Insecure) }
};
server.Start();
Console.WriteLine("gRPC server listening on port " + Port);
Console.WriteLine("任意键退出...");
Console.ReadKey();
server.ShutdownAsync().Wait();
}
}
class gRPCImpl : gRPC.gRPCBase
{
// 实现SayHello方法
public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
{
return Task.FromResult(new HelloReply { Message = "Hello " + request.Name });
}
}
}
C++版客户端
#include "../GrpcCPlusDemo/hello.pb.h"
#include "../GrpcCPlusDemo/hello.grpc.pb.h"
#include "grpcpp/create_channel.h"
#include "grpcpp/Channel.h"
#include "grpcpp/security/credentials.h"
//实现proto协议中定义的RPC服务
class HelloClientImpl
{
public:
HelloClientImpl(std::shared_ptr < grpc::Channel > channel) :stub_(gRPCDemo::gRPC::NewStub(channel)) {}
std::string InvokeCallRPC(const std::string v)
{
gRPCDemo::HelloRequest request;
request.set_name(v);
gRPCDemo::HelloReply reply;
grpc::ClientContext context;
grpc::Status status = stub_->SayHello(&context, request, &reply);
if (status.ok())
{
return reply.message();
}
else
{
std::cout << status.error_code() << ":" << status.error_message() << "InvokeCallRPC Failed" << std::endl;
return "";
}
return 0;
}
private:
std::unique_ptr<gRPCDemo::gRPC::Stub> stub_;
};
int main()
{
HelloClientImpl client(grpc::CreateChannel("127.0.0.1:9009", grpc::InsecureChannelCredentials()));
std::string str = client.InvokeCallRPC("Jim");
std::cout << "client received :" << str << std::endl;
return 0;
}
C++版服务端
#include "../GrpcCPlusDemo/hello.pb.h"
#include "../GrpcCPlusDemo/hello.grpc.pb.h"
#include "grpcpp/server_builder.h"
//实现proto协议中定义的RPC服务
class HelloServiceImpl : public gRPCDemo::gRPC::Service
{
public:
virtual ::grpc::Status SayHello(::grpc::ServerContext* context, const gRPCDemo::HelloRequest* req, gRPCDemo::HelloReply* respond)
{
respond->set_message("hello,client");
return grpc::Status::OK;
}
};
//启动服务,监听指定端口
void RunServer()
{
std::string server_address("127.0.0.1:50057");
HelloServiceImpl service;
grpc::ServerBuilder builder;
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
builder.RegisterService(&service);
std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
std::cout << "Server Listening on " << server_address << std::endl;
server->Wait();
}
int main()
{
RunServer();
return 0;
}
4. 几点注意
- grpc是跨语言跨平台通信框架,虽然C++和C#对同一份proto协议生成的文件或接口有所区别,但并不妨碍通信,grpc内部会做处理
- c++和C#版本可以不一致,而且不确定两者的版本是否具有对应关系,因为c++一般是从github上获取指定版本的grpc源码(目前最高release版本为1.23.0),然后编译成库进行使用,而C#的源码编译时会报强名称的错,提示snk文件被占用,其实文件一直在那,没被占用,不知道啥原因,目前C#版grpc一般通过NuGet获取,获取时一定要注意,NuGet获取三方库时,会自动生成一个package.config的文件,里面会记录三方库所依赖的其他库,千万不要自己手动添加,不然package.config会缺少文件,导致通信失败的
- 目前C#版是在.Net Framework上运行的,但看官网资料,一般都是基于.net Core,所以建议后面使用的话基于.Net Core框架来使用GRPC
5 参考
https://www.grpc.io/
https://blog.csdn.net/img_Guo/article/details/86096604
http://doc.oschina.net/grpc?t=57966