Obsah
1. Využití různých komunikačních strategií z příkazového řádku
3. Konfigurace adresy pro operace bind a connect
4. Využití nástroje nanocat v praxi
5. Použití knihovny nanomsg v aplikacích naprogramovaných v Pythonu
6. Jednosměrná komunikace využívající strategii PUSH-PULL (PIPELINE)
7. Zpracování většího množství zpráv při použití strategie PUSH-PULL
8. Jednosměrná komunikace mezi dvojicí uzlů s využitím strategie PAIR
9. Obousměrná komunikace mezi dvojicí uzlů s využitím strategie PAIR
10. Použití socketů ve funkci správce kontextu
11. Vytvoření klienta a serveru založených na strategii REQ-REP
12. Chování serveru i klientů při vyšší zátěži
13. Implementace klasické komunikační strategie PUB-SUB
14. Explicitní nastavení tématu (topic)
15. Příjemci s filtrací zpráv podle nastaveného tématu
16. Klient a server používající strategii SURVEY
17. Větší množství klientů – respondentů
18. Repositář s demonstračními příklady
19. Odkazy na předchozí části seriálu o message brokerech
1. Využití různých komunikačních strategií z příkazového řádku
Nejprve si řekněme, jakým způsobem je možné knihovnu nanomsg využít přímo na příkazovém řádku, tj. buď v interaktivně zadávaných příkazech nebo ve skriptech naprogramovaných například v BASHi. Součástí instalace knihovny nanomsg je totiž i nástroj nazvaný nanocat, který se spouští přímo na příkazovém řádku a který lze nakonfigurovat takovým způsobem, že bude ve vytvářené aplikaci zastupovat prakticky libovolný uzel (server, klient atd.). Alternativně je možné nanomsg použít při testování jednotlivých částí aplikace, podobně jako používáme curl pro testování webových služeb. Základní způsoby jeho použití jsou vypsány v příslušné manuálové stránce, takže si jen v krátkosti uveďme, s jakými parametry je tuto utilitu možné spustit:
nanocat --req {--connect ADDR|--bind ADDR} {--data DATA|--file PATH} [-i SEC] [-AQ] nanocat --rep {--connect ADDR|--bind ADDR} {--data DATA|--file PATH} [-AQ] nanocat --push {--connect ADDR|--bind ADDR} {--data DATA|--file PATH} [-i SEC] nanocat --pull {--connect ADDR|--bind ADDR} [-AQ] nanocat --pub {--connect ADDR|--bind ADDR} {--data DATA|--file PATH} [-i SEC] nanocat --sub {--connect ADDR|--bind ADDR} [--subscribe PREFIX ...] [-AQ] nanocat --surveyor {--connect ADDR|--bind ADDR} {--data DATA|--file PATH} [-i SEC] [-AQ] nanocat --respondent {--connect ADDR|--bind ADDR} {--data DATA|--file PATH} [-AQ] nanocat --bus {--connect ADDR|--bind ADDR} {--data DATA|--file PATH} [-i SEC] [-AQ] nanocat --pair {--connect ADDR|--bind ADDR} {--data DATA|--file PATH} [-i SEC] [-AQ]
Povšimněte si, že první přepínač vždy uvádí režim, v jakém se uzel (ať již ho budeme považovat za klienta či naopak za server) spustí. Podporováno je všech šest komunikačních strategií, s nimiž jsme se již seznámili v předchozích článcích, tj.:
# | Strategie | Stručný popis | První typ uzlu | Druhý typ uzlu |
---|---|---|---|---|
1 | PAIR | jedna z nejjednodušších komunikačních strategií s dvojicí uzlů a vazbou 1:1 | nanocat –pair | nanocat –pair |
2 | PIPELINE | jednosměrná komunikace buď s vazbami 1:1 (jeden vysílač a jeden přijímač), popř. mezi více vysílači a několika přijímači | nanocat –push | nanocat –pull |
3 | PUBSUB | klasická komunikační strategie PUB-SUB neboli PUBLISH-SUBSCRIBE | nanocat –pub | nanocat –sub |
4 | REQREP | klasická komunikační strategie REQ-REP neboli REQUEST-RESPONSE | nanocat –req | nanocat –rep |
5 | SURVEY | speciální strategie umožňující získat stav více uzlů (procesů) jediným dotazem a mnoha odpovědmi | nanocat –surveyor | nanocat –respondent |
6 | BUS | složitější strategie, v níž se používá obecnější vazba M:N | nanocat –bus | nanocat –bus |
2. Alternativní jména příkazů
V případě korektní instalace se kromě vlastního nástroje nanocat vytvoří i řada symbolických odkazů (symlinků), které všechny ukazují na totožný binární soubor „nanocat“, ovšem již nevyžadují použití prvního přepínače, jelikož funkce uzlu je plně patrná ze jména symlinku:
nn_req {--connect ADDR|--bind ADDR} {--data DATA|--file PATH} [-i SEC] [-AQ] nn_rep {--connect ADDR|--bind ADDR} {--data DATA|--file PATH} [-AQ] nn_push {--connect ADDR|--bind ADDR} {--data DATA|--file PATH} [-i SEC] nn_pull {--connect ADDR|--bind ADDR} [-AQ] nn_pub {--connect ADDR|--bind ADDR} {--data DATA|--file PATH} [-i SEC] nn_sub {--connect ADDR|--bind ADDR} [--subscribe PREFIX ...] [-AQ] nn_surveyor {--connect ADDR|--bind ADDR} {--data DATA|--file PATH} [-i SEC] [-AQ] nn_respondent {--connect ADDR|--bind ADDR} {--data DATA|--file PATH} [-AQ] nn_bus {--connect ADDR|--bind ADDR} {--data DATA|--file PATH} [-i SEC] [-AQ] nn_pair {--connect ADDR|--bind ADDR} {--data DATA|--file PATH} [-i SEC] [-AQ]
Význam jednotlivých symbolických odkazů je pravděpodobně zřejmý z jejich názvů:
# | Přepínač pro nanocat | Název symlinku | Typ socketu |
---|---|---|---|
1 | --req | nn_req | NN_REQ |
2 | --rep | nn_rep | NN_REP |
3 | --push | nn_push | NN_PUSH |
4 | --pull | nn_pull | NN_PULL |
5 | --pub | nn_pub | NN_PUB |
6 | --sub | nn_sub | NN_SUB |
7 | --surveyor | nn_surveyor | NN_SURVEYOR |
8 | --respondent | nn_respondent | NN_RESPONDENT |
9 | --bus | nn_bus | NN_BUS |
10 | --pair | nn_pair | NN_PAIR |
3. Konfigurace adresy pro operace bind a connect
I význam dalšího přepínače zadaného na příkazovém řádku je zřejmý:
--connect ADDR --bind ADDR
Tímto přepínačem se určuje režim otevření socketu. Buď se použije funkce nn_bind(), která otevře port, na němž bude nástroj naslouchat a čekat na připojení dalších uzlů. Nebo se naopak použije funkce nn_connect() pro připojení k otevřenému portu. To, která konkrétní metoda se použije, vyplývá z toho, které uzly považujeme za „servery“ a které za „klienty“. Typicky bývá serverem ten uzel, který je z hlediska architektury celé aplikace stabilnější, především z hlediska jeho dohledatelnosti a dostupnosti. Jak již víme z předchozích dvou článků, není nanomsg plnohodnotným message brokerem (i když je možné message brokera na nanomsg postavit), takže funkci centrálního uzlu většinou přenášíme na uzly jiné. To může způsobovat problémy v těch případech, kdy jsou funkce komunikujících uzlů symetrické.
Existují i další varianty těchto přepínačů, které se již vztahují k jednotlivým typům podporovaných komunikačních kanálů. Připomeňme si, že knihovna nanomsg podporuje následující způsoby komunikace jednotlivých uzlů:
# | Přenosový mechanismus | Stručný popis |
---|---|---|
1 | INPROC | komunikace v rámci jednoho procesu, například typizovaná komunikace mezi několika vlákny |
2 | IPC | komunikace mezi několika procesy běžícími na jednom počítači |
3 | TCP | komunikace mezi procesy běžícími na různých počítačích s využitím protokolu TCP |
4 | WS | komunikace mezi procesy běžícími na různých počítačích s využitím web socketů |
A právě pro dva velmi často používané komunikační kanály je možné použít speciální formu přepínače, který současně volí jak typ komunikačního kanálu, tak i příslušnou adresu (jejíž formát je pochopitelně konkrétně spjatý s určitým kanálem):
Typ kanálu | Přepínač pro nn_bind() | Přepínač pro nn_connect() |
---|---|---|
IPC | --bind-ipc,–X | --connect-ipc,–x |
TCP na localhost | --bind-local,–L | --connect-local,–l |
4. Využití nástroje nanocat v praxi
Podívejme se nyní na způsob použití jednotlivých komunikačních strategií. Příklady, které si uvedeme v dalším textu, budou zhruba odpovídat svým céčkovým protějškům, které byly popsány minule a předminule.
Spuštění serveru používajícího strategii PUSH-PULL:
$ nanocat --pull -Q -v --bind ipc:///tmp/example
Poslání (PUSH) zprávy na server:
$ nanocat --push --connect ipc:///tmp/example --data "test"
Poslání obsahu celého souboru na server:
$ nanocat --push --connect ipc:///tmp/example --file .bashrc
Spuštění příjemce zpráv založeného na strategii PUB-SUB:
$ nanocat --sub -Q -v --bind ipc:///tmp/example
Periodické posílání zprávy každou sekundu:
$ nanocat --pub --connect ipc:///tmp/example -i 1 --data "test"
V případě potřeby můžeme změnit i formát výpisu zpráv, namísto řetězců (uzavřených do uvozovek) se může zapnout hexadecimální výstup:
$ nanocat --sub --hex -v --bind ipc:///tmp/example "\x74\x65\x73\x74" "\x74\x65\x73\x74"
Výpis přijatých zpráv ve formě ASCII textů:
$ nanocat --sub --ascii -v --bind ipc:///tmp/example test test
Podobný příklad, ovšem používající jednodušší komunikační strategii PAIR:
$ nanocat --pair -Q -v --bind ipc:///tmp/example
druhý uzel (klient):
$ nanocat --pair --connect ipc:///tmp/example -i 1 --data "test"
5. Použití knihovny nanomsg v aplikacích naprogramovaných v Pythonu
Ve druhé části dnešního článku si popíšeme, jakým způsobem je možné použít knihovnu nanomsg z programovacího jazyka Python. Python pochopitelně není jediným programovacím jazykem, pro nějž vznikla knihovna s rozhraním pro nanomsg, protože kromě nativního céčka (nanomsg je psána totiž psána, jak již ostatně víme, v čistém C) existují rozhraní i pro tyto programovací jazyky, popř. platformy:
Jazyk (platforma) | Knihovna/projekt s rozhraním |
---|---|
C | nanomsg |
NNG (nová reimplementace, viz poznámka v úvodní kapitole) | |
C++ | nanomsgxx |
cppnanomsg | |
nngpp (pro NNG) | |
Clojure | jnanomsg (voláno přes standardní Java interop) |
D | nanomsg-wrapper |
Dylan | nanomsg-dylan |
Erlang | enm |
Fortran | nanofort |
Go | mangos (reimplementace v Go) |
mangos v2 (druhá verze) | |
go-nanomsg | |
Haskell | nanomsg-haskell |
nanomsg | |
Haxe | hx-nanomsg |
Swift | swiftc nanomsg |
Java | jnano |
jnanomsg | |
nngjvm (pro NNG) | |
JavaScript (Node.js) | node-nanomsg |
Lua | lua-nanomsg |
luajit-nanomsg (pro systém LuaJIT) | |
luananomsg | |
.NET | NNanomsg |
Ocaml | onanomsg |
Perl | NanoMsg::Raw |
PHP | php-nano |
PicoLisp | picolisp-nanomsg FFI bindings |
Python | nanomsg-python |
pynanomsg | |
nnpy | |
pynng (pro NNG, prozatím ve vývoji) | |
R | rnanomsg |
Ruby | nn-core |
nanomsg | |
Rust | rust-nanomsg |
nng-rs (opět pro NNG) | |
Scheme (CHICKEN) | chicken-nanomsg |
Smalltalk | NanoStrand |
V případě Pythonu existuje několik rozhraní mezi tímto programovacím jazykem (přesněji řečeno mezi aplikacemi psanými v Pythonu) a knihovnou nanomsg, ovšem nejvíce doporučovaná je knihovna nazvaná jednoduše nanomsg-python, kterou naleznete na adrese https://github.com/tonysimpson/nanomsg-python. Tato knihovna je pochopitelně dostupná i na PyPi, takže její instalace může proběhnout naprosto stejným způsobem, jaký jsme si již popsali u jiných pythoních knihoven – pomocí nástroje pip, popř. pip3:
$ pip3 install nanomsg
export LD_LIBRARY_PATH=/usr/local/lib64/
6. Jednosměrná komunikace využívající strategii PUSH-PULL (PIPELINE)
Strategie nazvaná PIPELINE zajišťuje jednosměrný přenos zpráv od vysílající aplikace (vlákna, procesu) k aplikaci přijímající. V tom nejjednodušším případě existuje pouze jediný vysílač (neboli zdroj zpráv, producent) a jediný přijímač (konzument). Nejprve se podívejme na implementaci producenta, která je nepatrně jednodušší, zejména s ohledem na to, že se nemusí žádným způsobem pracovat s bufferem pro příjem zprávy.
Implementace zdroje zpráv je v případě Pythonu velmi krátká (pokud ovšem prozatím vynecháme kontroly chybových stavů). Zdrojový kód producenta zpráv naleznete na adrese https://github.com/tisnik/message-queues-examples/blob/master/nanomsg-python/01_sender_receiver/sender.py:
from nanomsg import Socket, PUSH URL = "ipc:///tmp/example1" socket = Socket(PUSH) print("Socket created") socket.connect(URL) print("Connected to URL {}".format(URL)) socket.send("Hello world!") print("Message has been sent") socket.close() print("Socket closed")
Podobným způsobem je pochopitelně možné realizovat i klienta, který bude zprávy přijímat. Vysílací strana používá socket typu PUSH, strana přijímací tedy použije socket PULL (což je kontrolováno při navazování připojení) a výsledný zdrojový kód příjemce zpráv bude vypadat následovně:
from nanomsg import Socket, PULL URL = "ipc:///tmp/example1" socket = Socket(PULL) print("Socket created") socket.bind(URL) print("Bound to URL {}".format(URL)) message = socket.recv() print("Received: {}".format(message)) socket.close() print("Socket closed")
Pro zajímavost se podívejme na to, jak jsme předminule implementovali zdroj zpráv, tentokrát v céčku. Oproti pythoní verzi je příklad rozsáhlejší:
#include <stdio.h> #include <string.h> #include <unistd.h> #include <nanomsg/nn.h> #include <nanomsg/pipeline.h> const char *URL = "ipc:///tmp/example1"; void sender(const char *url, const char *message) { int message_size = strlen(message) + 1; int socket; int endpoint; int bytes; socket = nn_socket(AF_SP, NN_PUSH); puts("Socket created"); endpoint = nn_connect(socket, url); puts("Remote endpoint added to the socket"); printf("Sending message '%s'\n", message); bytes = nn_send(socket, message, message_size, 0); printf("Message with length %d bytes sent, flushing", bytes); sleep(1); puts("Done"); nn_shutdown(socket, endpoint); } int main(const int argc, const char **argv) { sender(URL, "Hello"); sender(URL, "world"); sender(URL, "!"); return 0; }
Příjemce zpráv, opět vytvořený v céčku, by mohl vypadat následovně:
#include <stdio.h> #include <nanomsg/nn.h> #include <nanomsg/pipeline.h> const char *URL = "ipc:///tmp/example1"; void receiver(const char *url) { int socket; socket = nn_socket(AF_SP, NN_PULL); puts("Socket created"); nn_bind(socket, url); puts("Endpoint bound to socket"); puts("Waiting for messages..."); while (1) { char *message = NULL; int bytes = nn_recv(socket, &message, NN_MSG, 0); printf("Received message '%s' with length %d bytes\n", message, bytes); nn_freemsg(message); } } int main(int argc, char **argv) { receiver(URL); return 0; }
7. Zpracování většího množství zpráv při použití strategie PUSH-PULL
Samozřejmě si můžeme naprogramovat i klienta, který serveru pošle větší množství zpráv s využitím strategie PUSH-PULL (PIPELINE). Mezi zprávy je vhodné vkládat nepatrné pauzy, aby se korektně vyprázdnily síťové buffery a zpráva byla skutečně přenesena na server (to je principiální problém nanomsg, s nímž se taktéž seznámili minule a předminule).
Zdrojový kód zdroje deseti zpráv poslaných příjemci (klientovi) naleznete na adrese https://github.com/tisnik/message-queues-examples/blob/master/nanomsg-python/02_sender_receiver_more_messages/sender.py:
from nanomsg import Socket, PUSH from time import sleep URL = "ipc:///tmp/example2" socket = Socket(PUSH) print("Socket created") socket.connect(URL) print("Connected to URL {}".format(URL)) for i in range(10): socket.send("Hello world #{}".format(i+1)) print("Message has been sent") sleep(0.1) socket.close() print("Socket closed")
Server, který zprávy bude přijímat, lze naprogramovat například následujícím způsobem. Povšimněte si, že zprávy jsou zpracovávány v nekonečné programové smyčce a v jediném vláknu (což je pochopitelně to nejjednodušší řešení, v němž ovšem neprovádíme korektní uzavření socketu).
Zdrojový kód příjemce zpráv je uložen na adrese https://github.com/tisnik/message-queues-examples/blob/master/nanomsg-python/02_sender_receiver_more_messages/receiver.py:
from nanomsg import Socket, PULL URL = "ipc:///tmp/example2" socket = Socket(PULL) print("Socket created") socket.bind(URL) print("Bound to URL {}".format(URL)) while True: message = socket.recv() print("Received: {}".format(message)) socket.close() print("Socket closed")
8. Jednosměrná komunikace mezi dvojicí uzlů s využitím strategie PAIR
Druhá komunikační strategie se jmenuje PAIR. Tato strategie umožňuje, aby mezi sebou jednotlivé uzly komunikovaly oboustranně, což nebylo při použití strategie PUSH-PULL/PIPELINE možné (přesněji řečeno to možné bylo, ovšem otevřením dvou komunikačních kanálů, což je velmi křehké řešení). I u strategie PAIR vystupuje jeden z komunikujících uzlů ve funkci serveru (otevírá svůj port a očekává, že se na něj připojí klient) a druhý uzel ve funkci klienta. Ovšem až na tento rozdíl jsou si po navázání spojení oba uzly rovnocenné, tj. každý z nich může vysílat i přijímat zprávy, a to libovolným způsobem, který si zvolí sám programátor. Ten například může implementovat jednoduchý systém typu dotaz-odpověď (což je ovšem lepší realizovat strategií REQREP popsanou níže) či skutečně použít plnohodnotný oboustranný komunikační kanál.
Opět se podívejme na úplný zdrojový kód prvního uzlu, který po svém spuštění pošle zprávu uzlu druhému a ihned poté se ukončí. Jedná se tedy o čistě jednosměrnou komunikaci:
from nanomsg import Socket, PAIR URL = "ipc:///tmp/example3" socket = Socket(PAIR) print("Socket created") socket.bind(URL) print("Bound to URL {}".format(URL)) socket.send("Hello world!") print("Message has been sent") socket.close() print("Socket closed")
Druhý uzel se pouze pokusí přijmout zprávu od uzlu prvního. Na tuto zprávu nijak neodpovídá, pouze ji zobrazí na standardním výstupu. Zatímco první uzel vystupoval v roli serveru, je druhý uzel klientem. Následuje výpis zdrojového kódu druhého uzlu:
from nanomsg import Socket, PAIR URL = "ipc:///tmp/example3" socket = Socket(PAIR) print("Socket created") socket.connect(URL) print("Connected to URL {}".format(URL)) message = socket.recv() print("Received: {}".format(message)) socket.close() print("Socket closed")
9. Obousměrná komunikace mezi dvojicí uzlů s využitím strategie PAIR
Vzhledem k tomu, že v komunikační strategii PAIR jsou si oba uzlu rovnocenné v tom, že každý může zprávy přijímat i odesílat, můžeme předchozí příklad upravit takovým způsobem, že první uzel nejdříve pošle zprávu o poté si přečte odpověď. Tento příklad naleznete na adrese https://github.com/tisnik/message-queues-examples/blob/master/nanomsg-python/04_two_way_pair_communication/node1.py:
from nanomsg import Socket, PAIR URL = "ipc:///tmp/example4" socket = Socket(PAIR) print("Socket created") socket.bind(URL) print("Bound to URL {}".format(URL)) socket.send("Hello world!") print("Message has been sent") message = socket.recv() print("Received response: {}".format(message)) socket.close() print("Socket closed")
Druhý uzel musí postupovat přesně naopak – nejdříve zprávu přijmout a poté na ni odpovědět. V kódu tedy bude volání metod recv a send prohozené, jak je to ostatně patrné již při pohledu na jeho zdrojový kód:
from nanomsg import Socket, PAIR URL = "ipc:///tmp/example4" socket = Socket(PAIR) print("Socket created") socket.connect(URL) print("Connected to URL {}".format(URL)) message = socket.recv() print("Received: {}".format(message)) socket.send("Thanks for your message '{}'".format(message)) print("Response has been sent") socket.close() print("Socket closed")
10. Použití socketů ve funkci správce kontextu
Volání dvojice párových příkazů Socket() a socket.close() je možné ve skutečnosti naprogramovat ještě jednodušším a mnohem čitelnějším způsobem. Samotnou instanci socketu lze použít ve funkci správce kontextu (context manager), což konkrétně v programovacím jazyku Python znamená, že se socket vytvoří v konstrukci with a na konci této konstrukce (přesněji řečeno na konci celého bloku with) se socket automaticky uzavře.
První komunikující uzel z předchozí kapitoly tedy můžeme přepsat takto:
from nanomsg import Socket, PAIR URL = "ipc:///tmp/example5" with Socket(PAIR) as socket: print("Socket created") socket.bind(URL) print("Bound to URL {}".format(URL)) socket.send("Hello world!") print("Message has been sent") message = socket.recv() print("Received response: {}".format(message)) print("Socket closed")
Podobně můžeme přepsat i druhý uzel, opět s použitím with:
from nanomsg import Socket, PAIR URL = "ipc:///tmp/example5" with Socket(PAIR) as socket: socket = Socket(PAIR) print("Socket created") socket.connect(URL) print("Connected to URL {}".format(URL)) message = socket.recv() print("Received: {}".format(message)) socket.send("Thanks for your message '{}'".format(message)) print("Response has been sent") print("Socket closed")
11. Vytvoření klienta a serveru založených na strategii REQ-REP
Další velmi často používaná komunikační strategie se nazývá Request-Response nebo zkráceně REQ-REP popř. pouze REQREP. Při použití této strategie spolu komunikují dva typy uzlů – server a teoreticky neomezené množství klientů. Server přijímá požadavky (request) a odpovídá na ně (response), přičemž je možné, aby požadavky posílalo několik klientů (a jeden klient naopak může v případě potřeby posílat požadavky více serverům). Tato velmi asymetrická komunikace se strategií REQ-REP je v praxi poměrně častá, ostatně je na ní založen i známý protokol HTTP a jeho pozdější varianty (dokonce se někdy nad HTTP staví jiné komunikační strategie, což je ovšem neoptimální řešení).
Nejprve si ukažme, jak lze naprogramovat klienta, který musí použít socket typu REQ a celá komunikace vždy probíhá ve dvojici příkazů socket.send(), který musí být následovaný příkazem socket.recv():
from nanomsg import Socket, REQ URL = "ipc:///tmp/example6" def send_request(socket, request): socket.send(request) def receive_response(socket): response = socket.recv() print("Received response: '{}'".format(response)) with Socket(REQ) as socket: print("Socket created") socket.connect(URL) print("Connected to URL {}".format(URL)) send_request(socket, "Hello from 'first'!") print("Waiting for response...") receive_response(socket) print("Socket closed")
Server je naproti tomu naprogramován symetricky – na každou přijatou zprávu (socket.recv() odpovídá pomocí socket.send(). Zdrojový kód serveru vypadá takto:
from nanomsg import Socket, REP URL = "ipc:///tmp/example6" def receive_request(socket): request = socket.recv() print("Received request: '{}'".format(request)) def send_response(socket, response): socket.send(response) with Socket(REP) as socket: print("Socket created") socket.bind(URL) print("Bound to URL {}".format(URL)) print("Waiting for requests...") while True: receive_request(socket) send_response(socket, "ACK!") print("Socket closed")
12. Chování serveru i klientů při vyšší zátěži
Strategie REQ-REP je v knihovně nanomsg velmi robustní, takže by nemělo docházet ke ztrátě zpráv. Ostatně si to můžeme velmi snadno otestovat. Nejdříve upravíme server takovým způsobem, aby počítal všechny přijaté zprávy, což je triviální úprava:
from nanomsg import Socket, REP URL = "ipc:///tmp/example7" def receive_request(socket): request = socket.recv() print("Received request: '{}'".format(request)) def send_response(socket, response): socket.send(response) # pocitadlo pozadavku received = 0 with Socket(REP) as socket: print("Socket created") socket.bind(URL) print("Bound to URL {}".format(URL)) print("Waiting for requests...") while True: receive_request(socket) received += 1 print("Received {}th request".format(received)) send_response(socket, "ACK!") print("Socket closed")
Dále vytvoříme dvojici klientů. První z nich pošle 1000 zpráv:
from nanomsg import Socket, REQ URL = "ipc:///tmp/example7" def send_request(socket, request): socket.send(request) def receive_response(socket): response = socket.recv() print("Received response: '{}'".format(response)) with Socket(REQ) as socket: print("Socket created") socket.connect(URL) print("Connected to URL {}".format(URL)) for i in range(1000): send_request(socket, "Hello #{} from 'first'!".format(i)) print("Waiting for response...") receive_response(socket) print("Socket closed")
Druhý taktéž pošle 1000 zpráv, bez jakýchkoli pauz mezi nimi:
from nanomsg import Socket, REQ URL = "ipc:///tmp/example7" def send_request(socket, request): socket.send(request) def receive_response(socket): response = socket.recv() print("Received response: '{}'".format(response)) with Socket(REQ) as socket: print("Socket created") socket.connect(URL) print("Connected to URL {}".format(URL)) for i in range(1000): send_request(socket, "Hello #{} from 'second'!".format(i)) print("Waiting for response...") receive_response(socket) print("Socket closed")
Otestování může proběhnout způsobem:
$ python3 server.py > server.out& $ python3 client1.py & python3 client2.py &
Server by měl zpracovat přesně 2000 zpráv.
13. Implementace klasické komunikační strategie PUB-SUB
V této kapitole si ukážeme způsob využití komunikační strategie PUBSUB, s níž jsme se již v tomto seriálu mnohokrát setkali. Tato strategie umožňuje rozesílat zprávy libovolnému množství příjemců. Zdroj zpráv bude v tomto případě implementován jako server a typ použitého socketu bude PUB. Podívejme se na zdrojový kód producenta/zdroje zpráv:
from time import sleep from nanomsg import Socket, PUB URL = "ipc:///tmp/example8" with Socket(PUB) as socket: print("Socket created") socket.bind(URL) print("Bound to URL {}".format(URL)) for i in range(1, 1000): message = "Message #{}".format(i) print("Publishing message {}".format(message)) socket.send(message) sleep(0.5) print("Socket closed")
Nejjednodušší, ovšem nefunkční :-) varianta příjemce zpráv se vytvoří nepatrnou úpravou předchozích příkladů. Problém ovšem spočívá v tom, že u komunikační strategie PUB-SUB je nutné specifikovat téma (topic), protože v opačném případě budou všechny zprávy zahozeny. Nicméně se podívejme na úplný zdrojový kód tohoto NEFUNKČNÍHO příkladu:
from time import sleep from nanomsg import Socket, SUB, SUB_SUBSCRIBE URL = "ipc:///tmp/example8" def receive_message(socket): message = socket.recv() print("Received message: '{}'".format(message)) with Socket(SUB) as socket: print("Socket created") socket.connect(URL) print("Connected to URL {}".format(URL)) while True: receive_message(socket) print("Socket closed")
14. Explicitní nastavení tématu (topic)
Aby klient dokázal přijímat všechny zpráv s libovolným tématem, musíme nastavit následující volbu:
socket.set_string_option(SUB, SUB_SUBSCRIBE, "")
Tato volba určuje, že se mají přijmout a zpracovat naprosto všechny zprávy, bez ohledu na nastavené téma.
from time import sleep from nanomsg import Socket, SUB, SUB_SUBSCRIBE URL = "ipc:///tmp/example9" def receive_message(socket): message = socket.recv() print("Received message: '{}'".format(message)) with Socket(SUB) as socket: print("Socket created") socket.connect(URL) socket.set_string_option(SUB, SUB_SUBSCRIBE, "") print("Connected to URL {}".format(URL)) while True: receive_message(socket) print("Socket closed")
15. Příjemci s filtrací zpráv podle nastaveného tématu
Pouze pro úplnost si ukažme, jak se budou zpracovávat zprávy s nastaveným tématem. Zdroj zpráv po svém spuštění vytvoří 2000 zpráv, přičemž tisíc z nich bude začínat prefixem „Message A“ a další tisíc prefixem „Message B“:
from time import sleep from nanomsg import Socket, PUB URL = "ipc:///tmp/example10" with Socket(PUB) as socket: print("Socket created") socket.bind(URL) print("Bound to URL {}".format(URL)) for i in range(1, 1000): message = "Message A #{}".format(i) print("Publishing message {}".format(message)) socket.send(message) sleep(0.5) message = "Message B #{}".format(i) print("Publishing message {}".format(message)) socket.send(message) sleep(0.5) print("Socket closed")
První příjemce je nakonfigurován takovým způsobem, aby přijímal (filtroval) pouze zprávy s prefixem „Message A“:
from time import sleep from nanomsg import Socket, SUB, SUB_SUBSCRIBE URL = "ipc:///tmp/example10" def receive_message(socket): message = socket.recv() print("Received message: '{}'".format(message)) with Socket(SUB) as socket: print("Socket created") socket.connect(URL) socket.set_string_option(SUB, SUB_SUBSCRIBE, "Message A") print("Connected to URL {}".format(URL)) while True: receive_message(socket) print("Socket closed")
Druhý příjemce je nakonfigurován takovým způsobem, aby přijímal (filtroval) pouze zprávy s prefixem „Message B“:
from time import sleep from nanomsg import Socket, SUB, SUB_SUBSCRIBE URL = "ipc:///tmp/example10" def receive_message(socket): message = socket.recv() print("Received message: '{}'".format(message)) with Socket(SUB) as socket: print("Socket created") socket.connect(URL) socket.set_string_option(SUB, SUB_SUBSCRIBE, "Message B") print("Connected to URL {}".format(URL)) while True: receive_message(socket) print("Socket closed")
Po spuštění by mělo být vidět, že filtrace zpráv skutečně probíhá na základě nastavených filtrů.
16. Klient a server používající strategii SURVEY
V pořadí pátou komunikační strategií, se kterou jsme se seznámili minule, je strategie nazvaná Survey. Jedná se o dosti příhodný název, protože v této strategii vystupuje jeden uzel, který se dotáže ostatních uzlů například na jejich stav a uzly následně mohou odpovídat. Dotazující se uzel má nastavenu dobu čekání, takže může jednotlivé odpovědi zaznamenat a po uplynutí zadaného časového okamžiku zjistit, které uzly vůbec neodpověděly (nebo to nestihly, což je ovšem z pohledu dotazujícího to samé).
Server nejdříve deset sekund počká na připojení klientů, pošle jim otázku a zpracuje jejich případné odpovědi:
from nanomsg import Socket, SURVEYOR from time import sleep URL = "ipc:///tmp/example11" def send_survey(socket, message): socket.send(message) def receive_answer(socket): answer = socket.recv() print("Received answer: '{}'".format(answer)) def wait_for_clients(seconds): print("Waiting for clients to connect...") for i in range(seconds, 0, -1): print(i) sleep(1) with Socket(SURVEYOR) as socket: print("Socket created") socket.bind(URL) print("Bound to URL {}".format(URL)) wait_for_clients(10) send_survey(socket, "What do you get when you multiply six by nine?") print("Survey send, waiting for answers...") answers = 0 while True: receive_answer(socket) answers += 1 print("Processed {} answers so far".format(answers)) print("Socket closed")
Klient (respondent) pro každou otázku přijatou od serveru vygeneruje příslušnou odpověď:
from nanomsg import Socket, RESPONDENT from random import seed, randint URL = "ipc:///tmp/example11" def receive_question(socket): question = socket.recv() print("Received question: '{}'".format(question)) def send_answer(socket, answer): print("Sending answer: '{}'".format(answer)) socket.send(answer) seed(None) with Socket(RESPONDENT) as socket: print("Socket created") socket.connect(URL) print("Connected to URL {}".format(URL)) while True: receive_question(socket) print("Question received") number = randint(0, 100) answer = "It must be {}".format(number) send_answer(socket, answer) print("Answer sent") print("Socket closed")
17. Větší množství klientů – respondentů
Samozřejmě, že je možné naprogramovat větší množství respondentů, které serveru budou na dotazy odpovídat. Druhý respondent:
from nanomsg import Socket, RESPONDENT from random import seed, randint URL = "ipc:///tmp/example12" def receive_question(socket): question = socket.recv() print("Received question: '{}'".format(question)) def send_answer(socket, answer): print("Sending answer: '{}'".format(answer)) socket.send(answer) seed(None) with Socket(RESPONDENT) as socket: print("Socket created") socket.connect(URL) print("Connected to URL {}".format(URL)) while True: receive_question(socket) print("Question received") number = randint(0, 100) answer = "Hello, I'm client #1. It must be {}".format(number) send_answer(socket, answer) print("Answer sent") print("Socket closed")
Třetí respondent:
from nanomsg import Socket, RESPONDENT from random import seed, randint URL = "ipc:///tmp/example12" def receive_question(socket): question = socket.recv() print("Received question: '{}'".format(question)) def send_answer(socket, answer): print("Sending answer: '{}'".format(answer)) socket.send(answer) seed(None) with Socket(RESPONDENT) as socket: print("Socket created") socket.connect(URL) print("Connected to URL {}".format(URL)) while True: receive_question(socket) print("Question received") number = randint(0, 100) answer = "Hello, I'm client #2. It must be {}".format(number) send_answer(socket, answer) print("Answer sent") print("Socket closed")
Pořadí spuštění by mělo být následující:
- Server (začne odpočítávat deset sekund do položení otázky)
- Všichni klienty (například spuštění na pozadí ve stejném terminálu)
18. Repositář s demonstračními příklady
Zdrojové kódy všech dnes popsaných demonstračních příkladů vyvinutých v programovacím jazyku Python byly uloženy do Git repositáře, který je dostupný na adrese https://github.com/tisnik/message-queues-examples (stále na GitHubu :-). V případě, že nebudete chtít klonovat celý repositář (ten je ovšem – alespoň prozatím – velmi malý, dnes má doslova několik kilobajtů), můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce. Každý příklad se skládá ze dvou samostatně překládaných a spouštěných souborů – producenta zpráv a konzumenta zpráv:
19. Odkazy na předchozí části seriálu o message brokerech
V této kapitole jsou uvedeny odkazy na všech sedmnáct předchozích částí seriálu, v němž se zabýváme různými způsoby implementace front zpráv a k nim přidružených technologií message brokerů:
- Použití nástroje RQ (Redis Queue) pro správu úloh zpracovávaných na pozadí
https://www.root.cz/clanky/pouziti-nastroje-rq-redis-queue-pro-spravu-uloh-zpracovavanych-na-pozadi/ - Celery: systém implementující asynchronní fronty úloh pro Python
https://www.root.cz/clanky/celery-system-implementujici-asynchronni-fronty-uloh-pro-python/ - Celery: systém implementující asynchronní fronty úloh pro Python (dokončení)
https://www.root.cz/clanky/celery-system-implementujici-asynchronni-fronty-uloh-pro-python-dokonceni/ - RabbitMQ: jedna z nejúspěšnějších implementací brokera
https://www.root.cz/clanky/rabbitmq-jedna-z-nejuspesnejsich-implementaci-brokera/ - Pokročilejší operace nabízené systémem RabbitMQ
https://www.root.cz/clanky/pokrocilejsi-operace-nabizene-systemem-rabbitmq/ - ØMQ: knihovna pro asynchronní předávání zpráv
https://www.root.cz/clanky/0mq-knihovna-pro-asynchronni-predavani-zprav/ - Další možnosti poskytované knihovnou ØMQ
https://www.root.cz/clanky/dalsi-moznosti-poskytovane-knihovnou-mq/ - Využití zařízení v knihovně ØMQ při tvorbě systému se složitější architekturou
https://www.root.cz/clanky/vyuziti-zarizeni-v-knihovne-mq-pri-tvorbe-systemu-se-slozitejsi-architekturou/ - Další možnosti nabízené knihovnou ØMQ, implementace protokolů ØMQ v čisté Javě
https://www.root.cz/clanky/dalsi-moznosti-nabizene-knihovnou-mq-implementace-protokolu-mq-v-ciste-jave/ - Apache ActiveMQ – další systém implementující message brokera
https://www.root.cz/clanky/apache-activemq-dalsi-system-implementujici-message-brokera/ - Použití Apache ActiveMQ s protokolem STOMP
https://www.root.cz/clanky/pouziti-apache-activemq-s-protokolem-stomp/ - Použití Apache ActiveMQ s protokolem AMQP, jazyk Go a message brokeři
https://www.root.cz/clanky/pouziti-apache-activemq-s-protokolem-amqp-jazyk-go-a-message-brokeri/ - Komunikace s message brokery z programovacího jazyka Go
https://www.root.cz/clanky/komunikace-s-message-brokery-z-programovaciho-jazyka-go/ - Použití message brokeru NATS
https://www.root.cz/clanky/pouziti-message-brokeru-nats/ - NATS Streaming Server
https://www.root.cz/clanky/nats-streaming-server/ - Implementace různých komunikačních strategií s využitím knihovny nanomsg
https://www.root.cz/clanky/implementace-ruznych-komunikacnich-strategii-s-vyuzitim-knihovny-nanomsg/ - Dokončení popisu komunikačních strategií poskytovaných knihovnou nanomsg
https://www.root.cz/clanky/dokonceni-popisu-komunikacnich-strategii-poskytovanych-knihovnou-nanomsg/
20. Odkazy na Internetu
- nanomsg na GitHubu
https://github.com/nanomsg/nanomsg - Referenční příručka knihovny nanomsg
https://nanomsg.org/v1.1.5/nanomsg.html - nng (nanomsg-next-generation)
https://github.com/nanomsg/nng - Differences between nanomsg and ZeroMQ
https://nanomsg.org/documentation-zeromq.html - NATS
https://nats.io/about/ - NATS Streaming Concepts
https://nats.io/documentation/streaming/nats-streaming-intro/ - NATS Streaming Server
https://nats.io/download/nats-io/nats-streaming-server/ - NATS Introduction
https://nats.io/documentation/ - NATS Client Protocol
https://nats.io/documentation/internals/nats-protocol/ - NATS Messaging (Wikipedia)
https://en.wikipedia.org/wiki/NATS_Messaging - Stránka Apache Software Foundation
http://www.apache.org/ - Informace o portu 5672
http://www.tcp-udp-ports.com/port-5672.htm - Třída MessagingHandler knihovny Qpid Proton
https://qpid.apache.org/releases/qpid-proton-0.27.0/proton/python/api/proton._handlers.MessagingHandler-class.html - Třída Event knihovny Qpid Proton
https://qpid.apache.org/releases/qpid-proton-0.27.0/proton/python/api/proton._events.Event-class.html - package stomp (Go)
https://godoc.org/github.com/go-stomp/stomp - Go language library for STOMP protocol
https://github.com/go-stomp/stomp - python-qpid-proton 0.26.0 na PyPi
https://pypi.org/project/python-qpid-proton/ - Qpid Proton
http://qpid.apache.org/proton/ - Using the AMQ Python Client
https://access.redhat.com/documentation/en-us/red_hat_amq/7.1/html-single/using_the_amq_python_client/ - Apache ActiveMQ
http://activemq.apache.org/ - Apache ActiveMQ Artemis
https://activemq.apache.org/artemis/ - Apache ActiveMQ Artemis User Manual
https://activemq.apache.org/artemis/docs/latest/index.html - KahaDB
http://activemq.apache.org/kahadb.html - Understanding the KahaDB Message Store
https://access.redhat.com/documentation/en-US/Fuse_MQ_Enterprise/7.1/html/Configuring_Broker_Persistence/files/KahaDBOverview.html - Command Line Tools (Apache ActiveMQ)
https://activemq.apache.org/activemq-command-line-tools-reference.html - stomp.py 4.1.21 na PyPi
https://pypi.org/project/stomp.py/ - Stomp Tutorial
https://access.redhat.com/documentation/en-US/Fuse_Message_Broker/5.5/html/Connectivity_Guide/files/FMBConnectivityStompTelnet.html - Heartbeat (computing)
https://en.wikipedia.org/wiki/Heartbeat_(computing) - Apache Camel
https://camel.apache.org/ - Red Hat Fuse
https://developers.redhat.com/products/fuse/overview/ - Confusion between ActiveMQ and ActiveMQ-Artemis?
https://serverfault.com/questions/873533/confusion-between-activemq-and-activemq-artemis - Staré stránky projektu HornetQ
http://hornetq.jboss.org/ - Snapshot JeroMQ verze 0.4.4
https://oss.sonatype.org/content/repositories/snapshots/org/zeromq/jeromq/0.4.4-SNAPSHOT/ - Difference between ActiveMQ vs Apache ActiveMQ Artemis
http://activemq.2283324.n4.nabble.com/Difference-between-ActiveMQ-vs-Apache-ActiveMQ-Artemis-td4703828.html - Microservices communications. Why you should switch to message queues
https://dev.to/matteojoliveau/microservices-communications-why-you-should-switch-to-message-queues–48ia - Stomp.py 4.1.19 documentation
https://stomppy.readthedocs.io/en/stable/ - Repositář knihovny JeroMQ
https://github.com/zeromq/jeromq/ - ØMQ – Distributed Messaging
http://zeromq.org/ - ØMQ Community
http://zeromq.org/community - Get The Software
http://zeromq.org/intro:get-the-software - PyZMQ Documentation
https://pyzmq.readthedocs.io/en/latest/ - Module: zmq.decorators
https://pyzmq.readthedocs.io/en/latest/api/zmq.decorators.html - ZeroMQ is the answer, by Ian Barber
https://vimeo.com/20605470 - ZeroMQ RFC
https://rfc.zeromq.org/ - ZeroMQ and Clojure, a brief introduction
https://antoniogarrote.wordpress.com/2010/09/08/zeromq-and-clojure-a-brief-introduction/ - zeromq/czmq
https://github.com/zeromq/czmq - golang wrapper for CZMQ
https://github.com/zeromq/goczmq - ZeroMQ version reporting in Python
http://zguide.zeromq.org/py:version - A Go interface to ZeroMQ version 4
https://github.com/pebbe/zmq4 - Broker vs. Brokerless
http://zeromq.org/whitepapers:brokerless - Learning ØMQ with pyzmq
https://learning-0mq-with-pyzmq.readthedocs.io/en/latest/ - Céčková funkce zmq_ctx_new
http://api.zeromq.org/4–2:zmq-ctx-new - Céčková funkce zmq_ctx_destroy
http://api.zeromq.org/4–2:zmq-ctx-destroy - Céčková funkce zmq_bind
http://api.zeromq.org/4–2:zmq-bind - Céčková funkce zmq_unbind
http://api.zeromq.org/4–2:zmq-unbind - Céčková C funkce zmq_connect
http://api.zeromq.org/4–2:zmq-connect - Céčková C funkce zmq_disconnect
http://api.zeromq.org/4–2:zmq-disconnect - Céčková C funkce zmq_send
http://api.zeromq.org/4–2:zmq-send - Céčková C funkce zmq_recv
http://api.zeromq.org/4–2:zmq-recv - Třída Context (Python)
https://pyzmq.readthedocs.io/en/latest/api/zmq.html#context - Třída Socket (Python)
https://pyzmq.readthedocs.io/en/latest/api/zmq.html#socket - Python binding
http://zeromq.org/bindings:python - Why should I have written ZeroMQ in C, not C++ (part I)
http://250bpm.com/blog:4 - Why should I have written ZeroMQ in C, not C++ (part II)
http://250bpm.com/blog:8 - About Nanomsg
https://nanomsg.org/ - Advanced Message Queuing Protocol
https://www.amqp.org/ - Advanced Message Queuing Protocol na Wikipedii
https://en.wikipedia.org/wiki/Advanced_Message_Queuing_Protocol - Dokumentace k příkazu rabbitmqctl
https://www.rabbitmq.com/rabbitmqctl.8.html - RabbitMQ
https://www.rabbitmq.com/ - RabbitMQ Tutorials
https://www.rabbitmq.com/getstarted.html - RabbitMQ: Clients and Developer Tools
https://www.rabbitmq.com/devtools.html - RabbitMQ na Wikipedii
https://en.wikipedia.org/wiki/RabbitMQ - Streaming Text Oriented Messaging Protocol
https://en.wikipedia.org/wiki/Streaming_Text_Oriented_Messaging_Protocol - Message Queuing Telemetry Transport
https://en.wikipedia.org/wiki/MQTT - Erlang
http://www.erlang.org/ - pika 0.12.0 na PyPi
https://pypi.org/project/pika/ - Introduction to Pika
https://pika.readthedocs.io/en/stable/ - Langohr: An idiomatic Clojure client for RabbitMQ that embraces the AMQP 0.9.1 model
http://clojurerabbitmq.info/ - AMQP 0–9–1 Model Explained
http://www.rabbitmq.com/tutorials/amqp-concepts.html - Part 1: RabbitMQ for beginners – What is RabbitMQ?
https://www.cloudamqp.com/blog/2015–05–18-part1-rabbitmq-for-beginners-what-is-rabbitmq.html - Downloading and Installing RabbitMQ
https://www.rabbitmq.com/download.html - celery na PyPi
https://pypi.org/project/celery/ - Databáze Redis (nejenom) pro vývojáře používající Python
https://www.root.cz/clanky/databaze-redis-nejenom-pro-vyvojare-pouzivajici-python/ - Databáze Redis (nejenom) pro vývojáře používající Python (dokončení)
https://www.root.cz/clanky/databaze-redis-nejenom-pro-vyvojare-pouzivajici-python-dokonceni/ - Redis Queue (RQ)
https://www.fullstackpython.com/redis-queue-rq.html - Python Celery & RabbitMQ Tutorial
https://tests4geeks.com/python-celery-rabbitmq-tutorial/ - Flower: Real-time Celery web-monitor
http://docs.celeryproject.org/en/latest/userguide/monitoring.html#flower-real-time-celery-web-monitor - Asynchronous Tasks With Django and Celery
https://realpython.com/asynchronous-tasks-with-django-and-celery/ - First Steps with Celery
http://docs.celeryproject.org/en/latest/getting-started/first-steps-with-celery.html - node-celery
https://github.com/mher/node-celery - Full Stack Python: web development
https://www.fullstackpython.com/web-development.html - Introducing RQ
https://nvie.com/posts/introducing-rq/ - Asynchronous Tasks with Flask and Redis Queue
https://testdriven.io/asynchronous-tasks-with-flask-and-redis-queue - rq-dashboard
https://github.com/eoranged/rq-dashboard - Stránky projektu Redis
https://redis.io/ - Introduction to Redis
https://redis.io/topics/introduction - Try Redis
http://try.redis.io/ - Redis tutorial, April 2010 (starší, ale pěkně udělaný)
https://static.simonwillison.net/static/2010/redis-tutorial/ - Python Redis
https://redislabs.com/lp/python-redis/ - Redis: key-value databáze v paměti i na disku
https://www.zdrojak.cz/clanky/redis-key-value-databaze-v-pameti-i-na-disku/ - Praktický úvod do Redis (1): vaše distribuovaná NoSQL cache
http://www.cloudsvet.cz/?p=253 - Praktický úvod do Redis (2): transakce
http://www.cloudsvet.cz/?p=256 - Praktický úvod do Redis (3): cluster
http://www.cloudsvet.cz/?p=258 - Connection pool
https://en.wikipedia.org/wiki/Connection_pool - Instant Redis Sentinel Setup
https://github.com/ServiceStack/redis-config - How to install REDIS in Linux
https://linuxtechlab.com/how-install-redis-server-linux/ - Redis RDB Dump File Format
https://github.com/sripathikrishnan/redis-rdb-tools/wiki/Redis-RDB-Dump-File-Format - Lempel–Ziv–Welch
https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch - Redis Persistence
https://redis.io/topics/persistence - Redis persistence demystified
http://oldblog.antirez.com/post/redis-persistence-demystified.html - Redis reliable queues with Lua scripting
http://oldblog.antirez.com/post/250 - Ost (knihovna)
https://github.com/soveran/ost - NoSQL
https://en.wikipedia.org/wiki/NoSQL - Shard (database architecture)
https://en.wikipedia.org/wiki/Shard_%28database_architecture%29 - What is sharding and why is it important?
https://stackoverflow.com/questions/992988/what-is-sharding-and-why-is-it-important - What Is Sharding?
https://btcmanager.com/what-sharding/ - Redis clients
https://redis.io/clients - Category:Lua-scriptable software
https://en.wikipedia.org/wiki/Category:Lua-scriptable_software - Seriál Programovací jazyk Lua
https://www.root.cz/serialy/programovaci-jazyk-lua/ - Redis memory usage
http://nosql.mypopescu.com/post/1010844204/redis-memory-usage - Ukázka konfigurace Redisu pro lokální testování
https://github.com/tisnik/presentations/blob/master/redis/redis.conf - Resque
https://github.com/resque/resque - Nested transaction
https://en.wikipedia.org/wiki/Nested_transaction - Publish–subscribe pattern
https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern - Messaging pattern
https://en.wikipedia.org/wiki/Messaging_pattern - Using pipelining to speedup Redis queries
https://redis.io/topics/pipelining - Pub/Sub
https://redis.io/topics/pubsub - ZeroMQ distributed messaging
http://zeromq.org/ - ZeroMQ: Modern & Fast Networking Stack
https://www.igvita.com/2010/09/03/zeromq-modern-fast-networking-stack/ - Publish/Subscribe paradigm: Why must message classes not know about their subscribers?
https://stackoverflow.com/questions/2908872/publish-subscribe-paradigm-why-must-message-classes-not-know-about-their-subscr - Python & Redis PUB/SUB
https://medium.com/@johngrant/python-redis-pub-sub-6e26b483b3f7 - Message broker
https://en.wikipedia.org/wiki/Message_broker - RESP Arrays
https://redis.io/topics/protocol#array-reply - Redis Protocol specification
https://redis.io/topics/protocol - Redis Pub/Sub: Intro Guide
https://www.redisgreen.net/blog/pubsub-intro/ - Redis Pub/Sub: Howto Guide
https://www.redisgreen.net/blog/pubsub-howto/ - Comparing Publish-Subscribe Messaging and Message Queuing
https://dzone.com/articles/comparing-publish-subscribe-messaging-and-message - Apache Kafka
https://kafka.apache.org/ - Iron
http://www.iron.io/mq - kue (založeno na Redisu, určeno pro node.js)
https://github.com/Automattic/kue - Cloud Pub/Sub
https://cloud.google.com/pubsub/ - Introduction to Redis Streams
https://redis.io/topics/streams-intro - glob (programming)
https://en.wikipedia.org/wiki/Glob_(programming) - Why and how Pricing Assistant migrated from Celery to RQ – Paris.py
https://www.slideshare.net/sylvinus/why-and-how-pricing-assistant-migrated-from-celery-to-rq-parispy-2 - Enqueueing internals
http://python-rq.org/contrib/ - queue — A synchronized queue class
https://docs.python.org/3/library/queue.html - Queue – A thread-safe FIFO implementation
https://pymotw.com/2/Queue/ - Queues
http://queues.io/ - Windows Subsystem for Linux Documentation
https://docs.microsoft.com/en-us/windows/wsl/about - RestMQ
http://restmq.com/ - ActiveMQ
http://activemq.apache.org/ - Amazon MQ
https://aws.amazon.com/amazon-mq/ - Amazon Simple Queue Service
https://aws.amazon.com/sqs/ - Celery: Distributed Task Queue
http://www.celeryproject.org/ - Disque, an in-memory, distributed job queue
https://github.com/antirez/disque - rq-dashboard
https://github.com/eoranged/rq-dashboard - Projekt RQ na PyPi
https://pypi.org/project/rq/ - rq-dashboard 0.3.12
https://pypi.org/project/rq-dashboard/ - Job queue
https://en.wikipedia.org/wiki/Job_queue - Why we moved from Celery to RQ
https://frappe.io/blog/technology/why-we-moved-from-celery-to-rq - Running multiple workers using Celery
https://serverfault.com/questions/655387/running-multiple-workers-using-celery - celery — Distributed processing
http://docs.celeryproject.org/en/latest/reference/celery.html - Chains
https://celery.readthedocs.io/en/latest/userguide/canvas.html#chains - Routing
http://docs.celeryproject.org/en/latest/userguide/routing.html#automatic-routing - Celery Distributed Task Queue in Go
https://github.com/gocelery/gocelery/ - Python Decorators
https://wiki.python.org/moin/PythonDecorators - Periodic Tasks
http://docs.celeryproject.org/en/latest/userguide/periodic-tasks.html - celery.schedules
http://docs.celeryproject.org/en/latest/reference/celery.schedules.html#celery.schedules.crontab - Pros and cons to use Celery vs. RQ
https://stackoverflow.com/questions/13440875/pros-and-cons-to-use-celery-vs-rq - Priority queue
https://en.wikipedia.org/wiki/Priority_queue - Jupyter
https://jupyter.org/ - How IPython and Jupyter Notebook work
https://jupyter.readthedocs.io/en/latest/architecture/how_jupyter_ipython_work.html - Context Managers
http://book.pythontips.com/en/latest/context_managers.html