gRPC系列(二) 异步服务使用

gRPC系列(二) 异步服务使用

相关文章:gRPC系列(一)安装和入门

异步的实现主要围绕的是grpc提供的队列:grpc::CompletionQueue

客户端代码

异步客户端代码相对于同步客户端来说并没有复杂多少,简单来说,就是同步rpc调用是调用完不会立刻返回,而是可以异步从队列中获得返回结果,实现调用的解耦,我们来看代码。

#include 
#include 
#include 
#include 
#include 
#include "../protos/simple/simple.grpc.pb.h"
using grpc::Status;
using grpc::Channel;
using grpc::CompletionQueue;
using grpc::ClientContext;
using grpc::ClientAsyncResponseReader;
using Simple::EchoRequest;
using Simple::EchoResponse;

int main()
{
    std::shared_ptr chan = grpc::CreateChannel("localhost:12345",grpc::InsecureChannelCredentials());
    std::unique_ptr stub(Simple::Server::NewStub(chan));

    ClientContext context;
    EchoRequest req;
    req.set_msg("hello world!");
    EchoResponse resp;
    CompletionQueue cq;
    // 完成rpc调用会将tag添加到cq队列中
    std::unique_ptr> rpc(stub->AsyncEcho(&context, req, &cq));
    Status status;
    // 第三个参数是一个上下文标签,用于帮我们标识这个请求
    // grpc框架只会将其保存起来
    rpc->Finish(&resp, &status, (void*)1);
    void* got_tag;
    bool ok = false;
    // 从队列中获取,请求的标签以及状态
    cq.Next(&got_tag, &ok);
    if(ok && got_tag == (void*)1){
        // check一下结果
        std::cout << resp.msg() << std::endl;
    }
    return 0;
}

服务端代码

异步服务端不方便理解,可以参考:grpc使用记录(三)简单异步服务实例
这里主要涉及到的类包括grpc::ServerCompletionQueuegrpc::ServerAsyncResponseWritergrpc::ServerAsyncResponseWriterSimple::Server::AsyncService
主要的处理流程是,

  1. 注册一个请求,传入上下文内容,包括context、req、resp以及你自己定义的上下文数据对象(可以作为tag)。
  2. 主循环消费队列,取出一个数据对象,调用处理逻辑。如果是还未处理,则进行rpc逻辑处理,然后用grpc::ServerAsyncResponseWriter异步写响应,并且分配一个新的数据对象(构造的时候会调用Proceed函数注册处理的请求);如果是以及处理好的请求,释放数据对象的空间。
    可以这么说,cq的作用就是存放CallData,然后主循环不断读取CallData,然后根据其中的上下文信息,做出相应的处理。
    RequestXXX()其实就是注册一个rpc请求,然后我们会传入CallData的地址参数,是为了再接受到指定rpc之后写入数据对象到消息队列中。
    Finish()是异步写回响应,这里的传入this指针也是为了让其完成写回后讲this添加到消息队列中。

代码如下:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "../protos/simple/simple.grpc.pb.h"
using grpc::Server;
using grpc::ServerAsyncResponseWriter;
using grpc::ServerBuilder;
using grpc::ServerCompletionQueue;
using grpc::ServerContext;
using grpc::Status;
using grpc::ServerBuilder;
using Simple::EchoRequest;
using Simple::EchoResponse;

class ServerImpl final {
    public:
        ~ServerImpl(){
            _server->Shutdown();
            _cq->Shutdown();
        }
        void Run(){
            std::string server_address("localhost:12345");
            ServerBuilder builder;
            builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
            builder.RegisterService(&_service);
            _cq = builder.AddCompletionQueue();
            _server = builder.BuildAndStart();
            std::cout << "Serfer listening on" << server_address << std::endl;
            // main loop
            HandleRpcs();
        }
    private:
        class CallData {
            public:
                CallData(Simple::Server::AsyncService* service, ServerCompletionQueue* cq)
                    :_service(service), _cq(cq), _responder(&_ctx), _status(CREATE) {
                        Proceed();
                    }
                void Proceed() {
                    if (_status == CREATE) {
                        _status = PROCESS;
                        // 注册请求处理
                        // RequestEcho其实就三确定注册方法Echo
                        // 然后传入处理请求的数据对象
                        // ctx、req、responder_、_cq、this
                        // this对于队列来说是标签
                        _service->RequestEcho(&_ctx, &_req,&_responder,_cq,_cq,this);
                    } else if (_status == PROCESS) {
                        // 已经开始处理一个请求了,生成一个新对象供下一个使用
                        new CallData(_service, _cq); // 会调用proceed注册请求
                        _resp.set_msg(_req.msg());
                        _status = FINISH;
                        _responder.Finish(_resp, Status::OK, this);
                    } else {
                        GPR_ASSERT(_status == FINISH);
                        delete this;
                    }
                }
            private:
                Simple::Server::AsyncService* _service;
                ServerCompletionQueue* _cq;
                ServerContext _ctx;
                EchoRequest _req;
                EchoResponse _resp;
                ServerAsyncResponseWriter _responder;
                enum CallStatus { CREATE, PROCESS, FINISH};
                CallStatus _status;
        };

        void HandleRpcs() {
            new CallData(&_service, _cq.get());
            void* tag;
            bool ok;
            while(true){
                GPR_ASSERT(_cq->Next(&tag, &ok));
                GPR_ASSERT(ok);
                static_cast(tag)->Proceed();
            }
        }
        std::unique_ptr _cq;
        Simple::Server::AsyncService _service;
        std::unique_ptr _server;
};

int main()
{
    ServerImpl server;
    server.Run();
    return 0;
}

你可能感兴趣的:(grpcc++异步)