RabbitMQ: jedna z nejúspěšnějších implementací brokera

20. 12. 2018
Doba čtení: 34 minut

Sdílet

Třetím nástrojem implementujícím fronty zpráv a popř. i asynchronní fronty úloh, s nímž se na stránkách Rootu seznámíme, je nástroj nazvaný RabbitMQ. Dnes si ukážeme základní vlastnosti tohoto systému a jeho použití v Pythonu a Clojure.

Obsah

1. RabbitMQ – jedna z nejúspěšnějších implementací brokera

2. Rozhraní mezi nástrojem RabbitMQ a programovacími jazyky

3. Protokoly podporované systémem RabbitMQ

4. Ukázky základních způsobů použití nástroje RabbitMQ

5. Směrování zpráv před jejich vložením do fronty

6. Instalace RabbitMQ a konfigurace jeho síťového rozhraní

7. Spuštění serveru ve funkci démona/služby

8. Instalace knihovny Pika s rozhraním mezi RabbitMQ a Pythonem

9. První příklad: nejjednodušší možná implementace systému producent+konzument

10. Konzument zpráv

11. Přenos společných prvků producenta a konzumenta do jednoho zdrojového kódu

12. Pomalá práce konzumenta a způsob dělby práce mezi větší množství workerů

13. Potvrzování zpracování zprávy, chování systému ve chvíli, kdy zpráva není zpracována

14. Vylepšené chování RabbitMQ při „dělbě práce“ mezi workery

15. Konzument zpráv naprogramovaný v jazyku Clojure

16. Konfigurace projektu využívajícího knihovnu Langohr

17. Realizace konzumenta pro příjem zpráv

18. Spuštění konzumenta a příjem zpráv

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

1. RabbitMQ – jedna z nejúspěšnějších implementací brokera

Třetím nástrojem implementujícím fronty zpráv a popř. i asynchronní fronty úloh, s nímž se na stránkách Rootu seznámíme, je nástroj nazvaný RabbitMQ. Původně se jednalo o klasického message brokera (de facto o implementaci fronty zpráv) a taktéž o jednu z implementací protokolu AMQP (Advanced Message Queuing Protocol), ovšem později byly možnosti nástroje RabbitMQ rozšířeny, takže dnes jsou podporovány i protokoly MQTT (Message Queuing Telemetry Transport, mimochodem se jedná o standard ISO) a STOMP neboli Streaming Text Oriented Messaging Protocol. RabbitMQ je poměrně široce konfigurovatelný a flexibilní, což administrátorům umožňuje zvolit si takové vlastnosti, které nejlépe vyhovují potřebám implementované služby. Například je možné RabbitMQ nastavit do režimu s velkou propustností, ovšem bez zapnutých mechanismů pro persistenci zpráv, nebo naopak do režimu, v němž zprávy přežijí restart jak workerů, tak i samotného serveru Rabbitu (dokonce lze zaručit vysokou dostupnost samotných front).

Na systému RabbitMQ je zajímavý i fakt, že je vyvinut v programovacím jazyce Erlang s využitím sady knihoven a nástrojů Open Telecom Platform (OTP). Volba tohoto programovacího jazyka pro systém zajišťující kooperaci mezi obecně velkým množstvím procesů a služeb je velmi dobrá, protože tento jazyk byl navržen právě s ohledem na tvorbu distribuovaných aplikací, které jsou do značné míry odolné vůči selhání.

Poznámka: nemusíte se ovšem bát, že by byl RabbitMQ kvůli tomu, že je naprogramován v Erlangu, hodně náročný na systémové zdroje (je tomu spíše naopak). Sice je pravda, že se společně se serverem RabbitMQ nainstaluje i velká část ekosystému Erlangu, ale spotřeba operační paměti při běhu serveru se pohybuje v rozumných mezích (navíc je možné relativně snadno zajistit, aby měl virtuální stroj Erlangu k dispozici pouze omezené množství paměti).

V dnešním článku si ukážeme, jakým způsobem je možné s využitím systému RabbitMQ nastavit a naprogramovat jednoduchou architekturu producent-konzument. Řekneme si, jak se řídí dělba práce mezi jednotlivými workery (které implementují algoritmus konzumenta), jakým způsobem se dají sledovat jednotlivé fronty použité pro posílání zpráv a konečně si ukážeme způsob potvrzení dokončení práce workerem. To je ovšem jen velmi malá část funkcionality nabízené tímto systémem; další podrobnosti si proto popíšeme v navazujícím článku.

2. Rozhraní mezi nástrojem RabbitMQ a programovacími jazyky

K nástroji RabbitMQ byla vytvořena rozhraní (různé knihovny) umožňující práci s tímto systémem (posílání a příjem zpráv) z mnoha programovacích jazyků:

Jazyk/platforma
Java
JavaScript (Node.js)
Python
Ruby
PHP
C#
Go
Elixir
(Java) Spring AMQP
Swift
Objective-C
Clojure

U některých výše zmíněných jazyků je k dispozici dokonce větší množství knihoven. V dnešním článku se zaměříme především na programovací jazyk Python, pro nějž existuje mj. i knihovna nazvaná Pika, která je samozřejmě instalovatelná přes PyPi (https://pypi.org/project/pika/). Abychom se ovšem nedrželi pouze čistého mainstreamu, ukážeme si v závěru článku i volání funkcí systému RabbitMQ z programovacího jazyka Clojure s využitím knihovny Langohr.

3. Protokoly podporované systémem RabbitMQ

Se systémem RabbitMQ mohou další části aplikace komunikovat s využitím několika protokolů, které se od sebe odlišují svými vlastnostmi (binární vs. textový protokol), složitostí implementace, sémantikou příkazů atd.

Popis podporovaných protokolů musíme začít zmínkou o podpoře AMQP (Advanced Message Queuing Protocol) verze 0–9–1, 0–9 a 0–8, protože RabbitMQ původně vznikl právě z toho důvodu, aby podporoval tyto standardy AMQP. Jedná se o binární protokol, v němž je velká váha kladena nejen na precizní specifikaci formátu dat, ale i na popis sémantiky operací prováděných službami, které AMQP implementují (či možná lépe řečeno akceptují). Jak uvidíme dále, jsou termíny používané v dokumentaci RabbitMQ odvozeny právě od terminologie převzaté ze specifikace AMQP. Specifikace pro verzi 0–9–1 je dostupná na stránce https://www.rabbitmq.com/re­sources/specs/amqp0–9–1.pdf.

Poznámka: verze uvádím schválně s pomlčkami namísto dnes obvyklejších teček, protože právě takto se verze označují v oficiální specifikaci a umožní vám přesnější hledání dalších informací.

Kromě verzí 0–9–1, 0–9 a 0–8 existuje ještě protokol AMQP verze 1.0. Jedná se o protokol, který je i přes shodné jméno značně odlišný od předchozích verzí. RabbitMQ sice ve své základní konfiguraci tento protokol nepodporuje, ovšem je možné si nainstalovat přídavný modul (plugin), který podporu do Rabbitu přidává. Další informace o tomto modulu naleznete v jeho repositáři na adrese https://github.com/rabbitmq/rabbitmq-amqp1.0 (v článku ani v demonstračních příkladech ho nepoužijeme).

Třetím protokolem, který lze použít pro komunikaci s frontami Rabbita, je protokol nazvaný STOMP neboli Streaming Text Oriented Messaging Protocol. Jedná se o relativně jednoduchý protokol založený – jak jeho název napovídá – na příkazech posílaných v textovém formátu se syntaxí, která se podobá protokolu HTTP. Předností je snadná implementace klientů a v případě potřeby (ladění, simulace útoků atd.) je dokonce možné namísto dedikovaného klienta použít běžný telnet.

Posledním protokolem, o němž se zmíníme a který je podporován přes plugin (stejně jako AMQP 1.0), je protokol nazvaný MQTT, který je mj. určený pro dnes populární IoT, tj. obecně pro zařízení s relativně malým výkonem popř. omezenými systémovými zdroji (a většinou i omezenou odolností proti útokům :).

Poznámka: díky tomu, že v demonstračních příkladech použijeme již připravené knihovny zajišťující rozhraní k RabbituMQ, se přímo s použitím těchto protokolů ve skutečnosti nesetkáme. Později si ovšem pro úplnost ukážeme, jak lze naprogramovat jednoduchého klienta používajícího protokol STOMP.

4. Ukázky základních způsobů použití nástroje RabbitMQ

Samotný RabbitMQ je možné použít mnoha různými způsoby. Na nejnižší úrovni se na tento systém můžeme dívat jako na klasickou a jednoduchou frontu zpráv (message queue). Tuto frontu je přitom možné nakonfigurovat takovým způsobem, aby byla zachována její robustnost, tj. aby zprávy ve frontě přežily restart serveru RabbitMQ a aby bylo zaručeno doručení zprávy (příjemce ovšem musí potvrdit, že zprávu převzal a nějakým způsobem zpracoval, což si ukážeme na praktických příkladech). Tuto konfiguraci můžete použít kdykoli je nutné propojit několik komponent do složitější architektury, přičemž jedna z komponent bude produkovat zprávy (data, příkazy) a další komponenta/komponenty zprávy přijímat a asynchronně je zpracovávat:

Obrázek 1: RabbitMQ je možné použít jako jednoduchou frontu zpráv, přičemž zpráva může vznikat na jiném počítači, než na kterém běží samotná fronta. I příjemce může být provozován na samostatném počítači (počítačích). Počet front, které jsou pojmenovány, není žádným drastickým způsobem omezen – v praxi se může jednat o stovky front.

RabbitMQ se dále poměrně často používá v roli brokera i pro plánování a asynchronní spouštění úloh. Ostatně přesně v této roli je RabbitMQ použit i systémem Celery, s nímž jsme se seznámili v předchozích dvou článcích [1] [].

Obrázek 2: RabbitMQ jako klasický broker využívaný dalšími systémy.

V případě, že se RabbitMQ využívá v roli brokera, umožňuje připojení různých konzumentů a nastavení strategií pro směrování zpráv mezi producenty a konzumenty. Samozřejmě je implementováno i rozložení zátěže mezi větší množství konzumentů v případě, že všichni tito konzumenti implementují stejnou logiku (službu). S tímto systémem jsme se ostatně již setkali při popisu Redis Queue i Celery, v nichž bylo relativně snadné spustit větší množství workerů a celou aplikaci tak škálovat podle potřeby:

Obrázek 3: Počet konzumentů není v podstatě nijak omezen.

Dále je možné nakonfigurovat rozvětvení (fanout) zpráv, tj. každá zpráva může být doručena většímu množství příjemců. Příkladem může být vzájemná distribuce textových zpráv mezi několika servery implementující internetová „kecátka“ (Slack, Mattermost, Jabber/XMPP…). Každý server přitom může současně vystupovat v roli příjemce zpráv i producenta zpráv.

5. Směrování zpráv před jejich vložením do fronty

Interní struktura nástroje RabbitMQ je ve skutečnosti mnohem složitější, než by se mohlo na první pohled zdát. Ústředním prvkem jsou samozřejmě fronty se zprávami, ovšem před samotnými frontami je umístěn takzvaný exchange sloužící pro směrování zpráv do jednotlivých front:

Obrázek 4: Interní konfigurovatelná struktura systému RabbitMQ.

Poznámka: termíny „exchange“ a „binding“ vychází právě ze specifikace AMQP,.

Samotný broker, v tomto případě RabbitMQ, podporuje několik strategií pro rozhodnutí, do jaké fronty či do jakých front se má přijatá zpráva vložit. Každá z těchto strategií je samozřejmě plně konfigurovatelná a má své jméno, které je dobré znát při hledání podrobnějších informací v dokumentaci:

  • Nejjednodušší strategie se jmenuje direct. Tato strategie je založena na tom, že samotná zpráva obsahuje klíč (key), který slouží pro výběr správné fronty. Pokud budeme mít k dispozici jedinou frontu a budeme používat jeden klíč, celý broker se nám vlastně zúží na „obyčejnou“ frontu zpráv podporující různé protokoly a nabízejí řešení s vysokou dostupností. Klíč je reprezentován jako běžný řetězec.
  • Další strategie se nazývá topic. Jedná se o složitější formu navázání zprávy na frontu, v němž se opět používá klíč uložený ve zprávě. Tento klíč se porovnává s regulárními výrazy specifikovanými v konfiguraci směrovače. Ve chvíli, kdy klíč odpovídá nějakému regulárnímu výrazu, je zpráva přesměrována do příslušné fronty. Můžeme tak například velmi snadno směrovat zprávy do různých front (a tím pádem i do různých workerů) na základě doménového jména serveru, kde zpráva vznikla apod.
  • Třetí strategie používá hlavičky (headers) připojené ke zprávě. To umožňuje detailnější konfiguraci směrování; podrobnosti si popíšeme v navazujícím článku.
  • A konečně čtvrtá strategie se nazývá fanout. Při použití této strategie se přijatá zpráva přenese (zduplikuje) do několika nakonfigurovaných front, což znamená, že bude přijata a zpracována několikrát. V praxi se například může jednat o přeposlání zprávy napsané klientem na různé servery implementující nějakou internetovou komunikační službu (Slack atd.).

V dnešním článku se budeme převážně zabývat první strategií, tj. přímým přeposláním přijaté zprávy do fronty určené klíčem ve zprávě. Trošku předběhneme, ovšem asi je vhodné si ukázat, jak se vlastně zpráva s klíčem pošle. Ve chvíli, kdy má klient nakonfigurovaný a otevřený komunikační kanál na brokera, může zprávu s klíčem poslat takto:

channel.basic_publish(exchange='',
                      routing_key='test',
                      body='Hello World!')

Povšimněte si, že se u zprávy specifikuje jak její hodnota/tělo (v našem případě se jedná o pouhopouhý řetězec), tak i klíč a popř. i jméno tzv. exchange, která souvisí s vybranou strategií. V případě, že strategie není specifikována, což je ostatně náš případ, bude RabbitMQ server předpokládat výchozí volbu exchange (viz též podrobnější popis na stránce http://www.rabbitmq.com/amqp-0–9–1-reference.html#basic.publish).

6. Instalace RabbitMQ a konfigurace jeho síťového rozhraní

Instalace systému RabbitMQ je na mainstreamových distribucích velmi snadná, pokud se ovšem spokojíme s tím, že může být použita poněkud starší verze Rabbitu. Balíček nazvaný rabbitmq-server je dostupný ve standardních repositářích Debianu, Ubuntu, RHELu, CentOSu i Fedory. Vyzkoušejme si instalaci na Fedoře, v níž byla v době instalace dostupná verze 3.6.16 (nejnovější verze Rabbitu je přitom 3.7.9). Instalaci provedeme ze standardního repositáře přičemž se společně s Rabbitem nainstaluje i běhové prostředí Erlangu:

# dnf install rabbitmq-server
 
Last metadata expiration check: 0:22:16 ago on Fri 16 Nov 2018 10:14:57 AM EST.
Dependencies resolved.
=========================================================================================================================================================
 Package                                     Arch                          Version                                    Repository                    Size
=========================================================================================================================================================
Installing:
 rabbitmq-server                             noarch                        3.6.16-3.fc29                              beaker-Fedora-Everything     4.7 M
Installing dependencies:
 erlang-asn1                                 x86_64                        20.3.8.9-2.fc29                            beaker-Fedora-Everything     742 k
 erlang-compiler                             x86_64                        20.3.8.9-2.fc29                            beaker-Fedora-Everything     1.1 M
 erlang-crypto                               x86_64                        20.3.8.9-2.fc29                            beaker-Fedora-Everything     124 k
 erlang-eldap                                x86_64                        20.3.8.9-2.fc29                            beaker-Fedora-Everything     111 k
 erlang-erts                                 x86_64                        20.3.8.9-2.fc29                            beaker-Fedora-Everything     2.0 M
 erlang-hipe                                 x86_64                        20.3.8.9-2.fc29                            beaker-Fedora-Everything     2.8 M
 erlang-inets                                x86_64                        20.3.8.9-2.fc29                            beaker-Fedora-Everything     781 k
 erlang-kernel                               x86_64                        20.3.8.9-2.fc29                            beaker-Fedora-Everything     1.1 M
 erlang-mnesia                               x86_64                        20.3.8.9-2.fc29                            beaker-Fedora-Everything     767 k
 erlang-os_mon                               x86_64                        20.3.8.9-2.fc29                            beaker-Fedora-Everything     110 k
 erlang-otp_mibs                             x86_64                        20.3.8.9-2.fc29                            beaker-Fedora-Everything      27 k
 erlang-public_key                           x86_64                        20.3.8.9-2.fc29                            beaker-Fedora-Everything     604 k
 erlang-runtime_tools                        x86_64                        20.3.8.9-2.fc29                            beaker-Fedora-Everything     208 k
 erlang-sasl                                 x86_64                        20.3.8.9-2.fc29                            beaker-Fedora-Everything     290 k
 erlang-sd_notify                            x86_64                        1.0-8.fc29                                 beaker-Fedora-Everything      14 k
 erlang-snmp                                 x86_64                        20.3.8.9-2.fc29                            beaker-Fedora-Everything     1.7 M
 erlang-ssl                                  x86_64                        20.3.8.9-2.fc29                            beaker-Fedora-Everything     859 k
 erlang-stdlib                               x86_64                        20.3.8.9-2.fc29                            beaker-Fedora-Everything     2.9 M
 erlang-syntax_tools                         x86_64                        20.3.8.9-2.fc29                            beaker-Fedora-Everything     408 k
 erlang-tools                                x86_64                        20.3.8.9-2.fc29                            beaker-Fedora-Everything     638 k
 erlang-xmerl                                x86_64                        20.3.8.9-2.fc29                            beaker-Fedora-Everything     995 k
 lksctp-tools                                x86_64                        1.0.16-10.fc29                             beaker-Fedora-Everything      92 k
 logrotate                                   x86_64                        3.14.0-4.fc29                              beaker-Fedora-Everything      68 k
 xemacs-filesystem                           noarch                        21.5.34-30.20171230hg92757c2b8239.fc29     beaker-Fedora-Everything      10 k
 
Transaction Summary
=========================================================================================================================================================
Install  25 Packages
 
Total download size: 23 M
Installed size: 36 M
Is this ok [y/N]
 
Downloading Packages:
(1/25): erlang-crypto-20.3.8.9-2.fc29.x86_64.rpm                                                                              1.4 MB/s | 124 kB     00:00
(2/25): erlang-asn1-20.3.8.9-2.fc29.x86_64.rpm                                                                                5.9 MB/s | 742 kB     00:00
(24/25): xemacs-filesystem-21.5.34-30.20171230hg92757c2b8239.fc29.noarch.rpm                                                  218 kB/s |  10 kB     00:00
(25/25): rabbitmq-server-3.6.16-3.fc29.noarch.rpm                                                                              21 MB/s | 4.7 MB     00:00
--------------------------------------------------------------------------------------------------------------------------------------------------------------
Total                                                                                                                          27 MB/s |  23 MB     00:00
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
 
Installed:
  rabbitmq-server-3.6.16-3.fc29.noarch                                          erlang-asn1-20.3.8.9-2.fc29.x86_64                        erlang-compiler-20.3.8.9-2.fc29.x86_64
  erlang-crypto-20.3.8.9-2.fc29.x86_64                                          erlang-eldap-20.3.8.9-2.fc29.x86_64                       erlang-erts-20.3.8.9-2.fc29.x86_64
  erlang-hipe-20.3.8.9-2.fc29.x86_64                                            erlang-inets-20.3.8.9-2.fc29.x86_64                       erlang-kernel-20.3.8.9-2.fc29.x86_64
  erlang-mnesia-20.3.8.9-2.fc29.x86_64                                          erlang-os_mon-20.3.8.9-2.fc29.x86_64                      erlang-otp_mibs-20.3.8.9-2.fc29.x86_64
  erlang-public_key-20.3.8.9-2.fc29.x86_64                                      erlang-runtime_tools-20.3.8.9-2.fc29.x86_64               erlang-sasl-20.3.8.9-2.fc29.x86_64
  erlang-sd_notify-1.0-8.fc29.x86_64                                            erlang-snmp-20.3.8.9-2.fc29.x86_64                        erlang-ssl-20.3.8.9-2.fc29.x86_64
  erlang-stdlib-20.3.8.9-2.fc29.x86_64                                          erlang-syntax_tools-20.3.8.9-2.fc29.x86_64                erlang-tools-20.3.8.9-2.fc29.x86_64
  erlang-xmerl-20.3.8.9-2.fc29.x86_64                                           lksctp-tools-1.0.16-10.fc29.x86_64                        logrotate-3.14.0-4.fc29.x86_64
  xemacs-filesystem-21.5.34-30.20171230hg92757c2b8239.fc29.noarch
 
Complete!

Po úspěšné instalaci si můžeme vyzkoušet server přímo spustit. V praxi se sice přímé spouštění neprovádí (lepší je RabbitMQ spustit jako démona), ale pro první odzkoušení, zda jsou dostupné všechny knihovny, je to pravděpodobně nejjednodušší cesta:

$ su -
 
# rabbitmq-server
 
              RabbitMQ 3.6.16. Copyright (C) 2007-2018 Pivotal Software, Inc.
  ##  ##      Licensed under the MPL.  See http://www.rabbitmq.com/
  ##  ##
  ##########  Logs: /var/log/rabbitmq/rabbit@intel-sugarbay-dh-02.log
  ######  ##        /var/log/rabbitmq/rabbit@intel-sugarbay-dh-02-sasl.log
  ##########
              Starting broker...
 completed with 0 plugins.

Pokud se spuštění podařilo, server ukončíme stiskem klávesové zkratky Ctrl+C a před dalšími pokusy omezíme možnosti síťového připojení takovým způsobem, aby se k serveru mohly připojit pouze lokální uživatelé a lokální služby. V souboru /etc/rabbitmq/rabbitmq.config je nutné provést následující změny, kterými se zajistí, že server bude přijímat pouze lokální požadavky:

[
 {rabbit,
  [
   {tcp_listeners, [{"127.0.0.1", 5672},
                    {"::1",       5672}]}
  ]
  ...
  ...
  ...
  }]
Poznámka: na konci řádku končícího „5672}]}“ je nutné zapsat čárku v případě, že následují další volby. Konfigurační soubor ve výchozím stavu však žádné další volby neobsahuje, resp. přesněji řečeno jsou všechny další řádky zakomentovány.

7. Spuštění serveru ve funkci démona/služby

Většinou se RabbitMQ spouští ve funkci démona, resp. služby. Pro operační systémy s klasickým Init systémem postačuje provést:

# chkconfig rabbitmq-server on
# service rabbitmq-server start

Pro systému používající Systemd lze použít buď předchozí příkazy (ty jsou transformovány na příkazy nové), nebo:

# systemctl enable rabbitmq-server.service
# systemctl start rabbitmq-server.service

S kontrolou, zda se spuštění služby podařilo:

# systemctl status rabbitmq-server.service 
 
● rabbitmq-server.service - RabbitMQ broker
   Loaded: loaded (/usr/lib/systemd/system/rabbitmq-server.service; enabled; ven
   Active: active (running) since Wed 2018-12-19 14:39:45 CET; 33s ago
 Main PID: 20495 (beam.smp)
   Status: "Initialized"
    Tasks: 143 (limit: 4915)
   CGroup: /system.slice/rabbitmq-server.service
           ├─20495 /usr/lib64/erlang/erts-8.3.5.4/bin/beam.smp -W w -A 128 -P 10
           ├─20774 erl_child_setup 1024
           ├─20790 inet_gethost 4
           └─20791 inet_gethost 4
 
pro 19 14:39:43 localhost.localdomain systemd[1]: Starting RabbitMQ broker...
pro 19 14:39:44 localhost.localdomain rabbitmq-server[20495]:               Rabb
pro 19 14:39:44 localhost.localdomain rabbitmq-server[20495]:   ##  ##      Lice
pro 19 14:39:44 localhost.localdomain rabbitmq-server[20495]:   ##  ##
pro 19 14:39:44 localhost.localdomain rabbitmq-server[20495]:   ##########  Logs
pro 19 14:39:44 localhost.localdomain rabbitmq-server[20495]:   ######  ##
pro 19 14:39:44 localhost.localdomain rabbitmq-server[20495]:   ##########
pro 19 14:39:44 localhost.localdomain rabbitmq-server[20495]:               Star
pro 19 14:39:45 localhost.localdomain systemd[1]: Started RabbitMQ broker.
pro 19 14:39:45 localhost.localdomain rabbitmq-server[20495]:  completed with 0

8. Instalace knihovny Pika s rozhraním mezi RabbitMQ a Pythonem

Posledním přípravným krokem je instalace knihovny Pika, která programátorům nabízí rozhraní mezi systémem RabbitMQ na straně jedné a Pythonem (resp. skripty napsanými v Pythonu) na straně druhé. Instalaci provedeme přes nástroj pip3, přičemž budeme specifikovat, že se má instalace provést pouze pro aktivního uživatele, aby se nezasahovalo do systémových knihoven a modulů:

$ pip3 install --user pika
 
Collecting pika
  Using cached https://files.pythonhosted.org/packages/bf/48/72de47f63ba353bacd74b76bb65bc63620b0706d8b0471798087cd5a4916/pika-0.12.0-py2.py3-none-any.whl
Installing collected packages: pika
Successfully installed pika-0.12.0

9. První příklad: nejjednodušší možná implementace systému producent+konzument

V této chvíli je náš testovací počítač připraven na příjem zpráv od producentů a na jejich následné zpracování konzumenty. Nejprve si ukažme, jak může vypadat producent. Jedná se o velmi krátký skript naprogramovaný v Pythonu, který se nejdříve připojí k serveru RabbitMQ, následně požádá o vytvoření či o zpřístupnění fronty nazvané „test“, pošle do fronty zprávu (nepřímo přes exchange) a následně zavře připojení k serveru:

import pika
 
# připojení k serveru RabbitMQ a vytvoření komunikačního kanálu
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()
 
# žádost o vytvoření či o použití fronty „test“
channel.queue_declare(queue='test')
 
# poslání zprávy se strategií direct
channel.basic_publish(exchange='',
                      routing_key='test',
                      body='Hello World!')
 
print("Sent 'Hello World!'")
# uzavření komunikace
connection.close()

Zajímavý je řádek:

channel.queue_declare(queue='test')

V případě, že fronta nazvaná „test“ neexistuje, je vytvořena. Pokud však fronta s tímto jménem existuje, nic se neděje – nedojde k jejímu vymazání ani k vyprázdnění (operace je idempotentní).

Dále si povšimněte, že používáme shodné jméno klíče a fronty. To je v pořádku, protože ve výchozím nastavení se využívá strategie direct, která přesměruje zprávu do fronty, jejíž název odpovídá klíči.

Pokud nyní výše uvedený skript spustíme:

$ python3 enqueue_work.py

… měla by se do fronty „test“ vložit jediná zpráva. O tom se můžeme snadno přesvědčit s využitím nástroje rabbitmqctl:

$ sudo rabbitmqctl list_queues
 
Listing queues
test    1

Samozřejmě je možné do fronty poslat větší počet zpráv, a to mírně upraveným producentem:

#!/usr/bin/env python
import pika
 
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()
 
channel.queue_declare(queue='test')
 
for i in range(1, 11):
    channel.basic_publish(exchange='',
                          routing_key='test',
                          body='Hello World! #{i}'.format(i=i))
 
print("Sent 'Hello World!' ten times")
connection.close()

Nyní by se mělo ve frontě nacházet jedenáct zpráv – jedna původní a deset nových:

$ sudo rabbitmqctl list_queues
 
Listing queues
test    11

10. Konzument zpráv

Implementace konzumenta (příjemce) zpráv je nepatrně složitější, protože je nutné deklarovat callback funkci automaticky zavolanou ve chvíli, kdy je zpráva z fronty přijata. Pro tento účel se používá metoda:

channel.basic_consume(on_receive,
                      queue='test',
                      no_ack=True)

Této metodě se předá reference na již zmíněnou callback funkci, dále jméno fronty, ze které se mají zprávy přijímat a konečně jako nepovinný parametr lze specifikovat, zda bude konzument aktivně informovat systém RabbitMQ o tom, že zpráva byla zpracována. Prozatím potvrzování o zpracování zpráv nebudeme potřebovat, takže parametr no_ack nastavíme na True.

Celá implementace konzumenta zpráv bude vypadat takto:

#!/usr/bin/env python
import pika
 
# připojení k serveru RabbitMQ a vytvoření komunikačního kanálu
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()
 
# pokus o nové vytvoření fronty ve skutečnosti neovlivní již existující frontu
# pokud samozřejmě existuje
channel.queue_declare(queue='test')
 
 
# callback funkce volaná při přijetí zprávy
def on_receive(ch, method, properties, body):
    print("Received %r" % body)
 
 
# přihlášení se k odebírání zpráv z fronty "test"
channel.basic_consume(on_receive,
                      queue='test',
                      no_ack=True)
 
print('Waiting for messages. To exit press CTRL+C')
print("...")
 
# smyčka s odebíráním zpráv
channel.start_consuming()

V případě, že konzumenta spustíme, zpracuje všechny zbývající zprávy z fronty „test“. Pokud jste v předchozí kapitole postupovali podle návodu, mělo by se jednat o jedenáct zpráv:

$ python3 consumer.py
 
Waiting for messages. To exit press CTRL+C
...
Received b'Hello World!'
Received b'Hello World! #1'
Received b'Hello World! #2'
Received b'Hello World! #3'
Received b'Hello World! #4'
Received b'Hello World! #5'
Received b'Hello World! #6'
Received b'Hello World! #7'
Received b'Hello World! #8'
Received b'Hello World! #9'
Received b'Hello World! #10'

11. Přenos společných prvků producenta a konzumenta do jednoho zdrojového kódu

Před dalšími pokusy nepatrně upravíme kód producenta i konzumenta (spotřebitele), protože jsme mohli vidět, že samotné připojení k serveru RabbitMQ je realizováno shodným kódem. Z tohoto kódu je tedy možné vytvořit samostatný modul, který může vypadat následovně:

import pika
 
 
def connect(where='localhost', queue_name='test'):
    # připojení k serveru RabbitMQ a vytvoření komunikačního kanálu
    connection = pika.BlockingConnection(pika.ConnectionParameters(where))
    channel = connection.channel()
 
    # pokus o nové vytvoření fronty ve skutečnosti neovlivní již existující frontu
    # pokud samozřejmě existuje
    channel.queue_declare(queue=queue_name)
    return connection, channel

Skript, který po svém spuštění vytvoří jednu zprávu a vloží ji do fronty „test“, se zjednoduší:

#!/usr/bin/env python
import pika
 
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
connection, channel = connect()
 
channel.basic_publish(exchange='',
                      routing_key='test',
                      body='Hello World!')
 
print("Sent 'Hello World!'")
connection.close()

Podobným způsobem se zjednoduší i skript pro vytvoření deseti zpráv:

#!/usr/bin/env python
from rabbitmq_connect import connect
 
connection, channel = connect()
 
for i in range(1, 11):
    channel.basic_publish(exchange='',
                          routing_key='test',
                          body='Hello World! #{i}'.format(i=i))
 
print("Sent 'Hello World!' ten times")
connection.close()

Prakticky stejná úprava se samozřejmě dotkne i konzumenta zpráv:

#!/usr/bin/env python
from rabbitmq_connect import connect
 
connection, channel = connect()
 
def on_receive(ch, method, properties, body):
    print("Received %r" % body)
 
 
channel.basic_consume(on_receive,
                      queue='test',
                      no_ack=True)
 
print('Waiting for messages. To exit press CTRL+C')
print("...")
channel.start_consuming()

12. Pomalá práce příjemce zpráv a způsob dělby práce mezi větší množství workerů

Pro další testování nepatrně upravíme kód konzumenta takovým způsobem, aby simuloval (sic!) složitější a déletrvající práci. Pro naše účely bude postačovat, když bude konzument v callback funkci zavolané při příjmu nové zprávy čekat zhruba pět sekund:

def on_receive(ch, method, properties, body):
    print("Received %r" % body)
    sleep(5)
    print("Done processing %r" % body)

Pokud nyní spustíme dva konzumenty, každý v jiném terminálu, budeme moci vidět, jak se dělí o práci.

První konzument:

$ python3 consumer.py 
 
Waiting for messages. To exit press CTRL+C
...
Received b'Hello World! #1'
Done processing b'Hello World! #1'
Received b'Hello World! #3'
Done processing b'Hello World! #3'
Received b'Hello World! #5'
Done processing b'Hello World! #5'
Received b'Hello World! #7'
Done processing b'Hello World! #7'
Received b'Hello World! #9'
Done processing b'Hello World! #9'

Druhý konzument:

$ python3 consumer.py 
Waiting for messages. To exit press CTRL+C
...
Received b'Hello World! #2'
Done processing b'Hello World! #2'
Received b'Hello World! #4'
Done processing b'Hello World! #4'
Received b'Hello World! #6'
Done processing b'Hello World! #6'
Received b'Hello World! #8'
Done processing b'Hello World! #8'
Received b'Hello World! #10'
Done processing b'Hello World! #10'

Toto rozdělování práce ve skutečnosti provádí přímo RabbitMQ a nazývá se „round robin“. V dalším textu poznáme, že výchozí chování není ideální ve chvíli, kdy se konzumenti (workeři) připojují a odpojují dynamicky.

13. Potvrzování zpracování zprávy, chování systému ve chvíli, kdy zpráva není zpracována

V předchozích příkladech jste si mohli povšimnout, že se konzument připojoval k RabbitMQ následovně:

channel.basic_consume(on_receive,
                      queue='test',
                      no_ack=True)

Posledním pojmenovaným parametrem jsme specifikovali, že jakmile konzument zprávu z fronty odebere, je zpráva považována za doručenou a RabbitMQ ji z fronty odstraní a úspěšně na ni zapomene. Ovšem v praxi se může stát (a na produkci zcela jistě tato situace nastane!), že konzument z nějakého důvodu zhavaruje nebo nebude připraven zpracování zprávy dokončit. Pokud budeme chtít, aby se v takovém případě zpráva vrátila zpět do fronty (a byla například zpracována jiným strojem), musíme připojení nepatrně upravit:

channel.basic_consume(on_receive,
                      queue='test',
                      no_ack=False)

V tomto případě se konzument zavazuje k tomu, že (ideálně) až na konci zpracování potvrdí zpracování zprávy:

ch.basic_ack(delivery_tag = method.delivery_tag)

V případě, že RabbitMQ nedostane informaci o zpracování zprávy, vloží ji do k tomu určené fronty (podrobnosti, jak tyto fronty nakonfigurovat, si řekneme příště).

Vylepšený kód konzumenta tedy může vypadat následovně:

#!/usr/bin/env python
 
from time import sleep
from rabbitmq_connect import connect
 
connection, channel = connect()
 
def on_receive(ch, method, properties, body):
    print("Received %r" % body)
    sleep(5)
    print("Done processing %r" % body)
    ch.basic_ack(delivery_tag = method.delivery_tag)
 
 
channel.basic_consume(on_receive,
                      queue='test',
                      no_ack=False)
 
print('Waiting for messages. To exit press CTRL+C')
print("...")
channel.start_consuming()
Poznámka: samozřejmě můžeme zprávu potvrdit již ihned při jejím přijetí, ovšem to se připravíte o významnou část funkcionality systému RabbitMQ.

14. Vylepšené chování RabbitMQ při „dělbě práce“ mezi workery

Ve dvanácté kapitole jsme si ukázali, že v případě, že jsou spuštěni dva konzumenti (nebo pochopitelně větší množství konzumentů), budou se spravedlivě dělit o práci, tj. o zpracování zpráv přidaných do zvolené fronty. Ovšem samotná dělba práce není ve výchozím nastavení založena na tom, jak jsou konzumenti (resp. workeři, kteří konzumenty implementují) vytíženi, protože o tom, jaký konzument zprávu zpracuje, je rozhodnuto už ve chvíli jejího přijetí do RabbitMQ. Toto chování není příliš vhodné, protože systém jako celek nedokáže zareagovat například na přidání nového konzumenta do systému. Toto chování si ostatně sami můžete otestovat ve chvíli, kdy nějaký konzument již zpracovává zprávy – přidání (spuštění) nového konzumenta nemá vliv na to, že původní konzument má svoji práci naplánovanou.

Aby RabbitMQ dokázal práci rozdělovat spravedlivěji a dynamicky podle aktuálního zatížení, musíte v konzumentovi provést jednoduchou změnu:

channel.basic_qos(prefetch_count=1)
channel.basic_consume(on_receive,
                      queue='test',
                      no_ack=False)

Nyní je možné konzumenty přidávat, popř. vypínat a systém by měl stále spravedlivě rozdělovat práci a reagovat i na to, že některá úloha trvá delší dobu.

Opět si pro úplnost ještě jednou vypišme celý kód konzumenta:

#!/usr/bin/env python
 
from time import sleep
from rabbitmq_connect import connect
 
connection, channel = connect()
 
def on_receive(ch, method, properties, body):
    print("Received %r" % body)
    sleep(5)
    print("Done processing %r" % body)
    ch.basic_ack(delivery_tag = method.delivery_tag)
 
 
channel.basic_qos(prefetch_count=1)
channel.basic_consume(on_receive,
                      queue='test',
                      no_ack=False)
 
print('Waiting for messages. To exit press CTRL+C')
print("...")
channel.start_consuming()

15. Konzument zpráv naprogramovaný v jazyku Clojure

V závěrečné části článku si ukážeme, jakým způsobem lze se systémem RabbitMQ komunikovat z programovacího jazyka Clojure. Pro tento jazyk vzniklo hned několik knihoven, které rozhraní k RabbitMQ realizují. Většina těchto knihoven je postavena na tzv. Java interop (interoperabilita mezi Javou a Clojure). Rozdíly mezi knihovnami spočívají v tom, zda se skutečně jedná o pouhou úzkou vrstvičku mezi Javou a Clojure či zda knihovna realizuje vlastní složitější (a abstraktnější) framework. Protože se zabýváme především RabbitMQ a nikoli nad ním postavenými systémy, použijeme knihovnu Langohr, která nám nebude poskytovat příliš abstraktní operace, což je dobře, protože jediné, co budeme potřebovat, je získávání zpráv z fronty s jejich dalším zpracováním.

Poznámka: v dalším textu se předpokládá, že máte nainstalován správce projektu Leiningen. Pokud tomu tak není, bude nutné si Leiningen nainstalovat, což ve skutečnosti není nic těžkého. Navíc se jedná o velmi užitečný projekt s mnoha přídavnými moduly, které využijete nejenom při testování, ale například i při přípravě dokumentace nebo ve chvíli, kdy se aplikace připravuje na deployment. To, zda je Leiningen nainstalován a lze ho spustit, zjistíte například příkazem which lein.

16. Konfigurace projektu využívajícího knihovnu Langohr

Na úplném začátku si připravíme kostru projektu, který bude představovat konzumenta. Pro vytvoření této kostry použijeme Leiningen. Kostra projektu se při použití Leiningenu vytvoří příkazem:

$ lein new app example-01
 
Generating a project called example-01 based on the 'app' template.

Výsledkem tohoto příkazu by měla být následující adresářová struktura:

.
├── CHANGELOG.md
├── doc
│   └── intro.md
├── LICENSE
├── project.clj
├── README.md
├── src
│   └── example_01
│       └── core.clj
└── test
    └── example_01
        └── core_test.clj

V dalším kroku přistoupíme k úpravám projektového souboru project.clj. Po vytvoření nového projektu by projektový soubor měl vypadat přibližně takto (pouze si pro jistotu zkontrolujte verzi interpretru jazyka Clojure; minimální požadovaná verze je 1.8.0):

(defproject example-01 "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.8.0"]]
  :main ^:skip-aot example-01.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all}})

Úprava projektového souboru spočívá v přidání informace o tom, že se v projektu bude používat knihovna langohr verze 5.0.0:

(defproject example-01 "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.8.0"]
                 [com.novemberain/langohr "5.0.0"]]
  :main ^:skip-aot example-01.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all}})

Posledním krokem konfigurace projektu je spuštění příkazu:

$ lein deps

Tento příkaz zajistí, že se do adresáře ~/.m2/ stáhnou všechny potřebné knihovny, tj. jak langohr, tak i její závislosti.

17. Realizace konzumenta pro příjem zpráv

Samotná implementace konzumenta zpráv (vzniklá úpravou getting started příkladu) je při použití programovacího jazyka Clojure nepatrně delší, než je tomu v případě Pythonu. Je tomu tak především proto, že knihovna Langohr je rozdělena na víc částí a budeme muset provést import čtyř konkrétních jmenných prostorů:

(require '[langohr.core      :as rabbit-mq])
(require '[langohr.channel   :as l-channel])
(require '[langohr.queue     :as l-queue])
(require '[langohr.consumers :as l-consumers])

Dále je v konzumentovi deklarována callback funkce zavolaná při příjmu každé zprávy. Povšimněte si, že tělo zprávy (poslední parametr) je typu bytes, ovšem v těle callback funkce ze sekvence bajtů vytvoříme řetězec. Zajímavý je i destructuring [1] použitý u druhého parametru. Jedná se o specialitu nabízenou některými Lispovskými jazyky ve chvíli, kdy se do funkcí předávají sekvence, vektory nebo mapy (slovníky):

(defn message-handler
    [ch {:keys [content-type delivery-tag type] :as meta} ^bytes payload]
    (println (format "Received a message: %s" (String. payload "UTF-8"))))

Zbývá nám provést připojení k RabbitMQ a vytvoření komunikačního kanálu:

(let [conn  (rabbit-mq/connect)
      ch    (l-channel/open conn)]

Další postup je prakticky totožný s kódem naprogramovaným v Pythonu: deklarace fronty, s níž se pracuje, přihlášení k příjmu zpráv s registrací callback funkce a na konci aplikace úklid – uzavření komunikačního kanálu a uzavření připojení k RabbitMQ:

(l-queue/declare ch "test" {:exclusive false :auto-delete false})
(l-consumers/subscribe ch "test" message-handler {:auto-ack true})
(println (format "Connected to channel id: %d" (.getChannelNumber ch)))
(Thread/sleep 10000)
(println "Disconnecting...")
(rabbit-mq/close ch)
(rabbit-mq/close conn)))

Výsledný kód realizující celého konzumenta vypadá následovně:

(ns example-01.core
    (:gen-class))
 
(require '[langohr.core      :as rabbit-mq])
(require '[langohr.channel   :as l-channel])
(require '[langohr.queue     :as l-queue])
(require '[langohr.consumers :as l-consumers])
 
 
(defn message-handler
    [ch {:keys [content-type delivery-tag type] :as meta} ^bytes payload]
    (println (format "Received a message: %s" (String. payload "UTF-8"))))
 
 
(defn -main
    [& args]
    (let [conn  (rabbit-mq/connect)
          ch    (l-channel/open conn)]
      (l-queue/declare ch "test" {:exclusive false :auto-delete false})
      (l-consumers/subscribe ch "test" message-handler {:auto-ack true})
      (println (format "Connected to channel id: %d" (.getChannelNumber ch)))
      (Thread/sleep 10000)
      (println "Disconnecting...")
      (rabbit-mq/close ch)
      (rabbit-mq/close conn)))

18. Spuštění konzumenta a příjem zpráv

Před spuštěním konzumenta přidáme do fronty „test“ několik zpráv. Můžeme přitom použít skript, s nímž jsme se seznámili v první polovině článku. Tento skript vloží do fronty deset zpráv, každou s jiným tělem:

$ cd message-queues-examples/rabbit-mq/python/example01/
 
$ python3 enqueue_more_work.py 
Sent 'Hello World!' ten times

Přesvědčíme se, že fronta existuje a není prázdná:

$ sudo rabbitmqctl list_queues
 
Listing queues
test    10

Následně se již můžeme přepnout do adresáře s projektem naprogramovaným v Clojure a spustit konzumenta:

$ cd message-queues-examples/rabbit-mq/clojure/example01/
 
$ lein run
 
Connected to channel id: 1
Received a message: Hello World! #1
Received a message: Hello World! #2
Received a message: Hello World! #3
Received a message: Hello World! #4
Received a message: Hello World! #5
Received a message: Hello World! #6
Received a message: Hello World! #7
Received a message: Hello World! #8
Received a message: Hello World! #9
Received a message: Hello World! #10
Disconnecting...

Vidíme, že konzument bez problémů přečetl všechny zprávy a příslušným způsobem je zpracoval.

19. Repositář s demonstračními příklady

Zdrojové kódy všech dnes popsaných demonstračních příkladů naprogramovaných v Pythonu a jednoho příkladu v Clojure 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.

ict ve školství 24

Příklady v Pythonu:

Příklad Skript Popis Cesta
1 enqueue_work.py skript pro vložení zprávy do fronty https://github.com/tisnik/message-queues-examples/blob/master/rabbit-mq/python/example01/enqueue_work.py
1 enqueue_more_work.py skript pro vložení deseti zpráv do fronty https://github.com/tisnik/message-queues-examples/blob/master/rabbit-mq/python/example01/enque­ue_more_work.py
1 consumer.py implementace konzumenta (workera) https://github.com/tisnik/message-queues-examples/blob/master/rabbit-mq/python/example01/consumer.py
       
2 rabbitmq_connect.py společná část pro producenta i konzumenta https://github.com/tisnik/message-queues-examples/blob/master/rabbit-mq/python/example02/rabbit­mq_connect.py
2 enqueue_work.py skript pro vložení zprávy do fronty https://github.com/tisnik/message-queues-examples/blob/master/rabbit-mq/python/example02/enqueue_work.py
2 enqueue_more_work.py skript pro vložení deseti zpráv do fronty https://github.com/tisnik/message-queues-examples/blob/master/rabbit-mq/python/example02/enque­ue_more_work.py
2 consumer.py implementace konzumenta (workera) https://github.com/tisnik/message-queues-examples/blob/master/rabbit-mq/python/example02/consumer.py
       
3 rabbitmq_connect.py společná část pro producenta i konzumenta https://github.com/tisnik/message-queues-examples/blob/master/rabbit-mq/python/example03/rabbit­mq_connect.py
3 enqueue_work.py skript pro vložení zprávy do fronty https://github.com/tisnik/message-queues-examples/blob/master/rabbit-mq/python/example03/enqueue_work.py
3 enqueue_more_work.py skript pro vložení deseti zpráv do fronty https://github.com/tisnik/message-queues-examples/blob/master/rabbit-mq/python/example03/enque­ue_more_work.py
3 consumer.py implementace konzumenta (workera) simulujícího delší práci https://github.com/tisnik/message-queues-examples/blob/master/rabbit-mq/python/example03/consumer.py
       
4 rabbitmq_connect.py společná část pro producenta i konzumenta https://github.com/tisnik/message-queues-examples/blob/master/rabbit-mq/python/example04/rabbit­mq_connect.py
4 enqueue_work.py skript pro vložení zprávy do fronty https://github.com/tisnik/message-queues-examples/blob/master/rabbit-mq/python/example04/enqueue_work.py
4 enqueue_more_work.py skript pro vložení deseti zpráv do fronty https://github.com/tisnik/message-queues-examples/blob/master/rabbit-mq/python/example04/enque­ue_more_work.py
4 consumer.py implementace konzumenta (workera) s potvrzením dokončení práce https://github.com/tisnik/message-queues-examples/blob/master/rabbit-mq/python/example04/consumer.py
       
5 rabbitmq_connect.py společná část pro producenta i konzumenta https://github.com/tisnik/message-queues-examples/blob/master/rabbit-mq/python/example05/rabbit­mq_connect.py
5 enqueue_work.py skript pro vložení zprávy do fronty https://github.com/tisnik/message-queues-examples/blob/master/rabbit-mq/python/example05/enqueue_work.py
5 enqueue_more_work.py skript pro vložení deseti zpráv do fronty https://github.com/tisnik/message-queues-examples/blob/master/rabbit-mq/python/example05/enque­ue_more_work.py
5 consumer.py implementace konzumenta (workera) s vylepšením dělby práce https://github.com/tisnik/message-queues-examples/blob/master/rabbit-mq/python/example05/consumer.py

Poslední příklad je naprogramován v jazyce Clojure. Proto se jedná o ucelený projekt s běžnou strukturou vyžadovanou nástrojem Leiningen. Tento projekt naleznete na adrese https://github.com/tisnik/message-queues-examples/tree/master/rabbit-mq/clojure/example01.

20. Odkazy na Internetu

  1. Advanced Message Queuing Protocol
    https://www.amqp.org/
  2. Advanced Message Queuing Protocol na Wikipedii
    https://en.wikipedia.org/wi­ki/Advanced_Message_Queuin­g_Protocol
  3. RabbitMQ
    https://www.rabbitmq.com/
  4. RabbitMQ Tutorials
    https://www.rabbitmq.com/get­started.html
  5. RabbitMQ: Clients and Developer Tools
    https://www.rabbitmq.com/dev­tools.html
  6. RabbitMQ na Wikipedii
    https://en.wikipedia.org/wi­ki/RabbitMQ
  7. Streaming Text Oriented Messaging Protocol
    https://en.wikipedia.org/wi­ki/Streaming_Text_Oriented_Mes­saging_Protocol
  8. Message Queuing Telemetry Transport
    https://en.wikipedia.org/wiki/MQTT
  9. Erlang
    http://www.erlang.org/
  10. pika 0.12.0 na PyPi
    https://pypi.org/project/pika/
  11. Introduction to Pika
    https://pika.readthedocs.i­o/en/stable/
  12. Langohr: An idiomatic Clojure client for RabbitMQ that embraces the AMQP 0.9.1 model
    http://clojurerabbitmq.info/
  13. AMQP 0–9–1 Model Explained
    http://www.rabbitmq.com/tutorials/amqp-concepts.html
  14. Part 1: RabbitMQ for beginners – What is RabbitMQ?
    https://www.cloudamqp.com/blog/2015–05–18-part1-rabbitmq-for-beginners-what-is-rabbitmq.html
  15. Downloading and Installing RabbitMQ
    https://www.rabbitmq.com/dow­nload.html
  16. celery na PyPi
    https://pypi.org/project/celery/
  17. Databáze Redis (nejenom) pro vývojáře používající Python
    https://www.root.cz/clanky/databaze-redis-nejenom-pro-vyvojare-pouzivajici-python/
  18. 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/
  19. 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/
  20. Redis Queue (RQ)
    https://www.fullstackpython.com/redis-queue-rq.html
  21. Python Celery & RabbitMQ Tutorial
    https://tests4geeks.com/python-celery-rabbitmq-tutorial/
  22. Flower: Real-time Celery web-monitor
    http://docs.celeryproject­.org/en/latest/userguide/mo­nitoring.html#flower-real-time-celery-web-monitor
  23. Asynchronous Tasks With Django and Celery
    https://realpython.com/asynchronous-tasks-with-django-and-celery/
  24. First Steps with Celery
    http://docs.celeryproject­.org/en/latest/getting-started/first-steps-with-celery.html
  25. node-celery
    https://github.com/mher/node-celery
  26. Full Stack Python: web development
    https://www.fullstackpython.com/web-development.html
  27. Introducing RQ
    https://nvie.com/posts/introducing-rq/
  28. Asynchronous Tasks with Flask and Redis Queue
    https://testdriven.io/asynchronous-tasks-with-flask-and-redis-queue
  29. rq-dashboard
    https://github.com/eoranged/rq-dashboard
  30. Stránky projektu Redis
    https://redis.io/
  31. Introduction to Redis
    https://redis.io/topics/introduction
  32. Try Redis
    http://try.redis.io/
  33. Redis tutorial, April 2010 (starší, ale pěkně udělaný)
    https://static.simonwilli­son.net/static/2010/redis-tutorial/
  34. Python Redis
    https://redislabs.com/lp/python-redis/
  35. 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/
  36. Praktický úvod do Redis (1): vaše distribuovaná NoSQL cache
    http://www.cloudsvet.cz/?p=253
  37. Praktický úvod do Redis (2): transakce
    http://www.cloudsvet.cz/?p=256
  38. Praktický úvod do Redis (3): cluster
    http://www.cloudsvet.cz/?p=258
  39. Connection pool
    https://en.wikipedia.org/wi­ki/Connection_pool
  40. Instant Redis Sentinel Setup
    https://github.com/ServiceStack/redis-config
  41. How to install REDIS in LInux
    https://linuxtechlab.com/how-install-redis-server-linux/
  42. Redis RDB Dump File Format
    https://github.com/sripat­hikrishnan/redis-rdb-tools/wiki/Redis-RDB-Dump-File-Format
  43. Lempel–Ziv–Welch
    https://en.wikipedia.org/wi­ki/Lempel%E2%80%93Ziv%E2%80%93­Welch
  44. Redis Persistence
    https://redis.io/topics/persistence
  45. Redis persistence demystified
    http://oldblog.antirez.com/post/redis-persistence-demystified.html
  46. Redis reliable queues with Lua scripting
    http://oldblog.antirez.com/post/250
  47. Ost (knihovna)
    https://github.com/soveran/ost
  48. NoSQL
    https://en.wikipedia.org/wiki/NoSQL
  49. Shard (database architecture)
    https://en.wikipedia.org/wi­ki/Shard_%28database_archi­tecture%29
  50. What is sharding and why is it important?
    https://stackoverflow.com/qu­estions/992988/what-is-sharding-and-why-is-it-important
  51. What Is Sharding?
    https://btcmanager.com/what-sharding/
  52. Redis clients
    https://redis.io/clients
  53. Category:Lua-scriptable software
    https://en.wikipedia.org/wi­ki/Category:Lua-scriptable_software
  54. Seriál Programovací jazyk Lua
    https://www.root.cz/seria­ly/programovaci-jazyk-lua/
  55. Redis memory usage
    http://nosql.mypopescu.com/pos­t/1010844204/redis-memory-usage
  56. Ukázka konfigurace Redisu pro lokální testování
    https://github.com/tisnik/pre­sentations/blob/master/re­dis/redis.conf
  57. Resque
    https://github.com/resque/resque
  58. Nested transaction
    https://en.wikipedia.org/wi­ki/Nested_transaction
  59. Publish–subscribe pattern
    https://en.wikipedia.org/wi­ki/Publish%E2%80%93subscri­be_pattern
  60. Messaging pattern
    https://en.wikipedia.org/wi­ki/Messaging_pattern
  61. Using pipelining to speedup Redis queries
    https://redis.io/topics/pipelining
  62. Pub/Sub
    https://redis.io/topics/pubsub
  63. ZeroMQ distributed messaging
    http://zeromq.org/
  64. ZeroMQ: Modern & Fast Networking Stack
    https://www.igvita.com/2010/09/03/ze­romq-modern-fast-networking-stack/
  65. Publish/Subscribe paradigm: Why must message classes not know about their subscribers?
    https://stackoverflow.com/qu­estions/2908872/publish-subscribe-paradigm-why-must-message-classes-not-know-about-their-subscr
  66. Python & Redis PUB/SUB
    https://medium.com/@johngrant/python-redis-pub-sub-6e26b483b3f7
  67. Message broker
    https://en.wikipedia.org/wi­ki/Message_broker
  68. RESP Arrays
    https://redis.io/topics/protocol#array-reply
  69. Redis Protocol specification
    https://redis.io/topics/protocol
  70. Redis Pub/Sub: Intro Guide
    https://www.redisgreen.net/blog/pubsub-intro/
  71. Redis Pub/Sub: Howto Guide
    https://www.redisgreen.net/blog/pubsub-howto/
  72. Comparing Publish-Subscribe Messaging and Message Queuing
    https://dzone.com/articles/comparing-publish-subscribe-messaging-and-message
  73. Apache Kafka
    https://kafka.apache.org/
  74. Iron
    http://www.iron.io/mq
  75. kue (založeno na Redisu, určeno pro node.js)
    https://github.com/Automattic/kue
  76. Cloud Pub/Sub
    https://cloud.google.com/pubsub/
  77. Introduction to Redis Streams
    https://redis.io/topics/streams-intro
  78. glob (programming)
    https://en.wikipedia.org/wi­ki/Glob_(programming)
  79. Why and how Pricing Assistant migrated from Celery to RQ – Paris.py
    https://www.slideshare.net/syl­vinus/why-and-how-pricing-assistant-migrated-from-celery-to-rq-parispy-2
  80. Enqueueing internals
    http://python-rq.org/contrib/
  81. queue — A synchronized queue class
    https://docs.python.org/3/li­brary/queue.html
  82. Queues
    http://queues.io/
  83. Windows Subsystem for Linux Documentation
    https://docs.microsoft.com/en-us/windows/wsl/about
  84. RestMQ
    http://restmq.com/
  85. ActiveMQ
    http://activemq.apache.org/
  86. Amazon MQ
    https://aws.amazon.com/amazon-mq/
  87. Amazon Simple Queue Service
    https://aws.amazon.com/sqs/
  88. Celery: Distributed Task Queue
    http://www.celeryproject.org/
  89. Disque, an in-memory, distributed job queue
    https://github.com/antirez/disque
  90. rq-dashboard
    https://github.com/eoranged/rq-dashboard
  91. Projekt RQ na PyPi
    https://pypi.org/project/rq/
  92. rq-dashboard 0.3.12
    https://pypi.org/project/rq-dashboard/
  93. Job queue
    https://en.wikipedia.org/wi­ki/Job_queue
  94. Why we moved from Celery to RQ
    https://frappe.io/blog/technology/why-we-moved-from-celery-to-rq
  95. Running multiple workers using Celery
    https://serverfault.com/qu­estions/655387/running-multiple-workers-using-celery
  96. celery — Distributed processing
    http://docs.celeryproject­.org/en/latest/reference/ce­lery.html
  97. Chains
    https://celery.readthedoc­s.io/en/latest/userguide/can­vas.html#chains
  98. Routing
    http://docs.celeryproject­.org/en/latest/userguide/rou­ting.html#automatic-routing
  99. Celery Distributed Task Queue in Go
    https://github.com/gocelery/gocelery/
  100. Python Decorators
    https://wiki.python.org/mo­in/PythonDecorators
  101. Periodic Tasks
    http://docs.celeryproject­.org/en/latest/userguide/pe­riodic-tasks.html
  102. celery.schedules
    http://docs.celeryproject­.org/en/latest/reference/ce­lery.schedules.html#celery­.schedules.crontab
  103. Pros and cons to use Celery vs. RQ
    https://stackoverflow.com/qu­estions/13440875/pros-and-cons-to-use-celery-vs-rq

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.