[英]gRPC and etcd client
这个问题涉及etcd
特定的东西,但是我认为这个问题通常与gRPC
工作有关。 我正在尝试为某些键创建etcd
Watch
,因为文档稀疏,所以我看了一下诺基亚的实现 。很容易将代码适应我的需求,我想出了一个效果很好的第一个版本,创建WatchCreateRequest
并启动密钥更新回调。 到现在为止还挺好。 然后,我尝试添加多个按键来观看。 惨败! 在这种情况下, ClientAsyncReaderWriter
无法读取/写入。 现在到问题。
如果我班上有以下成员
Watch::Stub watchStub;
CompletionQueue completionQueue;
ClientContext context;
std::unique_ptr<ClientAsyncReaderWriter<WatchRequest, WatchResponse>> stream;
WatchResponse reply;
并且我想支持添加到我的班级的多个Watches
,我想我必须为每个Watcher保留几个变量,而不是作为类成员。 首先,我想, WatchResponse reply
应该是每个Watch
WatchResponse reply
。 我对信息stream
不太确定,是否应该每个Watch
保持一个? 我几乎可以确定context
可以被所有Watches
重用,并且100%可以确保stub
和completionQueue
可以被所有Watches
重用。 所以问题是我的猜测正确吗? 线程安全性如何? 没有找到任何描述可以从多线程安全使用哪些对象以及必须在哪里同步访问的文档。 任何指向文档的链接( 不是此链接)将不胜感激!
在将成员分成单个Watch
属性之前测试代码(我知道没有适当的关闭)
using namespace grpc;
class Watcher
{
public:
using Callback = std::function<void(const std::string&, const std::string&)>;
Watcher(std::shared_ptr<Channel> channel) : watchStub(channel)
{
stream = watchStub.AsyncWatch(&context, &completionQueue, (void*) "create");
eventPoller = std::thread([this]() { WaitForEvent(); });
}
void AddWatch(const std::string& key, Callback callback)
{
AddWatch(key, callback, false);
}
void AddWatches(const std::string& key, Callback callback)
{
AddWatch(key, callback, true);
}
private:
void AddWatch(const std::string& key, Callback callback, bool isRecursive)
{
auto insertionResult = callbacks.emplace(key, callback);
if (!insertionResult.second) {
throw std::runtime_error("Event handle already exist.");
}
WatchRequest watch_req;
WatchCreateRequest watch_create_req;
watch_create_req.set_key(key);
if (isRecursive) {
watch_create_req.set_range_end(key + "\xFF");
}
watch_req.mutable_create_request()->CopyFrom(watch_create_req);
stream->Write(watch_req, (void*) insertionResult.first->first.c_str());
stream->Read(&reply, (void*) insertionResult.first->first.c_str());
}
void WaitForEvent()
{
void* got_tag;
bool ok = false;
while (completionQueue.Next(&got_tag, &ok)) {
if (ok == false) {
break;
}
if (got_tag == (void*) "writes done") {
// Signal shutdown
}
else if (got_tag == (void*) "create") {
}
else if (got_tag == (void*) "write") {
}
else {
auto tag = std::string(reinterpret_cast<char*>(got_tag));
auto findIt = callbacks.find(tag);
if (findIt == callbacks.end()) {
throw std::runtime_error("Key \"" + tag + "\"not found");
}
if (reply.events_size()) {
ParseResponse(findIt->second);
}
stream->Read(&reply, got_tag);
}
}
}
void ParseResponse(Callback& callback)
{
for (int i = 0; i < reply.events_size(); ++i) {
auto event = reply.events(i);
auto key = event.kv().key();
callback(event.kv().key(), event.kv().value());
}
}
Watch::Stub watchStub;
CompletionQueue completionQueue;
ClientContext context;
std::unique_ptr<ClientAsyncReaderWriter<WatchRequest, WatchResponse>> stream;
WatchResponse reply;
std::unordered_map<std::string, Callback> callbacks;
std::thread eventPoller;
};
很抱歉,我对此处的Watch
设计不太确定。 我不清楚您是否要为每个Watch
创建一个gRPC调用。
无论如何,每个gRPC调用都有其自己的ClientContext
, ClientAsyncReaderWriter
。 但是stub
和CompletionQueue
不是按调用的。
据我所知,没有中心位置可以找到线程安全类。 您可能需要阅读API文档才能获得正确的期望。
当我编写异步服务器负载报告服务时 ,我自己添加同步的唯一地方是在CompletionQueue
周围,因此,如果关闭了CQ,我就不会将新标签排队。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.