Building Blocks১১ মিনিট পাঠ

মেসেজ কিউ (Message Queues)

সবকিছু এখনই করবেন না — লাইনে দাঁড় করিয়ে দিন
scope:বিল্ডিং ব্লকdifficulty:মাঝারি

মেসেজ কিউ (Message Queues) কেন ব্যবহার করা হয়?

কল্পনা করুন আপনি বিয়েবাড়িতে বা বড় কোনো খাবারের হোটেলে আছেন। আপনি যখন খাবার অর্ডার দেন, ওয়েটার কিন্তু চুলার সামনে দাঁড়িয়ে আপনার খাবার তৈরি হওয়ার জন্য অপেক্ষা করেন না। তারা আপনার অর্ডারটি একটি টিকিটে লিখে লাইনে দাঁড় করিয়ে দেন। এরপর বাবুর্চি বা কুকরা ক্রমানুসারে টিকিটগুলো স্ক্যান করে এবং অর্ডারগুলো তৈরি করতে থাকেন। আর এদিকে ওয়েটার আবার নতুন কোনো গ্রাহকের অর্ডার নিতে ফিরে যেতে পারেন।

সেই টিকিটের লাইনটিই হলো একটি মেসেজ কিউ (message queue)। এটি সেন্ডার (ওয়েটার) এবং রিসিভারকে (বাবুর্চি) আলাদা করে বা ডিকাপল (decouple) করে দেয় যাতে তারা উভয়েই নিজের মতো করে কাজ চালিয়ে যেতে পারেন।

সফটওয়্যারের ভাষায় মেসেজ কিউ মূলত বিভিন্ন সার্ভিসগুলোর মাঝখানে কাজ করে। সার্ভিস A সরাসরি সার্ভিস B কে কল করে কোনো রেসপন্সের জন্য অপেক্ষা করার পরিবর্তে, সার্ভিস A একটি মেসেজ ড্রপ করে কিউতে দিয়ে দেয় এবং অন্য কাজে চলে যায়। এরপর যখন সার্ভিস B ফ্রি হয়, তখন সে ওই মেসেজটি পিক করে নেয়।

এই প্যাটার্নটি তিনটি গুরুত্বপূর্ণ সমস্যার সমাধান করে:

  • ডিকাপলিং (Decoupling) — সার্ভিসগুলোকে একে অপরের সম্পর্কে বিস্তারিত জানার কোনো দরকার নেই। সার্ভিস A শুধু একটি মেসেজ পাঠায়; এই মেসেজটি কে প্রসেস করবে সেটি জানার তার কোনো দরকার নেই।
  • বাফারিং (Buffering) — যদি হঠাৎ ট্র্যাফিক বেড়ে যায়, তবে কিউ সেই চাপ সামলে নিতে পারে। কনজিউমাররা বাফারিংয়ের কোনো সমস্যা ছাড়াই নিজেদের মতো করে ডেটা প্রসেস করতে পারে।
  • নির্ভরযোগ্যতা (Reliability) — যদি সার্ভিস B ক্র্যাশ করে, তবে মেসেজগুলো নিরাপদে কিউতে স্টোর হয়ে থাকে। কোনো ডেটাই হারায় না।
ডিরেক্ট সংযোগে সমস্যা
সার্ভিস ডিকাপলিং এর জন্য একটি কিউ ব্যবহার

মেসেজিং প্যাটার্নসমূহ

পয়েন্ট-টু-পয়েন্ট (Queue)

একজন প্রডিউসার (producer) একটি মেসেজ পাঠায় এবং ঠিক একজন কনজিউমার (consumer) সেটি গ্রহণ করে। এটিকে আপনি টাস্ক কিউ এর মতো ভাবতে পারেন — একবার একজন কর্মী একটি টাস্ক গ্রহণ করলে, অন্য কোনো কর্মী সেটি আর পায় না। জব প্রসেসিং (job processing), অর্ডার হ্যান্ডলিং বা যেকোনো কাজের ক্ষেত্রে এটি দুর্দান্ত, যেখানে কাজটি ঠিক একবারই করা উচিত।

পাবলিশ/সাবস্ক্রাইব (Pub/Sub)

একজন প্রডিউসার একটি মেসেজ টপিক (topic) বা ক্যাটাগরিতে পাবলিশ করে এবং সকল সাবস্ক্রাইবার তার একটি করে কপি পায়। এটি রেডিও সম্প্রচারের মতো — চ্যানেলে টিউন করা সবাই মেসেজটি শুনতে পায়। ইভেন্ট নোটিফিকেশন, রিয়েল-টাইম আপডেট বা ফ্যান-আউট প্রসেসিংয়ের জন্য এটি দুর্দান্ত।

উদাহরণস্বরূপ, যখন কোনো ব্যবহারকারী একটি ছবি আপলোড করেন:

  • ইমেজ সার্ভিস একটি টপিকে "photo-uploaded" নামের একটি ইভেন্ট পাবলিশ করে
  • থাম্বনেইল (thumbnail) সার্ভিসটি এটি সাবস্ক্রাইব করে এবং ছবির থাম্বনেইল জেনারেট করে
  • নোটিফিকেশন সার্ভিস সাবস্ক্রাইব করে এবং ব্যবহারকারীর ফলোয়ারদের অ্যালার্ট করে
  • অ্যানালিটিক্স সার্ভিস এই ইভেন্টটি সাবস্ক্রাইব করে এবং লগ স্টোর করে রাখে

প্রতিটি সার্ভিস স্বাধীনভাবে কাজ করে। নতুন কোনো সাবস্ক্রাইবার যুক্ত করতে চাইলে (যেমন কোনো মডারেশন সার্ভিস) পাবলিশার অংশে কোনো পরিবর্তন করার প্রয়োজন পড়ে না।

বাফারিং দিয়ে ট্রাফিকের চাপ সামলানো
Pub/Sub ফ্যান-আউট প্যাটার্ন
Click chart to zoom
পয়েন্ট-টু-পয়েন্ট: প্রডিউসার মেসেজ পাঠায়, কিউ তা সংরক্ষণ করে, কনজিউমার তা প্রসেস করে এবং প্রাপ্তিস্বীকার (acknowledge) করে
Click chart to zoom
Pub/Sub ফ্যান-আউট: একটি ইভেন্ট প্রতিটি সাবস্ক্রাইব করা সার্ভিসের কাছে স্বাধীনভাবে ডেলিভারি করা হয়

একটি সাধারণ কিউ সহ Producer/Consumer প্যাটার্ন

import json
import time
import threading
from queue import Queue
# Simulating a message queue (use Kafka/RabbitMQ/SQS in production)
message_queue = Queue()
def producer(orders: list[dict]):
"""Restaurant waiter — takes orders and adds them to the queue."""
for order in orders:
message = json.dumps(order)
message_queue.put(message)
print(f"[Producer] Order placed: {order['item']}")
time.sleep(0.1)
def consumer(name: str):
"""Chef — pulls orders from the queue and processes them."""
while True:
message = message_queue.get()
if message == "STOP":
break
order = json.loads(message)
print(f" [{name}] Cooking: {order['item']} for {order['customer']}")
time.sleep(0.3) # Simulate cooking time
print(f" [{name}] Done: {order['item']}!")
message_queue.task_done()
# Start 2 cooks (consumers)
for i in range(2):
t = threading.Thread(target=consumer, args=(f"Cook-{i+1}",), daemon=True)
t.start()
# Place orders (producer)
orders = [
{"item": "Burger", "customer": "Anika"},
{"item": "Pizza", "customer": "Rafi"},
{"item": "Salad", "customer": "Sadia"},
{"item": "Taco", "customer": "Tariq"},
]
producer(orders)
message_queue.join() # Wait until all orders are processed
print("\nAll orders completed!")
Output
[Producer] Order placed: Burger
[Producer] Order placed: Pizza
  [Cook-1] Cooking: Burger for Anika
  [Cook-2] Cooking: Pizza for Rafi
[Producer] Order placed: Salad
[Producer] Order placed: Taco
  [Cook-1] Done: Burger!
  [Cook-1] Cooking: Salad for Sadia
  [Cook-2] Done: Pizza!
  [Cook-2] Cooking: Taco for Tariq
  [Cook-1] Done: Salad!
  [Cook-2] Done: Taco!

All orders completed!

জনপ্রিয় মেসেজ ব্রোকারসমূহ

Apache Kafka — একটি ডিস্ট্রিবিউটেড স্ট্রিমিং প্ল্যাটফর্ম যা হাই থ্রুপুট বা উচ্চ ক্ষমতা সরবরাহের জন্য তৈরি। মেসেজগুলো লগ (log) আকারে সংরক্ষিত হয় এবং এগুলো টপিক এবং পার্টিশনে বিভক্ত থাকে। কনজিউমাররা তাদের পজিশন (offset) ট্র্যাক করে রাখে এবং মেসেজগুলো পরে পুনরায় প্লে করতে পারে। এটি প্রতি সেকেন্ডে লক্ষ লক্ষ মেসেজ পরিচালনা করতে পারে এবং দিন বা সপ্তাহ ধরে ডেটা ধরে রাখতে পারে। মূলত বা উপযুক্ত ব্যবহার: ইভেন্ট স্ট্রিমিং, লগ এগ্রিগেশন, রিয়েল-টাইম অ্যানালিটিক্স।

RabbitMQ — একটি সাধারণ মেসেজ ব্রোকার যা যেকোনো জটিল রাউটিংয়ে অসাধারণ কাজ করে। এটি ডিরেক্ট, টপিক, ফ্যান-আউট, এবং হেডার্স এক্সচেঞ্জ ইত্যাদির মতো মাল্টিপল মেসেজিং প্যাটার্ন সমর্থন করে। কনজিউম করার পরে মেসেজগুলো মুছে ফেলা হয়। উপযুক্ত ব্যবহার: টাস্ক কিউ, রিকোয়েস্ট-রিপ্লাই প্যাটার্ন, জটিল রাউটিং লজিক।

Amazon SQS — এটি AWS থেকে সম্পূর্ণ নিয়ন্ত্রিত একটি কিউ পরিষেবা। এতে কোনো ইনফ্রাস্ট্রাকচার বা পরিকাঠামো পরিচালনা করার প্রয়োজন নেই। মূলত ২ ধরণের SQS রয়েছে: Standard (বেস্ট-এফর্ট অর্ডারিং, অন্তত একবার ডেলিভারি) এবং FIFO (স্ট্রিক্ট অর্ডারিং, যথাযথ একবার প্রসেসিং)। উপযুক্ত ব্যবহার: সহজ ক্লাউড ওয়ার্কলোড বা কাজ, সার্ভারলেস আর্কিটেকচার, এমন টিম যে ইনফ্রাস্ট্রাকচার মেইন্টেইন করতে চায় না।

সংক্ষিপ্ত তুলনা:

  • Throughput (থ্রুপুট): Kafka >> RabbitMQ > SQS
  • Message replay (মেসেজ রিপ্লে): Kafka (হ্যাঁ) এবং RabbitMQ/SQS (না, কনজিউম করার পর মুছে ফেলা হয়)
  • Complexity (জটিলতা): Kafka (খুব বেশি) বনাম RabbitMQ (মাঝারি) বনাম SQS (খুব কম)
  • Managed option (নিয়ন্ত্রিত বিকল্প): SQS (সম্পূর্ণ), Kafka (Confluent Cloud, AWS MSK), RabbitMQ (Amazon MQ)

ডেলিভারি গ্যারান্টি (Delivery Guarantees)

একজন কনজিউমার (consumer) প্রতিটি মেসেজ কতবার গ্রহণ করে? ডিস্ট্রিবিউটেড সিস্টেমের অন্যতম কঠিন সমস্যা এটি।

সর্বোচ্চ একবার (At-most-once): মেসেজটি শূন্য বা একবার ডেলিভারি করা হয়। যদি কিছু ভুল হয়ে যায় তবে মেসেজটি হারিয়ে যায়। এটি দ্রুত কিন্তু অনির্ভরযোগ্য। একটি পোস্টকার্ড পাঠানোর মতো — এটি পৌঁছাতেও পারে নাও পারে। আপনি কখনও তা জানতে পারবেন না।

অন্তত একবার (At-least-once): মেসেজটি এক বা একাধিকবার ডেলিভারি করা হয়। কনজিউমার স্বীকার করার আগেই ক্র্যাশ করলে মেসেজটি পুনরায় ডেলিভারি করা হয়। আপনি এটি দুবার প্রসেস করতে পারেন, তাই আপনার কনজিউমারকে অবশ্যই ইডম্পোটেন্ট (idempotent) হতে হবে (একই মেসেজ দুবার প্রসেস করলে এবং একবার পড়লে এর ফলাফল একই হবে)। এটি সবচেয়ে প্রচলিত ডেলিভারি গ্যারান্টি।

যথাযথ একবার (Exactly-once): এর সবচেয়ে বড় চ্যালেঞ্জ হলো প্রতিটি মেসেজ কেবল ঠিক একবারই ডেলিভারি করা হয়। ডিস্ট্রিবিউটেড সিস্টেমগুলোতে এটি করা বেশ কঠিন। এটি অর্জনের জন্য ইডম্পোটেন্ট প্রডিউসার এবং ট্রানজাকশনাল কনজিউমার ব্যবহার করা হয়। Kafka তে এই বৈশিষ্ট্য আছে, তবে এটি সিস্টেমে পারফরম্যান্সে কিছুটা প্রভাব ফেলে।

বাস্তবে, অধিকাংশ সিস্টেম idempotent consumers-দের সাথে at-least-once ডেলিভারি ব্যবহার করে। উদাহরণস্বরূপ, একটি পেমেন্ট প্রসেস করার সময় কার্ড থেকে পুনরায় চার্জ কাটার আগে চেক করুন পেমেন্ট আইডিটি ইতোমধ্যে রিড করা হয়েছে কিনা।

Note: মেসেজ কিউ-এর সাথে সঠিকভাবে কাজ করার জন্য আপনার কনজিউমারকে idempotent করে তোলা সবচেয়ে গুরুত্বপূর্ণ বিষয়। একটি সহজ কৌশল: যেসকল মেসেজ ইতোমধ্যেই প্রসেস করা হয়েছে তাদের ID গুলো স্টোর করে রাখা। কোনো মেসেজ প্রসেস করার আগে চেক করা যে এটি আগে দেখা হয়েছে কিনা। যদি হ্যাঁ হয়, তবে এটি এড়িয়ে যান বা স্কিপ করুন। এটি মূলত 'at-least-once' কে 'effectively-exactly-once'-এ পরিণত করে।
ব্যর্থ মেসেজের জন্য Dead Letter Queue

ডেড লেটার কিউ (Dead Letter Queues)

মেসেজগুলো প্রসেস হতে ব্যর্থ হলে কী হয়? ডেটা হয়তো বিকৃত বা ত্রুটিপূর্ণ হতে পারে, অথবা কোনো ডিপেন্ডেন্সি হয়তো সাময়িক সময়ের জন্য স্থগিত বা ডাউন হয়ে গেছে। আপনি যদি বারবার পুনরায় চেষ্টা করতে থাকেন, তবে খারাপ মেসেজগুলো পুরো কিউ আটকে বা ব্লক করে দিতে পারে — আর এটাই হলো একটি পয়জন পিল (poison pill)

এর সমাধান: একটি ডেড লেটার কিউ (Dead Letter Queue, DLQ)। একটি মেসেজ N বার (যেমন, ৩টি চেষ্টা) ফেইল করার পর, এটি পরীক্ষা করার জন্য অন্য একটি কিউ-তে সরিয়ে নেওয়া হয়। মেইন কিউ এর কাজ চলতে থাকে এবং কোনো প্রকৌশলী পরবর্তীতে ডেড লেটার কিউ টি পরীক্ষা করে এর সমস্যা সমাধানের ব্যবস্থা নিতে পারেন।

প্রোডাকশন বা লাইভ সিস্টেমের জন্য DLQ অপরিহার্য। এটি একটি মাত্র খারাপ মেসেজের জন্য পুরো সিস্টেম থামিয়ে দেওয়া থেকে রক্ষা করে।

ব্যাকপ্রেসার (Backpressure)

কনজিউমার যে গতিতে মেসেজগুলো প্রসেস করছে, প্রডিউসার যদি কনজিউমারদের চেয়ে দ্রুত মেসেজ জেনারেট করে তবে কী হবে? মেমোরির ডিস্ক ফুরিয়ে না যাওয়া পর্যন্ত কিউ এর আকার বা সাইজ বাড়তেই থাকে। এটিকে ব্যাকপ্রেসার (backpressure) সমস্যা বলা হয়।

এটি সমাধানের জন্য কিছু কৌশল বা স্ট্রাটেজি ব্যবহার করা যেতে পারে:

  • মেসেজ বাতিল (Drop messages): যখন কিউ ভর্তি হয়ে যায়, তখন নতুন মেসেজ প্রত্যাখ্যান বা বাতিল করা। এটি করা সহজ কিন্তু এর ফলে ডেটা হারিয়ে যায়। ম্যাট্রিক্স বা লগের জন্য এটি ওকে হলেও, অর্ডারের জন্য একদমই ওকে না।
  • প্রডিউসারকে ব্লক করে দেওয়া: মেসেজ রাখার জায়গা তৈরি না হওয়া পর্যন্ত প্রডিউসারকে অপেক্ষা করিয়ে রাখা। এতে সিস্টেম সামগ্রিকভাবে ধীরগতিতে কাজ করে এবং ক্যাসকেডিং (cascading slow downs) সমস্যা সৃষ্টি করতে পারে।
  • কনজিউমারের স্টোরেজ বৃদ্ধি: যখন কিউয়ের গভীরতা বা ডেপথ (depth) একটি নির্দিষ্ট সীমা অতিক্রম করে, তখন স্বয়ংক্রিয়ভাবে আরও কনজিউমার যোগ করা। এটি সবচেয়ে প্রচলিত একটি ক্লাউড পদ্ধতি বা অপশন।
  • কিউ-এর সীমা নির্ধারণ করা: কিউ-এর সর্বোচ্চ সাইজ নির্ধারণ করে একটি 'overflow policy' (যেমন ডেড লেটার, পুরানো মেসেজ ফেলে দেওয়া ইত্যাদি) ব্যবহার করা।

কিউয়ের সাইজ মনিটর বা পর্যবেক্ষণ করা অত্যন্ত গুরুত্বপূর্ণ। যদি তা ক্রমাগত বাড়তে থাকে, তবে আপনার আরও কনজিউমার বা প্রোডিউসার প্রয়োজন। কারণ, যদি কিউ বাড়তেই থাকে তবে একসময় তা সিস্টেম ক্র্যাশ করতে বাধ্য করবে যা একটি টিকিং টাইম বোমার মতো।

Note: সাক্ষাৎকারের টিপস: সিস্টেম ডিজাইনের সময় খেয়াল রাখুন কোন কার্যকলাপগুলো অ্যাসিংক্রোনাস (অপেক্ষা ছাড়াই) হতে পারে। যেমন সাইন আপ করার পর একটি নিশ্চিতকরণ ইমেইল পাঠানো কিউতে রাখুন, আবার নতুন রিপোর্ট তৈরি করার কাজটিও কিউতে রাখুন। ভিডিও আপলোডের মতো ভারী কাজগুলোও কিউতে যুক্ত করুন। এক কথায়, যেই কাজের জন্য সাথে সাথে রেসপন্সের দরকার নেই তার সবই মেসেজ কিউ-র দারুণ প্রার্থী। এটি স্কেলেবিলিটির জন্য অপরিহার্য।

Key Metrics

Kafka রাইট (per msg)
লগ বা স্টোরে যুক্ত আছে, ব্যাচ করা
~০.৫-২ ms \(O(1)\)
Kafka রিড (per msg)
সিরিয়াল ডিস্ক রিড
~১-৫ ms \(O(1)\)
Kafka থ্রুপুট
একটি ক্লাস্টার বা জোট থেকে
১ মিলিয়ন+ msg/sec —
RabbitMQ থ্রুপুট
Per node বা প্রতিটি নোড থেকে
~৫০ হাজার msg/sec —
SQS থ্রুপুট
প্রতিটি কিউ এর ক্ষেত্রে, সাধারণগুলোতে আনলিমিটেড
~৩ হাজার msg/sec (FIFO) —
মেসেজ রিটেনশন (Kafka)
কনফিগার বা পরিবর্তনযোগ্য
কয়েক দিন বা সপ্তাহ —
মেসেজ রিটেনশন (SQS)
এরপর স্বয়ংক্রিয়ভাবে মুছে ফেলা হয়
১৪ দিন পর্যন্ত —

ছোট কুইজ

একটি pub/sub সিস্টেমে একটি 'photo-uploaded' ইভেন্ট পাবলিশ হয়েছে। কোন সার্ভিসগুলো এটি পাবে?

পড়া চালিয়ে যান