Bootcamp i backendutveckling med Node.js · Lektion

Exchange-typer: Direct, Topic, Fanout och Headers

Dirigera meddelanden till rätt konsumenter genom att välja lämplig exchange- och binding-strategi.

Lektion 2 av 413 steg

Exchange-typer: Direct, Topic, Fanout och Headers är en gratis lektion i Bootcamp i backendutveckling med Node.js på CoddyKit. Detta är lektion 2 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för Bootcamp i backendutveckling med Node.js, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Bootcamp i backendutveckling med Node.js innehåller totalt 4 lektioner.

Varför exchanges finns

I RabbitMQ publicerar producenter aldrig direkt till en kö. De publicerar till en exchange, och exchangen avgör vilka köer som tar emot meddelandet utifrån bindningar och en routingnyckel.

Denna indirekta koppling utgör kärnan i händelsestyrd routing. För att få meddelanden till rätt konsumenter måste ni välja rätt exchange-typ:

  • direct — exakt matchning av routingnyckeln
  • topic — mönstermatchning med jokertecken
  • fanout — broadcast till alla bundna köer
  • headers — matchning baserad på meddelandets header-attribut

Om ni väljer fel typ missar konsumenterna meddelanden eller översvämmas av irrelevanta meddelanden.

Den mentala modellen: exchange + bindning + routingnyckel

Tre delar samverkar:

  • Routingnyckel: en sträng som producenten lägger till i varje meddelande (t.ex. order.created).
  • Bindning: en regel som kopplar en exchange till en kö, ofta med en bindingsnyckel.
  • Exchange-typ: algoritmen som jämför routingnyckeln med bindningarna.

Exchangen utvärderar varje bindning. Ett meddelande kan hamna i noll, en eller flera köer. Om det inte matchar någon bindning tas det bort, såvida inte en alternativ exchange har konfigurerats.

Vi använder biblioteket amqplib genomgående i dessa exempel.

// Establishing a channel with amqplib
const amqp = require('amqplib');

async function connect() {
  const conn = await amqp.connect('amqp://localhost');
  const channel = await conn.createChannel();
  return { conn, channel };
}

module.exports = { connect };

Direct-exchange: exakt matchning

En direct-exchange levererar ett meddelande till köer vars bindingsnyckel exakt motsvarar meddelandets routingnyckel.

Det är det självklara valet när varje meddelandetyp motsvarar en specifik worker. Ett klassiskt användningsfall är att routa loggmeddelanden efter allvarlighetsgrad (error, warning, info) till dedikerade köer.

  • Routingnyckeln error → endast kön som är bunden med error.
  • Flera köer kan dela samma bindingsnyckel — alla får en kopia.
const amqp = require('amqplib');

async function setupDirect(channel) {
  const ex = 'logs_direct';
  await channel.assertExchange(ex, 'direct', { durable: true });

  const errors = await channel.assertQueue('errors', { durable: true });
  await channel.bindQueue(errors.queue, ex, 'error');

  const allLogs = await channel.assertQueue('all_logs', { durable: true });
  await channel.bindQueue(allLogs.queue, ex, 'error');
  await channel.bindQueue(allLogs.queue, ex, 'warning');
  await channel.bindQueue(allLogs.queue, ex, 'info');
}

module.exports = { setupDirect };

Publicera till en direct-exchange

Producenten skickar routingnyckeln som det andra argumentet till channel.publish(exchange, routingKey, content). Exchangen sköter resten.

Här når ett meddelande med routingnyckeln error både kön errors och kön all_logs från föregående scen, medan ett info-meddelande endast når all_logs.

Observera att meddelandekroppen måste vara en Buffer.

async function publishLog(channel, severity, message) {
  const ex = 'logs_direct';
  channel.publish(
    ex,
    severity, // routing key: 'error' | 'warning' | 'info'
    Buffer.from(JSON.stringify({ message, ts: Date.now() })),
    { persistent: true }
  );
  console.log(`Sent [${severity}] ${message}`);
}

// publishLog(channel, 'error', 'DB connection lost');
module.exports = { publishLog };

Topic-exchange: mönstermatchning

En topic-exchange matchar routingnycklar mot bindningsmönster med hjälp av två jokertecken:

  • * (stjärna) matchar exakt ett ord.
  • # (hash) matchar noll eller flera ord.

Routingnycklar består av punktavgränsade ord, till exempel order.eu.created. Detta är den mest flexibla exchange-typen och passar utmärkt för hierarkiska händelsenamn.

  • order.*.created matchar order.eu.created men inte order.created.
  • order.# matchar order.created, order.eu.shipped och så vidare.
  • *.eu.* matchar alla EU-händelser med tre ord.

Konfigurera topic-bindningar

Föreställ er en orderpipeline. Olika tjänster är intresserade av olika delar av händelseströmmen:

  • En audit-tjänst vill ha allt: order.#.
  • En tjänst för EU-efterlevnad vill endast ha EU-händelser: order.eu.*.
  • En shipping-tjänst vill ha skickade händelser från alla regioner: order.*.shipped.

En publicerad händelse kan uppfylla flera mönster samtidigt och skickas endast vidare till de intresserade konsumenterna.

const amqp = require('amqplib');

async function setupTopic(channel) {
  const ex = 'orders_topic';
  await channel.assertExchange(ex, 'topic', { durable: true });

  const audit = await channel.assertQueue('audit', { durable: true });
  await channel.bindQueue(audit.queue, ex, 'order.#');

  const euCompliance = await channel.assertQueue('eu_compliance', { durable: true });
  await channel.bindQueue(euCompliance.queue, ex, 'order.eu.*');

  const shipping = await channel.assertQueue('shipping', { durable: true });
  await channel.bindQueue(shipping.queue, ex, 'order.*.shipped');
}

module.exports = { setupTopic };

Resonera kring topic-matchningar

Vi följer vilka köer som tar emot varje händelse utifrån bindningarna order.# (audit), order.eu.* (EU) och order.*.shipped (shipping).

  • order.eu.created → audit + EU
  • order.us.shipped → audit + shipping
  • order.eu.shipped → audit + EU + shipping
  • order.created → endast audit (det finns bara ett ord efter order, så order.eu.* och order.*.shipped matchar inte)

Denna hjälpfunktion i ren JavaScript efterliknar topic-matchningsalgoritmen, så att ni kan verifiera era mönster offline.

function topicMatch(pattern, key) {
  const p = pattern.split('.');
  const k = key.split('.');

  function rec(pi, ki) {
    if (pi === p.length) return ki === k.length;
    if (p[pi] === '#') {
      for (let skip = ki; skip <= k.length; skip++) {
        if (rec(pi + 1, skip)) return true;
      }
      return false;
    }
    if (ki < k.length && (p[pi] === '*' || p[pi] === k[ki])) {
      return rec(pi + 1, ki + 1);
    }
    return false;
  }
  return rec(0, 0);
}

const keys = ['order.eu.created', 'order.us.shipped', 'order.eu.shipped', 'order.created'];
for (const key of keys) {
  console.log(key, {
    audit: topicMatch('order.#', key),
    eu: topicMatch('order.eu.*', key),
    shipping: topicMatch('order.*.shipped', key),
  });
}

Fanout-exchange: broadcast

En fanout-exchange ignorerar routingnyckeln helt och levererar varje meddelande till alla bundna köer. Det är den enklaste och snabbaste exchange-typen.

Använd den i broadcastsituationer: ogiltigförklaring av cache i alla appinstanser, realtidsaviseringar eller utskick av uppdateringar till många WebSocket-gateways.

Varje konsument deklarerar vanligtvis en egen exklusiv, automatiskt borttagen kö, så att den får en privat kopia av broadcasten.

const amqp = require('amqplib');

async function subscribeBroadcast(channel, onMessage) {
  const ex = 'cache_invalidations';
  await channel.assertExchange(ex, 'fanout', { durable: true });

  // Exclusive, server-named queue: unique per consumer, auto-deleted on disconnect
  const q = await channel.assertQueue('', { exclusive: true });
  await channel.bindQueue(q.queue, ex, ''); // routing key ignored for fanout

  await channel.consume(q.queue, (msg) => {
    if (msg) {
      onMessage(JSON.parse(msg.content.toString()));
      channel.ack(msg);
    }
  });
}

module.exports = { subscribeBroadcast };

Headers-exchange: matchning på attribut

En headers-exchange ignorerar routingnyckeln och matchar i stället meddelandets headers (nyckel/värde-par). Bindningar anger vilka headers som ska matcha samt ett särskilt x-match-argument:

  • x-match: all — varje angiven header måste matcha (OCH).
  • x-match: any — minst en angiven header måste matcha (ELLER).

Detta är användbart när routningen beror på flera oberoende dimensioner (t.ex. format och region) som inte enkelt kan kombineras till en enda punktavgränsad nyckel.

const amqp = require('amqplib');

async function setupHeaders(channel) {
  const ex = 'reports_headers';
  await channel.assertExchange(ex, 'headers', { durable: true });

  const pdfEu = await channel.assertQueue('pdf_eu', { durable: true });
  await channel.bindQueue(pdfEu.queue, ex, '', {
    'x-match': 'all',
    format: 'pdf',
    region: 'eu',
  });

  const anyCsv = await channel.assertQueue('any_csv', { durable: true });
  await channel.bindQueue(anyCsv.queue, ex, '', {
    'x-match': 'any',
    format: 'csv',
    priority: 'high',
  });
}

module.exports = { setupHeaders };

Publicera med headers

Producenten lägger till headervärden via egenskapen headers i publiceringsalternativen. En tom routingnyckel är det konventionella valet för headers-exchanges eftersom den inte används.

Med bindningarna från föregående scen når ett meddelande med { format: 'pdf', region: 'eu' } pdf_eu. Ett meddelande med { format: 'csv', region: 'us' } når any_csv (eftersom x-match: any matchade på format).

async function publishReport(channel, payload, headers) {
  const ex = 'reports_headers';
  channel.publish(
    ex,
    '', // routing key unused for headers exchange
    Buffer.from(JSON.stringify(payload)),
    { headers, persistent: true }
  );
}

// publishReport(channel, { id: 42 }, { format: 'pdf', region: 'eu' });
// publishReport(channel, { id: 43 }, { format: 'csv', region: 'us' });
module.exports = { publishReport };

Välj rätt exchange

En praktisk beslutsvägledning:

  • Direct — ni har en fast uppsättning kategorier och vill ha exakt routing (allvarlighetsnivåer, uppgiftstyper).
  • Topic — händelsenamn är hierarkiska och konsumenter prenumererar på flexibla mönster. Det vanligaste valet för händelsebussar i mikrotjänster.
  • Fanout — varje konsument måste se varje meddelande (broadcast, cache-rensning).
  • Headers — routningen beror på flera icke-hierarkiska attribut, eller så behöver ni OCH/ELLER-logik över flera dimensioner.

Tumregel: välj topic som standard i händelsestyrda system; den omfattar direct (ett mönster utan jokertecken fungerar som direct) och är betydligt mer utbyggbar än fanout.

Snabbtest

Testa er förståelse av valet av exchange.

Sammanfattning

Ni har lärt er hur RabbitMQ routar meddelanden genom exchanges och bindningar:

  • Producenter publicerar till exchanges, aldrig direkt till köer; exchange-typen är routningsalgoritmen.
  • Direct — exakt matchning av routingnyckeln för fasta kategorier.
  • Topic — mönstermatchning med jokertecken (* = ett ord, # = noll eller flera) för hierarkiska händelser; standardvalet för händelsebussar.
  • Fanout — broadcast till alla bundna köer, utan hänsyn till routingnyckeln.
  • Headers — matchning på header-attribut med x-match: all (OCH) eller any (ELLER).

Deklarera alltid exchangen och köerna med assertExchange/assertQueue och anslut dem med bindQueue. Välj den exchange som motsvarar hur era konsumenter behöver prenumerera, så routar ni rätt meddelanden till rätt platser med minimal koppling.

Gratis att börja

Lär dig JavaScript med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
22
Lektioner
92

Vanliga frågor

Är lektionen ”Exchange-typer: Direct, Topic, Fanout och Headers” gratis?

Ja – hela texten till ”Exchange-typer: Direct, Topic, Fanout och Headers” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i Bootcamp i backendutveckling med Node.js, kan Ni uppgradera till CoddyKit PRO. Kursen i Bootcamp i backendutveckling med Node.js innehåller totalt 4 lektioner.

Vad lär jag mig i ”Exchange-typer: Direct, Topic, Fanout och Headers”?

Dirigera meddelanden till rätt konsumenter genom att välja lämplig exchange- och binding-strategi. Ni övar på Bootcamp i backendutveckling med Node.js med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig Bootcamp i backendutveckling med Node.js?

Du behöver inga förkunskaper. Utbildningen i Bootcamp i backendutveckling med Node.js på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 2 av 4.

Hur lång tid tar lektionen ”Exchange-typer: Direct, Topic, Fanout och Headers”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här Bootcamp i backendutveckling med Node.js-lektionen?

Ja. Varje Bootcamp i backendutveckling med Node.js-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Producenter, konsumenter och AMQP-modellen
  2. Exchange-typer: Direct, Topic, Fanout och Headers
  3. Bekräftelser, dead-letter-köer och omförsök
  4. Arbetsköer, prefetch och konkurrerande konsumenter
← Tillbaka till Bootcamp i backendutveckling med Node.js