首页 > 解决方案 > gRPC (cpp) - 如何检查 RPC 通道是否连接成功?

问题描述

当我创建通道和存根时,如何检查 RPC 客户端是否真的成功连接到 RPC 服务器?

如果连接失败,我希望能够抛出异常或信号。我不确定在这里尝试哪种方法有资格作为“操作”。我不想进行我们定义的任何 RPC 调用,因为它们都会产生影响......除非唯一的选择是去实现一些“你好,我在这里”的 RPC 方法,但这似乎很愚蠢, 不?

void ChatSyncClient::connect(QUrl const& endpoint)
{
    disconnect();

    // TODO - How do we tell if this fails?
    //        The comments say a lame channel will be returned, where all operations fail
    //        How are we suppose to check right here rather than later?
    auto channel = ::grpc::CreateChannel(endpoint.toString().toStdString(), ::grpc::InsecureChannelCredentials());
    m_stub = std::make_unique<chat_sync::Chat::Stub>(channel);

    emit signalOnConnected();
}

标签: c++grpc

解决方案


在我直接回答你的问题之前,让我提供一些背景知识。

gRPC 通道实际上与连接不同。事实上,一个通道可能有多个并行活动的底层连接,具体取决于通道使用的负载平衡策略。通道 API 的目的是从应用程序中抽象出各个连接的细节;这个想法是应用程序应该将通道视为发送 RPC 的通用接口,通道将在内部自动处理连接管理的细节,因此应用程序不必担心它们。

所以在高层次上,我认为您的应用程序不需要知道或关心通道是否已连接。如果您认为它确实需要知道这一点,那么我实际上会认为这表明您需要退后一步并重新考虑如何构建应用程序。

特别是,我不确定您所说的“我不想进行我们定义的任何 RPC 调用,因为它们都会产生影响”,但这对我来说听起来有点奇怪。我假设您的意思是可能会对服务器的状态产生影响,这意味着 RPC 不是幂等的。您可能想考虑以某种方式将其更改为幂等,因为这样更安全(我稍后会回到原因)。但是,即使您正在处理非幂等 RPC,我也不知道了解底层连接的状态将如何帮助您避免该问题。即使在客户端启动 RPC 时通道已连接,也无法保证服务器会看到它,因为连接可能在应用程序告诉 gRPC 发送它之后但在它上线之前失败。或者在 gRPC 将其连接到网络后,网络本身可能无法将其传送到服务器。或者,更糟糕的是,服务器可以看到它,但网络可能会在服务器的响应返回客户端之前出现故障。确实不能保证 RPC 是原子的,也不能保证客户端和服务器对 RPC 是否成功有相同的看法。在任何这些情况下,客户端所知道的只是 RPC 失败,但它无法从中推断服务器是否真的看到了 RPC。s 响应返回给客户端。不能保证 RPC 是原子的,也不能保证客户端和服务器对 RPC 是否成功有相同的看法。在任何这些情况下,客户端所知道的只是 RPC 失败,但它无法从中推断服务器是否真的看到了 RPC。s 响应返回给客户端。不能保证 RPC 是原子的,也不能保证客户端和服务器对 RPC 是否成功有相同的看法。在任何这些情况下,客户端所知道的只是 RPC 失败,但它无法从中推断服务器是否真的看到了 RPC。

这就是为什么将 RPC 设计为幂等通常更有意义的原因。您可以关注单个 RPC 是否成功,而不是关注通道是否已连接。每当客户端看到 RPC 失败时,它都可以重试。如果服务器碰巧两次看到相同的 RPC,这不会破坏事情(例如,如果您重放删除操作,它会注意到数据已经被删除,因此它将是空操作)。

因此,我的高级回答是,您的客户端代码应该对单个 RPC 故障做出反应,而不是对连接故障做出反应。

综上所述,如果您确实想要监控通道的连接状态,您可以使用连接状态 API来实现。请注意,当您创建 gRPC 通道时,它实际上并不会立即启动连接。最初,它处于空闲状态。channel->GetState(true)当(a)您在通道上发送第一个 RPC 或(b)您调用明确要求它连接时,它将尝试连接(并转换到状态 CONNECTING) 。如果连接尝试失败,通道将转换到状态 TRANSIENT_FAILURE,并定期重试。当它建立连接时,它将转换到状态 READY。

在状态为 TRANSIENT_FAILURE 的通道上发送的任何 RPC 都会立即失败,除非它是wait_for_ready。对于在通道转换到状态 TRANSIENT_FAILURE 时仍处于挂起状态的 RPC 也是如此(例如,如果通道最初处于状态 IDLE 并由于发送 RPC 而开始连接,但随后初始连接尝试失败) .

关于蹩脚的通道,请注意,这只发生在极少数情况下,即您尝试使用 gRPC 不知道如何解析的格式错误的目标 URI 创建通道。如果 gRPC 知道它永远无法解析您指定的名称,它将返回一个蹩脚的通道,这基本上是一个永久处于状态 TRANSIENT_FAILURE 的通道。

我希望这些信息对你有用。


推荐阅读