This repository has been archived by the owner on Nov 28, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
25 changed files
with
664 additions
and
130 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
book |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
[book] | ||
authors = ["jean-airoldie"] | ||
multilingual = false | ||
src = "src" | ||
title = "Guide" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# Summary | ||
|
||
[About](./about.md) | ||
|
||
* [Basics](./basics/README.md) | ||
* [Socket](./basics/socket.md) | ||
* [Methods](./basics/methods.md) | ||
* [Patterns](./basics/patterns.md) | ||
* [Advanced](./advanced/README.md) | ||
* [Custom Protocols](./advanced/protocols.md) | ||
* [Examples](./examples/README.md) | ||
* [Basic Request Reply](./examples/basic_req_rep.md) | ||
* [Reliable Request Reply](./examples/reliable_req_rep.md) | ||
* [Secure Request Reply](./examples/secure_req_rep.md) | ||
|
||
[Glossary](./glossary.md) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# About | ||
|
||
## This is a WIP guide | ||
|
||
I believe that `ZeroMQ` is a diamond in the rough that is still a in the early | ||
adoption stage despite being a mature project. In my opinon, the lack of traction | ||
for the library is due to the community rather than the technology itself. | ||
|
||
`ZeroMQ` suffers from a significant learning curve due to its foreign concepts. | ||
It requires the programmer to think differently about messaging. When this is | ||
combined with a lacking documentation, it results in very significant time | ||
requirement to properly understand how the library works and how it can be used. | ||
|
||
I want this guide to reduce this time requirement by explaining the key concepts of | ||
`ZeroMQ`, give real world usage examples as well as a general vision of how it | ||
could be use. To do so, we will use [libzmq-rs] which is a library aimed at | ||
making `ZeroMQ` dead simple to use. | ||
|
||
## Reading Tips | ||
* There is a search-bar friendly glossary at the end of the guide. | ||
|
||
[libzmq-rs]: https://github.com/jean-airoldie/libzmq-rs |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Advanced | ||
|
||
Now that the basic stuff is taken care off, lets dig deeper. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
# Custom Protocols | ||
|
||
For two peers to be able to communicate, they must share a contract. In the | ||
world of communication, these are called protocols. `ZeroMQ` enables | ||
programmer to create protocols that suit their needs by removing most of the | ||
boilerplate. | ||
|
||
You might have realized by now that there is no strict concept of request-reply | ||
as a socket operation. Indeed the library does not enforce a client socket | ||
to follow a `send` call by a `recv` call. This does't mean however that this | ||
strict type of request-reply could not be achieved. To do so, a programmer could | ||
easily write the following code: | ||
|
||
```rust | ||
// Client side | ||
fn request_reply(&mut self, msg: Msg) -> Result<Msg, Error> { | ||
self.client.send(msg)?; | ||
self.client.recv_msg()? | ||
} | ||
|
||
// Server side | ||
fn run(&mut self) -> Result<(), Error> { | ||
loop { | ||
let request = self.server.recv_msg()?; | ||
let reply = self.on_request(request)?; | ||
self.server.send(reply) | ||
} | ||
} | ||
``` | ||
|
||
This creates an implicit contract between the client and the server. | ||
We will disregard the error handling and timeouts for simplicity. | ||
* The client must send one request at a time and wait for one reply. | ||
* The server must wait for a request and send one reply. | ||
|
||
Since contract must be ensured at the application level, it must be properly | ||
documentated for developpers to be able to respect it. | ||
|
||
`ZeroMQ` does not enforce a particular messaging protocol, instead | ||
it offers all the tools to build one. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Basics | ||
|
||
This is the minimal set of concepts required to get a decent grasp of `libzmq`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
# Basics | ||
|
||
These are the basic methods required to use a socket. | ||
|
||
## Connect | ||
|
||
The socket [connect] method is use to connect to a peer socket bound | ||
at an endpoint to communicate with a peer. Usually a client socket will connect | ||
to a server socket, but it could be the other way around. | ||
|
||
```rust | ||
let addr: TcpAddr = "8.8.8.8:420".try_into()?; | ||
client.connect(addr)?; | ||
``` | ||
|
||
Calling [connect] on a socket is not guaranteed to connect to the peer right | ||
away. Usually, the actual connect call will be delayed until it is needed | ||
(e.g. when sending a message). | ||
|
||
Connections in `ZeroMQ` are different from traditional connections is the | ||
sense that they automatically handle failure. For instance, if a connection | ||
fails because of a network error, it will be automatically reconnected if | ||
possible. | ||
|
||
Furthermore, to successfully connect to a peer, the handshake corresponding to | ||
the mechanism used must succeed. This handshake is also done in the background | ||
and might fail for various reasons. | ||
|
||
## Bind | ||
|
||
The socket [bind] method is used to bind a local endpoint to accept connections | ||
from peers. Usually a server socket will bind a known endpoint so that other socket | ||
can connect to it. | ||
|
||
```rust | ||
let addr: TcpAddr = "127.0.0.1:*".try_into()?; | ||
server.bind(addr)?; | ||
``` | ||
|
||
Contrairy to [connect], [bind] will attempt to bind to the endpoint straight | ||
away. If the bind call succeeds, the socket will start to accept connections | ||
attempts to this endpoint. | ||
|
||
## Send | ||
|
||
This a fundamental operation of a socket used to transfert messages to another | ||
socket. To be able to send messages, a socket must implement the [SendMsg] trait. | ||
|
||
```rust | ||
client.send(msg)?; | ||
``` | ||
|
||
When [send] is called on a socket, it will attempt to queue the message | ||
to its outgoing buffer. If the buffer is full, meaning it has reached the | ||
high water mark, the operation will block. If the [send_timeout] is set | ||
to `None`, the operation will block until the buffer can accomodate for | ||
the message. Otherwise if a duration is specified, it attempt to queue | ||
the message for that duration and if it fails, return [WouldBlock]. | ||
the timeout. | ||
|
||
There is also the [try_send] method which will return with [WouldBlock] immediately | ||
if it cannot queue the message. | ||
|
||
Queued messages are send by a background I/O thread to the peer socket. | ||
For the messages to be actually sent two conditions must be met: | ||
* The connection with the peer socket is up. | ||
* The peer socket can receive messages (its incoming buffer is not full). | ||
|
||
Conceptually, a full outgoing buffer can mean many things: | ||
* The connection has crashed temporarily (network error etc.) | ||
* The peer socket has crashed and is restarting. | ||
* The peer socket receives message slower than we can send | ||
them (thus this is a back throttling mechanism) | ||
* Etc. | ||
|
||
Many of these scenarios are conceptually indistinguishable. Therefore | ||
the user has to decide what to do depending on the context. | ||
|
||
## Recv | ||
|
||
You guessed it, [recv] is a socket operation used to receive messages from | ||
another socket. To be able to receive messages, a socket must implement | ||
the [RecvMsg] trait. | ||
|
||
```rust | ||
let msg = client.recv_msg()?; | ||
``` | ||
|
||
Calling [recv] on a socket will attempt to extract a message from its | ||
incoming buffer. If the incoming buffer is empty, the operation will | ||
block until a mesage is received in the buffer. If the [recv_timeout] | ||
is specified, it will try to extract a message from the buffer for the | ||
given duration and return [WouldBlock] if it failed. | ||
|
||
There is also the [try_recv] method which, similarly to [try_send], will return | ||
with [WouldBlock] immediately if it cannot queue the message. | ||
|
||
The incoming buffer receives message from the background I/O thread from the | ||
peer socket. For the messages to be actually received two conditions must be met: | ||
* The connection with the peer socket is up. | ||
* The incoming buffer is not full. | ||
|
||
Conceptually, an empty incoming buffer can mean many things: | ||
* The socket can receive messages faster than what the peer can send. | ||
* The peer has no messages to send. | ||
* The connection has a network error. | ||
* The peer has crashed | ||
* Etc. | ||
|
||
Like before, many of these scenarios are conceptually indistinguishable. | ||
We have to decide what to do depending on the context. | ||
|
||
[send]: https://docs.rs/libzmq/0.1/libzmq/prelude/trait.SendMsg.html#method.send | ||
[try_send]: https://docs.rs/libzmq/0.1/libzmq/prelude/trait.SendMsg.html#method.try_send | ||
[SendMsg]: https://docs.rs/libzmq/0.1/libzmq/prelude/trait.SendMsg.html | ||
[recv]: https://docs.rs/libzmq/0.1/libzmq/prelude/trait.RecvMsg.html#method.recv | ||
[try_recv]: https://docs.rs/libzmq/0.1/libzmq/prelude/trait.RecvMsg.html#method.try_recv | ||
[RecvMsg]: https://docs.rs/libzmq/0.1/libzmq/prelude/trait.RecvMsg.html | ||
[connect]: https://docs.rs/libzmq/0.1/libzmq/prelude/trait.Socket.html#method.connect | ||
[bind]: https://docs.rs/libzmq/0.1/libzmq/prelude/trait.Socket.html#method.bind | ||
[send_timeout]: https://docs.rs/libzmq/0.1/libzmq/prelude/trait.SendMsg.html#method.send_timeout | ||
[recv_timeout]: https://docs.rs/libzmq/0.1/libzmq/prelude/trait.RecvMsg.html#method.recv_timeout |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
# Patterns | ||
|
||
These are the most basic socket patterns in `libzmq`. | ||
|
||
## Client-Server | ||
|
||
The `Client-Server` pattern is a advanced asynchronous request-reply pattern. | ||
|
||
The [Server] receives messages with a unique [RoutingId] associated with a | ||
[Client]. This [RoutingId] can be used by the [Server] to route replies to the | ||
[Client]. | ||
``` | ||
<───> client | ||
server <───> client | ||
<───> client | ||
``` | ||
|
||
The [Client] socket receives upstream messages in a fair-queued fashion | ||
``` | ||
server ─┐ | ||
server ────> client | ||
server ─┘ | ||
``` | ||
|
||
## Radio-Dish | ||
|
||
The `Radio-Dish` pattern is an asynchronous publish-subscribe pattern. | ||
|
||
The [Radio] socket send messages in a fan-out fashion to all [Dish] | ||
that [joined] the message's [Group]. | ||
``` | ||
────> dish | ||
radio ────> dish | ||
────> dish | ||
``` | ||
|
||
The [Dish] socket receive messages from [Group] it has [joined] in a | ||
fair-queued fashion. | ||
``` | ||
radio ─┐ | ||
radio ────> dish | ||
radio ─┘ | ||
``` | ||
|
||
## Scatter-Gather | ||
|
||
The `Scatter-Gather` pattern is an asynchronous pipeline pattern. | ||
|
||
The [Scatter] socket send messages downstream in a round-robin fashion | ||
``` | ||
┌──> gather | ||
scatter ────> gather | ||
└──> gather | ||
``` | ||
|
||
The [Gather] socket receives upstream messages in a fair-queued fashion | ||
``` | ||
scatter ─┐ | ||
scatter ───> gather | ||
scatter ─┘ | ||
``` | ||
|
||
[Server]: https://docs.rs/libzmq/0.1/libzmq/struct.Server.html | ||
[RoutingId]: https://docs.rs/libzmq/0.1/libzmq/struct.RoutingId.html | ||
[Client]: https://docs.rs/libzmq/0.1/libzmq/struct.Client.html | ||
[Radio]: https://docs.rs/libzmq/0.1/libzmq/struct.Radio.html | ||
[Dish]: https://docs.rs/libzmq/0.1/libzmq/struct.Dish.html | ||
[Group]: https://docs.rs/libzmq/0.1/libzmq/struct.Group.html | ||
[Scatter]: https://docs.rs/libzmq/0.1/libzmq/struct.Scatter.html | ||
[Gather]: https://docs.rs/libzmq/0.1/libzmq/struct.Gather.html | ||
[joined]: https://docs.rs/libzmq/0.1/libzmq/struct.Dish.html#method.join |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Socket | ||
|
||
The concept of a `socket` in `ZeroMQ` is completely novel. A `ZeroMQ` socket | ||
differs from a traditional `TCP` socket in the following ways (but not limited to): | ||
|
||
* A socket sends and receives atomic messages; messages are guaranteed to | ||
either be transmitted in their entirety, or not transmitted at all. | ||
* A socket send and receive messages asynchronously. | ||
* A socket socket can transmit messages over many supported transports, including `TCP`. | ||
* Incoming and outgoing messages can be queued and transmitted asynchronously | ||
by a background I/O thread. | ||
* A socket can be connected to zero or more peers at any time. | ||
* A socket can be bound to zero or more endpoints at any time. Each bound | ||
endpoint can listen to zero or more peers. | ||
* Peer reconnection and disconnection is handled in the background. | ||
* Support for many authentication and encryption strategies via [Mechanism]. | ||
|
||
[Mechanism]: https://docs.rs/libzmq/0.1/libzmq/auth/enum.Mechanism.html |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Examples | ||
|
||
Here are a few examples usage of varying complexity. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# Basic Request Reply | ||
|
||
This is as simple as it gets. We have a [Server] that does some request-reply | ||
work in a dedicated thread. We have a [Client] that sends a "ping" and gets | ||
a "pong" back. There is no attempt at security and no attempt at error handling. | ||
For a `INPROC` server, that might be enough. | ||
|
||
{{#playpen ../../../libzmq/examples/basic_req_rep.rs}} | ||
|
||
[Server]: https://docs.rs/libzmq/0.1/libzmq/struct.Server.html | ||
[Client]: https://docs.rs/libzmq/0.1/libzmq/struct.Client.html |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# Reliable Request Reply | ||
|
||
This is a basic example when using the `TCP` transport adapting the code | ||
from the previous `Basic Request Reply` example. | ||
|
||
Note that this example does not make any attempt at security. | ||
|
||
Since `TCP` is connection oriented transport, we have to take in account that | ||
the connection might fail at any time. We use heartbeating to detect failure | ||
but also `send` and `recv` timeouts to prevent blocking forever. | ||
|
||
In this example, the server is protected against failures since it will drop | ||
messages if it is unable to route them before `send_timeout` expires (`WouldBlock`), | ||
or it detects that the peer disconnected via the heartbeats (`HostUnreachable`). | ||
|
||
The client in this case will simply fail if it unable to send a request before the | ||
`send_timeout` or unable to receive a reply before the `recv_timeout` (`WouldBlock`). | ||
The client might choose to retry later or connect to another server etc. | ||
|
||
{{#playpen ../../../libzmq/examples/reliable_req_rep.rs}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# Secure Request Reply | ||
|
||
The previous example did not offer neither authentication nor encryption. | ||
For a public `TCP` connection, its a must. Let's fix that by adapting the | ||
previous example. | ||
|
||
However, this time we will use an external configuration file to get | ||
rid of all the boilerplate. This will also allows our application | ||
to run indepently of the socket configuration. | ||
|
||
## Config File | ||
|
||
In this case we used `yaml` configuration file, but any file format | ||
supported by `Serde` will work (as long as it supports typed enums). | ||
```yml | ||
{{#include ../../../libzmq/examples/secure_req_rep.yml}} | ||
``` | ||
|
||
## The code | ||
|
||
Aside from the additionnal logic for reading the config file, | ||
the code is now simpler than before. | ||
|
||
{{#playpen ../../../libzmq/examples/secure_req_rep.rs}} |
Oops, something went wrong.