首先需要编译gRPC
获取gRPC源码
gRPC是开源框架,项目代码在github上,所以首先要安装github。
github安装后,在指定文件夹中,执行git命令就可以获取gRPC的所有源码。
git clone https://github.com/grpc/grpc.git
cd grpc
git submodule update --init 获取third_party的源代码
step1. 编译protobuf
参考readme用CMAKE生成工程文件,编译即可。首先打开vs2015开发人员命令提示符窗口,切换到对应的protobuf目录
具体步骤:
1:cd protobuf
2: Git clone -b release-1.7.0 https://github.com/google/googlemock.git gmock
3:cd gmock
4:git clone -b release-1.7.0 https://github.com/google/googletest.git gtest
5:cd ..\cmake
6:mkdir build & cd build & mkdir install
7:mkdir debug & cd debug
8:cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=../install ../..
9:nmake && nmake install
10:生成完成,在install目录下面有对应的Lib文件。在cmake目录下面mkdir debug,然后把install/lib目录下的所有库文件拷贝的debug路径,并把后缀d去掉。例如protobuf生成的库名称为libprotocd.lib,应该改名成libprotoc.lib。其他的依次类推。后面编译grpc会用到这些库。
方法二、可以直接在cmake目录用cmakelists.txt 直接生产工程文件编译
step2. 编译grpc,grpc_protoc_plugin:在vsprojects里有建好的工程文件,下载nuget.exe,用于依赖包的网络下载。主要是依赖于openssl和zlib库。在编译grpc时,出现编译boringssl,出现很多错误,可以把工程移除
具体步骤:
1:cd vsprojects
2:nuget restore grpc.sln
3:msbuild grpc.sln /p:Configuration=Debug
grpc库生成成功。
4:编译grpc_cpp_plugin,执行命令:msbuild grpc_protoc_plugins.sln /p:Configuration=Debug
grpc_cpp_plugin.exe插件编译成功
用vs2015编译基本不会遇到什么问题,除了:
grpc_cpp_plugin依赖libprotoc.lib,而protobuf生成的库名称为libprotocd.lib,这块需要手动改一下
定义如下格式的 asyncservice.proto文件,内容如下
syntax = "proto3";
package cecily;
service School {
rpc HelloStudent(Student) returns (Teacher) {}
rpc HelloTeacher(Teacher) returns (Student) {}
}
message Student {
bytes name = 1;
int32 age = 2;
int32 number = 3;
}
message Teacher {
bytes name = 1;
int32 age = 2;
bytes school = 3;
}
定义了两个rpc 服务, HelloStudent(入参Student 返回值 Teacher) 和 HelloTeacher(入参Teacher 返回值 Student), 然后使用 protoc.exe 和
grpc_cpp_plugin.exe生成 asyncservice.pb.h、 asyncservice.pb.cc 、asyncservice.grpc.pb.h 、asyncservice.grpc.pb.cc 使用命令如下,这样就生成了描述类
protoc.exe -I=.\ --grpc_out=.\ --plugin=protoc-gen-grpc=.\grpc_cpp_plugin.exe .\asyncservice.proto
protoc.exe -I=.\ --cpp_out=.\ .\asyncservice.proto
多线程异步服务端代码如下:
// AsyncServer_Demo.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <memory>
#include <iostream>
#include <string>
#include <thread>
#include <process.h>
#include <grpc++/grpc++.h>
#include <grpc/support/log.h>
#include "asyncservice.grpc.pb.h"
using grpc::Server;
using grpc::ServerAsyncResponseWriter;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::ServerCompletionQueue;
using grpc::Status;
using cecily::Student;
using cecily::Teacher;
using cecily::School;
class ServerImpl final{
public:
~ServerImpl() {
server_->Shutdown(); //停止服务
cq_->Shutdown();
}
void Run() {
std::string server_address("0.0.0.0:66666");
ServerBuilder builder;
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
builder.RegisterService(&service_);
cq_ = builder.AddCompletionQueue();
server_ = builder.BuildAndStart();
std::cout << "Server listening on " << server_address << std::endl;
//注册服务 通过CallData对象的状态,在初始状态下,将对应类型的CallData对象传递进入ServerCompletionQueue 的 cq_对象中
//当有服务请求过来的时候 调用next 能得到对应的请求, 请求类型通过CallData对象的s_type_值区分,用来区分不同的服务,做不同的处理
new CallData(&service_, cq_.get(), ServerImpl::CallData::SS_HelloStudent);
new CallData(&service_, cq_.get(), ServerImpl::CallData::SS_HelloTeacher);
//注册服务 应该有多少个服务有要注册多少个服务, 例子只有两个服务
//开启多线程处理rpc调用请求
for ( int i = 0 ; i < 8; i++ )
{
_beginthreadex(NULL,
0,
ServerImpl::ThreadHandlerRPC,
(void*)this,
0,
0);
}
}
private:
class CallData {
public:
enum ServiceType {
SS_HelloStudent = 0,
SS_HelloTeacher
};
public:
CallData(School::AsyncService* service, ServerCompletionQueue* cq, ServiceType s_type)
:service_(service),cq_(cq),s_type_(s_type), student_response(&ctx_), teacher_response(&ctx_), status_(CREATE){
Process();
}
void Process() {
if ( status_ == CREATE )
{
status_ = PROCESS;
switch (s_type_)
{
//根据不同的服务 注册不同的 服务类型到 ServerCompletionQueue 队列, 看名字和使用有点像完成端口 (没有去验证研究)
case ServerImpl::CallData::SS_HelloStudent:
service_->RequestHelloStudent(&ctx_, &student_request, &student_response, cq_, cq_, this);
break;
case ServerImpl::CallData::SS_HelloTeacher:
service_->RequestHelloTeacher(&ctx_, &teacher_request, &teacher_response, cq_, cq_, this);
break;
default:
break;
}
}
else if (status_ == PROCESS) {
status_ = FINISH;
new CallData(service_, cq_, this->s_type_);
switch (s_type_)
{
case ServerImpl::CallData::SS_HelloStudent: {
std::string name = student_request.name();
int age = student_request.age();
std::string prefix("Cecily HelloStudent: ");
int nThreadID = GetCurrentThreadId();
char szTmp[20] = { 0 };
sprintf_s(szTmp,20, "线程:%d", nThreadID);
std::string end(szTmp);
teacher_request.set_name(prefix + name + end);
status_ = FINISH;
student_response.Finish(teacher_request, Status::OK, this);
}
break;
case ServerImpl::CallData::SS_HelloTeacher: {
std::string schoolname = teacher_request.school();
std::string prefix("Cecily HelloTeacher: ");
int nThreadID = GetCurrentThreadId();
char szTmp[20] = { 0 };
sprintf_s(szTmp, 20, "线程:%d", nThreadID);
std::string end(szTmp);
student_request.set_name(prefix + schoolname +end);
status_ = FINISH;
teacher_response.Finish(student_request, Status::OK, this);
}
break;
default:
break;
}
}
else {
GPR_ASSERT(status_ == FINISH);
delete this;
}
}
private:
School::AsyncService* service_;
ServerCompletionQueue* cq_;
ServerContext ctx_;
ServiceType s_type_;
Student student_request;
::grpc::ServerAsyncResponseWriter< ::cecily::Teacher> student_response;
Teacher teacher_request;
::grpc::ServerAsyncResponseWriter< ::cecily::Student> teacher_response;
enum CallStatus { CREATE, PROCESS, FINISH };
CallStatus status_;
};
private:
static unsigned __stdcall ThreadHandlerRPC(void* lparam) {
ServerImpl* impl = (ServerImpl*)lparam;
impl->HandleRPCS();
return 1;
}
void HandleRPCS() {
void* tag;
bool ok;
while (true) {
GPR_ASSERT(cq_->Next(&tag, &ok));//从ServerCompletionQueue 队列拿到获取请求任务,根据请求任务处理 逻辑
GPR_ASSERT(ok);
static_cast<CallData*>(tag)->Process();
}
}
private:
std::shared_ptr<ServerCompletionQueue> cq_;
School::AsyncService service_;
std::shared_ptr<Server> server_;
};
int main()
{
ServerImpl server;
server.Run();
char c;
std::cin >> c;
system("pause");
return 0;
}
以上内容属于学习记录随笔,因为从接触到写这个博客只有两天时间,中间难免会有不正确的地方,后续发现再更改。