Есть ли какая-либо структура RPC с функциональностью сигнала, как в DBus?
В настоящее время мы ищем RPC
фреймворки и не смогли найти ни одного с функциональностью сигнала, к сожалению, однако нам это нужно. Мы смотрели на gRPC
, Apache Thrift
, Cap-n-Proto
и обнаружил, что никто из них не предоставляет такую функциональность из коробки, как DBus. Стоит упомянуть, нам нужно это как IPC. Кроме того, нам нужно отслеживать еще 1 сокет, так что один предназначен для RPC-сервера, а другой - для другого сервера. В DBus
мы могли бы добавить его в основную часть glib. Наш целевой RPC должен позволить это.
PS DBus
на самом деле это не то, что нам нужно, потому что нам нужна только клиент-серверная архитектура вместо client-bus-daemon.
О PPS off-topic
- Я не вижу в этом вопросе ничего, что требовало бы самоуверенного ответа. Ответ должен содержать факты, но не мнения.
1 ответ
Сигналы могут быть реализованы поверх Cap'n Proto несколькими различными способами.
Цепочка объектов
Нет проблем с RPC-вызовом Cap'n Proto, который занимает очень много времени. Другие вызовы по тому же соединению могут продолжаться в обычном режиме, и вы можете иметь много ожидающих вызовов одновременно. Следовательно, одна из стратегий для приема сигналов состоит в том, чтобы иметь вызов, который ждет сигнала перед возвратом.
Многие системы 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 автоматически вызывает деструктор каждого объекта, как только все клиенты сообщают, что с ним покончено (уничтожая ссылку на стороне клиента, иначе говоря, "отбрасывая" ее). Если клиент отключается, все его ссылки неявно отбрасываются.
Callbacks
Поскольку 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 были нет-нет. Однако это было давно, и, возможно, с тех пор все изменилось.