簡體   English   中英

GRPC/C++ - 服務器僅從雙向流中讀取第一條消息

[英]GRPC/C++ - Server reads only the first message from the bidi stream

我在 C++ 中使用 gRPC。 我有一個異步服務器和同步客戶端。 rpc 是雙向流類型。

這是我與客戶端發送消息的方式:

class ConnectionService {
public:
    ConnectionService(std::shared_ptr<Channel> channel)
            : stub_(Connection::NewStub(channel)) {}

    void HearthBeat() {
        ClientContext context;

        std::shared_ptr<grpc::ClientReaderWriter<Pulse, Pulse> > stream(
                stub_->HearthBeat(&context));

        std::thread writer([stream]() {
            for (int i = 0; i < 100; ++i) {
                Pulse p;
                p.set_rate(50);
                stream->Write(p);
            }
            stream->WritesDone();
        });

        Pulse server_pulse;
        while (stream->Read(&server_pulse)) {
            std::cout << "Got message " << server_pulse.rate()<< std::endl;
        }
        writer.join();
        Status status = stream->Finish();
        if (!status.ok()) {
            std::cout << "RouteChat rpc failed." << std::endl;
        }
    }

private:
    std::unique_ptr<Connection::Stub> stub_;
};

這是我在服務器上閱讀和回復的方式:

void Vibranium::ConnectionManager::HearthBeatMethod::Create() {
    connectionService_->RequestHearthBeat(&ctx_, &stream_, cq_, cq_,this);
    status_ = PROCESS;
}

void Vibranium::ConnectionManager::HearthBeatMethod::Process() {
    new HearthBeatMethod(connectionService_, cq_);
    stream_.Read(&request_, this);
    status_ = READ_CALLED;
}

bool Vibranium::ConnectionManager::HearthBeatMethod::CheckForClientMetadata() {
    return false;
}

void Vibranium::ConnectionManager::HearthBeatMethod::ReadStream() {
    std::cout << "Received: " << request_.rate() << std::endl;
    reply_.set_rate(65);
    std::cout << "Rate replied: " << reply_.rate() << std::endl;
    stream_.Write(reply_, this);
    status_ = WRITE_CALLED;
}

void Vibranium::ConnectionManager::HearthBeatMethod::WriteToStream() {
    stream_.Finish(grpc::Status::OK, this);
    status_ = FINISH;
}

這是我啟動服務器的方式:

void Vibranium::Server::Run() {
    std::string server_address(serverIp_+":"+serverPort_);

    grpc::ServerBuilder builder;
    // Listen on the given address without any authentication mechanism.
    builder.AddChannelArgument(GRPC_ARG_KEEPALIVE_TIME_MS, 3000);
    builder.AddChannelArgument(GRPC_ARG_KEEPALIVE_TIMEOUT_MS, 3000);
    builder.AddChannelArgument(GRPC_ARG_HTTP2_BDP_PROBE, 1);
    builder.AddChannelArgument(GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS, 1);
    builder.AddChannelArgument(GRPC_ARG_HTTP2_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS, 1000);
    builder.AddChannelArgument(GRPC_ARG_HTTP2_MIN_SENT_PING_INTERVAL_WITHOUT_DATA_MS, 3000);
    builder.AddChannelArgument(GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA, 0);
    builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
    RegisterServices(builder);
    cq_ = builder.AddCompletionQueue();
    // Finally assemble the server.
    server_ = builder.BuildAndStart();
    std::cout << "Server listening on " << server_address << std::endl;

    // Proceed to the server's main loop.
    HandleRpcs();
}

void Vibranium::Server::HandleRpcs() {
    RegisterMethods();
    void* tag;  // uniquely identifies a request.
    bool ok;
    while (true) {
        GPR_ASSERT(cq_->Next(&tag, &ok));
        GPR_ASSERT(ok);
        static_cast<ServiceMethod*>(tag)->Proceed();
    }
}

這是Proceed(); 是在做:

void ServiceMethod::Proceed() {
    if (status_ == CREATE) {
        Create();
    } else if (status_ == PROCESS) {
        CheckClient();
        Process();
    } else if(status_ == READ_CALLED){
        ReadStream();
    } else if(status_ == WRITE_CALLED){
        WriteToStream();
    } else {
        Finish();
    }
}

void ServiceMethod::Finish() {
    GPR_ASSERT(status_ == FINISH);
    // Once in the FINISH state, deallocate ourselves (ServiceMethod).
    delete this;
}

因此,當我觸發客戶端時,它會發送 1 條消息而不是 for 循環中描述的 100 條消息。 在服務器上我可以看到輸出:

Received: 50
Rate replied: 65

在客戶端,輸出是:

Got message 65

所以通過這個我可以看到客戶端和服務器之間有通信,但是服務器似乎只接收和發送第一條消息。 為什么會這樣,我該如何解決?

我認為您看到的崩潰的直接原因是在HearthBeatMethod::Process() ,您使用相同的標簽開始讀取和寫入,並且該標簽甚至沒有初始化(它是一個void* tag從未給出值),因此您基本上無法判斷這些操作中的任何一個何時完成。 更重要的是(這可能是崩潰發生的地方), Server::HandleRpcs()中輪詢完成隊列的代碼假設每個標簽實際上是一個ServiceMethod對象的地址,其Proceed()方法將是叫。 由於返回的標記是未初始化的指針,因此您基本上是在任意地址上調用方法,這會導致崩潰。

我也有一些關於這里更廣泛設計的評論,以防它們有用。

首先,對於雙向流媒體服務,您可能需要更多的狀態,而不僅僅是 CREATE、PROCESS 和 FINISH,因為您一次只能進行一個讀取或寫入。 您需要知道每次讀取何時完成,以便您可以等到上一次讀取完成后再開始下一次讀取。 寫同樣的事情。 請注意,讀取和寫入根本不同步,因此它們可以在完全不同的時間開始和結束。

其次,我不清楚你為什么要創建一個單獨的流式 RPC 來處理連接管理。 gRPC 通道本身應該為您處理連接管理; 在您的應用程序中,您無需擔心。 原則上,應用程序應該只發送它想要的單個 RPC,讓通道為您處理連接管理。 如果您關心這一點的原因是您正在做某種會話關聯性的事情,那么請考慮為您的應用程序本身使用流式 RPC。

我希望這些信息有幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM