我们目前正在寻找RPC
框架,但遗憾的是我们无法找到任何具有信号功能的框架。我们查看了gRPC
,Apache Thrift
,Cap-n-Proto
,发现其中没有一个提供开箱即用的功能,就像DBus一样。值得一提的是,我们需要它作为IPC。此外,我们需要监视另外一个套接字,因此一个用于RPC服务器,另一个用于另一个服务器。在DBus
我们可以将它添加到glib的mainloop中。我们的目标RPC必须允许这样做。
P.S。 DBus
并不是我们真正需要的,因为我们只需要客户端 - 服务器架构而不是客户端总线守护进程。
P.P.S。关于off-topic
- 我在这个问题中看不到什么需要自以为是的答案。答案应该包含事实而不是意见。
答案 0 :(得分:7)
信号可以通过几种不同的方式在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调用(客户端 - &gt;服务器和服务器 - >客户端),所以您可以使用回调实现“信号”或发布/订阅机制:
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是禁止的。然而,那是很久以前的事了,也许从那时起它就发生了变化。