有没有像 DBus 那样具有信号功能的 RPC 框架?
Posted
技术标签:
【中文标题】有没有像 DBus 那样具有信号功能的 RPC 框架?【英文标题】:Is there any RPC framework with signal functionality as in DBus? 【发布时间】:2017-05-31 08:32:00 【问题描述】:我们目前正在寻找RPC
框架,但遗憾的是找不到任何具有信号功能的框架,但我们需要它。我们查看了gRPC
、Apache Thrift
、Cap-n-Proto
,发现它们中没有一个像 DBus 那样提供开箱即用的功能。值得一提的是,我们需要它作为 IPC。此外,我们需要再监控 1 个套接字,一个用于 RPC 服务器,另一个用于另一个服务器。在DBus
中,我们可以将它添加到 glib 的主循环中。我们的目标 RPC 必须允许这样做。
附: DBus
并不是我们真正需要的,因为我们只需要客户端-服务器架构而不是客户端-总线-守护进程。
P.P.S.关于off-topic
- 我在这个问题中看不到任何需要固执己见的答案。答案应包含事实而非观点。
【问题讨论】:
【参考方案1】:信号可以通过几种不同的方式在 Cap'n Proto 之上实现。
对象链
Cap'n Proto RPC 调用需要很长时间才能完成没有问题。同一连接上的其他呼叫可以正常继续,并且您一次可以有许多未完成的呼叫。因此,接收信号的一种策略是在返回之前有一个等待信号的调用。
许多 RPC 系统支持挂起调用,但还有一个额外的挑战:如果您有一个信号流,并且客户端观察流中的每个信号很重要,那么如果是新的,事情就会变得复杂信号的生成速度比客户端调用 RPC 读取它们的速度要快。您需要为每个客户保留一个缓冲区。但是如果客户端死了并且停止发出请求怎么办?现在您需要某种超时,然后再清除它。
与大多数其他 RPC 系统不同,Cap'n Proto 支持动态生成新对象。因此,您可以将信号流表示为对象链。例如:
struct MyPayload ...
interface MyInterface
subscribe @0 () -> (firstSignal :Signal(MyPayload));
# Subscribe to signals from this interface.
interface Signal(Type)
# One signal in a stream of signals. Has a payload, and lets you
# wait for the next signal.
get @0 () -> (value :Type);
# Gets the payload value of this signal. (Returns immediately.)
waitForNext @1 () -> (nextSignal :Signal(Type));
# Waits for the next signal in the sequence, returning a new
# `Signal` object representing it.
这极大地简化了服务器端的状态管理,因为 Cap'n Proto 会在所有客户端都表明它们完成处理后立即自动调用每个对象的析构函数(通过销毁客户端引用,也就是“丢弃”它)。如果客户端断开连接,它的所有引用都将被隐式删除。
回调
因为 Cap'n Proto 允许双向 RPC 调用(客户端 -> 服务器和服务器 -> 客户端),您可以使用回调实现“信号”或发布/订阅机制:
struct MyPayload ...
interface MyInterface
subscribe @0 (cb :Callback(MyPayload)) -> (handle :Handle);
interface Callback(Type)
call @0 (value :Type);
interface Handle
客户端调用subscribe()
并传递一个回调对象cb
。然后,服务器可以在有信号时随时回调客户端。
注意subscribe()
返回一个Handle
,这是一个没有方法的对象。这样做的目的是检测客户端何时取消订阅。如果客户端丢弃handle
,将通知服务器(服务器端对象的析构函数将运行),然后服务器可以取消注册回调。这也处理了客户端断开连接的情况——所有对象引用都在断开连接时隐式删除。
乍一看,由于其简单性,该解决方案可能看起来比对象链解决方案要好得多。但是,它的问题是您现在有指向两个方向的对象引用,这可能导致循环。在您的客户端代码中,您必须小心确保回调实现不“拥有”保持它注册的句柄,否则它将永远不会被清理(除非连接关闭)。您还必须确保在删除句柄后仍然可以在短时间内调用回调,同时等待服务器取消注册回调。这些问题在对象链解决方案中不存在,这可能会使该解决方案更易于实施。
其他 RPC 系统
我在上面讨论了 Cap'n Proto,因为我是作者,而且它提供了比大多数 RPC 系统更多的选项。
如果你使用 gRPC,你可以使用它的“流”特性来支持信号之类的东西。流式 RPC 可以随时间返回多个响应。
我不确定 Thrift。我上次尝试时,请求必须是 FIFO,这意味着长时间运行的 RPC 是禁忌。然而,那是很久以前的事了,也许从那以后它已经改变了。
【讨论】:
第一次收到信号后不想注销回调怎么办?你的意思是我应该以某种方式将Handle
存储在某个地方吗?
@VictorPolevoy 客户端应该将Handle
保存在内存中,是的。不过,您可能不会存储到磁盘,因为整个操作都与特定的实时网络连接相关联。如果连接断开,则回调将不再起作用。客户端需要重新连接并注册一个新的回调。
有没有办法通过向 capnp 提供一个数据缓冲区来解析之前从套接字接收到的数据,从而从外部使用套接字?
@VictorPolevoy 在 C++ 实现中,您可以通过提供自己的 kj::Asynciostream
实现来完成此操作。但是请注意,Cap'n Proto 的 RPC 协议通常需要任意双向通信。以上是关于有没有像 DBus 那样具有信号功能的 RPC 框架?的主要内容,如果未能解决你的问题,请参考以下文章