Implementering af RabbitMQ
Introduktion
RabbitMQ bruges som en message broker til at facilitere kommunikation mellem microservices. Denne tilgang decouplerer services, så de kan arbejde asynkront og uafhængigt af hinanden.
I denne demo vises:
- En producer-service, der sender beskeder.
- RabbitMQ som en message broker.
- En Consumer-service, der modtager beskeder.
Arkitekturdiagram
Problematik i Kommunikation mellem Microservices
Når microservices kommunikerer direkte med hinanden, kan følgende udfordringer opstå:
- Tight Coupling: Services bliver afhængige af hinandens tilgængelighed og implementation, hvilket gør systemet mindre fleksibelt.
- Skaleringsproblemer: Direkte kommunikation mellem mange services kan føre til en kompleks netværksarkitektur og belastningsproblemer.
- Håndtering af fejl: Hvis en service går ned, kan det medføre, at afhængige tjenester også fejler, især i real-time systemer.
- Kompleks integration: Ved flere services kan der opstå udfordringer med at sikre, at alle services kan kommunikere korrekt med hinanden.
Hvordan RabbitMQ som Message Broker Løser Problemet
En message broker som RabbitMQ introducerer en middelware, der hjælper med at håndtere asynkron kommunikation mellem services. Dette giver flere fordele:
- Decoupling (Løs kobling): Services behøver ikke være online samtidig. Producenten (Producer) kan sende beskeder, som forbrugeren (Consumer) henter senere.
- Pålidelighed: RabbitMQ garanterer levering af beskeder og kan gemme dem, indtil de er leveret.
- Skalérbarhed: RabbitMQ kan håndtere mange forbindelser og beskeder, hvilket gør systemet mere skalerbart.
- Fejlhåndtering: Hvis en forbruger er midlertidigt nede, gemmer RabbitMQ beskeden, indtil forbrugeren er klar igen.
Dette sikrer, at ordrer ikke går tabt, selv hvis lagerstyringsservicen ikke er tilgængelig i øjeblikket. RabbitMQ vil gemme beskederne og levere dem, når servicen er oppe igen.
Arkitekturen løser således problematikken med kompleks direkte kommunikation og sikrer, at services kan arbejde uafhængigt af hinanden.
Opsætning med docker
docker run -it --rm --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:4.0-management
Denne Docker-kommando starter en midlertidig RabbitMQ-container med det officielle Management-plugin. Den mapper port 5672 (AMQP) og 15672 (Management-UI) til din host, så du kan sende/afhente beskeder og overvåge køer via http://localhost:15672
(standard login: guest/guest
). Containeren navngives rabbitmq
og fjernes automatisk ved stop.
Kode eksempel
Producer
Jeg har implementeret en Producer her
var factory = new ConnectionFactory() { HostName = "localhost" };
using var connection = factory.CreateConnection();
using var channel = connection.CreateModel();
channel.QueueDeclare(queue: "hello",
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);
string message = "Hello World!";
var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(exchange: "",
routingKey: "hello",
basicProperties: null,
body: body);
1. Opret forbindelse til RabbitMQ
var factory = new ConnectionFactory() { HostName = "localhost" };
using var connection = factory.CreateConnection();
using var channel = connection.CreateModel();
ConnectionFactory
: Bruges til at konfigurere og oprette forbindelse til RabbitMQ-serveren.HostName
angiver, at RabbitMQ kører lokalt.CreateConnection
: Opretter en TCP-forbindelse til RabbitMQ-serveren.CreateModel
: Opretter en kanal, som er den virtuelle forbindelse, der bruges til at sende beskeder.
RabbitMQ arbejder med kanaler for effektiv kommunikation mellem klient og server.
2. Opret en kø
channel.QueueDeclare(queue: "hello",
durable: false,
exclusive: false,
autoDelete: false,
arguments: null
);
QueueDeclare
: Sikrer, at køen med navnet"hello"
eksisterer. Hvis køen allerede er oprettet, genbruger RabbitMQ den.queue: "hello"
: Navnet på køen.durable: false
: Angiver, at køen ikke overlever en genstart af RabbitMQ. (Hvis det sættes tiltrue
, gemmes køen)exclusive: false
: Køen kan bruges af flere forbindelser, ikke kun den nuværende.autoDelete: false
: Køen slettes ikke automatisk, når forbindelsen lukkes.arguments: null
: Ingen ekstra parametre til køen.
3. Klargør beskeden
string message = "Hello World!";
var body = Encoding.UTF8.GetBytes(message);
message
: En simpel tekstbesked, der skal sendes.Encoding.UTF8.GetBytes
: Konverterer beskeden til en byte-array, som er det format RabbitMQ kræver.
4. Send beskeden
channel.BasicPublish(exchange: "",
routingKey: "hello",
basicProperties: null,
body: body
);
BasicPublish
: Bruges til at sende en besked til RabbitMQ.
Consumer
Jeg har implementeret en consumer her
var factory = new ConnectionFactory() { HostName = "localhost" };
using var connection = factory.CreateConnection();
using var channel = connection.CreateModel();
channel.QueueDeclare(queue: "hello",
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
Console.WriteLine(" [x] Received {0}", message);
};
channel.BasicConsume(queue: "hello",
autoAck: true,
consumer: consumer);
1. Opret forbindelse til RabbitMQ
var factory = new ConnectionFactory() { HostName = "localhost" };
using var connection = factory.CreateConnection();
using var channel = connection.CreateModel();
ConnectionFactory
: Konfigurerer forbindelse til RabbitMQ-serveren.HostName
angiver, at serveren kører lokalt.CreateConnection
: Opretter en TCP-forbindelse til RabbitMQ-serveren.CreateModel
: Opretter en kanal, som bruges til at kommunikere med RabbitMQ.
2. Sikre eksistensen af køen
channel.QueueDeclare(queue: "hello",
durable: false,
exclusive: false,
autoDelete: false,
arguments: null
);
QueueDeclare
: Sikrer, at køen"hello"
eksisterer. Dette er vigtigt, fordi consumer'en ikke kan modtage beskeder, hvis køen ikke findes.
3. Opsæt en consumer
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) => {
var body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
Console.WriteLine(" [x] Received {0}", message);
};
EventingBasicConsumer
: En RabbitMQ-standard til at modtage beskeder asynkront.consumer.Received
: En event-handler, der udløses, når der modtages en besked.ea.Body.ToArray()
: Konverterer beskedens data fra byte-array til en brugbar form.Encoding.UTF8.GetString(body)
: Dekoder byte-array til en tekststreng.Console.WriteLine
: Viser beskeden i konsollen.
4. Begynd at lytte til køen
channel.BasicConsume(queue: "hello",
autoAck: true,
consumer: consumer);
BasicConsume
: Starter consumeren, så den lytter til køen"hello"
.queue: "hello"
: Angiver navnet på køen, som consumeren lytter til.autoAck: true
: Beskeden markeres som leveret automatisk, så snart den er modtaget.- Hvis dette sættes til
false
, skal applikationen eksplicit bekræfte leveringen for at undgå tab af beskeder.
- Hvis dette sættes til
Sammenfatning
Med denne opsætning kan du vise, hvordan RabbitMQ fungerer som message broker i en microservices-arkitektur, og hvordan producer- og consumer-tjenester kommunikerer asynkront.