Grpc学习指南 (Qt实现)

目录

1.背景

2.环境配置

3.创建.proto文件并生成对应的pb文件

4.代码实现


1.背景

GRPC 是一个高性能、开源和通用的 RPC 框架,面向服务端和移动端,基于 HTTP/2 设计;

GRPC 默认使用protocol buffers,使用protocol buffers作为IDL和消息交换格式,google开源的成熟的数据序列化机制;

定义服务:通过指定方法调用的参数和返回值来定义,就是使用IDL来 描述你的服务接口和传输消息结构;

gRPC 特点:

①语言中立,支持多种语言;

②基于 IDL 文件定义服务,通过 proto3 工具生成指定语言的数据结构、服务端接口以及客户端 Stub;

③通信协议基于标准的 HTTP/2 设计,支持双向流、消息头压缩、单 TCP 的多路复用、服务端推送等特性,这些特性使得 gRPC 在移动端设备上更加省电和节省网络流量;

④序列化支持 PB(Protocol Buffer)和 JSON,PB 是一种语言无关的高性能序列化框架,基于 HTTP/2 + PB, 保障了 RPC 调用的高性能。

Grpc学习指南 (Qt实现)_第1张图片

简单理解:Grpc  Server 实现方法,Grpc Client 像调用本地方法一样调用server的方法,Server不主动进行通信,主要依靠client调用来进行交互通信;

2.环境配置

开发环境:Linux  Qt

grpc源码地址:https://github.com/grpc/grpc/tree/v1.42.0

grpc库文件:需要自己编译或者下载已编译好的库文件即可,这里不做细致讲解。

主要讲解一下protobuf的配置,

安装包地址:https://github.com/protocolbuffers/protobuf/releases

Grpc学习指南 (Qt实现)_第2张图片

 可以根据自己需求进行瞎下载对应的安装包即可,这里我下载的时c++版本;

解压:
unzip protobuf-3.19.1.zip
进入目录:
cd protobuf-3.19.1
配置安装目录:
./config --prefix=/usr/local/protobuf    
编译安装:
make    
make install
建立链接到bin下:
ln -sf /usr/local/protobuf/bin/protoc /usr/bin/protoc  
查看版本号:
protoc --version  

 3.创建.proto文件并生成对应的pb文件

proto编写:

syntax = "proto3";
package MyGrpcS;  //类似于c++  命名空间

//定义请求信息
message SearchARequest
{
    //参数类型  名称 标识号
	string request = 1;
	int32 statusr = 2;
}
//定义响应信息
message SearchAResponse
{
    //参数类型  名称 标识号
	string response = 3;
	int32 statusr = 4;
}
//同上
message SearchBRequest
{
	string request = 5;
	int32 statusr = 6;
}

message SearchBResponse
{
	string response = 7;
	int32 statusr = 8;
}

message StreamRequest
{
	int32 mode = 9;
}
message StreamResponse
{
	string data = 10;
	string type = 11;
	int32 num = 12;
}
service SearchService{
	//sample rpc
	rpc getDataA(SearchARequest)  returns (SearchAResponse){}
	rpc setDataA(SearchAResponse)  returns (SearchARequest){}
	rpc getDataB(SearchBRequest)  returns (SearchBResponse){}
	rpc setDataB(SearchBResponse)  returns (SearchBRequest){}
	//server rpc
	rpc streamData(StreamRequest) returns (stream StreamResponse){}
	//client rpc
	rpc ctreamData(stream StreamRequest) returns (StreamResponse){}
	//server and client rpc
	rpc scstreamData(stream StreamRequest) returns (stream StreamResponse){}
	
}	

编译:

protoc  --cpp_out=./ GrpcServer.proto          
//将生成GrpcServer.pb.cc GrpcServer.pb.h文件
protoc  --grpc_out=./ --plugin=protoc-gen-grpc=/home/jiang/ProgramQt/libs/grpc/grpc_cpp_plugin GrpcServer.proto
//将生成GrpcServer.grpc.pb.h  GrpcServer.grpc.pb.cc文件

文件如下:

Grpc学习指南 (Qt实现)_第3张图片

 4.代码实现

server端通信需要实现的:

//运行服务器
void runServer(){
    std::string server_addr("0.0.0.0:50051");
    GrpcServerImp1 service;

    ServerBuilder builder;
    builder.AddListeningPort(server_addr,grpc::InsecureServerCredentials());
    builder.RegisterService(&service);

    std::unique_ptr server(builder.BuildAndStart());
    std::cout<<"server listening on"<Wait();
}

//重载service的虚函数
class GrpcServerImp1 final:public SearchService::Service
{
public:
     ::grpc::Status getDataA(::grpc::ServerContext* context, const ::MyGrpcS::SearchARequest* request, ::MyGrpcS::SearchAResponse* response)override;
     ::grpc::Status setDataA(::grpc::ServerContext* context, const ::MyGrpcS::SearchAResponse* request, ::MyGrpcS::SearchARequest* response)override;
     ::grpc::Status getDataB(::grpc::ServerContext* context, const ::MyGrpcS::SearchBRequest* request, ::MyGrpcS::SearchBResponse* response)override;
     ::grpc::Status setDataB(::grpc::ServerContext* context, const ::MyGrpcS::SearchBResponse* request, ::MyGrpcS::SearchBRequest* response)override;
     ::grpc::Status streamData(::grpc::ServerContext* context, const ::MyGrpcS::StreamRequest* request, ::grpc::ServerWriter< ::MyGrpcS::StreamResponse>* writer)override;
     ::grpc::Status ctreamData(::grpc::ServerContext* context, ::grpc::ServerReader< ::MyGrpcS::StreamRequest>* reader, ::MyGrpcS::StreamResponse* response)override;
     ::grpc::Status scstreamData(::grpc::ServerContext* context, ::grpc::ServerReaderWriter< ::MyGrpcS::StreamResponse, ::MyGrpcS::StreamRequest>* stream)override;

};

注意:建议虚函数直接跳转到service类下进行拷贝,避免无法识别,之前就因为是自己手动敲的,一直通信都不成功,报错UNIMPLEMENTED(service 方法找不到),找了很久才解决,也不知道是不是编译器的问题还是什么问题就很奇怪。

Grpc学习指南 (Qt实现)_第4张图片

client需要实现的:

//定义类对象
class ExmapleClint{
public:
    explicit ExmapleClint(std::shared_ptr channel):
        stub_(SearchService::NewStub(channel)){}

    std::unique_ptr stub_;
};

//创建对象
ChannelArguments args;
m_pClient = new ExmapleClint(grpc::CreateCustomChannel("127.0.0.1:50051", grpc::InsecureChannelCredentials(),args));

grpc有四种交互模式,如下:

①简单模式(Simple RPC),客户端发起请求并等待服务端响应;

Grpc学习指南 (Qt实现)_第5张图片

	//sample rpc
	rpc getDataA(SearchARequest)  returns (SearchAResponse){}
	rpc setDataA(SearchAResponse)  returns (SearchARequest){}
	rpc getDataB(SearchBRequest)  returns (SearchBResponse){}
	rpc setDataB(SearchBResponse)  returns (SearchBRequest){}

server 代码如下:

grpc::Status GrpcServerImp1::getDataA(grpc::ServerContext *context, const SearchARequest *request, SearchAResponse *response)
{
    response->set_statusr(5);
    response->set_response(m_sSetA);

    return Status::OK;
}

grpc::Status GrpcServerImp1::setDataA(grpc::ServerContext *context, const SearchAResponse *request, SearchARequest *response)
{

   int status = response->statusr();
    m_sGetA = request->response();
    m_sGetA += "status:"+to_string(status);
     return Status::OK;
}

grpc::Status GrpcServerImp1::getDataB(grpc::ServerContext *context, const SearchBRequest *request, SearchBResponse *response)
{
    response->set_statusr(10);
    response->set_response(m_sSetB);

    return Status::OK;
}

grpc::Status GrpcServerImp1::setDataB(grpc::ServerContext *context, const SearchBResponse *request, SearchBRequest *response)
{
    int status = response->statusr();
     m_sGetB = request->response();
     m_sGetB += "status:"+to_string(status);

     return Status::OK;
}

client代码如下:(这里我是在Qt的槽函数中实现的)

 connect(m_pSetDA_But,&QPushButton::clicked,this,[&](){

        MyGrpcS::SearchARequest request;
        MyGrpcS::SearchAResponse response;
        ClientContext context;
        response.set_response(m_pSetDA_Edit->toPlainText().toStdString());
        response.set_statusr(1);

        Status status = m_pClient->stub_->setDataA(&context,response,&request);
        if(status.ok())
        {
            this->statusBar()->showMessage("set data A success");
        }
        else
        {
            cout<statusBar()->showMessage("RPC FAIL");
        }
    });
    connect(m_pGetDA_But,&QPushButton::clicked,this,[&](){
        MyGrpcS::SearchARequest request;
        MyGrpcS::SearchAResponse response;
        ClientContext context;
        request.set_request("A ");
        request.set_statusr(1);
        Status status = m_pClient->stub_->getDataA(&context,request,&response);
        if(status.ok())
        {
            string param1 = response.response();
            int param2 = response.statusr();
            m_pGetDA_Brower->setText(QString("Data A:%1 %2").arg(param1.c_str()).arg(param2));
        }
        else
        {
            cout<setText(QString("rpc failed"));
        }
    });
    connect(m_pSetDB_But,&QPushButton::clicked,this,[&](){
        MyGrpcS::SearchBRequest request;
        MyGrpcS::SearchBResponse response;
        ClientContext context;
        response.set_response(m_pSetDB_Edit->toPlainText().toStdString());
        response.set_statusr(1);

        Status status = m_pClient->stub_->setDataB(&context,response,&request);
        if(status.ok())
        {
            this->statusBar()->showMessage("set data B success");
        }
        else
        {
            cout<statusBar()->showMessage("RPC FAIL");
        }
    });
    connect(m_pGetDB_But,&QPushButton::clicked,this,[&](){
        MyGrpcS::SearchBRequest request;
        MyGrpcS::SearchBResponse response;
        ClientContext context;
        request.set_request("B ");
        request.set_statusr(1);
        Status status = m_pClient->stub_->getDataB(&context,request,&response);
        if(status.ok())
        {
            string param1 = response.response();
            int param2 = response.statusr();
            m_pGetDB_Brower->setText(QString("Data B:%1 %2").arg(param1.c_str()).arg(param2));
        }
        else
        {
            cout<setText(QString("rpc failed"));
        }
    });

client 主要是通过stub_去调用server重载的方法来进行请求和响应的。

②服务端流式 RPC(Server-side streaming RPC),客户端发起一个请求到服务端,服务端返回一段连续的数据流响应;

Grpc学习指南 (Qt实现)_第6张图片

	//server rpc
	rpc streamData(StreamRequest) returns (stream StreamResponse){}

 server实现:

grpc::Status GrpcServerImp1::streamData(grpc::ServerContext *context, const StreamRequest *request, ::grpc::ServerWriter *writer)
{
    cout<<"enter streamdata"< *stream = new GrpcStream;
    stream->isclosed = 0;
    stream->request = (StreamRequest*)request;
    stream->writer = writer;
    //listStreams->push_back(stream); //save once time request
 
    //这里我手动简单的进行循环响应,实际开发中可以设置一个线程进行不断write
    for(int i=1;i<6;i++)
    {
        MyGrpcS::StreamResponse *response = new MyGrpcS::StreamResponse;
        response->set_num(i);
        response->set_data("ssssssssssss"+to_string(i));
        response->set_type("server-rpc");
        ::grpc::WriteOptions options;
        stream->writer->Write(*response,options);
        delete response;
        response = nullptr;
    }

    delete  stream;
    stream = nullptr;
    return Status::OK;
}

client实现:

    connect(m_pStreamBut,&QPushButton::clicked,this,[&](){
        ClientContext context;
        MyGrpcS::StreamRequest  request;
        request.set_mode(1);

        std::unique_ptr> reader = m_pClient->stub_->streamData(&context,request);
        MyGrpcS::StreamResponse response;
        int count = 0;
        while(1)  //客户端不断的去read 直到没有数据就推出循环(开发中可以加线程进行不断的read)
        {
            if(reader->Read(&response))
            {
                string data = response.data();
                string type= response.type();
                int num = response.num();
                QString cc = QString("data:%1 ").arg(data.c_str());
                QString tt = QString("type:%1 ").arg(type.c_str());
                QString ss = QString("num:%1 ").arg(num);
                if(m_pStream_Brower)
                {
                    m_pStream_Brower->insertPlainText(cc);
                    m_pStream_Brower->insertPlainText(tt);
                    m_pStream_Brower->insertPlainText(ss);

                    QApplication::processEvents();//因为槽函数也是线程,会导致界面不能刷新,因此使用线程事件刷新

                }

                count = 0;
                cout<<"read success"<

③客户端流式 RPC(Client-side streaming RPC),与服务端流式相反,客户端流式是客户端不断地向服务端发送数据流,最后由服务端返回一个响应;

Grpc学习指南 (Qt实现)_第7张图片

	//client rpc
	rpc ctreamData(stream StreamRequest) returns (StreamResponse){}

server实现:

  grpc::Status GrpcServerImp1::ctreamData(grpc::ServerContext *context, ::grpc::ServerReader *reader, StreamResponse *response)
{
    //client rpc
    cout<<"client rpc"< *stream = new GrpcStream;
    stream->isclosed = 0;
    stream->response = response;
    stream->reader = reader;

    StreamRequest request;

    while(reader->Read(&request))  //如果客户端不执行writedone,就会出现阻塞在此处,也不会退出while循环
    {
        cout<<"mode:"<set_num(1);
    response->set_data("server recv cilent request...");
    response->set_type("client-rpc");

    return Status::OK;
}

client实现:

  connect(m_pCStreamBut,&QPushButton::clicked,this,[&](){
        ClientContext context;
        MyGrpcS::StreamRequest  request;
        MyGrpcS::StreamResponse response;
        std::unique_ptr> writer = m_pClient->stub_->ctreamData(&context,&response);
        grpc::WriteOptions args;
        int count = 1;
        bool status = false;
        //send request
        while(1)
        {
            request.set_mode(count);
            if(writer->Write(request,args))  //写入数据
            {

            }
            else
                break;
            if(count >6)
                break;
            ++count;
        }
        writer->WritesDone();//写入完成  如果不执行此函数会导致  server 出现read一直不结束会阻塞在那儿
        Status st = writer->Finish();
        if(!st.ok())
            cout<<"status error:"<insertPlainText(cc);
                m_pCStream_Brower->insertPlainText(tt);
                m_pCStream_Brower->insertPlainText(ss);

                QApplication::processEvents();//同理,刷新线程

            }
        }
        cout<<"exit thread"<

④双向流式 RPC(Bidirectional streaming RPC),客户端和服务端可同时向对方发送数据流,同时也可以接收数据;

Grpc学习指南 (Qt实现)_第8张图片

	//server and client rpc
	rpc scstreamData(stream StreamRequest) returns (stream StreamResponse){}

server实现:

grpc::Status GrpcServerImp1::scstreamData(grpc::ServerContext *context, ::grpc::ServerReaderWriter *stream)
{
    cout<<"server and client rpc"<Read(&request)) //也需要client运行writedone()才会结束
        {
            cout<<"mode:"<Write(response,options);
        i++;
        if(status)
            break;
   }
    return Status::OK;
}

client实现:

    connect(m_pSCStreamBut,&QPushButton::clicked,this,[&](){
        ClientContext context;
        MyGrpcS::StreamRequest  request;
        MyGrpcS::StreamResponse response;
        std::unique_ptr< ::grpc::ClientReaderWriter< ::MyGrpcS::StreamRequest, ::MyGrpcS::StreamResponse>> wRriter = m_pClient->stub_->scstreamData(&context);
        grpc::WriteOptions args;
        int count = 1;
        bool status = false;
        //send request   //此处可以使用线程同时执行
        while(1)
        {
            request.set_mode(count);
            if(wRriter->Write(request,args))
            {

            }
            else
            {
               break;
            }

            if(count >6)
                break;
            ++count;
        }
        wRriter->WritesDone(); //写入结束  避免server read 阻塞


        count =1;
        status = false;
        //get response
        while(wRriter->Read(&response))  //一直读取响应的数据
        {
           string data = response.data();
           string type= response.type();
           int num = response.num();
           QString cc = QString("data:%1 ").arg(data.c_str());
           QString tt = QString("type:%1 ").arg(type.c_str());
           QString ss = QString("num:%1 ").arg(num);
           if(m_pSCStream_Brower)
           {
               m_pSCStream_Brower->insertPlainText(cc);
               m_pSCStream_Brower->insertPlainText(tt);
               m_pSCStream_Brower->insertPlainText(ss);

               QApplication::processEvents();//while cause thread make   not plush ui

           }

        }
        Status st = wRriter->Finish();
        if(!st.ok())
            cout<<"error:"<

以上四种方式就简单实现了,实际开发中很多都是使用线程处理实时请求和响应数据。

原理:

A.grpc使用.proto生成client和 server代码及API,Client调用生成的API,server实现这些API.

B.serve:实现服务声明的方法,并运行grpc server来处理client的调用,grpc帮助反序列请求,执行服务方法,序列化响应并返回个client.

C.client:根据grpc生成的client,这个client提供和server一样的接口供用户调用,并且使用protocol buffer序列化用户的请求参数,发送个 server,然后反序列化server的响应.

Grpc学习指南 (Qt实现)_第9张图片

Grpc学习指南 (Qt实现)_第10张图片

四种方式rpc就简单实现了,grpc的原理也基本搞懂,能够实际运用到开发中。至于底层原理等有时间再去分析grpc源码。

参考:​​​​​​https://blog.csdn.net/asd1126163471/article/details/117794401

https://www.jianshu.com/p/9e57da13b737

https://www.cnblogs.com/MakeView660/p/11512346.html

你可能感兴趣的:(Qt,qt,grpc)