mirror of https://github.com/oxen-io/oxen-mq.git
Loki->Oxen rebrand the README
This commit is contained in:
parent
780246858f
commit
dccbd1e8cd
67
README.md
67
README.md
|
@ -1,14 +1,15 @@
|
||||||
# LokiMQ - zeromq-based message passing for Loki projects
|
# OxenMQ - high-level zeromq-based message passing for network-based projects
|
||||||
|
|
||||||
This C++17 library contains an abstraction layer around ZeroMQ to support integration with Loki
|
This C++17 library contains an abstraction layer around ZeroMQ to provide a high-level interface to
|
||||||
authentication, RPC, and message passing. It is designed to be usable as the underlying
|
authentication, RPC, and message passing. It is used extensively within Oxen projects (hence the
|
||||||
communication mechanism of SN-to-SN communication ("quorumnet"), the RPC interface used by wallets
|
name) as the underlying communication mechanism of SN-to-SN communication ("quorumnet"), the RPC
|
||||||
and local daemon commands, communication channels between lokid and auxiliary services (storage
|
interface used by wallets and local daemon commands, communication channels between oxend and
|
||||||
server, lokinet), and also provides a local multithreaded job scheduling within a process.
|
auxiliary services (storage server, lokinet), and also provides local multithreaded job scheduling
|
||||||
|
within a process.
|
||||||
|
|
||||||
Messages channels can be encrypted (using x25519) or not -- however opening an encrypted channel
|
Messages channels can be encrypted (using x25519) or not -- however opening an encrypted channel
|
||||||
requires knowing the server pubkey. All SN-to-SN traffic is encrypted, and other traffic can be
|
requires knowing the server pubkey. Within Oxen, all SN-to-SN traffic is encrypted, and other
|
||||||
encrypted as needed.
|
traffic can be encrypted as needed.
|
||||||
|
|
||||||
This library makes minimal use of mutexes, and none in the hot paths of the code, instead mostly
|
This library makes minimal use of mutexes, and none in the hot paths of the code, instead mostly
|
||||||
relying on ZMQ sockets for synchronization; for more information on this (and why this is generally
|
relying on ZMQ sockets for synchronization; for more information on this (and why this is generally
|
||||||
|
@ -16,20 +17,20 @@ much better performing and more scalable) see the ZMQ guide documentation on the
|
||||||
|
|
||||||
## Basic message structure
|
## Basic message structure
|
||||||
|
|
||||||
LokiMQ messages come in two fundamental forms: "commands", consisting of a command named and
|
OxenMQ messages come in two fundamental forms: "commands", consisting of a command named and
|
||||||
optional arguments, and "requests", consisting of a request name, a request tag, and optional
|
optional arguments, and "requests", consisting of a request name, a request tag, and optional
|
||||||
arguments.
|
arguments.
|
||||||
|
|
||||||
All channels are capable of bidirectional communication, and multiple messages can be in transit in
|
All channels are capable of bidirectional communication, and multiple messages can be in transit in
|
||||||
either direction at any time. LokiMQ sets up a "listener" and "client" connections, but these only
|
either direction at any time. OxenMQ sets up a "listener" and "client" connections, but these only
|
||||||
determine how connections are established: once established, commands can be issued by either party.
|
determine how connections are established: once established, commands can be issued by either party.
|
||||||
|
|
||||||
The command/request string is one of two types:
|
The command/request string is one of two types:
|
||||||
|
|
||||||
`category.command` - for commands/requests registered by the LokiMQ caller (e.g. lokid). Here
|
`category.command` - for commands/requests registered by the OxenMQ caller (e.g. oxend). Here
|
||||||
`category` must be at least one character not containing a `.` and `command` may be anything. These
|
`category` must be at least one character not containing a `.` and `command` may be anything. These
|
||||||
categories and commands are registered according to general function and authentication level (more
|
categories and commands are registered according to general function and authentication level (more
|
||||||
on this below). For example, for lokid categories are:
|
on this below). For example, for oxend categories are:
|
||||||
|
|
||||||
- `system` - is for RPC commands related to the system administration such as mining, getting
|
- `system` - is for RPC commands related to the system administration such as mining, getting
|
||||||
sensitive statistics, accessing SN private keys, remote shutdown, etc.
|
sensitive statistics, accessing SN private keys, remote shutdown, etc.
|
||||||
|
@ -42,14 +43,14 @@ on this below). For example, for lokid categories are:
|
||||||
The difference between a request and a command is that a request includes an additional opaque tag
|
The difference between a request and a command is that a request includes an additional opaque tag
|
||||||
value which is used to identify a reply. For example you could register a `general.backwards`
|
value which is used to identify a reply. For example you could register a `general.backwards`
|
||||||
request that takes a string that receives a reply containing that string reversed. When invoking
|
request that takes a string that receives a reply containing that string reversed. When invoking
|
||||||
the request via LokiMQ you provide a callback to be invoked when the reply arrives. On the wire
|
the request via OxenMQ you provide a callback to be invoked when the reply arrives. On the wire
|
||||||
this looks like:
|
this looks like:
|
||||||
|
|
||||||
<<< [general.backwards] [v71.&a] [hello world]
|
<<< [general.backwards] [v71.&a] [hello world]
|
||||||
>>> [REPLY] [v71.&a] [dlrow olleh]
|
>>> [REPLY] [v71.&a] [dlrow olleh]
|
||||||
|
|
||||||
where each [] denotes a message part and `v71.&a` is a unique randomly generated identifier handled
|
where each [] denotes a message part and `v71.&a` is a unique randomly generated identifier handled
|
||||||
by LokiMQ (both the invoker and the recipient code only see the `hello world`/`dlrow olleh` message
|
by OxenMQ (both the invoker and the recipient code only see the `hello world`/`dlrow olleh` message
|
||||||
parts).
|
parts).
|
||||||
|
|
||||||
In contrast, regular registered commands have no identifier or expected reply callback. For example
|
In contrast, regular registered commands have no identifier or expected reply callback. For example
|
||||||
|
@ -92,7 +93,7 @@ handled for you transparently.
|
||||||
|
|
||||||
## Command arguments
|
## Command arguments
|
||||||
|
|
||||||
Optional command/request arguments are always strings on the wire. The LokiMQ-using developer is
|
Optional command/request arguments are always strings on the wire. The OxenMQ-using developer is
|
||||||
free to create whatever encoding she wants, and these can vary across commands. For example
|
free to create whatever encoding she wants, and these can vary across commands. For example
|
||||||
`wallet.tx` might be a request that returns a transaction in binary, while `wallet.tx_info` might
|
`wallet.tx` might be a request that returns a transaction in binary, while `wallet.tx_info` might
|
||||||
return tx metadata in JSON, and `p2p.send_tx` might encode tx data and metadata in a bt-encoded
|
return tx metadata in JSON, and `p2p.send_tx` might encode tx data and metadata in a bt-encoded
|
||||||
|
@ -101,8 +102,8 @@ data string.
|
||||||
No structure at all is imposed on message data to allow maximum flexibility; it is entirely up to
|
No structure at all is imposed on message data to allow maximum flexibility; it is entirely up to
|
||||||
the calling code to handle all encoding/decoding duties.
|
the calling code to handle all encoding/decoding duties.
|
||||||
|
|
||||||
Internal commands passed between LokiMQ-managed threads use either plain strings or bt-encoded
|
Internal commands passed between OxenMQ-managed threads use either plain strings or bt-encoded
|
||||||
dictionaries. See `lokimq/bt_serialize.h` if you want a bt serializer/deserializer.
|
dictionaries. See `oxenmq/bt_serialize.h` if you want a bt serializer/deserializer.
|
||||||
|
|
||||||
## Sending commands
|
## Sending commands
|
||||||
|
|
||||||
|
@ -115,9 +116,9 @@ Sending a command to a peer is done by using a connection ID, and generally fall
|
||||||
|
|
||||||
The connection ID generally has two possible values:
|
The connection ID generally has two possible values:
|
||||||
|
|
||||||
- a string containing a service node pubkey. In this mode LokiMQ will look for the given SN in
|
- a string containing a service node pubkey. In this mode OxenMQ will look for the given SN in
|
||||||
already-established connections, reusing a connection if one exists. If no connection already
|
already-established connections, reusing a connection if one exists. If no connection already
|
||||||
exists, a new connection to the given SN is attempted (this requires constructing the LokiMQ
|
exists, a new connection to the given SN is attempted (this requires constructing the OxenMQ
|
||||||
object with a callback to determine SN remote addresses).
|
object with a callback to determine SN remote addresses).
|
||||||
- a ConnectionID object, typically returned by the `connect_remote` method (although there are other
|
- a ConnectionID object, typically returned by the `connect_remote` method (although there are other
|
||||||
places to get one, such as from the `Message` object passed to a command: see the following
|
places to get one, such as from the `Message` object passed to a command: see the following
|
||||||
|
@ -143,7 +144,7 @@ The connection ID generally has two possible values:
|
||||||
## Command invocation
|
## Command invocation
|
||||||
|
|
||||||
The application registers categories and registers commands within these categories with callbacks.
|
The application registers categories and registers commands within these categories with callbacks.
|
||||||
The callbacks are passed a LokiMQ::Message object from which the message (plus various connection
|
The callbacks are passed a OxenMQ::Message object from which the message (plus various connection
|
||||||
information) can be obtained. There is no structure imposed at all on the data passed in subsequent
|
information) can be obtained. There is no structure imposed at all on the data passed in subsequent
|
||||||
message parts: it is up to the command itself to deserialize however it wishes (e.g. JSON,
|
message parts: it is up to the command itself to deserialize however it wishes (e.g. JSON,
|
||||||
bt-encoded, or any other encoding).
|
bt-encoded, or any other encoding).
|
||||||
|
@ -151,13 +152,13 @@ bt-encoded, or any other encoding).
|
||||||
The Message object also provides methods for replying to the caller. Simple replies queue a reply
|
The Message object also provides methods for replying to the caller. Simple replies queue a reply
|
||||||
if the client is still connected. Replies to service nodes can also be "strong" replies: when
|
if the client is still connected. Replies to service nodes can also be "strong" replies: when
|
||||||
replying to a SN that has closed connection with a strong reply we will attempt to reestablish a
|
replying to a SN that has closed connection with a strong reply we will attempt to reestablish a
|
||||||
connection to deliver the message. In order for this to work the LokiMQ caller must provide a
|
connection to deliver the message. In order for this to work the OxenMQ caller must provide a
|
||||||
lookup function to retrieve the remote address given a SN x25519 pubkey.
|
lookup function to retrieve the remote address given a SN x25519 pubkey.
|
||||||
|
|
||||||
### Callbacks
|
### Callbacks
|
||||||
|
|
||||||
Invoked command functions are always invoked with exactly one arguments: a non-const LokiMQ::Message
|
Invoked command functions are always invoked with exactly one arguments: a non-const OxenMQ::Message
|
||||||
reference from which the connection info, LokiMQ object, and message data can be obtained.
|
reference from which the connection info, OxenMQ object, and message data can be obtained.
|
||||||
|
|
||||||
The Message object also contains a `ConnectionID` object as the public `conn` member; it is safe to
|
The Message object also contains a `ConnectionID` object as the public `conn` member; it is safe to
|
||||||
take a copy of this and then use it later to send commands to this peer. (For example, a wallet
|
take a copy of this and then use it later to send commands to this peer. (For example, a wallet
|
||||||
|
@ -187,7 +188,7 @@ logins.
|
||||||
Configuration defaults allows controlling the default access for an incoming connection based on its
|
Configuration defaults allows controlling the default access for an incoming connection based on its
|
||||||
remote address. Typically this is used to allow connections from localhost (or a unix domain
|
remote address. Typically this is used to allow connections from localhost (or a unix domain
|
||||||
socket) to automatically be an Admin connection without requiring explicit authentication. This
|
socket) to automatically be an Admin connection without requiring explicit authentication. This
|
||||||
also allows configuration of how public connections should be treated: for example, a lokid running
|
also allows configuration of how public connections should be treated: for example, an oxend running
|
||||||
as a public RPC server would do so by granting Basic access to all incoming connections.
|
as a public RPC server would do so by granting Basic access to all incoming connections.
|
||||||
|
|
||||||
Explicit logins allow the daemon to specify username/passwords with mapping to Basic or Admin
|
Explicit logins allow the daemon to specify username/passwords with mapping to Basic or Admin
|
||||||
|
@ -196,7 +197,7 @@ authentication levels.
|
||||||
Thus, for example, a daemon could be configured to be allow Basic remote access with authentication
|
Thus, for example, a daemon could be configured to be allow Basic remote access with authentication
|
||||||
(i.e. requiring a username/password login given out to people who should be able to access).
|
(i.e. requiring a username/password login given out to people who should be able to access).
|
||||||
|
|
||||||
For example, in lokid the categories described above have authentication levels of:
|
For example, in oxend the categories described above have authentication levels of:
|
||||||
|
|
||||||
- `system` - Admin
|
- `system` - Admin
|
||||||
- `sn` - ServiceNode
|
- `sn` - ServiceNode
|
||||||
|
@ -205,7 +206,7 @@ For example, in lokid the categories described above have authentication levels
|
||||||
|
|
||||||
### Service Node authentication
|
### Service Node authentication
|
||||||
|
|
||||||
In order to handle ServiceNode authentication, LokiMQ uses an Allow callback invoked during
|
In order to handle ServiceNode authentication, OxenMQ uses an Allow callback invoked during
|
||||||
connection to determine both whether to allow the connection, and to determine whether the incoming
|
connection to determine both whether to allow the connection, and to determine whether the incoming
|
||||||
connection is an active service node.
|
connection is an active service node.
|
||||||
|
|
||||||
|
@ -226,7 +227,7 @@ such aliases be used only temporarily for version transitions.
|
||||||
|
|
||||||
## Threads
|
## Threads
|
||||||
|
|
||||||
LokiMQ operates a pool of worker threads to handle jobs. The simplest use just allocates new jobs
|
OxenMQ operates a pool of worker threads to handle jobs. The simplest use just allocates new jobs
|
||||||
to a free worker thread, and we have a "general threads" value to configure how many such threads
|
to a free worker thread, and we have a "general threads" value to configure how many such threads
|
||||||
are available.
|
are available.
|
||||||
|
|
||||||
|
@ -241,7 +242,7 @@ Note that these actual reserved threads are not exclusive: reserving M of N tota
|
||||||
category simply ensures that no more than (N-M) threads are being used for other categories at any
|
category simply ensures that no more than (N-M) threads are being used for other categories at any
|
||||||
given time, but the actual jobs may run on any worker thread.
|
given time, but the actual jobs may run on any worker thread.
|
||||||
|
|
||||||
As mentioned above, LokiMQ tries to avoid exceeding the configured general threads value (G)
|
As mentioned above, OxenMQ tries to avoid exceeding the configured general threads value (G)
|
||||||
whenever possible: the only time we will dispatch a job to a worker thread when we have >= G threads
|
whenever possible: the only time we will dispatch a job to a worker thread when we have >= G threads
|
||||||
already running is when a new command arrives, the category reserves M threads, and the thread pool
|
already running is when a new command arrives, the category reserves M threads, and the thread pool
|
||||||
is currently processing fewer than M jobs for that category.
|
is currently processing fewer than M jobs for that category.
|
||||||
|
@ -277,7 +278,7 @@ when a command with reserve threads arrived.
|
||||||
A common pattern is one where a single thread suddenly has some work that can be be parallelized.
|
A common pattern is one where a single thread suddenly has some work that can be be parallelized.
|
||||||
You could employ some blocking, locking, mutex + condition variable monstrosity, but you shouldn't.
|
You could employ some blocking, locking, mutex + condition variable monstrosity, but you shouldn't.
|
||||||
|
|
||||||
Instead LokiMQ provides a mechanism for this by allowing you to submit a batch of jobs with a
|
Instead OxenMQ provides a mechanism for this by allowing you to submit a batch of jobs with a
|
||||||
completion callback. All jobs will be queued and, when the last one finishes, the finalization
|
completion callback. All jobs will be queued and, when the last one finishes, the finalization
|
||||||
callback will be queued to continue with the task.
|
callback will be queued to continue with the task.
|
||||||
|
|
||||||
|
@ -302,7 +303,7 @@ double do_my_task(int input) {
|
||||||
return 3.0 * input;
|
return 3.0 * input;
|
||||||
}
|
}
|
||||||
|
|
||||||
void continue_big_task(std::vector<lokimq::job_result<double>> results) {
|
void continue_big_task(std::vector<oxenmq::job_result<double>> results) {
|
||||||
double sum = 0;
|
double sum = 0;
|
||||||
for (auto& r : results) {
|
for (auto& r : results) {
|
||||||
try {
|
try {
|
||||||
|
@ -323,7 +324,7 @@ void continue_big_task(std::vector<lokimq::job_result<double>> results) {
|
||||||
void start_big_task() {
|
void start_big_task() {
|
||||||
size_t num_jobs = 32;
|
size_t num_jobs = 32;
|
||||||
|
|
||||||
lokimq::Batch<double /*return type*/> batch;
|
oxenmq::Batch<double /*return type*/> batch;
|
||||||
batch.reserve(num_jobs);
|
batch.reserve(num_jobs);
|
||||||
|
|
||||||
for (size_t i = 0; i < num_jobs; i++)
|
for (size_t i = 0; i < num_jobs; i++)
|
||||||
|
@ -341,7 +342,7 @@ void start_big_task() {
|
||||||
|
|
||||||
This code deliberately does not support blocking to wait for the tasks to finish: if you want such a
|
This code deliberately does not support blocking to wait for the tasks to finish: if you want such a
|
||||||
poor design (which is a recipe for deadlocks: imagine jobs that queuing other jobs that can end up
|
poor design (which is a recipe for deadlocks: imagine jobs that queuing other jobs that can end up
|
||||||
exhausting the worker threads with waiting jobs) then you can implement it yourself; LokiMQ isn't
|
exhausting the worker threads with waiting jobs) then you can implement it yourself; OxenMQ isn't
|
||||||
going to help you hurt yourself like that.
|
going to help you hurt yourself like that.
|
||||||
|
|
||||||
### Single-job queuing
|
### Single-job queuing
|
||||||
|
@ -358,7 +359,7 @@ either using your own thread or a periodic timer (see below) to shepherd those o
|
||||||
|
|
||||||
## Timers
|
## Timers
|
||||||
|
|
||||||
LokiMQ supports scheduling periodic tasks via the `add_timer()` function. These timers have an
|
OxenMQ supports scheduling periodic tasks via the `add_timer()` function. These timers have an
|
||||||
interval and are scheduled as (single-job) batches when the timer fires. They also support
|
interval and are scheduled as (single-job) batches when the timer fires. They also support
|
||||||
"squelching" (enabled by default) that supresses the job being scheduled if a previously scheduled
|
"squelching" (enabled by default) that supresses the job being scheduled if a previously scheduled
|
||||||
job is already scheduled or running.
|
job is already scheduled or running.
|
||||||
|
|
Loading…
Reference in New Issue