RabbitMQ – מה זה, איך ומתי להשתמש?

RabbitMQ – מה זה, איך ומתי להשתמש?

RabbitMq

לאחרונה שמעתי על מערכת שנקראת RabbitMq ויצא לי להתנסות בה במסגרת העבודה. למי שעדיין לא מכיר, RabbitMQ הינה מערכת תיווך הודעות (message broker) פופולרית, המאפשרת להעביר הודעות בין שירותים או יישומים בצורה אמינה ויעילה. במאמר זה, נסקור את העקרונות הבסיסיים של RabbitMQ, נבין כיצד הוא פועל ונראה דוגמאות פרקטיות לשימוש בו. המטרה היא להציג את היתרונות של RabbitMQ ואת השימושים האפשריים בו.

מה זה RabbitMQ?

RabbitMQ הוא message broker בקוד פתוח שנכתב ב-Erlang. הוא תומך בפרוטוקול AMQP (Advanced Message Queuing Protocol), אך תומך גם בפרוטוקולים אחרים כמו MQTT ו-STOMP. RabbitMQ מאפשר ליישומים לתקשר זה עם זה באמצעות שליחה וקבלה של הודעות.

מונחים בסיסיים ב-RabbitMQ:

  1. Producer: היישום ששולח את ההודעות.
  2. Queue: התור שבו מאוחסנות ההודעות.
  3. Consumer: היישום שקורא את ההודעות מהתור.
  4. Exchange: מחלק את ההודעות המתקבלות מה-Producer לתורים המתאימים על פי חוקים מוגדרים מראש (bindings).

התקנה והגדרה

התקנת RabbitMQ

ניתן להתקין RabbitMQ במספר דרכים, אך נשתמש כאן בדוגמה של התקנה על Docker.

אם אתם לא מכירים עדיין Docker, מוזמנים לקרוא עוד מאמרים באתר על Docker או לדבר איתנו על קורס Docker.

docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3-management

הפקודה מריצה קונטיינר של RabbitMQ עם ממשק ניהול בכתובת http://localhost:15672. כברירת מחדל, שם המשתמש והסיסמה הם guest.

התחברות ל-RabbitMQ

נראה דוגמה להתחברות ל-RabbitMQ ב-Node.js בעזרת הספרייה amqplib.

npm install amqplib

Producer (שליחת הודעות):

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

amqp.connect('amqp://localhost', function(error0, connection) {
  if (error0) {
    throw error0;
  }
  connection.createChannel(function(error1, channel) {
    if (error1) {
      throw error1;
    }

    const queue = 'hello';
    const msg = 'Hello World!';

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

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

  setTimeout(function() {
    connection.close();
    process.exit(0);
  }, 500);
});

Consumer (קריאת הודעות):

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

amqp.connect('amqp://localhost', function(error0, connection) {
  if (error0) {
    throw error0;
  }
  connection.createChannel(function(error1, channel) {
    if (error1) {
      throw error1;
    }

    const queue = 'hello';

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

    console.log(" [*] Waiting for messages in %s. To exit press CTRL+C", queue);

    channel.consume(queue, function(msg) {
      console.log(" [x] Received %s", msg.content.toString());
    }, {
      noAck: true
    });
  });
});

דוגמה לפרויקט: מערכת עיבוד הזמנות

בואו נבחן דוגמה לפרויקט שבו נדרש להשתמש ב-RabbitMQ – מערכת עיבוד הזמנות למסחר אלקטרוני. במערכת זו יש מספר שירותים (microservices) שכל אחד מהם מטפל בחלק אחר מהתהליך, והם מתקשרים ביניהם באמצעות RabbitMQ.

תיאור הפרויקט

הפרויקט כולל שלושה שירותים עיקריים:

  1. Order Service: שירות שמקבל ומנהל הזמנות מלקוחות.
  2. Payment Service: שירות שמטפל בתשלומים של ההזמנות.
  3. Inventory Service: שירות שמטפל במלאי ומעדכן את המלאי כאשר מבוצעת הזמנה.

זרימת התהליך

  1. קבלת הזמנה: הלקוח מבצע הזמנה דרך ה-Order Service.
  2. תשלום: ה-Order Service שולח הודעה ל-Payment Service כדי לבצע את התשלום.
  3. עדכון מלאי: לאחר שהתשלום מתקבל, ה-Payment Service שולח הודעה ל-Inventory Service כדי לעדכן את המלאי.

הגדרת RabbitMQ

נשתמש ב-RabbitMQ כדי לתווך בין השירותים, כאשר כל שירות ישלח ויקבל הודעות מתורים שונים.

התקנת RabbitMQ

docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3-management

דוגמה למימוש

Order Service (Producer)

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

function sendOrder(order) {
  amqp.connect('amqp://localhost', function(error0, connection) {
    if (error0) {
      throw error0;
    }
    connection.createChannel(function(error1, channel) {
      if (error1) {
        throw error1;
      }

      const queue = 'order_queue';
      const msg = JSON.stringify(order);

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

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

    setTimeout(function() {
      connection.close();
    }, 500);
  });
}

const order = { orderId: 1, customerId: 123, items: [{ productId: 456, quantity: 2 }] };
sendOrder(order);

Payment Service (Consumer & Producer)

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

function processPayment(order) {
  // תהליך התשלום (לצורך הדוגמה נניח שהתשלום תמיד מצליח)
  console.log("Processing payment for order", order.orderId);
  return true;
}

amqp.connect('amqp://localhost', function(error0, connection) {
  if (error0) {
    throw error0;
  }
  connection.createChannel(function(error1, channel) {
    if (error1) {
      throw error1;
    }

    const queue = 'order_queue';

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

    channel.consume(queue, function(msg) {
      const order = JSON.parse(msg.content.toString());
      console.log(" [x] Received %s", order);

      const paymentSuccess = processPayment(order);
      if (paymentSuccess) {
        const inventoryQueue = 'inventory_queue';
        channel.assertQueue(inventoryQueue, {
          durable: true
        });

        channel.sendToQueue(inventoryQueue, Buffer.from(msg.content));
        console.log(" [x] Sent to inventory %s", msg.content.toString());
      }
    }, {
      noAck: true
    });
  });
});

Inventory Service (Consumer)

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

function updateInventory(order) {
  // עדכון המלאי
  console.log("Updating inventory for order", order.orderId);
}

amqp.connect('amqp://localhost', function(error0, connection) {
  if (error0) {
    throw error0;
  }
  connection.createChannel(function(error1, channel) {
    if (error1) {
      throw error1;
    }

    const queue = 'inventory_queue';

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

    channel.consume(queue, function(msg) {
      const order = JSON.parse(msg.content.toString());
      console.log(" [x] Received %s", order);

      updateInventory(order);
    }, {
      noAck: true
    });
  });
});

בפרויקט זה, ניתן לראות כיצד ניתן להשתמש ב-RabbitMQ כדי לתווך בין שירותים שונים במערכת עיבוד הזמנות למסחר אלקטרוני. ה-Order Service שולח הודעות על הזמנות חדשות, ה-Payment Service מטפל בתשלומים ושולח הודעות על תשלומים שהתקבלו ל-Inventory Service, שמעדכן את המלאי בהתאם.

שימושים נפוצים ב-RabbitMQ

  1. פיזור עומסים (Load Balancing):
    • RabbitMQ יכול לשמש לפיזור עומסים בין מספר צרכנים (consumers), מה שמאפשר טיפול בעומסים כבדים בצורה יעילה.
  2. תורים מתוזמנים (Delayed Messaging):
    • ניתן להגדיר תורים מתוזמנים לשליחת הודעות במועדים עתידיים, לדוגמה שליחת מיילים אחרי זמן מסוים.
  3. עיבוד נתונים מבוזר (Distributed Processing):
    • שימוש ב-RabbitMQ לעיבוד מבוזר של נתונים בין מספר שירותים, מה שמאפשר סקלאביליות גבוהה יותר.
  4. אינטגרציה בין מערכות (System Integration):
    • RabbitMQ משמש כמתווך בין מערכות שונות, מה שמקל על האינטגרציה והתקשורת ביניהן.

ניהול RabbitMQ

RabbitMQ מגיע עם ממשק ניהול מתקדם שמאפשר לצפות במצב המערכת, לנהל תורים, exchanges, וצרכנים, ולבצע פעולות נוספות. ניתן לגשת לממשק הניהול בכתובת http://localhost:15672 ולהתחבר עם שם המשתמש והסיסמה guest.

סיכום

RabbitMQ הוא כלי רב עוצמה לתקשורת בין שירותים ויישומים. במאמר זה סקרנו את העקרונות הבסיסיים של RabbitMQ, ראינו כיצד להתקין ולהגדיר אותו, ולמדנו כיצד להשתמש בו לשליחת וקבלת הודעות. היתרונות של RabbitMQ כוללים פיזור עומסים, עיבוד נתונים מבוזר ואינטגרציה בין מערכות, מה שהופך אותו לכלי חשוב בכל ארכיטקטורת מיקרו-שירותים (Microservices).

בזכות היכולות המתקדמות והקלות שבה ניתן להשתמש בו, RabbitMQ הוא בחירה מצוינת לכל פרויקט שדורש תקשורת אמינה ויעילה בין רכיבים שונים.

שתפו את הפוסט

צרו קשר

דילוג לתוכן