主要記錄下使用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