angle-uparrow-clockwisearrow-counterclockwisearrow-down-uparrow-leftatcalendarcard-listchatcheckenvelopefolderhouseinfo-circlepencilpeoplepersonperson-fillperson-plusphoneplusquestion-circlesearchtagtrashx

Van monolithische code naar diensten met RabbitMQ en Pika

Gebruik de voorbeelden in de Pika documentatie om je eigen RabbitMQ publisher en consumer classes te maken.

8 januari 2023 Bijgewerkt 9 januari 2023
post main image
https://www.pexels.com/nl-nl/@jeshoots-com-147458

Dit bericht gaat over het gebruik van RabbitMQ in uw Python toepassing. Als je Rabbit al gebruikt, zul je waarschijnlijk niets nuttigs vinden in deze post. Waarom een bericht over RabbitMQ? Omdat ik een applicatie heb die het gebruikt, die al een jaar draait, en ik wilde mijn ervaringen delen.

In dit bericht transformeren we een monolithische applicatie in diensten die ontkoppeld zijn door wachtrijen. Ook zal ik het hebben over services in plaats van microservices, jij bepaalt hoe 'groot' je service is. Er zijn geen codevoorbeelden in deze post.

Voorbeeld van een monolithische toepassing: Contactformulier

Het voorbeeld dat we zullen gebruiken is het contactformulierproces. Een user van onze website voert de gegevens van het contactformulier in. Dit wordt verwerkt, een e-mail wordt voorbereid en de e-mail wordt verzonden naar een externe SMTP-server.

        +-----------------+    smtp
user    | - process form  |  server
------->| - prepare email |------->
        | - send email    |
        +-----------------+

Waarom zou u uw code willen splitsen in services?

Een aantal voor- en nadelen van het splitsen van uw code zijn:

Voordelen (idealiter):

  • Strengere scheiding tussen de belangrijkste services (functies) van je code.
  • Services kunnen gestopt en opnieuw gestart worden zonder verlies van gegevens.
  • Prestaties. Door ontkoppeling komt een dienst eerder beschikbaar.
  • Asynchroon. Een dienst zal wachten tot een andere dienst beschikbaar is, zonder andere diensten te beïnvloeden.

Nadelen:

  • Verhoogde complexiteit.
  • Verhoogde ontwikkelingstijd.
  • Moeilijker te testen.

Samenvatting van de voordelen is: een sneller en flexibeler systeem. Samenvatting van de nadelen: (veel) meer werk.

Er zijn veel artikelen op het internet over de 'microservices hel', maar er zijn evenveel artikelen over de 'monolithische hel'. Ik stel voor dat u zelf wat onderzoek doet. Hier is een leuk artikel: 'You Don't Need Microservices', zie onderstaande links. Lees ook de commentaren.

De diensten na splitsing

Hier zijn de services, nadat we onze code hebben gesplitst.

        +---------+     +---------+     +---------+    smtp
user    | process |     | prepare |     | send    |  server
------->| form    |---->| email   |---->| email   |-------> 
        |         |     |         |     |         |
        +---------+     +---------+     +---------+

Niet heel bijzonder, dit is gewoon een goed gestructureerde monoliet.

RabbitMQ en Pika

Om de diensten te ontkoppelen, gebruiken we wachtrijen. RabbitMQ is een message broker, en ondersteunt message queues, meerdere berichtenprotocollen, en meer. Om te communiceren met RabbitMQ in Python gebruiken we het Python pakket Pika. Pika is een pure-Python implementatie van het AMQP 0-9-1 protocol.

Wachtrijen in RabbitMQ zijn FIFO, first-in-first-out. Met RabbitMQ kunt u uitwisselingen en wachtrijen definiëren. Gewoonlijk definieert u een uitwisseling en bindt u vervolgens de wachtrijen aan deze uitwisseling.

Een uitgever wordt gebruikt om items in de wachtrij te plaatsen. Een consument wordt gebruikt om items uit de wachtrij te halen. Voorbeelden van publishers en consumers zijn te vinden in de Pika documentatie.

        +-----------+     +-------------+     +-----------+    
        |           |     | RabbitMQ    |     |           |  
------->| publisher |---->| - exchange  |---->| consumer  |-------> 
        |           |     | - queue     |     |           |
        +-----------+     +-------------+     +-----------+

Publishers en consumenten kunnen synchroon of asynchroon zijn.

In veel gevallen zullen we een:

  • Synchrone uitgever. Dit betekent dat we een item naar de RabbitMQ wachtrij sturen en wachten op de bevestiging dat het item in de wachtrij is geplaatst, alvorens verder te gaan met andere dingen.
  • Asynchrone consument. Dit betekent dat onze ontvangstfunctie wordt aangeroepen door RabbitMQ zodra er een item beschikbaar is in de wachtrij.

RabbitMQ en ontvangstbevestigingen

RabbitMQ heeft vele opties, de belangrijkste om te begrijpen zijn de ontvangstbevestigingen. Er zijn twee soorten bevestigingen.

  • Positieve ontvangstbevestiging
  • Negatieve bevestiging

(Synchrone) Bevestigingen van de uitgever

Wanneer de uitgever (onze code) een item naar RabbitMQ stuurt, antwoordt RabbitMQ met een:

  • Positieve bevestiging
    Het item is ontvangen door RabbitMQ.
  • Negatief bevestigingsbericht
    Er is iets misgegaan. Misschien bestond de uitwisseling of wachtrij niet, de routingsleutel was verkeerd.

(Asynchrone) Consumentenbevestigingen

Onze consument wordt aangeroepen door RabbitMQ met het volgende item uit de wachtrij. Na verwerking van het item antwoordt de consumer (onze code) met een:

  • Positieve bevestiging
    Het item is ontvangen en verwerkt, ik ben klaar voor het volgende item.
  • Negatieve bevestiging
    Het item is ontvangen, maar er is iets misgegaan tijdens de verwerking. RabbitMQ zal het item NIET uit de wachtrij verwijderen, en het opnieuw proberen.

Ontkoppeling van de diensten met wachtrijen

Om onze diensten te ontkoppelen, plaatsen we een wachtrij tussen de dienst 'process form' en de dienst 'prepare email' :

         publisher                     consumer
        +---------+     +-------+     +---------+    
user    | process |     |       |     | prepare |  
------->| form    |---->| queue |---->| email   |---->
        |         |     |       |     |         |
        +---------+     +-------+     +---------+

Dienst 'process form' fungeert als uitgever. Deze bevat code om items naar de wachtrij te sturen. Dienst 'prepare email' fungeert als consument. Deze bevat code om items naar de wachtrij te ontvangen.

De uitvoerwachtrij is 'onderdeel' van de uitgever

Stel nu dat de twee diensten op twee servers draaien. Omdat we willen dat onze diensten gestopt en opnieuw gestart kunnen worden zonder verlies van gegevens, is het belangrijk te begrijpen waar we de wachtrij tussen de diensten plaatsen. Op de publisher server, of op de consumer server.

Het antwoord is natuurlijk dat de wachtrij op dezelfde server moet staan als de 'process form' service. Als dan de server van de 'prepare email' dienst uitvalt, kan de 'process form' dienst blijven draaien. User's kunnen contactformulieren blijven versturen, de gegevens worden tijdelijk in de wachtrij opgeslagen. Zodra de 'prepare email' dienst weer up is, verwerkt hij alle gegevens die in de wachtrij wachten.

                  server 1                      server 2
         - - - - - - - - - - -  - -        - - - - - - - - - - 
                                    |     | 
         publisher                             consumer
        +---------+     +-------+   |     |   +---------+    
user    | process |     |       |             | prepare |  
------->| form    |---->| queue |------------>| email   |---->
        |         |     |       |             |         |
        +---------+     +-------+   |     |   +---------+

                                    |     | 
         - - - - - - - - - - -  - -        - - - - - - - - - - 

In het bovenstaande voorbeeld gebruikten we servers. Maar hetzelfde geldt als je alles op één server hebt staan, maar twee verschillende Docker-Compose projecten hebt, één voor de 'process form' service en één voor de 'prepare email' service. In dit geval voegen we RabbitMQ toe aan het 'process form' Docker-Compose project.

Een consument en uitgever tegelijk

Het is volkomen logisch wanneer een dienst tegelijkertijd consument en uitgever is. In ons voorbeeld treden zowel de dienst 'prepare email' als de dienst 'e-mail verzenden' op als consument en uitgever.

                                       consumer                      consumer
                                          +                             +
         publisher                     publisher                     publisher
        +---------+     +-------+     +---------+     +-------+     +---------+    
 user   | process |     |       |     | prepare |     |       |     | send    |  
------->| form    |---->| queue |---->| email   |---->| queue |---->| email   |---->
        |         |     |       |     |         |     |       |     |         |
        +---------+     +-------+     +---------+     +-------+     +---------+

De normale werking voor de 'prepare email' service is:

  1. Consument wordt aangeroepen met volgende item van de input-queue.
  2. Item wordt verwerkt.
  3. Nieuwe gegevens worden gepubliceerd naar de output-queue.
  4. Er wordt een positieve bevestiging van publicatie ontvangen.
  5. De consument stuurt een positieve bevestiging naar de input-queue.
  6. RabbitMQ verwijdert het item uit de input-queue.
  7. RabbitMQ roept de consument op met het volgende item uit de input-queue, zodra dat beschikbaar is.

Als er iets misgaat in het consumentenproces, bijvoorbeeld het niet publiceren naar de output-queue, kan de consument een negatieve bevestiging sturen naar RabbitMQ. In dat geval zal RabbitMQ het item niet uit de input-queue verwijderen, maar hetzelfde item opnieuw proberen.

Aan de slag met RabbitMQ

Aan de slag gaan met RabbitMQ is overweldigend. Applicatieontwikkelaars willen recepten gebruiken, niet een eindeloze hoeveelheid documenten lezen. Ik ben begonnen met het installeren van Docker met de Management Plugin. Met de Management Plugin kun je uitwisselingen en wachtrijen aanmaken, het aantal items in wachtrijen zien en nog veel meer.

Vervolgens heb ik de voorbeelden uit de Pika documentatie uitgeprobeerd. Zodra je deze begrijpt, kun je deze code gebruiken om je eigen publisher en consumer classes te bouwen, voor gebruik in je applicatie.

Willekeurige opmerkingen

Asynchroon publiceren is complex, ik heb dit tot nu toe niet gebruikt. Synchroon publiceren met RabbitMQ is niet erg snel, vooral als je de gegevens ook wilt laten persisteren. En je wilt waarschijnlijk dat de gegevens in de wachtrij blijven bestaan, want met deze optie gaan de gegevens niet verloren als je RabbitMQ stopt of bij een systeemcrash.

U kunt synchroon publiceren versnellen door een lijst van items naar de wachtrij te sturen in plaats van een enkel item. Maar dit hangt af van uw toepassing.

Ik gebruik geen enkele fouttolerante RabbitMQ instantie, maar draai in plaats daarvan verschillende RabbitMQ instanties samen met de diensten. Ik gebruik RabbitMQ met meerdere Docker-Compose projecten, het is slechts één van de diensten.

RabbitMQ is geheugenhongerig. Mijn applicatie is niet erg complex of veeleisend, maar één instantie van RabbitMQ gebruikt ongeveer 200MB volgens Docker Stats.

Sommige mensen op het internet klagen over hoog CPU gebruik. Ik merkte enkele CPU gebruikspieken op. RabbitMQ slaat veel gegevens op, die blijkbaar niet verdwijnen na een crash/kill. Na het verwijderen hiervan gingen de CPU pieken weg.

Ik gebruik ook de Shovel Plugin om gegevens van de ene RabbitMQ instantie naar een andere te sturen, met behulp van Docker Network. Dit werkt ook prima.

U kunt uw applicatie gebruiken om RabbitMQ uitwisselingen en wachtrijen te configureren. Ik doe dit niet, maar configureer de uitwisselingen en wachtrijen met de Management Plugin, exporteer dan het nieuwe configuratiebestand en kopieer het naar het configuratiebestand van RabbitMQ .

Ik gebruik Dockerized nu meer dan een jaar en het draait zeer betrouwbaar. Ik heb nog nooit opnieuw hoeven opstarten omdat er iets mis was.

Samenvatting

Is het opsplitsen van uw monolithische in services met RabbitMQ de moeite waard? Ja, maar, tenzij je een groot bedrijf bent, niet overdrijven want het voegt ook veel complexiteit toe. Zoals altijd wordt deze pijn minder zodra u meer van RabbitMQ begint te begrijpen. Tenslotte zou RabbitMQ in de gereedschapskist van elke ontwikkelaar moeten zitten.

Links / credits

Pika
https://pika.readthedocs.io

RabbitMQ
https://www.rabbitmq.com

You Don’t Need Microservices
https://itnext.io/you-dont-need-microservices-2ad8508b9e27

Laat een reactie achter

Reageer anoniem of log in om commentaar te geven.

Opmerkingen

Laat een antwoord achter

Antwoord anoniem of log in om te antwoorden.