برنامه نویسی

شکستن سد مقیاس: 1 میلیون پیام با NodeJS و Kafka

آپاچی کافکا یک پلتفرم پخش رویداد توزیع شده است که می تواند به عنوان یک کارگزار Pub-Sub در میکروسرویس های NodeJS استفاده شود. اخیراً در پروژه‌ای کار کردم که در آن ناشر باید به 1 میلیون کاربر پیام بفرستد و پاسخ را پس دهد. در اینجا یک کار جالب می تواند توسط Message Queue انجام شود و ما می توانیم 1 میلیون پیام را در یک صف ارسال کنیم و پیام ها را یکی یکی برای کاربران ارسال می کند. اما، یک pub-sub می‌تواند برای همه موارد مصرف‌کننده که مشترک یک موضوع خاص هستند، پخش کند.

جادوی صف های پیام در مقابل Pub-Sub در کافکا

هنگامی که با چنین مقیاس عظیمی از پیام‌رسانی مواجه می‌شویم، اغلب دو رویکرد به ذهن متبادر می‌شوند:

  1. صف پیام: ارسال 1 میلیون پیام به یک صف، جایی که هر پیام توسط مصرف کنندگان پردازش می شود.

  2. Pub-Sub: یک پیام واحد را برای همه موارد مصرف کنندگان مشترک در یک موضوع خاص پخش کنید.

برای مورد استفاده ما، انعطاف پذیری کافکا در مدیریت هر دو الگو، آن را به انتخاب ایده آل تبدیل کرده است. بیایید به نحوه پیاده‌سازی سیستم میکروسرویس تولیدکننده-مصرف کننده با کافکا بپردازیم.

کد دمو را بررسی کنید: https://github.com/fahadfahim13/producer-consumer-kafka.git

معماری

NodeJS Microservice با کافکا

میکروسرویس تولید کننده:

بیایید یک میکروسرویس تولیدکننده و یک میکروسرویس مصرف کننده ایجاد کنیم.
کد در producer/index.js به این صورت خواهد بود:

const { Kafka } = require('kafkajs');

const kafka = new Kafka({
  clientId: 'my-producer',
  brokers: [process.env.KAFKA_BROKER],
});

const producer = kafka.producer();

(async () => {
  await producer.connect();
  for (let i = 0; i < 1000000; i++) {
    await producer.send({
      topic: process.env.TOPIC_NAME,
      messages: [{ value: `Message ${i}` }],
    });
    console.log(`Sent message ${i}`);
  }
  await producer.disconnect();
})();
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در اینجا، می‌توانیم ببینیم که تهیه‌کننده در حال تولید 1 میلیون پیام و ارسال به یک موضوع کافکا است.

ریز سرویس مصرف کننده

کد در میکروسرویس مصرف کننده index.js فایل به این صورت خواهد بود:

const { Kafka } = require('kafkajs');

const kafka = new Kafka({
  clientId: 'my-consumer',
  brokers: [process.env.KAFKA_BROKER],
});

const consumer = kafka.consumer({ groupId: 'test-group' });

(async () => {
  await consumer.connect();
  await consumer.subscribe({ topic: process.env.TOPIC_NAME, fromBeginning: true });

  await consumer.run({
    eachMessage: async ({ topic, partition, message }) => {
      console.log(`Received message: ${message.value} in topic: ${topic} at partition: ${partition}`);
    },
  });
})();
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

مصرف کنندگان در یک موضوع کافکا مشترک می شوند و پیام ها را به صورت ناهمزمان دریافت می کنند و آن پیام ها را پردازش می کنند.

فایل Docker-compose

فایل docker-compose.yml شبیه این است که خدمات باغ وحش، کافکا و تولید کننده و مصرف کننده را در کانتینرهای مختلف اجرا می کند.

version: '3.8'
services:
  zookeeper:
    image: confluentinc/cp-zookeeper:latest
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181
      ZOOKEEPER_TICK_TIME: 2000
    ports:
      - "22181:2181"
    networks:
      - kafka-network
    volumes:
      - kafka_data:/var/lib/kafka/data

  kafka:
    image: confluentinc/cp-kafka:latest
    depends_on:
      - zookeeper
    ports:
      - "29092:29092"
    environment:
      KAFKA_BROKER_ID: 1
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092,PLAINTEXT_HOST://localhost:29092
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
      KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
    networks:
      - kafka-network

  producer:
    build: 
      context: ./producer
      dockerfile: Dockerfile
    container_name: kafka-producer
    depends_on:
      - kafka
    networks:
      - kafka-network
    volumes:
      - ./producer:/app
    environment:
      KAFKA_BOOTSTRAP_SERVERS: kafka:9092
      KAFKA_BROKER: kafka:9092
      TOPIC_NAME: my-topic

  consumer:
    build: 
      context: ./consumer
      dockerfile: Dockerfile
    container_name: kafka-consumer
    depends_on:
      - kafka
    networks:
      - kafka-network
    volumes:
      - ./consumer:/app
    environment:
      KAFKA_BOOTSTRAP_SERVERS: kafka:9092
      KAFKA_BROKER: kafka:9092
      TOPIC_NAME: my-topic

networks:
  kafka-network:
    driver: bridge


volumes:
  kafka_data:
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بهینه سازی عملکرد کافکا با پارتیشن ها

برای مدیریت توان عملیاتی بالا، افزایش تعداد پارتیشن‌ها در موضوع کافکا ضروری است. پارتیشن‌های بیشتر امکان پردازش موازی را فراهم می‌کند و تولیدکنندگان و مصرف‌کنندگان را قادر می‌سازد تا به صورت افقی مقیاس شوند. به عنوان مثال:

kafka-topics --create \
  --zookeeper localhost:2181 \
  --replication-factor 1 \
  --partitions 10 \
  --topic my-topic
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

هر پارتیشن به عنوان یک صف جداگانه عمل می کند و بار را بین مصرف کنندگان توزیع می کند.

کد دمو را بررسی کنید: https://github.com/fahadfahim13/producer-consumer-kafka.git

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا