Server-Sent Events ও পোলিং বিকল্প
এই পাঠে যা শিখবেন
- Polling কীভাবে কাজ করে — একটি সত্যিকারের timer-loop সিমুলেশন
- Server-Sent Events (SSE) কী — polling ও WebSocket-এর মাঝামাঝি একটি সমাধান
- একই ইভেন্ট সিকোয়েন্সে polling ও push পদ্ধতির প্রকৃত নেটওয়ার্ক-কল সংখ্যা গণনা করে তুলনা করা
- কখন polling-এর সরলতা যথেষ্ট, আর কখন push-ভিত্তিক পদ্ধতি সত্যিই প্রয়োজন
১ · Polling — ক্লায়েন্ট বারবার জিজ্ঞেস করে
PollingPollingক্লায়েন্ট নিয়মিত বিরতিতে বারবার সার্ভারকে "নতুন কিছু আছে?" জিজ্ঞেস করে — সার্ভার নিজে থেকে কখনো কিছু পাঠায় না। হলো real-time-এর সবচেয়ে সহজ সিমুলেশন — কোনো persistent connection বা special protocol লাগে না, ক্লায়েন্ট শুধু একটি টাইমারে নিয়মিত বিরতিতে সার্ভারকে জিজ্ঞেস করে "নতুন কিছু আছে?"। প্রতিটি জিজ্ঞাসাই একটি সম্পূর্ণ নেটওয়ার্ক রাউন্ড-ট্রিপ — উত্তর "না, কিছু নেই" হলেও।
২ · Server-Sent Events (SSE) — মাঝামাঝি একটি সমাধান
SSE সাধারণ HTTP-এর উপর একটি একমুখী (server → client) স্ট্রিম খোলে — ক্লায়েন্ট একবার কানেক্ট
করার পর সার্ভার সেই একই কানেকশনে বারবার নতুন ইভেন্ট পাঠাতে থাকে, ক্লায়েন্টকে বারবার নতুন রিকোয়েস্ট পাঠাতে হয়
না। এটি polling-এর চেয়ে দক্ষ (একটিমাত্র কানেকশন, বারবার রিকোয়েস্ট নয়) কিন্তু WebSocket-এর চেয়ে সহজ (শুধু
সার্ভার → ক্লায়েন্ট দিকে কাজ করে, সাধারণ HTTP-এর উপর তৈরি, ক্লায়েন্ট থেকে সার্ভারে বার্তা পাঠাতে হলে এখনো
আলাদা একটি রিকোয়েস্ট লাগে) — অনেকটা L45-এর Broker.publish()-এর মতোই আচরণ করে, শুধু দিক একমুখী।
এই সাব্যাক্স-এ যেহেতু সত্যিকারের HTTP কানেকশন সম্ভব নয়, নিচের কোড সেলে আমরা মূল তুলনাটা — push বনাম pull —
প্রকৃত কল-গণনা দিয়ে দেখাচ্ছি।
ক্লায়েন্ট বারবার নতুন রিকোয়েস্ট পাঠায়, প্রতিটি টিকে — সরল কিন্তু নষ্ট নেটওয়ার্ক কল বেশি।
একটি কানেকশনে সার্ভার একমুখী push করতে থাকে, HTTP-এর উপর — polling-এর চেয়ে দক্ষ, WebSocket-এর চেয়ে সহজ।
দ্বিমুখী, persistent কানেকশন — উভয় দিকেই যেকোনো সময় বার্তা যেতে পারে (L45)।
৩ · প্রকৃত কল-গণনা — Polling বনাম Push
নিচের কোড সেলে একই সিমুলেটেড ঘটনাপ্রবাহ — ১০টি টাইমার-টিক, যেখানে নতুন ডেটা শুধু টিক ৩ ও ৭-এ আসে — দুটো
ভিন্ন পদ্ধতিতে চালানো হয়েছে। প্রথমে polling: প্রতিটি টিকে check_for_updates() কল করা হয় (মোট
১০টি কল), তারপর L45-এর Broker পুনরায় ব্যবহার করে push: broker.publish() শুধু
তখনই কল হয় যখন সত্যিই নতুন ডেটা আছে (মোট ২টি কল)।
# ---------- একই ইভেন্ট সিকোয়েন্স -- নতুন ডেটা শুধু টিক ৩ ও ৭-এ ----------
updates_source = {
3: "নতুন অর্ডার: #1042",
7: "নতুন অর্ডার: #1043",
}
# ---------- পদ্ধতি ১ -- POLLING ----------
def check_for_updates(tick):
"""প্রতিটি কলই একটি প্রকৃত নেটওয়ার্ক রাউন্ড-ট্রিপ -- উত্তর None হলেও কলটা হয়েই গেছে।"""
return updates_source.get(tick) # নতুন কিছু না থাকলে None
poll_call_count = 0
poll_received = []
print("=== POLLING ===")
for tick in range(1, 11):
poll_call_count += 1 # প্রতিটি টিকেই একটি কল হয়, ফলাফল যা-ই হোক
result = check_for_updates(tick)
if result is not None:
poll_received.append((tick, result))
print(f"[Polling] টিক {tick}: নতুন ডেটা -> {result}")
else:
print(f"[Polling] টিক {tick}: কল করা হলো, কিছুই নতুন নেই")
print(f"\nমোট পোলিং নেটওয়ার্ক কল: {poll_call_count}")
print(f"এর মধ্যে আসলে দরকারি ছিল (নতুন ডেটা পাওয়া গেছে): {len(poll_received)}")
# ---------- পদ্ধতি ২ -- PUSH (L45-এর Broker পুনরায় ব্যবহার) ----------
class Broker:
def __init__(self):
self.subscribers = {}
def subscribe(self, channel, callback):
self.subscribers.setdefault(channel, []).append(callback)
def publish(self, channel, message):
for callback in self.subscribers.get(channel, []):
callback(message)
push_call_count = 0
def client_handler(message):
global push_call_count
push_call_count += 1
print(f"[Push] সার্ভার push করলো -> {message}")
broker = Broker()
broker.subscribe("orders", client_handler)
print("\n=== PUSH (Broker) ===")
for tick in range(1, 11):
if tick in updates_source:
broker.publish("orders", updates_source[tick]) # শুধু তখনই কল, যখন সত্যিই ডেটা আছে
# অন্য টিকগুলোতে broker.publish() একবারও কল করা হয়নি -- কোনো নেটওয়ার্ক কলই নেই
print(f"\nমোট push নেটওয়ার্ক কল (client_handler আসলে কতবার কল হয়েছে): {push_call_count}")
print(f"\n--- তুলনা ---")
print(f"Polling: {poll_call_count} টি কল, এর মধ্যে {len(poll_received)} টি দরকারি ছিল")
print(f"Push: {push_call_count} টি কল, সবগুলোই দরকারি ছিল")
poll_call_count ঠিক 10 হয়, কারণ for tick in range(1, 11) লুপে প্রতিটি
টিকেই poll_call_count += 1 চলে — check_for_updates(tick)-এর রিটার্ন মান
None হোক বা না হোক, গণনাটা লুপের প্রতিটি ধাপেই বাড়ে। অন্যদিকে push_call_count শুধু
তখনই বাড়ে যখন client_handler সত্যিই কল হয় — আর সেটা কল হয় শুধু if tick in
updates_source সত্য হলে, অর্থাৎ টিক ৩ ও ৭-এ — বাকি ৮টি টিকে broker.publish() একবারও কল
করা হয়নি, তাই সেই টিকগুলোতে কোনো নেটওয়ার্ক কল-ই নেই। ফলে push_call_count ঠিক 2
হয়, ঠিক যতগুলো টিকে updates_source-এ সত্যিই ডেটা ছিল।
Polling সরল ও stateless — কোনো persistent connection দরকার নেই — কিন্তু প্রতিটি টাইমার-টিকে একটি নেটওয়ার্ক
কল খরচ করে, উত্তর কিছু থাকুক বা না থাকুক। উপরের সিমুলেশনে ১০টি টিকে মাত্র ২বার আসল ডেটা থাকা সত্ত্বেও
polling ১০টি কল করেছে, যেখানে push (L45-এর Broker, বা বাস্তবে SSE/WebSocket) ঠিক ততবারই কল
হয়েছে যতবার সত্যিই কিছু ঘটেছে — মাত্র ২বার। যত বেশি সময় "কিছুই ঘটেনি" থাকে, polling তত বেশি অপচয় করে;
সেই ক্ষেত্রে push-ভিত্তিক পদ্ধতি (SSE বা WebSocket) বেছে নেওয়াই যুক্তিসঙ্গত। তবে ইভেন্ট যদি প্রায় প্রতি
টিকেই ঘটে, তাহলে polling-এর সরলতা push-এর অতিরিক্ত জটিলতার চেয়ে ভালো পছন্দ হতে পারে।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ কেন polling ১০টি কল করলো, অথচ পুরো সিকোয়েন্সে মাত্র ২বার নতুন ডেটা ছিল?
কারণ polling-এর কোড poll_call_count += 1-কে লুপের প্রতিটি পুনরাবৃত্তিতে চালায়,
check_for_updates(tick)-এর ফলাফল দেখার আগেই — অর্থাৎ কলটা হয়েই যায়, তারপর ফলাফল
None নাকি প্রকৃত ডেটা তা চেক করা হয়। Polling-এর সংজ্ঞাই এটা: "নিয়মিত বিরতিতে জিজ্ঞেস করা",
উত্তর যা-ই হোক না কেন — তাই প্রতিটি টিকই একটি কল গণনা করে, নতুন ডেটা থাকা-না-থাকা নির্বিশেষে।
প্র ০২
push পদ্ধতিতে broker.publish() মোট কতবার কল হয়েছে, আর সেটা
poll_call_count-এর সাথে কেন মেলে না?
broker.publish() কল হয়েছে ঠিক ২বার — if tick in updates_source শর্তটি শুধু
টিক ৩ ও ৭-এই সত্য হয়। বাকি ৮টি টিকে সেই if-এর ভেতরের কোড ব্লকটাই এক্সিকিউট হয়নি, তাই
broker.publish() কলই হয়নি। এটা poll_call_count-এর সাথে মেলে না, কারণ পোলিং
লুপে কলটা শর্তহীনভাবে (unconditionally) প্রতি টিকেই ঘটে, আর push লুপে কলটা শর্তসাপেক্ষে (conditionally)
শুধু ডেটা থাকলেই ঘটে — এটাই দুই পদ্ধতির মূল কাঠামোগত পার্থক্য।
প্র ০৩ SSE কীভাবে polling ও WebSocket-এর "মাঝামাঝি" একটি সমাধান?
SSE polling-এর চেয়ে দক্ষ, কারণ এটি একটিমাত্র দীর্ঘস্থায়ী HTTP কানেকশনে বারবার নতুন ইভেন্ট পাঠাতে পারে — পোলিং-এর মতো প্রতি টিকে নতুন কানেকশন/রিকোয়েস্ট লাগে না, তাই "কিছুই ঘটেনি"-তেও কোনো বাড়তি কল খরচ হয় না। আবার এটি WebSocket-এর চেয়ে সহজ, কারণ এটি একমুখী (শুধু server → client) এবং সাধারণ HTTP-এর উপর তৈরি — WebSocket-এর মতো সম্পূর্ণ দ্বিমুখী প্রোটোকল আপগ্রেড দরকার হয় না। যেসব ক্ষেত্রে শুধু সার্ভার থেকে ক্লায়েন্টে ডেটা যেতে হয় (যেমন লাইভ স্কোর আপডেট, নোটিফিকেশন), SSE প্রায়ই যথেষ্ট।
অনুশীলন
-
চিন্তা করুন: যদি polling-এর ব্যবধান দ্বিগুণ ঘন করা হয় (১০ বারের বদলে ২০ বার জিজ্ঞেস করা,
একই সময়ে একই ২টি আসল আপডেট থাকা অবস্থায়),
poll_call_countও push-এরpush_call_count-এর মধ্যে ব্যবধান কেমন বদলাবে?poll_call_countদ্বিগুণ হয়ে20-এ পৌঁছাবে (প্রতিটি টিকেই একটি কল, আর টিক সংখ্যা দ্বিগুণ), কিন্তুpush_call_countএকই থাকবে —2, কারণ push শুধু সত্যিকারের ইভেন্ট ঘটলেই কল হয়, টিক কত ঘন সেটার সাথে এর কোনো সম্পর্ক নেই। অর্থাৎ polling-কে আরও "রেসপন্সিভ" করতে গেলে অপচয়ও বাড়ে, push পদ্ধতির খরচ একই থাকে — এটাই polling-এর মৌলিক দুর্বলতা। -
পরীক্ষা করুন: উপরের কোড সেলে
updates_sourcedict-এ5: "নতুন অর্ডার: #1044"যোগ করুন (এখন তিনটি টিকে ডেটা আছে — ৩, ৫, ৭), Run চেপে দেখুনlen(poll_received)ওpush_call_countদুটোই এখন কত হয়।দুটোই
3হবে —poll_received-এ এখন তিনটি এন্ট্রি যোগ হবে (টিক ৩, ৫, ৭), আরpush_call_count-ও তিনবার বাড়বে (একই তিনটি টিকেtick in updates_sourceসত্য হবে)। তবেpoll_call_countএখনও10-ই থাকবে, কারণ পোলিং লুপ সবসময় দশটি টিকই চালায়, ডেটা কতবার এলো তার উপর নির্ভর করে না — এটাই দেখায় polling-এর কল-সংখ্যা ইভেন্টের সংখ্যা থেকে সম্পূর্ণ স্বাধীন, push-এর কল-সংখ্যা ঠিক ইভেন্টের সংখ্যার সমান।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পরবর্তী পাঠ: ফুল-স্ট্যাক অ্যাপের জন্য টেস্টিং পিরামিড L47 M10 শেষ হলো — এখন M11-এ আমরা দেখব কীভাবে এই কোর্সে তৈরি করা প্রতিটি প্যাটার্ন নিজেই টেস্ট করা যায়।
- আগের পাঠে ফিরে যান: WebSocket ও রিয়েল-টাইম কমিউনিকেশন L45 Broker ক্লাসের সম্পূর্ণ subscribe/publish নির্মাণ, একাধিক সাবস্ক্রাইবারের প্রকৃত মেসেজ ডেলিভারিসহ।
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৮টি পাঠ আর্কিটেকচার প্যাটার্ন, ফ্রন্ট-এন্ড/ব্যাক-এন্ড ফ্রেমওয়ার্ক ফান্ডামেন্টাল, স্টেট ম্যানেজমেন্ট, REST API, ORM, অথেন্টিকেশন, রেন্ডারিং স্ট্র্যাটেজি ও ডিপ্লয়মেন্ট।
- সব Courses দেখুন ABCL TECH C, C++, Python, Java, JavaScript, DSA, DBMS, Discrete Mathematics, System Design, Cybersecurity, Cloud Computing & DevOps, Computer Networks, Operating Systems, Computer Architecture, Programming Languages & Compiler Design, Software Engineering & Git, Theory of Computation, Engineering Economics ও Full-Stack Web Frameworks — সব এক জায়গায়।