Seastar
High performance C++ framework for concurrent servers
|
Defines a protocol for communication between a server and a client.
A protocol is defined by a Serializer
and a MsgType
. The Serializer
is responsible for serializing and unserializing all types used as arguments and return types used in the protocol. The Serializer
is expected to define a read()
and write()
method for each such type T
as follows:
template <typename Output> void write(const serializer&, Output& output, const T& data); template <typename Input> T read(const serializer&, Input& input, type<T> type_tag); // type_tag used to disambiguate
Where Input
and Output
have a void read(char*, size_t)
and write(const char*, size_t)
respectively. MsgType
defines the type to be used as the message id, the id which is used to identify different messages used in the protocol. These are also often referred to as "verbs". The client will use the message id, to specify the remote method (verb) to invoke on the server. The server uses the message id to dispatch the incoming call to the right handler. MsgType
should be hashable and serializable. It is preferable to use enum for message types, but do not forget to provide hash function for it.
Use register_handler() on the server to define the available verbs and the code to be executed when they are invoked by clients. Use make_client() on the client to create a matching callable that can be used to invoke the verb on the server and wait for its result. Note that register_handler() also returns a client, that can be used to invoke the registered verb on another node (given that the other node has the same verb). This is useful for symmetric protocols, where two or more nodes all have servers as well as connect to the other nodes as clients.
Use protocol::server to listen for and accept incoming connections on the server and protocol::client to establish connections to the server. Note that registering the available verbs can be done before/after listening for connections, but best to ensure that by the time incoming requests are to be expected, all the verbs are set-up.
TODO
RPC supports isolating verb handlers from each other. There are two ways to achieve this: per-handler isolation (the old way) and per-connection isolation (the new way). If no isolation is configured, all handlers will be executed in the context of the scheduling_group in which the protocol::server was created.
Per-handler isolation (the old way) can be configured by using the register_handler() overload which takes a scheduling_group. When invoked, the body of the handler will be executed from the context of the configured scheduling_group.
Per-connection isolation (the new way) is a more flexible mechanism that requires user application provided logic to determine how connections are isolated. This mechanism has two parts, the server and the client part. The client configures isolation by setting client_options::isolation_cookie. This cookie is an opaque (to the RPC layer) string that is to be interpreted on the server using user application provided logic. The application provides this logic to the server by setting resource_limits::isolate_connection to an appropriate handler function, that interprets the opaque cookie and resolves it to an isolation_config. The scheduling_group in the former will be used not just to execute all verb handlers, but also the connection loop itself, hence providing better isolation.
There a few gotchas related to mixing the two isolation mechanisms. This can happen when the application is updated and one of the client/server is still using the old/new mechanism. In general per-connection isolation overrides the per-handler one. If both are set up, the former will determine the scheduling_group context for the handlers. If the client is not configured to send an isolation cookie, the server's resource_limits::isolate_connection will not be invoked and the server will fall back to per-handler isolation if configured. If the client is configured to send an isolation cookie but the server doesn't have a resource_limits::isolate_connection configured, it will use default_isolate_connection() to interpret the cookie. Note that this still overrides the per-handler isolation if any is configured. If the server is so old that it doesn't have the per-connection isolation feature at all, it will of course just use the per-handler one, if configured.
TODO
Serializer | the serializer for the protocol. |
MsgType | the type to be used as the message id or verb id. |
#include <seastar/rpc/rpc.hh>
Classes | |
class | client |
Represents a client side connection. More... | |
class | server |
Represents the listening port and all accepted connections. More... | |
Public Member Functions | |
protocol (Serializer &&serializer) | |
template<typename Func > | |
auto | make_client (MsgType t) |
template<typename Func > | |
auto | register_handler (MsgType t, Func &&func) |
template<typename Func > | |
auto | register_handler (MsgType t, scheduling_group sg, Func &&func) |
future | unregister_handler (MsgType t) |
void | set_logger (std::function< void(const sstring &)> logger) |
void | set_logger (::seastar::logger *logger) |
Set a logger to be used to log messages. | |
const logger & | get_logger () const |
shared_ptr< rpc::server::connection > | make_server_connection (rpc::server &server, connected_socket fd, socket_address addr, connection_id id) override |
bool | has_handler (MsgType msg_id) |
bool | has_handlers () const noexcept |
Public Attributes | |
friend | server |
|
inlinenoexcept |
Checks if any there are handlers registered. Debugging helper, should only be used for debugging and not relied on.
auto seastar::rpc::protocol< Serializer, MsgType >::make_client | ( | MsgType | t | ) |
Creates a callable that can be used to invoke the verb on the remote.
Func | The signature of the verb. Has to be either the same or compatible with the one passed to register_handler on the server. |
t | the verb to invoke on the remote. |
Func == Ret(Args...)
the returned callable has the following signature: future<Ret>(protocol::client&, Args...)
.
|
inlineoverridevirtual |
Implements seastar::rpc::protocol_base.
auto seastar::rpc::protocol< Serializer, MsgType >::register_handler | ( | MsgType | t, |
Func && | func | ||
) |
Register a handler to be called when this verb is invoked.
Func | the type of the handler for the verb. This determines the signature of the verb. |
t | the verb to register the handler for. |
func | the callable to be called when the verb is invoked by the remote. |
auto seastar::rpc::protocol< Serializer, MsgType >::register_handler | ( | MsgType | t, |
scheduling_group | sg, | ||
Func && | func | ||
) |
Register a handler to be called when this verb is invoked.
Func | the type of the handler for the verb. This determines the signature of the verb. |
t | the verb to register the handler for. |
sg | the scheduling group that will be used to invoke the handler in. This can be used to execute different verbs in different scheduling groups. Note that there is a newer mechanism to determine the scheduling groups a handler will run it per invocation, see isolation_config. |
func | the callable to be called when the verb is invoked by the remote. |
|
inline |
Set a logger function to be used to log messages.
future seastar::rpc::protocol< Serializer, MsgType >::unregister_handler | ( | MsgType | t | ) |
Unregister the handler for the verb.
Waits for all currently running handlers, then unregisters the handler. Future attempts to invoke the verb will fail. This becomes effective immediately after calling this function.
t | the verb to unregister the handler for. |