[英]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.