Zarządzanie i bezpieczeństwo dokumentów elektronicznych

Marcin
Lewandowski

Marcin Lewandowski

  • programista z ponad 15 letnim doświadczeniem,
  • tworzę i rozwijam systemy dla logistyki, 
  • prowadzę bloga czterytygodnie.pl

Plan szkolenia

  1. Bezpieczeństwo pracy zdalnej w ramach zasobów firmy:

    a) Czym jest zdalny dostęp?

    b) VPN, metody, konfiguracje, zastosowanie

    c) Remote Desktop,

    d) Konfiguracja i działanie Remote Desktop,

    e) Zabezpieczenie usług związanych ze zdalnym dostępem,

    f) Bezpieczeństwo pracy w chmurze.

     

  2. Przejście z papierowego obiegu dokumentów do elektronicznego:

    a) Podstawowe zasady obiegu dokumentów,

    b) Co to jest elektroniczny obieg dokumentów,

    c) Co jest nam minimalnie potrzebne do wdrożenia elektronicznego obiegu dokumentów,

    d) Jak wdrażać system.

     

  3. Cyberbezpieczeństwo w praktyce. Jak samodzielnie zabezpieczyć swoje stanowisko pracy?:

    a) Złote zasady zapewniające bezpieczeństwo sprzętu i informacji,

    b) Podstawowe zagrożenia związane z korzystaniem z Internetu: phishing, ransomware, poczta e-mail, strony www, serwisy społecznościowe,

    c) Reguły tworzenia i zmiany haseł do systemów informatycznych i aplikacji,

    d) Bezpieczeństwo urządzeń mobilnych,

    e) Zabezpieczenie informatycznych nośników danych – pendrivy i pamięci zewnętrzne,

    f) Zdalny dostęp do zasobów firmy oraz korzystanie z urządzeń prywatnych przez pracowników i związane z tym zagrożenia,

    g) Przechowywanie danych w chmurze i korzystanie z zewnętrznych dostawców usług informatycznych,

    h) Prawidłowe korzystanie z oprogramowania antywirusowego,

    i) Zasady aktualizacji programów i aplikacji,

    j) Monitorowanie przez pracodawcę stanu bezpieczeństwa systemów informatycznych oraz działań podejmowanych przez użytkowników,

    k) Aspekty RODO w pracy zdalnej.

 

4.   Przygotowywanie dokumentów dostępnych cyfrowo w aplikacjach MS Word, Excel, Power Point, Adobe Acrobat:

a) Dobre praktyki w dostosowaniu dokumentów w formatach MS Word, Excel, Power Point, Adobe Acrobat,

b) Praktycznych sposobów redakcji tekstów dostępnych cyfrowo,

c) Narzędzia ułatwiające przygotowywanie dokumentów dostępnych w różnych formatach cyfrowych,

d) Obsługa popularnych funkcji arkusza kalkulacyjnego,

e) Tworzenie, analiza i dostosowanie danych na potrzeby różnych typów wykresów.

 

5.   Delegowanie i monitorowanie realizacji zadań z wykorzystaniem narzędzi online:

a) Efektywna komunikacja jako podstawowe narzędzie w delegowaniu,

b) Co, komu i dlaczego warto delegować?

c) Metoda SMART w delegowaniu obowiązków,

d) Narzędzia do monitorowania delegowanych zadań.

 

6.    Zarządzanie zespołem w firmie poprzez wykorzystanie narzędzi cyfrowych:

a) Aplikacje do zarządzanie zespołem,

b) Skuteczna komunikacja w firmie na przykładzie narzędzi Slack, Teams, Skype,

c) Zarządzanie osobami w czasie, poszukiwanie złodziei czasu,

d) Skuteczność działań - Zasada Pareto 80/20.

 

7.   Automatyzacja procesów w firmie poprzez wykorzystanie narzędzi cyfrowych:

a) Pojęcie procesu, standardu i zarządzanie zmiennością w procesach,

b) Piramida procesów w centrach usług biznesowych,

c) Jakie procesy są możliwe do automatyzacji,

d) Pojęcie i rodzaje aplikacji automatyzujących procesy oraz ich parametryzacja,

e) Interakcja automatyzacji z systemami i infrastrukturą IT,

f) Narzędzia dostępne na rynku.

Marcin
Lewandowski

Zarządzanie i bezpieczeństwo dokumentów elektronicznych

Przygotowanie środowiska

Linux

apt-get install rabbitmq-server

Instalacja sprowadza się do wydania jednego polecenia:

Po instalacji sprawdzamy stan serwera

/etc/init.d/rabbitmq-server status

Instalacja serwera

apt-get update

Przed instalacją aktualizujemy repozytoria

Zalogowanie na administratora

sudo su

Linux

Możemy także sprawdzić stan serwera za pomocą polecenia

rabbitmqctl status

Polecenie to zwraca dużo więcej informacji o serwerze i jego stanie. Z wyświetlonych informacji możemy dowiedzieć się:

  • jaka jest wersja serwera
  • nazwa noda
  • włączone pluginy
  • gdzie przechowywane są dane
  • jakie pliki konfiguracyjne zostały załadowane
  • gdzie znajduje się plik z logami
  • zużycie pamięci
  • ilość wirtualnych hostów

Instalacja serwera

Do instalacji pluginów mamy specjalne polecenie:

rabbitmq-plugins

Linux

 Instalacja pluginów 

rabbitmq-plugins --help

Dodając flagę help dowiemy się jakie to polecenie ma możliwości

Nas interesuje sekcja Plugin Management

disable      Disables one or more plugins
enable       Enables one or more plugins
list         Lists plugins and their state
set          Enables one or more plugins, disables the rest

Sprawdźmy jakie pluginy możemy zainstalować

rabbitmq-plugins list

Linux

 Instalacja pluginów 

Jest ich sporo

[  ] rabbitmq_amqp1_0                  3.8.2
[  ] rabbitmq_auth_backend_cache       3.8.2
[  ] rabbitmq_auth_backend_http        3.8.2
[  ] rabbitmq_auth_backend_ldap        3.8.2
[  ] rabbitmq_auth_backend_oauth2      3.8.2
[  ] rabbitmq_auth_mechanism_ssl       3.8.2
[  ] rabbitmq_consistent_hash_exchange 3.8.2
[  ] rabbitmq_event_exchange           3.8.2
[  ] rabbitmq_federation               3.8.2
[  ] rabbitmq_federation_management    3.8.2
...

Nas interesuje plugin rabbitmq_management i to go włączamy

rabbitmq-plugins enable rabbitmq_management

Linux

 Instalacja pluginów 

Co w rezultacie spowodowało automatyczne włączenie dwóch innych pluginów: rabbitmq_management_agent, rabbitmq_web_dispatch.

Enabling plugins on node rabbit1
rabbitmq_management
The following plugins have been configured:
  rabbitmq_management
  rabbitmq_management_agent
  rabbitmq_web_dispatch
Applying plugin configuration to rabbit1
The following plugins have been enabled:
  rabbitmq_management
  rabbitmq_management_agent
  rabbitmq_web_dispatch

started 3 plugins.

Aby nowo zainstalowany plugin działał konieczny jest restart serwera 

/etc/init.d/rabbitmq-server restart

Linux

 Instalacja pluginów 

Teraz w przeglądarce otwieramy adres http://localhost:15672 pod którym powinniśmy zobaczyć ekran logowania do panelu administracyjnego.

Login: guest
Hasło: guest

Docker

Instalacja Docker

apt-get install docker.io

Instalacja Docker Compose

apt-get install docker-compose

Wymagania:

  • Docker, dostarcza kontenery,
  • Docker Compose, pozwala stworzyć plik z konfiguracją środowiska

docker-compose.yml

Plik z konfiguracją środowiska testowego

version: '3'
 
services:
  rabbit:
    image: rabbitmq:3.8-management
    ports:
      - "5673:5672"
      - "15673:15672"
  • wykorzystujemy obraz rabbitmq:3-management, który ma włączony plugin panelu administracyjnego,
  • przekierowujemy porty,
    • 5672 - RabbitMQ,
    • 15672 - panel administracyjny

Docker

docker-compose.yml

Uruchomienie środowiska

docker-compose up

Docker

Uruchomienie środowiska w tle

docker-compose up -d

Sprawdzenie uruchomionych kontenerów

docker ps

Zatrzymanie środowiska

ctrl + c

Zatrzymanie środowiska

docker-compose down

Wprowadzenie
do RabbitMQ

Connection & Channel

  Wprowadzenie do RabbitMQ 

  • Aplikacja ustanawia jedno połączenie z RabbitMQ

  • Połączenie powinno trwać tak długo jak aplikacja żyje

  • Połączenie TCP

    • duży narzut związany z nawiązaniem połączenia,

    • przesyłanie nadmiarowych informacji dotyczących samego połączenia, a nie danych 

Connection & Channel

  Wprowadzenie do RabbitMQ 

  • Aplikacja po nawiązaniu połączenia ustanawia kanały komunikacji,

  • Jedna aplikacja może ustanowić wiele kanałów komunikacji,

  • W ramach kanałów komunikacji możliwe jest:

    • deklarowanie central, kolejek, połączeń pomiędzy nimi itp.

    • publikowanie wiadomości,

    • pobieranie wiadomości

    • ...

Połączenie TPC

AMQP

Protokoły

  Wprowadzenie do RabbitMQ 

MQTT

  Wprowadzenie do RabbitMQ 

  • lekki protokół opracowany przez IBM

  • broker pełni rolę serwera

  • aynchroniczny

  • klient może pełnić rolę producenta i konsumenta
  • klient publikuje / subskrybuje tematy
  • dynamiczne tworzenie tematów
  • wszyscy subskrybenci otrzymują wiadomości z danego tematu

MQTT

  Wprowadzenie do RabbitMQ 

MQTT

  Wprowadzenie do RabbitMQ 

Włączenie obsługi MQTT w RabbitMQ

rabbitmq-plugins enable rabbitmq_mqtt

# restart RabbitMQ
/etc/init.d/rabbitmq-server restart

Instalacja klienta MQTT

apt install mosquitto-clients

Subskrypcja tematu czujnik_1

mosquitto_sub -h 127.0.0.1 -p 1883 -t "czujnik_1"

Publikowanie wiadomości w temacie czujnik_1

mosquitto_pub -h 127.0.0.1 -p 1883 -t "czujnik_1" -m "Odczyt 1"

MQTT

  Wprowadzenie do RabbitMQ 

AMQP

  Wprowadzenie do RabbitMQ 

  • protokół podobny do HTTP, TCP

  • aynchroniczny

  • Binding - możliwość określenia gdzie przesłać wiadomość,
  • Queuing - kolejki,
  • Exchange - centrale wiadomości

AMQP

  Wprowadzenie do RabbitMQ 

AMQP

  Wprowadzenie do RabbitMQ 

AMQP

  Wprowadzenie do RabbitMQ 

Virtual Hosts

Virtual Hosts

  • Każdy virtual host posiada unikalną nazwę
  • Są to wyizolowane brokery w serwerze zapewniające pełną separację:
  • użytkowników,
  • połączeń,
  • kolejek,
  • central wiadomości,
  • obiektów powiązanych
  • Standardowo serwer posiada zdefiniowanego jednego wirtualnego hosta /
  • Rozwiązanie stworzone w celach administracyjnych

Virtual Hosts

Zarządzanie - Panel administracyjny

Virtual Hosts

Zarządzanie - Panel administracyjny

Lista Virtual Host

Dodawanie Virtual Host

Virtual Hosts

Zarządzanie - Panel administracyjny

Szczegóły dostępne po kliknięciu w nazwę

  • przypisywanie uprawnień użytkownikom do virtual host,
  • przypisywanie uprawnień do exchange,
  • usuwanie

W podglądzie dostępnych jest klika bardzo istotnych opcji:

Virtual Hosts

Zarządzanie - Panel administracyjny

Virtual Hosts

Zarządzanie - CLI

rabbitmqctl list_vhosts

Lista

Dodawanie

rabbitmqctl add_vhost app_1

Usuwanie

rabbitmqctl delete_vhost app_1

Virtual Hosts

Zarządzanie - CLI

rabbitmqctl set_permissions -p app_1 guest ".*" ".*" ".*"

Przypisywanie uprawnień

rabbitmqctl set_topic_permissions -p app_1 guest amq.topic ".*" ".*"

Przypisywanie uprawnień do Topic

rabbitmqctl clear_permissions -p app_1 guest
rabbitmqctl clear_topic_permissions -p app_1 guest amq.topic

Użytkownicy

Uprawnienia

Użytkownicy

Uwierzytelnianie

  • proste przez login i hasło,
  • bardziej zaawansowane wykorzystujące certyfikaty
  • domyślny użytkownik guest może logować się tylko z localhost

Tagi - definiują uprawnienia dla panelu administracyjnego

Uprawnienia - definiują możliwości użytkownika w zakresie wykonywanych operacji konfiguracyjnych, odczytu i zapisu wiadomości

Użytkownicy

Tagi - Panel administracyjny

W przypadku, gdy mówimy o uprawnieniach do panelu administracyjnego myślimy o Tagach. Dzięki nim mamy różne poziomy dostępu do panelu administracyjnego.

Użytkownicy

Tagi - Panel administracyjny

Użytkownicy

Tagi - Panel administracyjny

Nasz użytkownik guest na, którym obecnie pracujemy ma tag administrator. Dzięki czemu ma uprawnienia do wszystko w panelu administracyjnym.

Użytkownicy

Dodawanie użytkownika - PA

Użytkownicy

Dodawanie użytkownika - CLI

rabbitmqctl add_user app_1 password

Użytkownicy

Dodawanie użytkownika - CLI

# przypisujemy tag administrator
rabbitmqctl set_user_tags app_1 administrator 

Użytkownicy

Uprawnienia

Mamy trzy rodzaje uprawnień:

  • configure - konfiguracja, czyli zarządzanie kolejkami, centralami, połączeniami pomiędzy nimi,  
  • read - wszystko związane z odbiorem wiadomości,
  • write - wszystko związane z odczytem wiadomości

Przypisywanie uprawnień, jest oparte o wyrażenia regularne.

  • ustawienie uprawnień do dwóch kolejek "^(kolejka_1|kolejka_2)$"
  • uprawnienia do wszystkiego ".*"
  • brak uprawnień do czegokolwiek '^$' lub '""'

Użytkownicy

Uprawnienia - PA

Użytkownicy

Uprawnienia - CLI

rabbitmqctl list_user_permissions app_1

Lista uprawnień użytkownika

Usuwanie uprawnień

rabbitmqctl clear_permissions -p app_1 app_1

Dodawanie uprawnień

Producent

Producent

PRODUCENT

KONSUMENT

RabbitMQ

Producent

var amqp = require('amqplib/callback_api');

amqp.connect('amqp://localhost', function(err1, connection) {

    if (err1)
    {
        return console.error(err1);
    }

    console.info('Connected');

    connection.close();
});

connect.js

Połączenie z RabbitMQ

Producent

Połączenie z RabbitMQ

Scenariusze testowe:

  • stworzenie użytkownika app_full z maksymalnymi uprawnieniami, test połączenia,
  • stworzenie użytkownika app_read z uprawnieniami tylko do odczytu, test połączenia,
  • stworzenie użytkownika app_write z uprawnieniami tylko do zapisu, test połączenia
amqp://username:password@localhost/vhost

Producent

Format wiadomości

RabbitMQ akceptuje dowolny ciąg znaków, co niesie ze sobą konsekwencje.

PRODUCENT 1

KONSUMENT

RabbitMQ

XML

XML

XML

Producent

Format wiadomości

RabbitMQ akceptuje dowolny ciąg znaków, co niesie ze sobą konsekwencje.

PRODUCENT 1

KONSUMENT

RabbitMQ

XML

XML  JSON

PRODUCENT 2

JSON

Producent

Format wiadomości

RabbitMQ akceptuje dowolny ciąg znaków, co niesie ze sobą konsekwencje.

PRODUCENT 1

KONSUMENT

RabbitMQ

XML

XML  JSON TXT

PRODUCENT 3

TXT

PRODUCENT 2

JSON

Producent

Format wiadomości

Wybierz format, który jest standardem jak JSON

Producent

Tworzenie wiadomości

var amqp = require('amqplib/callback_api');

amqp.connect('amqp://app_full:app_full@localhost/app_1', function(err1, connection) {

    if (err1)
    {
        return console.error(err1);
    }

    console.info('Connected');

    connection.createChannel(function(err2, channel) {

        if (err2)
        {
            throw err2;
        }

        console.info('Channel created');

        var queue = 'q_1';
        var msg = JSON.stringify({
            'text': 'Hello world'
        });

        channel.sendToQueue(queue, Buffer.from(msg));
        console.log("[x] Sent %s", msg);
    });
});

Producent

Tworzenie wiadomości

W panelu administracyjnym widzimy trwające połączenie i otwarty kanał komunikacji.

Producent

Tworzenie wiadomości

Nie widać, aby jakakolwiek wiadomość dotarła.

Producent

Tworzenie wiadomości

connection.createChannel(function(err2, channel) {

  if (err2)
  {
    throw err2;
  }

  console.info('Channel created');

  var queue = 'q_1';
  var msg = JSON.stringify({
    'text': 'Hello world'
  });

  channel.sendToQueue(queue, Buffer.from(msg));
  console.log("[x] Sent %s", msg);
});

Producent

Tworzenie wiadomości

connection.createChannel(function(err2, channel) {

  if (err2)
  {
    throw err2;
  }

  console.info('Channel created');

  var queue = 'q_1';
  var msg = JSON.stringify({
    'text': 'Hello world'
  });

  // tworzy kolejkę jeśli nie istnieje
  channel.assertQueue(queue, {
    durable: false
  });

  channel.sendToQueue(queue, Buffer.from(msg));
  console.log(" [x] Sent %s", msg);
});

Tworzymy kolejkę z poziomu kodu, jeśli nie istnieje

create_first_message_full.js

Producent

Tworzenie wiadomości

Producent

Tworzenie wiadomości

Czemu wysyłamy

do kolejki ??

Producent

Tworzenie wiadomości

Architektura

Producent

Tworzenie wiadomości

Architektura

Producent

TTL Wiadomości

channel.sendToQueue(queue, Buffer.from(msg), {
    expiration: 15000
});

TTL czyli Time To Live to określenie czasu życia wiadomości, które może być zdefiniowane na poziomie pojedynczej wiadomości. 

Aby zobaczyć jak mechanizm zadziała otwieramy plik create_first_message_ttl.js, który zawiera dodatkowy parametr określający czas życia wiadomości na 15 sek.

Producent

TTL Wiadomości

Producent

Weryfikacja czy wiadomość została doręczona

Możliwe problemy z doręczeniem wiadomości:

  • brak routingu określonego w wiadomości,
  • osiągnięto limit wiadomości w kolejce

Producent

Weryfikacja czy wiadomość została doręczona

Sprawdzimy scenariusz w którym osiągniemy limit wiadomości. W tym celu usuwamy kolejkę q_1 i tworzymy nową z dwoma parametrami.

Producent

Weryfikacja czy wiadomość została doręczona

Przechodzimy do pliku message_delivery_guaranteed.js

connection.createConfirmChannel(function(err2, channel) {

Pierwsza zmiana jaka zaszła to zmiana tworzonego kanału na kanał wymagający potwierdzeń createConfirmChannel

Dodany został kod generujący 10 wiadomości.

for (var i = 0; i < 10; i++)
{
  var msg = JSON.stringify({
    'text': 'Message ' + i
  });

  console.log(" [x] Sent %s", msg);

  channel.sendToQueue(queue, Buffer.from(msg), {}, errorCallback(msg));
}

Producent

Weryfikacja czy wiadomość została doręczona

function errorCallback(msg) {

    return function(err) {

        if (err)
        {
            return console.log('Message: ' + msg + ' - NOT confirm');
        }

        console.log(msg + ' - confirm');
    };
}

Przy każdym wysłaniu wiadomości do kolejki mamy funkcję, która jest wywoływana. Dozwala nam to obsłużyć błędy.

Producent

Weryfikacja czy wiadomość została doręczona

channel.waitForConfirms(function(err) {

    if (err)
    {
        return console.log('Not confirm all message');
    }

    console.log('All messages confirm');
});

Ostatni element jaki został dodany to wywołanie specjalnej metody, która czeka aż wszystkie wiadomości się wykonają lub zwrócą błędy.

Producent

Weryfikacja czy wiadomość została doręczona

Mając tak przygotowaną aplikację możemy przeprowadzić testy, które pokażą czy rzeczywiście dostaniemy informacje o braku dostarczenia wiadomości.

Producent

Weryfikacja czy wiadomość została doręczona

Kolejne uruchomienie aplikacji zwróci nam same błędy.

Producent

Weryfikacja czy wiadomość została doręczona

Usuńmy 2 wiadomości z kolejki, aby sprawdzić czy rzeczywiście tylko 2 wiadomości trafią po ponownym uruchomieniu aplikacji.

Producent

Weryfikacja czy wiadomość została doręczona

Uruchamiamy jeszcze raz aplikację i sprawdzamy czy zostaną dodane dwie nowe wiadomości, a dla pozostałych otrzymamy błąd.

Exchange

Exchange

  • Agent zarządzający przesyłaniem wiadomości do kolejek,
  • Kilka rodzajów central wiadomości:

 

 

  • Centrale wiadomości mogą mieć parametry:
    • durable - przeżyje restart serwera,
    • auto-delete - automatyczne usuwanie po odłączeniu ostatniej kolejki,
    • dodatkowe parametry zależne od typu centrali oraz pluginów
  • binding - link pomiędzy kolejką a centralą wiadomości,
  • headers
  • fanout
  • direct
  • topic

Exchange

Narzędzie do wizualizacji przepływu wiadomości w RabbitMQ

Exchange

Fanout

Wszystkie wiadomości przesłane do centrali będą przekazywane do powiązanych kolejek.

Exchange

Fanout

Wszystkie wiadomości przesłane do centrali będą przekazywane do powiązanych kolejek.

Exchange

Fanout

Wszystkie wiadomości przesłane do centrali będą przekazywane do powiązanych kolejek.

Exchange

Fanout

Definiujemy centralę wiadomości

fanout_ex

Exchange

Fanout

Definiujemy dwie kolejki
fanout_q1 i fanout_q2

Exchange

Fanout

Łączymy centralę z kolejkami binding
(możliwe z poziomu kolejki i centrali )

Exchange

Fanout

Po połączeniu powinniśmy zobaczyć
wizualizację połączeń

Exchange

Fanout

Wysyłamy wiadomość do centrali wiadomości

Exchange

Fanout

Po wysłaniu wiadomości powinniśmy otrzymać wiadomość w obu kolejkach

Exchange

Fanout

Treść możemy podejrzeć wchodząc do jednej z kolejek, a następnie w sekcji Get Messages mamy możliwość pobrania wiadomości z kolejki.

Exchange

Fanout

connection.createChannel(function(error1, channel) {

    if (error1)
    {
    	throw error1;
    }

    var exchange = 'fanout_ex_app';
    var msg = JSON.stringify({
    	'text': 'Hello world'
    });

    // tworzy centralę wiadomości
    // channel.assertExchange(exchange, 'fanout', {
    //     durable: false
    // });

    channel.publish(exchange, '', Buffer.from(msg));

    console.log("[x] Sent %s", msg);
});

producer_exchange_fanout.js

Exchange

Fanout

connection.createChannel(function(error1, channel) {

        if (error1){
            throw error1;
        }

        var exchange = 'fanout_ex_app';
        var queue1 = 'fanout_q1_app';
        var msg = JSON.stringify({
            'id': '145'
        });

        channel.assertExchange(exchange, 'fanout', {
            durable: true
        });

        channel.assertQueue(queue1, {durable: true}, function(err2) {

            if (err2){
                throw err2;
            }

            channel.bindQueue(queue1, exchange);

producer_exchange_fanout_bind.js.js

Exchange

Direct

Wiadomości przesłane do centrali muszą posiadać parametr routingKey, aby zostały przesłane do kolejki o takim samym parametrze.

Exchange

Direct

Centrala w działaniu

Po co centrala ? Kolejka nie wystarczy ?

Exchange

Direct

Funkcjonalność pobierz wszystkie

Exchange

Direct

Odwzorozujemy strukturę w panelu administracyjnym

Exchange

Direct

connection.createChannel(function(error1, channel) {

    if (error1)
    {
    	throw error1;
    }

    var exchange = 'direct_ex';
    var msg = JSON.stringify({
    	'id': '145'
    });

    channel.publish(exchange, 'epub', Buffer.from(msg));

    console.log("[x] Sent %s", msg);
});

Exchange

Direct

connection.createChannel(function(error1, channel) {

    if (error1) {
    	throw error1;
    }

    var exchange = 'direct_ex_app';
    var queue1 = 'direct_epub_app';
    var queue2 = 'direct_mobi_app';
    var queue3 = 'direct_pdf_app';
    var msg = JSON.stringify({
	    'id': '145'
    });

    channel.assertExchange(exchange, 'direct', {
    	durable: true
    });

    channel.assertQueue(queue1, {durable: true}, function(err2) {

        if (err2){
            throw err2;
        }

        channel.bindQueue(queue1, exchange, 'download_epub');

        channel.assertQueue(queue2, {durable: true}, function(err3) {

            if (err3){
                throw err3;
            }

            channel.bindQueue(queue2, exchange, 'download_mobi');

            channel.assertQueue(queue3, {durable: true}, function(err4) {

                if (err4){
                    throw err4;
                }

                channel.bindQueue(queue3, exchange, 'download_pdf');

                channel.publish(exchange, 'download_epub', Buffer.from(msg));

                console.log("[x] Sent %s", msg);
            });
        });
    });
});

Exchange

Topic

  • Rozszerza możliwości parametru routingKey, gdzie podajemy listę słów rozdzielanych kropką
  • * (gwiazdka), zastępuje pojedyncze słowo,
  • # (hasz), zastępuje dowolną ilość słów

info.log

error.log

all.log

#.log

info.*

error.*

all.*

Exchange

Topic

Zastosowanie w praktyce bindowania z uwzględnieniem wyrażeń regularnych

Exchange

Topic

Tworzymy nową centralę wiadomości
topic_ex

Exchange

Topic

Tworzymy kolejki:
topic_info, topic_error, topic_all

Exchange

Topic

Tworzymy powiązanie
topic_info <--- info.* -----> topic_ex

Exchange

Topic

Tworzymy powiązanie
topic_error <--- error.* -----> topic_ex

Exchange

Topic

Tworzymy powiązanie
topic_all <--- *.log -----> topic_ex

Exchange

Topic

Wysyłamy wiadomość o routingu info.log

Exchange

Topic

Wysyłamy wiadomość o routingu error.log

Exchange

Topic

connection.createChannel(function(error1, channel) {

        if (error1)
        {
            throw error1;
        }

        var exchange = 'topic_ex_app';
        var queueInfo = 'topic_info_app';
        var queueError = 'topic_error_app';
        var queueAll = 'topic_all_app';
        var msg = JSON.stringify({
            'id': '145'
        });

        channel.assertExchange(exchange, 'topic', {
            durable: true
        });

        channel.assertQueue(queueInfo, {durable: true}, function(err2) {

            if (err2)
            {
                throw err2;
            }

            channel.bindQueue(queueInfo, exchange, 'info.*');

            channel.assertQueue(queueError, {durable: true}, function(err2) {

                if (err2)
                {
                    throw err2;
                }

                channel.bindQueue(queueError, exchange, 'error.*');

                channel.assertQueue(queueAll, {durable: true}, function(err2) {

                    if (err2)
                    {
                        throw err2;
                    }

                    channel.bindQueue(queueAll, exchange, '*.log');

                    console.log("Ready");

                    // channel.publish(exchange, 'info.log', Buffer.from(msg));
                    // console.log("[x] Sent %s", msg);
                });
            });
        });
    });

Exchange

Headers

  • Bardzo podobny do typu direct z tą różnicą, że zamiast parametru routingKey wykorzystywane są nagłówki, a routingKey jest ignorowany.
  • Nagłówki mogą przyjmować dowolne typy danych, a nie jak to miało miejsce w typie direct tylko string.
  • Dodatkowo mamy specjalny nagłówek o nazwie x-match, który może przyjąć dwie wartości:
    • any – wystarczy jedno dopasowanie, aby wiadomość trafiła do kolejki,
    • all – wiadomości trafią do kolejki jedynie gdy wszystkie klucze zostaną dopasowane ( domyślne )

Exchange

Headers

RabbitMQ Simulator niestety nie wspiera tego typu centrali wiadomości. Dlatego zaprojektujemy od razu w panelu poniższą strukturę.

Exchange

Headers

Dodajemy centralę

Exchange

Headers

Dodajemy centralę

Exchange

Headers

Dodajemy kolejki

Exchange

Headers

Definiujemy połączenia pomiędzy centralą, a kolejkami.

headers_ex <-> header_info

Exchange

Headers

Definiujemy połączenia pomiędzy centralą, a kolejkami.

headers_ex <-> header_error

Exchange

Headers

Definiujemy połączenia pomiędzy centralą, a kolejkami.

headers_ex <-> header_all

Exchange

Headers

Po wszystkim powinniśmy w centrali headers_ex zobaczyć następujące połączenia

Możemy je teraz przetestować ;)

Exchange

Headers

Publikujemy wiadomości z poziomu centrali

Exchange

Headers

Publikujemy wiadomości z poziomu centrali

Exchange

Headers

Publikujemy wiadomości z poziomu centrali

Exchange

Headers

Aby zmienić zachowanie połączenia z kolejką header_all konieczne jest usunięcie bieżącego połączenia.

Exchange

Headers

Na miejsce usuniętego połączenia dodajemy nowe z uwzględnieniem parametru x-match

Exchange

Headers

Przed testami czyścimy zawartość kolejek, aby łatwiejsza była interpretacja wyników.

Exchange

Headers

Publikujemy wiadomość w centrali
headers_ex

Exchange

Headers

Publikujemy wiadomość w centrali
headers_ex

Exchange

Headers

connection.createChannel(function(error1, channel) {

  // ... pominięto kod
  
  var exchange = 'headers_ex_app';
  var queueError = 'headers_log_error_app';
  var queueInfo = 'headers_log_info_app';
  var queueAll = 'headers_log_all_app';
  var msg = JSON.stringify({
    'id': '145'
  });

  channel.assertExchange(exchange, 'headers', {
    durable: true
  });

  channel.assertQueue(queueError, {durable: true}, function(err2) {

    // ... pominięto kod
    
    channel.bindQueue(queueError, exchange, '', {
      'log_error': '1'
    });

    channel.assertQueue(queueInfo, {durable: true}, function(err2) {

      // ... pominięto kod

      channel.assertQueue(queueAll, {durable: true}, function(err2) {

        // ... pominięto kod

        channel.bindQueue(queueAll, exchange, '', {
          'log_info': '1',
          'log_error': '1'
        });

        channel.publish(exchange, '', Buffer.from(msg), {
          'headers': {
            'log_error': '1'
          }
        });

        console.log("[x] Sent %s", msg);
      });
    });
  });
});

Exchange

Durable

/etc/init.d/rabbitmq-server restart

Exchange

Auto delete

  • usunięcie ostatniego powiązania pomiędzy centralą, a kolejką powoduje usunięcie centrali,
  • usunięcie ostatniej kolejki powiązanej z centralą spowoduje usunięcie centrali,

Exchange

Internal

Centrala wewnętrzna nie pozwala na bezpośrednie publikowanie wiadomości przez producentów.

Exchange

Internal

Centrala wewnętrzna nie pozwala na bezpośrednie publikowanie wiadomości przez producentów.

connection.createChannel(function(err1, channel) {

    if (err1) {
    	throw err1;
    }

    var exchange = 'internal_ex_app';
    var msg = JSON.stringify({
    	'id': '145'
    });

    channel.assertExchange(exchange, 'fanout', {
    	internal: true
    });

    channel.assertQueue('tmp_app', {durable: true}, function(err2) {

        if (err2) {
            throw err2;
        }

        channel.bindQueue('tmp_app', exchange, 'test');

        channel.publish(exchange, 'test', Buffer.from(msg));

        console.log("[x] Sent %s", msg);
    });
});

Exchange

Alternate exchange

Jeśli wiadomość nie jest rutowalna to zostanie przesłana do innej centrali

Kolejki

Kolejki

  • Dwa rodzaje kolejek:
    • classic - klasyczna kolejka FIFO będąca buforem wiadomości,
    • quorum - nowy rodzaj kolejek oparty o algorytm RAFT przeznaczony dla systemów rozproszonych,
  • Kolejki mogą mieć parametry:
    • durable - przeżyje restart serwera,
    • auto-delete - automatyczne usunięcie kolejki po rozłączeniu konsumenta ( kolejki tymczasowe ),
    • exclusive - dostępna tylko dla jednego kanału

Kolejki

Zarządzanie

Kolejki

Zarządzanie - CLI

rabbitmqctl list_queues -p app_1

Lista kolejek

Jeśli chcemy wyświetlić konkretne kolumny to podajemy je na końcu polecenia.

rabbitmqctl list_queues -p app_1 name durable
rabbitmqadmin -V app_1 declare queue name=test

Dodawanie kolejek

Kolejki

Durable - przeżyć restart serwera

connection.createChannel(function (err2, channel) {

    if (err2) {
    	throw err2;
    }

    console.info('Channel created');

    var queue = 'q_durable_fale_app';
    var msg = JSON.stringify({
    	'text': 'Czy wiadomość przetrwa restart serwera ??'
    });

    channel.assertQueue(queue, {
    	durable: false
    });

    channel.sendToQueue(queue, Buffer.from(msg));

    console.log("OK");
});

Producent tworzy kolejkę z ustawionym parametrem durable na false. Co spowoduje, że kolejka zostanie usunięta po restarcie serwera.

/etc/init.d/rabbitmq-server restart

durable_false_producer.js

Kolejki

TTL

Wcześniej definiowaliśmy TTL na poziomie wiadomości,
ale czas życia wiadomości może być zdefiniowany w kolejce.

Kolejki

TTL

Do tak zdefiniowanej kolejki możemy dodać wiadomość i zobaczyć czy rzeczywiście zostanie usunięta po 15 sekundach.

Kolejki

TTL

connection.createChannel(function (err2, channel) {

    if (err2) {
    	throw err2;
    }

    console.info('Channel created');

    var queue = 'q_ttl_app';
    var msg = JSON.stringify({
    	'text': 'TTL 15s.'
    });

    channel.assertQueue(queue, {
    	messageTtl: 15000
    });

    channel.sendToQueue(queue, Buffer.from(msg));

    console.log("OK");
});

ttl_producer.js

Producent tworzy kolejkę z ustawionym maksymalnym czasem życia wiadomości na 15 sek.

Kolejki

Expiry

connection.createChannel(function (err2, channel) {

    if (err2) {
        throw err2;
    }

    console.info('Channel created');

    var queue = 'q_expiry_app';

    channel.assertQueue(queue, {
    	expires: 15000
    });

    var msg = JSON.stringify({
        'text': 'Hello world'
    });

    channel.sendToQueue(queue, Buffer.from(msg));
    console.log(" [x] Sent %s", msg);
});

expiry_producer.js

Producent tworzy kolejkę z ustawionym maksymalnym czasem życia na 15 sek. Jeśli w tym czasie nie podłączy się żaden konsument to kolejka zostanie usunięta.

Kolejki

Expiry

connection.createChannel(function(err2, channel) {

    if (err2)
    {
        throw err2;
    }

    channel.consume('q_expiry_app', function(msg) {

        console.log(msg);
    });

    console.info('OK');
});

expiry_consumer.js

Konsument podtrzymujący życie kolejki do czasu rozłączenia. Brak potwierdzenia pobrania wiadomości spowoduje oznaczenie wiadomości jako Unacked

Kolejki

Tymczasowe

Kolejka jest usuwana, gdy ostatni konsument zakończy połączenie

Kolejki

Tymczasowe

Uruchamiamy plik consumer_connect.js jego jedynym zadaniem jest podłączenie do kolejki. Pobranie wiadomości jeśli są jakieś w kolejce i wyświetlenie ich w konsoli. 

W kolejce wyświetli się informacja, że jest podłączony jeden konsument.

Kolejki

Tymczasowe

Po zakończeniu działania konsumenta Ctrl + C kolejka powinna zostać automatycznie usunięta. A nam w panelu wyświetli się poniższy komunikat.

Kolejki

Tymczasowe

connection.createChannel(function (err2, channel) {

    if (err2) {
    	throw err2;
    }

    console.info('Channel created');

    channel.assertQueue('q_auto_delete', {
    	autoDelete: true
    });

    console.log("OK");
});

Producent tworzy kolejkę, która zostanie usunięta automatycznie po rozłączeniu ostatniego konsumenta

auto_delete_producer.js

Kolejki

Tymczasowe

connection.createChannel(function(err2, channel) {

    if (err2)
    {
    	throw err2;
    }

    channel.consume('q_auto_delete', function(msg) {

    	console.log(msg);
    });

    console.info('OK');
});

Konsument kolejki po rozłączeniu spowoduje automatyczne usunięcie kolejki

auto_delete_consumer.js

Kolejki

Max Length

connection.createChannel(function(err2, channel) {

    if (err2)
    {
    	throw err2;
    }

    console.info('Channel created');

    var queue = 'q_max_length_10_app';

    channel.assertQueue(queue, {
    	maxLength: 10
    });

    for (var i = 0; i < 20; i++)
    {
        var msg = JSON.stringify({
            'text': 'Message ' + i
        });

        console.log(" [x] Sent %s", msg);

        channel.sendToQueue(queue, Buffer.from(msg));
    }
});

Umożliwia określenie maksymalnej ilości wiadomości jaka może być przechowywana w kolejce.

max_length_producer.js

Kolejki

Max Length + Overflow

connection.createChannel(function(err2, channel) {

    if (err2){
    	throw err2;
    }

    console.info('Channel created');

    var queue = 'q_max_length_10_app';

    channel.assertQueue(queue, {
    	maxLength: 10,
        overflow: 'reject-publish'
    });

    for (var i = 0; i < 20; i++)
    {
        var msg = JSON.stringify({
            'text': 'Message ' + i
        });

        console.log(" [x] Sent %s", msg);

        channel.sendToQueue(queue, Buffer.from(msg));
    }
});

Parametr x-max-length występuje w połączeniu z parametrem
x-overflow. A to dlatego, że przepełnienie kolejki może spowodować pewne zachowania:

  • drop-head, usunięcie wiadomości z kolejki ( domyślne zachowanie ),
  • reject-publish, odrzucenie przesyłanej wiadomości

max_length_overflow_reject_producer.js

Kolejki

Single Active Consumer

Pozwala na wykorzystywanie kolejki tylko jednemu konsumentowi.

Kolejki

Single Active Consumer

connection.createChannel(function(err2, channel) {

    if (err2)
    {
        throw err2;
    }

    channel.consume('q_single_active_consumer_app', function(msg) {

        setTimeout(function () {
            console.log(msg.content.toString());
            channel.ack(msg);
        }, 1000);
    });

    console.info('OK');
});

single_active_consumer_consumer.js

Konsument

Kolejki

Lazy

  • zapisuje wszystkie wiadomości na dysku natychmiast,
  • wpływa na wydajność, gdyż zależna jest od IO dysku,
  • rozwiązanie to tworzy bardziej stabilny klaster z  przewidywalną wydajnością,
  • zaleca się stosowanie, gdy spodziewamy się dużych ilości wiadomości, których konsument prawdopodobnie nie będzie w stanie obsłużyć,
  • należy wyłączyć opcję, gdy oczekujemy bardzo dużej wydajności i jesteśmy pewni, że nasze kolejki będą krótkie

Kolejki

Dead Letter Exchange - DLX

Kolejki

Dead Letter Exchange - DLX

Kolejki

Dead Letter Exchange - DLX

Kolejki

Dead Letter Routing Key

Zastępuje routingKey, gdy wiadomość jest niepotwierdzona

Konsument

Konsument

PRODUCENT

KONSUMENT

RabbitMQ

Konsument

Pobieranie wiadomości

connection.createChannel(function(err2, channel) {

    if (err2) {
    	throw err2;
    }

    channel.consume('report_app', function(msg) {

        console.log(msg.content.toString());
    });

    console.info('OK');
});

Konsument

Pobieranie wiadomości

fields

  • exchange,
  • routingKey,
  • redelivered

properties

  • headers,
  • expiration

content

Zwracana wiadomość zawiera:

Konsument

Potwierdzanie wiadomości

connection.createChannel(function(err2, channel) {

    if (err2) {
    	throw err2;
    }

    channel.consume('test', function(msg) {

        console.log(msg.content.toString());

        // console.log('Potwierdzam przetworzenie');
        // channel.ack(msg);
    });

    console.info('OK');
});

Konsument

Obsługa błędów

connection.createChannel(function(err2, channel) {

    if (err2) {
    	throw err2;
    }

    channel.consume('report_app', function(msg) {

        console.log(msg.content.toString());

        console.log('Error - powrót do kolejki');

        setTimeout(function() {

            channel.nack(msg);
        }, 15000);
    });

    console.info('OK');
});

Zarządzanie RabbitMQ

Zarządzanie RabbitMQ

Plik konfiguracyjny

/etc/rabbitmq/rabbitmq.conf
# od wersji 3.8
load_definitions = /etc/rabbitmq/definitions.json

# starsze wersje
# management.load_definitions = /etc/rabbitmq/definitions.json
/etc/init.d/rabbitmq-server restart
/etc/init.d/rabbitmq-server status

Zarządzanie RabbitMQ

Plik konfiguracyjny

/etc/rabbitmq/definitions.json
{
  "users": [
    {
      "name": "app_2",
      "password": "app_2",
      "tags": "administrator"
    }
  ]
}

Zarządzanie RabbitMQ

Plik konfiguracyjny

/etc/rabbitmq/definitions.json
{
  "vhosts":[
    {
      "name":"app_2"
    }
  ]
}

Zarządzanie RabbitMQ

Plik konfiguracyjny

/etc/rabbitmq/definitions.json
{
  "permissions": [
    {
      "user": "app_2",
      "vhost": "app_2",
      "configure": ".*",
      "write": ".*",
      "read": ".*"
    }
  ]
}

Zarządzanie RabbitMQ

Plik konfiguracyjny

/etc/rabbitmq/definitions.json
{
  "exchanges": [
    {
      "vhost": "app_2",
      "name": "reports_ex",
      "type": "direct",
      "durable": true,
      "auto_delete": false,
      "arguments": {}
    }
  ]
}

Zarządzanie RabbitMQ

Plik konfiguracyjny

/etc/rabbitmq/definitions.json
{
  "queues":[
    {
      "name":"reports_pdf",
      "vhost":"app_2",
      "durable":true,
      "auto_delete":false,
      "arguments":{}
    }
  ]
}

Zarządzanie RabbitMQ

Plik konfiguracyjny

/etc/rabbitmq/definitions.json
{
  "bindings": [
    {
      "arguments":{},
      "destination":"reports_pdf",
      "destination_type":"queue",
      "routing_key":"download_pdf",
      "source":"reports_ex",
      "vhost":"app_2"
    }
  ]
}

Zarządzanie RabbitMQ

Plik konfiguracyjny

/etc/rabbitmq/definitions.json
{
  "users": [
    {
      "name": "app_2",
      "password": "app_2",
      "tags": "administrator"
    }
  ],
  "vhosts":[
    {
      "name":"app_2"
    }
  ],
  "permissions": [
    {
      "user": "app_2",
      "vhost": "app_2",
      "configure": ".*",
      "write": ".*",
      "read": ".*"
    }
  ],
  "parameters": [],
  "policies": [],
  "queues":[
    {
      "name":"reports_pdf",
      "vhost":"app_2",
      "durable":true,
      "auto_delete":false,
      "arguments":{}
    }
  ],
  "exchanges": [
    {
      "vhost": "app_2",
      "name": "reports_ex",
      "type": "direct",
      "durable": true,
      "auto_delete": false,
      "arguments": {}
    }
  ],
  "bindings": [
    {
      "arguments":{},
      "destination":"reports_pdf",
      "destination_type":"queue",
      "routing_key":"download_pdf",
      "source":"reports_ex",
      "vhost":"app_2"
    }
  ]
}

Zarządzanie RabbitMQ

Plik konfiguracyjny

rabbitmqctl import_definitions /etc/rabbitmq/definitions.json

Import pliku - nie wymaga pluginu management

rabbitmqadmin import /etc/rabbitmq/definitions.json

Import pliku - wymaga pluginu management

curl --user guest:guest --data "@/etc/rabbitmq/definitions.json" --header "Content-Type: application/json" --request POST http://127.0.0.1:15672/api/definitions

Import pliku przez API

Zarządzanie RabbitMQ

Plik konfiguracyjny

rabbitmqctl export_definitions /etc/rabbitmq/definitions.file.json

Eksport danych do pliku - nie wymaga pluginu management

rabbitmqadmin export /etc/rabbitmq/definitions_export.json

Eksport danych do pliku - wymaga pluginu management

curl --user guest:guest --header "Content-Type: application/json" --request GET http://127.0.0.1:15672/api/definitions > export_data.json

Eksport do pliku przez API

Zarządzanie RabbitMQ

REST API

Zarządzanie RabbitMQ

REST API

http://localhost:15672/api/vhosts

Lista vhosts

http://localhost:15672/api/exchanges

Lista central

curl -u guest:guest http://localhost:15672/api/vhosts

Lista vhosts - CURL

curl -u guest:guest http://localhost:15672/api/vhosts | json_pp -json_opt pretty,canonical

Zarządzanie RabbitMQ

Z linii poleceń - CLI

rabbitmqctl list_queues name durable arguments
rabbitmqctl list_queues name type durable --formatter=pretty_table

Ładne formatowanie zwracanych informacji

Standardowe formatowanie zwracanych informacji

rabbitmqadmin list queues

Ładne formatowanie zwracanych informacji

rabbitmqctl list_queues name durable arguments --formatter=pretty_table --silent

Ładne formatowanie zwracanych informacji - bez nagłówka

Klaster

Klaster

Replikacja w RabbitMQ wspierana jest natywnie w trybie master-slave.

 

W RabbitMQ znajdziesz dwa typy węzłów:

  • Węzeł dysku , gdzie wykonawcze stan klastra zapisywane RAM lub węzłów dysku.
  • Węzeł RAM , która przechowuje stan wykonania w pamięci.

Klaster

docker run -d \
  --name rabbit_cluster_1 \
  --hostname docker1 \
  -e RABBITMQ_ERLANG_COOKIE="cookie" \
  -e RABBITMQ_NODENAME=rabbit1 \
  -p 5673:5672 \
  -p 15673:15672 \
  -p 25673:25672 \
  rabbitmq:3.8-management

Node 1 - master

Tworzymy klaster

Klaster

Tworzymy klaster

docker run -d \
  --name rabbit_cluster_2 \
  --hostname docker2 \
  -e RABBITMQ_ERLANG_COOKIE="cookie" \
  -e RABBITMQ_NODENAME=rabbit2 \
  -p 5674:5672 \
  -p 15674:15672 \
  -p 25674:25672 \
  --link rabbit_cluster_1 \
  rabbitmq:3.8-management

Node 2 - slave

docker run -d \
  --name rabbit_cluster_3 \
  --hostname docker3 \
  -e RABBITMQ_ERLANG_COOKIE="cookie" \
  -e RABBITMQ_NODENAME=rabbit3 \
  -p 5675:5672 \
  -p 15675:15672 \
  -p 25675:25672 \  
  --link rabbit_cluster_1 \
  --link rabbit_cluster_2 \
  rabbitmq:3.8-management

Node 3 - slave

Klaster

Panel administracyjny - brak węzłów

Klaster

docker exec -it rabbit_cluster_2 /bin/bash
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster rabbit1@docker1
rabbitmqctl start_app

Konieczne jest usuniecie stanu środowiska uruchomieniowego RabbitMQ w tym węźle, aby pomyślnie dołączyć go do klastra

Zatrzymujemy wewnętrzne procesy

Dołączamy do klastra

Uruchamiamy wewnętrzne procesy

Dodawanie węzłów do klastra

Klaster

Panel administracyjny - po dodaniu węzła

Klaster

Konfiguracja

version: '3'

services:

  rabbit1:
    image: rabbitmq:3.8-management
    hostname: docker1
    environment:
      - RABBITMQ_ERLANG_COOKIE=test
      - RABBITMQ_NODENAME=rabbit1
    ports:
      - "5673:5672"
      - "15673:15672"
      - "25673:25672"
    volumes:
      - ./rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf

  rabbit2:
    image: rabbitmq:3.8-management
    depends_on:
      - rabbit1
    hostname: docker2
    ports:
      - "5674:5672"
      - "15674:15672"
      - "25674:25672"
    environment:
      - RABBITMQ_ERLANG_COOKIE=test
      - RABBITMQ_NODENAME=rabbit2
    volumes:
      - ./rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf

  rabbit3:
    image: rabbitmq:3.8-management
    depends_on:
      - rabbit1
    hostname: docker3
    ports:
      - "5675:5672"
      - "15675:15672"
      - "25675:25672"
    environment:
      - RABBITMQ_ERLANG_COOKIE=test
      - RABBITMQ_NODENAME=rabbit3
    volumes:
      - ./rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf

docker-compose.yml

Klaster

Plik konfiguracyjny

loopback_users = none
listeners.tcp.default = 5672

cluster_formation.peer_discovery_backend = rabbit_peer_discovery_classic_config

cluster_formation.classic_config.nodes.1 = rabbit1@docker1
cluster_formation.classic_config.nodes.2 = rabbit2@docker2
cluster_formation.classic_config.nodes.3 = rabbit3@docker3

rabbitmq.conf

docker-compose.yml

volumes:
  - ./rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf

Klaster

Plik konfiguracyjny

global
        log 127.0.0.1   local1
        maxconn 4096
 
defaults
        log     global
        mode    tcp
        option  tcplog
        retries 3
        option redispatch
        maxconn 2000
        timeout connect 5000
        timeout client 50000
        timeout server 50000
 
listen  stats
        bind *:1936
        mode http
        stats enable
        stats hide-version
        stats realm Haproxy\ Statistics
        stats uri /
 
listen rabbitmq
        bind *:5672
        mode            tcp
        balance         roundrobin
        timeout client  3h
        timeout server  3h
        option          clitcpka
        server          rabbitmq1 rabbitmq1:5672  check inter 5s rise 2 fall 3
        server          rabbitmq2 rabbitmq2:5672  check inter 5s rise 2 fall 3
        server          rabbitmq3 rabbitmq3:5672  check inter 5s rise 2 fall 3

listen mgmt
        bind *:15672
        mode            tcp
        balance         roundrobin
        timeout client  3h
        timeout server  3h
        option          clitcpka
        server          rabbitmq1 rabbitmq1:15672  check inter 5s rise 2 fall 3
        server          rabbitmq2 rabbitmq2:15672  check inter 5s rise 2 fall 3
        server          rabbitmq3 rabbitmq3:15672  check inter 5s rise 2 fall 3

haproxy.cfg

Klaster

Producent

let amqp = require('amqp-connection-manager');
let q = 'tasks_app';

function sleep(ms) {
  if(ms <= 0){
    return
  }
    return new Promise(resolve => setTimeout(resolve, ms));
}

let main = async () => {

    var connection = amqp.connect([
        'amqp://localhost:5672',
        'amqp://localhost:5673',
        'amqp://localhost:5674',
    ]);

    var channelWrapper = connection.createChannel({
        json: true,
        setup: function(channel) {
            return channel.assertQueue(q, { durable: true });
        }
    });

    console.log('Starting message stream')
    while (true) {
        await channelWrapper.sendToQueue(q, { value: Math.random() })
        await sleep(100)
    }
}

main()

cluster_producer.js

Klaster

Konsument

let amqp = require('amqp-connection-manager');
let q = 'tasks_app';

let main = async () => {

    var connection = amqp.connect([
        'amqp://localhost:5672',
        'amqp://localhost:5673',
        'amqp://localhost:5674',
    ]);

    var channelWrapper = connection.createChannel({
        json: true,
        setup: function(channel) {
            return channel.assertQueue(q, { durable: true });
        }
    });

    channelWrapper.addSetup(function(channel) {
        return Promise.all([
            channel.consume(q, (msg) => {
                console.log(msg.content.toString())
            }, {noAck: true , exclusive: false })
        ])
    });
}

main()

cluster_consumer.js

Klaster

Mirrored Queues

Klaster

Mirrored Queues

Replikacja danych ma kilka trybów ha-mode:

  • all - kopia na każdym węźle,
  • exactly - stałą liczbę kopii,
  • nodes - na konkretnych węzłach

 

Dodawanie kolejnych replik może odbywa się w dwóch trybach synchronizacji ha-sync-mode:

  • automatic - automatyczna synchronizacja,
  • manual - ręczna, która nie blokuje kolejki na czas synchronizacji

 

W przypadku awarii master node, następuje wyłonienie nowego master node z slave node.

Klaster

Mirrored Queues

Klaster

Mirrored Queues

Klaster

Quorum Queues

Klaster

Quorum Queues

Zalety

  • kolejki Quorum Queues kładą nacisk na bezpieczeństwo danych,
  • uproszczenie replikacji,
  • nadają się jako długożyjące kolejki pełniące krytyczne funkcje,
  • gwarantują bezpieczeństwo wiadomości w przypadku awarii węzłów ( kworum N/2+1 )
  • minimum 3 węzły,
  • klaster toleruje utratę 1 węzła przy klastrze trzywęzłowym, 2 przy klastrze 5 węzłowym,

Klaster

Quorum Queues

Ograniczenia

  • nie wspierają parametru exclusive ( kolejki usuwane po rozłączeniu ),
  • nie wspierają parametru TTL ( usunięcie kolejki po określonym czasie, od rozłączenia ostatniego konsumenta ),
  • kolejki zawsze są trwałe ( durable )
  • brak zastosowania lazy mode, gdyż wiadomości zawsze są zapisywane na dysk oraz trzymane w pamięci

Klaster

Quorum Queues

Dodajemy kolejkę

Klaster

Quorum Queues

Po dodaniu kolejki na liście powinna pojawi się nasza kolejka typu Quorum oraz informacja o jej synchronizacji

Klaster

Quorum Queues

quorum_producer.js

connection.createConfirmChannel(function(err2, channel) {

    if (err2){
    	throw err2;
    }

    console.info('Channel created');

    var queue = 'quorum_q1';

    var msg = JSON.stringify({
    	'text': 'Message 1'
    });

    console.log(" [x] Sent %s", msg);

    channel.sendToQueue(queue, Buffer.from(msg), {}, errorCallback(msg));

    channel.waitForConfirms(function(err3) {

        if (err3) {
        	return console.log('Not confirm all message');
        }

        console.log('All messages confirm');
    });
});
Connected
Channel created
 [x] Sent {"text":"Message 1"}
{"text":"Message 1"} - confirm
All messages confirm

wynik działania

Klaster

Quorum Queues

Klaster

Quorum Queues

Klaster

Quorum Queues

docker stop mle_rabbit2_1
docker ps
node quorum_producer.js
Connected
Channel created
 [x] Sent {"text":"Message 1"}
{"text":"Message 1"} - confirm
All messages confirm

Klaster

Quorum Queues

docker stop mle_rabbit3_1
node quorum_producer.js
Connected
Channel created
 [x] Sent {"text":"Message 1"}
Message: {"text":"Message 1"} - NOT confirm
Not confirm all message

Dziękuję :)

Materiały dodatkowe

  • https://www.cloudamqp.com/blog/part4-rabbitmq-13-common-errors.html
  • https://sleeplessbeastie.eu/2020/03/18/how-export-or-import-rabbitmq-configuration/
  • https://tomasz.jarosik.online/2018/09/rabbitmq-performance-test-in-a-home-lab/
  • https://git.zabbix.com/projects/ZBX/repos/zabbix/browse/templates/app/rabbitmq_http
  • https://www.rabbitmq.com/prometheus.html
  • https://www.rabbitmq.com/monitoring.html#rabbitmq-metrics
Made with Slides.com