ویرگول
ورودثبت نام
حامد پارسا
حامد پارسا
حامد پارسا
حامد پارسا
خواندن ۱۷ دقیقه·۲۱ ساعت پیش

معماری ارتباط

قبل از اینکه درباره HTTP، WebSocket یا هر تکنولوژی دیگری صحبت کنیم، بهتر است اول خود «معماری ارتباط» را بشناسیم.
چون تفاوت اصلی سیستم‌ها معمولاً نه در زبان برنامه‌نویسی است، نه در Framework؛ بلکه در مدلی است که برای ارتباط بین Client و Server انتخاب می‌کنند.

وقتی یک اپلیکیشن با سرور ارتباط برقرار می‌کند، چند سؤال بنیادی پشت صحنه وجود دارد:

  • چه کسی ارتباط را شروع می‌کند؟

  • ارتباط کوتاه‌مدت است یا دائمی؟

  • Server وضعیت Client را نگه می‌دارد یا نه؟

  • داده فقط هنگام درخواست ارسال می‌شود یا به‌صورت لحظه‌ای Push می‌شود؟

  • سیستم synchronous است یا asynchronous؟

پاسخ به همین سؤال‌هاست که معماری ارتباط را شکل می‌دهد.


Push در مقابل Pull

اولین تفاوت مهم بین معماری‌های ارتباطی این است که چه کسی شروع‌کننده ارتباط باشد.


معماری Pull

در مدل Pull، این Client است که درخواست ارسال می‌کند و Server فقط در پاسخ به همان درخواست داده برمی‌گرداند.

Client ---> Request ---> Server Client <--- Response --- Server

این همان مدلی است که سال‌ها پایه اصلی Web بوده است.

مثال‌ها:

  • HTTP

  • REST APIs

  • Polling

مزیت اصلی این مدل، سادگی و مقیاس‌پذیری بالاست.

اما یک محدودیت مهم دارد:

Server نمی‌تواند خودش داده‌ای ارسال کند مگر اینکه Client درخواست جدیدی بفرستد.

به همین دلیل realtime کردن سیستم‌هایی مثل Chat یا Live Dashboard روی معماری Pull سخت‌تر می‌شود.


معماری Push

در مدل Push، Server می‌تواند بدون درخواست جدید از سمت Client، داده را ارسال کند.

Server ---> Event/Data ---> Client

این مدل برای سیستم‌های realtime طراحی شده است.

مثال‌ها:

  • WebSocket

  • SSE

  • Firestore Realtime Listeners

در این معماری، Client معمولاً یک connection باز نگه می‌دارد و Server هر زمان event جدیدی رخ دهد، داده را Push می‌کند.

مزیت اصلی:

  • تاخیر (latency ) پایین

  • realtime واقعی

  • کاهش درخواست‌های غیرضروری

اما در عوض:

  • مدیریت connection پیچیده‌تر می‌شود

  • scaling سخت‌تر می‌شود

  • Server باید state بیشتری نگه دارد


Stateless در مقابل Stateful

یکی از مهم‌ترین تفاوت‌های معماری‌های ارتباطی، نحوه مدیریت state است.


معماری Stateless

در معماری Stateless، هر request مستقل از request قبلی است.

Server هیچ اطلاعاتی درباره ارتباط قبلی نگه نمی‌دارد.

Request #1 -> مستقل Request #2 -> مستقل Request #3 -> مستقل

مثال کلاسیک:

  • HTTP

  • REST APIs

مزیت بزرگ Stateless بودن:

  • Horizontal Scaling ساده‌تر

  • Load Balancing راحت‌تر

  • Fault tolerance بهتر

چون هر request می‌تواند توسط هر سروری پردازش شود.

به همین دلیل HTTP هنوز هم backbone بسیاری از سیستم‌های اینترنتی است.


Stateful Architecture

در معماری Stateful، Server وضعیت connection یا session را نگه می‌دارد.

Client <==== Persistent Connection ====> Server

مثال‌ها:

  • WebSocket

  • Realtime Sync Engines

  • Multiplayer Game Servers

در این مدل، ارتباط فقط یک request ساده نیست؛

بلکه یک session دائمی بین Client و Server شکل می‌گیرد.

مزایا:

  • realtime communication

  • synchronization

  • bidirectional messaging

اما هزینه‌هایی هم دارد:

  • memory usage بیشتر

  • scaling پیچیده‌تر

  • مدیریت connection lifecycle

  • نیاز به distributed coordination


Sync در مقابل Async

تمام ارتباط‌ها قرار نیست فوری و هم‌زمان باشند.


ارتباط Synchronous

در این مدل Client درخواست ارسال می‌کند و تا دریافت پاسخ منتظر می‌ماند.

Request ---> Wait ---> Response

این مدل ساده و قابل‌فهم است، اما برای عملیات سنگین یا طولانی مناسب نیست.


ارتباط Asynchronous

در معماری asynchronous، درخواست ثبت می‌شود اما پاسخ ممکن است بعداً پردازش شود.

Client -> Queue -> Worker -> Result

مثال‌ها:

  • Message Queues

  • Kafka

  • RabbitMQ

  • Event Bus Architectures

این مدل در سیستم‌های توزیع‌شده و scalable بسیار رایج است.


Persistent در مقابل Non-Persistent Connections

یکی دیگر از تفاوت‌های مهم، طول عمر connection است.


اتصال Non-Persistent

ارتباط فقط برای یک request ایجاد می‌شود و بعد بسته می‌شود.

Connect -> Request -> Response -> Close

مثل HTTP سنتی.


Persistent اتصال

ارتباط باز باقی می‌ماند و چندین پیام روی همان connection ردوبدل می‌شود.

Connect ======================= Active

مثل:

  • WebSocket

  • SSE

  • HTTP/2 Streams

این مدل latency را کاهش می‌دهد و برای realtime communication مناسب‌تر است، اما مدیریت آن پیچیده‌تر است.


در ادامه مقاله، تمام تکنولوژی‌های ارتباطی را با همین مدل ذهنی بررسی می‌کنیم؛

چون بدون درک این مفاهیم، HTTP فقط یک request است و WebSocket فقط یک socket.

اما وقتی این تفاوت‌های معماری روشن شوند، می‌توان فهمید که هر روش دقیقاً چه مسئله‌ای را حل می‌کند و چه trade-off هایی به همراه دارد.


اولین معماری غالب وب: HTTP Request/Response

قبل از اینکه Web تبدیل به چیزی شبیه اپلیکیشن‌های امروزی شود، اینترنت بر پایه یک مدل بسیار ساده ساخته شده بود:

Client ---> Request ---> Server Client <--- Response --- Server

Client درخواستی ارسال می‌کرد، Server پاسخ می‌داد و ارتباط تمام می‌شد. همین.

امروز این مدل آن‌قدر بدیهی به‌نظر می‌رسد که شاید ساده و ابتدایی دیده شود، اما واقعیت این است که بخش بزرگی از موفقیت و رشد اینترنت، نتیجه همین سادگی بود.


مدل معماری HTTP

HTTP بر پایه معماری Request/Response ساخته شده است.

یعنی:

  • Client همیشه شروع‌کننده ارتباط است

  • Server فقط در پاسخ به request عمل می‌کند

  • ارتباط معمولاً کوتاه‌مدت است

  • هر request مستقل از request قبلی است

مثلاً:

GET /products

یا:

POST /login

Server درخواست را پردازش می‌کند، response را برمی‌گرداند و connection بسته می‌شود.


Stateless بودن؛ مهم‌ترین ویژگی HTTP

یکی از مهم‌ترین ویژگی‌های معماری HTTP، Stateless بودن آن است.

یعنی Server به‌صورت پیش‌فرض چیزی از request قبلی به خاطر نمی‌سپارد.

Request #1 -> مستقل Request #2 -> مستقل Request #3 -> مستقل

هر request باید تمام اطلاعات لازم برای پردازش را همراه خودش داشته باشد.

مثلاً:

  • authentication token

  • session id

  • headers

  • request data

همه باید در همان request ارسال شوند.


چرا Stateless بودن مهم بود؟

این تصمیم معماری، یکی از دلایل اصلی scalability اینترنت شد.

وقتی Server نیازی به نگه‌داری state نداشته باشد:

  • هر request می‌تواند توسط هر server پردازش شود

  • load balancing ساده‌تر می‌شود

  • horizontal scaling راحت‌تر می‌شود

  • failure recovery بهتر انجام می‌شود

مثلاً اگر ۱۰ server داشته باشیم:

Client Request ↓ Load Balancer ↙ ↓ ↘ S1 S2 S3

هر request می‌تواند به هر node ارسال شود بدون اینکه server قبلی مهم باشد.

و این دقیقاً همان چیزی بود که Web برای رشد massive خودش نیاز داشت.


معماری Pull-Based

HTTP یک معماری Pull-based است.

یعنی همیشه Client باید درخواست جدیدی ارسال کند تا داده دریافت کند.

Client ---> Request ---> Server

Server نمی‌تواند خودش ارتباط را شروع کند یا داده‌ای ارسال کند.

این موضوع در سال‌های اولیه Web کاملاً منطقی بود، چون بیشتر صفحات فقط document بودند:

  • HTML page

  • image

  • form submission

اما بعدها همین ویژگی تبدیل به محدودیت شد.


مزایای معماری HTTP

سادگی HTTP یکی از بزرگ‌ترین نقاط قوت آن است.


سادگی توسعه

تقریباً تمام زبان‌ها و platformها از HTTP پشتیبانی می‌کنند.


مقیاس‌پذیری بالا

به دلیل Stateless بودن، scaling ساده‌تر است.


امکان Cache

HTTP برای caching طراحی بسیار خوبی دارد:

Cache-Control ETag Expires

و همین موضوع فشار روی server را کاهش می‌دهد.


سازگاری عالی با Infrastructure

تقریباً تمام زیرساخت اینترنت برای HTTP optimized شده است:

  • CDN

  • Reverse Proxy

  • Load Balancer

  • Browser

  • Firewall


محدودیت‌های معماری HTTP

با رشد Web، محدودیت‌های این معماری کم‌کم مشخص شد.


عدم پشتیبانی واقعی از Realtime

بزرگ‌ترین مشکل HTTP این بود:

Server بدون request جدید از سمت Client نمی‌تواند داده‌ای ارسال کند.

مثلاً اگر پیام جدیدی در Chat برسد، Server راه مستقیمی برای اطلاع دادن به Client ندارد.


Connection کوتاه‌مدت

در مدل سنتی HTTP:

Connect -> Request -> Response -> Close

بعد از هر response ارتباط بسته می‌شود.

برای ارتباط‌های مکرر، این موضوع overhead ایجاد می‌کند.


Latency بیشتر در ارتباط‌های تعاملی

برای هر interaction جدید:

  • connection

  • request

  • response

دوباره تکرار می‌شود.


نتیجه چه شد؟

وقتی Web از «صفحه‌های ساده» به سمت «اپلیکیشن‌های realtime» حرکت کرد، HTTP دیگر به‌تنهایی کافی نبود.

نیازهای جدیدی به وجود آمد:

  • Chat

  • Notification

  • Live Dashboard

  • Multiplayer Systems

  • Streaming Data

اما معماری HTTP برای این نوع ارتباط طراحی نشده بود.

در نتیجه توسعه‌دهنده‌ها شروع کردند به ساخت workaround هایی برای realtime communication.

اولین راه‌حل رایج: Polling بود؛

روشی ساده اما پرهزینه که تلاش می‌کرد محدودیت Pull-based بودن HTTP را دور بزند.

اولین تلاش برای Realtime کردن وب:‌ Polling

در Polling، کلاینت به‌صورت مداوم و در بازه‌های زمانی مشخص، درخواست جدید ارسال می‌کند تا بررسی کند آیا داده جدیدی وجود دارد یا نه.

مثلاً:

Every 5 seconds: GET /notifications

یا:

Every 2 seconds: GET /messages

معماری کلی:

Client ---> Request ---> Server Client <--- Response --- Server (wait) Client ---> Request ---> Server Client <--- Response --- Server

در واقع Client دائماً می‌پرسد:

«چیزی تغییر کرده؟»

Polling هنوز هم Pull-Based است

با اینکه هدف Polling ایجاد حس realtime است، اما از نظر معماری هنوز همان مدل Pull-based HTTP باقی می‌ماند.

یعنی:

  • ارتباط همیشه توسط Client شروع می‌شود

  • Server همچنان passive است

  • connection کوتاه‌مدت باقی می‌ماند

فقط تعداد requestها بیشتر شده است.


مزیت اصلی Polling

دلیل محبوب شدن Polling خیلی ساده بود:

پیاده‌سازی آن بسیار آسان بود.

بدون نیاز به:

  • protocol جدید

  • connection دائمی

  • infrastructure پیچیده

فقط کافی بود Client هر چند ثانیه یک request بفرستد.

به همین دلیل سال‌ها در بسیاری از سیستم‌ها استفاده می‌شد.


Use Case های رایج

Polling هنوز هم در بعضی سناریوها منطقی است.

مثلاً:

  • بررسی وضعیت Jobها

  • سیستم‌های ساده Notification

  • Admin Panels

  • Monitoring با حساسیت پایین

  • Synchronizationهای غیر realtime

اگر latency چندثانیه‌ای قابل‌قبول باشد، Polling می‌تواند انتخاب ساده و مناسبی باشد.


مشکل معماری Polling

مشکل اصلی Polling از جایی شروع می‌شود که scale بالا می‌رود.

فرض کنیم ۱۰۰ هزار Client داشته باشیم و هر Client هر ۵ ثانیه یک request ارسال کند.

100,000 clients × Request every 5s

حتی اگر هیچ داده جدیدی وجود نداشته باشد، Server همچنان باید requestها را پردازش کند.

یعنی بخش بزرگی از traffic کاملاً useless است.


مشکل شماره ۱: Requestهای غیرضروری

در بسیاری از مواقع response چیزی شبیه این است:

{ "updates": [] }

یعنی:

  • request ارسال شده

  • connection باز شده

  • server پردازش انجام داده

  • ولی هیچ اتفاقی نیفتاده

این یعنی:

  • مصرف CPU

  • مصرف bandwidth

  • فشار روی infrastructure

بدون value واقعی.


مشکل شماره ۲: Trade-off بین Latency و Cost

هرچه interval کوتاه‌تر باشد:

Polling every 1 second

Realtime بهتر می‌شود، اما:

  • تعداد requestها افزایش پیدا می‌کند

  • هزینه infrastructure بالا می‌رود

  • فشار روی backend بیشتر می‌شود

و اگر interval را زیاد کنیم:

Polling every 30 seconds

هزینه کمتر می‌شود، اما realtime بودن تقریباً از بین می‌رود.

پس Polling همیشه بین این دو گیر می‌کند:

Lower Latency Higher Cost

Higher Latency Lower Cost


مشکل شماره ۳: Connection Overhead

در HTTP سنتی، هر request معمولاً شامل:

  • TCP handshake

  • HTTP headers

  • authentication

  • request parsing

است.

وقتی requestها مدام تکرار شوند، این overhead بسیار noticeable می‌شود.


مشکل شماره ۴: Scalability

در مقیاس بالا، Polling می‌تواند backend را overload کند.

خصوصاً وقتی:

  • تعداد کاربران زیاد باشد

  • interval کوتاه باشد

  • responseها سنگین باشند

در چنین شرایطی، بخش بزرگی از منابع Server صرف بررسی requestهایی می‌شود که هیچ تغییر جدیدی ندارند.


از نگاه معماری

Polling در واقع تلاشی بود برای شبیه‌سازی realtime روی معماری‌ای که ذاتاً realtime نبود.

و همین موضوع باعث شد محدودیت‌های آن خیلی زود مشخص شود.


نتیجه چه شد؟

توسعه‌دهنده‌ها به دنبال راهی بودند که:

  • تعداد requestهای غیرضروری کمتر شود

  • latency کاهش پیدا کند

  • realtime طبیعی‌تر شود

اما هنوز نمی‌خواستند کاملاً از مدل HTTP خارج شوند.

نتیجه این تلاش‌ها: Long Polling بود؛

مدلی که سعی می‌کرد Server را کمی «فعال‌تر» کند بدون اینکه معماری Web کاملاً تغییر کند.

وقتی Server منتظر Event می‌ماند: Long Polling

Polling یک مشکل اساسی داشت:

Client مدام request ارسال می‌کرد، حتی وقتی هیچ داده جدیدی وجود نداشت.

این یعنی مقدار زیادی traffic و پردازش غیرضروری.

برای حل این مشکل، ایده جدیدی شکل گرفت:

«اگر Server به‌جای پاسخ فوری، کمی صبر کند چه؟»

و این دقیقاً پایه Long Polling بود.


Long Polling چگونه کار می‌کند؟

در Long Polling، Client درخواست ارسال می‌کند، اما اگر داده جدیدی وجود نداشته باشد، Server connection را باز نگه می‌دارد و فوراً پاسخ نمی‌دهد.

Client ---> Request ---> Server (wait...) Client <--- Response ---- Server

Server منتظر می‌ماند تا:

  • event جدیدی اتفاق بیفتد

  • داده‌ای تغییر کند

  • یا timeout رخ دهد

و بعد response را ارسال می‌کند.


تفاوت اصلی با Polling

در Polling معمولی:

Request -> Immediate Response

حتی اگر data جدیدی وجود نداشته باشد.

اما در Long Polling:

Request -> Wait Until Update

این تفاوت ساده، تعداد زیادی از requestهای useless را حذف می‌کرد.


جریان ارتباط در Long Polling

معمولاً روند به این شکل است:

1. Client sends request 2. Server waits 3. Event happens 4. Server responds 5. Client immediately reconnects

یعنی connection دائمی نیست، اما تقریباً همیشه یک request فعال وجود دارد.


آیا Long Polling realtime است؟

تا حد زیادی بله.

چون به‌محض رخ دادن event، Server پاسخ را ارسال می‌کند.

Latency نسبت به Polling بسیار کمتر می‌شود.


چرا Long Polling مهم بود؟

چون برای اولین بار، Web را به چیزی نزدیک به realtime تبدیل کرد بدون اینکه نیاز به protocol جدیدی باشد.

همه‌چیز هنوز روی HTTP انجام می‌شد.

این موضوع در زمان خودش بسیار مهم بود، چون:

  • Browserها محدود بودند

  • Infrastructure برای WebSocket آماده نبود

  • Proxyها و Firewallها با HTTP راحت‌تر کار می‌کردند


Use Case های رایج

سال‌ها بسیاری از سیستم‌های realtime اولیه از Long Polling استفاده می‌کردند:

  • Chat applications

  • Notification systems

  • Realtime feeds

  • Collaboration tools

حتی بعضی نسخه‌های اولیه:

  • Facebook Chat

  • Gmail updates

  • Slack

تا مدت‌ها از مدل‌هایی شبیه Long Polling استفاده می‌کردند.


مزایای معماری Long Polling

کاهش requestهای غیرضروری

بزرگ‌ترین مزیت آن نسبت به Polling همین بود.


Realtime بهتر

Server می‌توانست به‌محض وقوع event پاسخ بدهد.


سازگاری کامل با HTTP

بدون نیاز به protocol جدید.


Compatibility عالی

با:

  • Browserها

  • Proxyها

  • Firewallها

  • CDNها

سازگارتر از بسیاری از راه‌حل‌های realtime اولیه بود.


اما مشکل‌های جدید شروع شدند

Long Polling نسبت به Polling بهتر بود، اما هنوز محدودیت‌های مهمی داشت.


مشکل شماره ۱: Connectionهای طولانی

در HTTP سنتی، connectionها کوتاه‌مدت بودند.

اما حالا Server باید هزاران connection باز را نگه می‌داشت:

10,000 waiting connections

و این از نظر resource management ارزان نبود.


مشکل شماره ۲: Memory Pressure

هر connection باز:

  • memory مصرف می‌کند

  • file descriptor مصرف می‌کند

  • resource نگه می‌دارد

در scale بالا، این موضوع به bottleneck تبدیل می‌شود.


مشکل شماره ۳: Reconnect Overhead

بعد از هر response:

Response -> Reconnect -> Wait Again

Client باید دوباره request جدید بفرستد.

یعنی هنوز:

  • reconnect وجود دارد

  • headers تکرار می‌شوند

  • HTTP overhead باقی می‌ماند


مشکل شماره ۴: Half-Realtime بودن

Long Polling هنوز ذاتاً:

  • request/response

  • client-driven

است.

Server فقط زمانی می‌تواند data ارسال کند که request فعالی وجود داشته باشد.


از نگاه معماری

Long Polling یک مرحله transitional بود.

نه کاملاً realtime،

نه کاملاً traditional HTTP.

بلکه تلاشی برای نزدیک کردن معماری Request/Response به event-driven communication.


نتیجه چه شد؟

نیاز به ارتباطی وجود داشت که:

  • دائمی باشد

  • overhead کمتری داشته باشد

  • bidirectional باشد

  • reconnect مداوم نداشته باشد

  • واقعاً realtime باشد

و اینجا بود که Web کم‌کم به سمت: Persistent Connections حرکت کرد.

اولین قدم مهم در این مسیر: Server-Sent Events (SSE) بود؛

مدلی ساده و lightweight که برای اولین بار اجازه می‌داد Server به‌صورت واقعی داده را به Client push کند.

اولین Push واقعی در وب: Server-Sent Events (SSE)

تا اینجا تمام مدل‌هایی که دیدیم، هنوز یک ویژگی مشترک داشتند:

ارتباط همیشه توسط Client شروع می‌شد.

حتی در Long Polling هم Server فقط زمانی می‌توانست داده ارسال کند که یک request فعال از سمت Client وجود داشته باشد.

اما کم‌کم Web به سمت سیستم‌هایی حرکت کرد که نیاز داشتند Server بتواند مستقیماً و لحظه‌ای داده ارسال کند:

  • Live notifications

  • Monitoring dashboards

  • News feeds

  • Streaming updates

  • AI response streaming

و اینجا بود که معماری: Server-Sent Events (SSE) شکل گرفت.


SSE چگونه کار می‌کند؟

در SSE، Client یک request HTTP ارسال می‌کند، اما برخلاف HTTP معمولی، connection باز باقی می‌ماند.

Client ---> Connection ---> Server Client <--- Stream of Events ---

بعد از برقراری connection، Server می‌تواند هر زمان event جدیدی رخ دهد، داده را به Client ارسال کند.


معماری SSE

SSE یک معماری:

  • Push-based

  • Persistent

  • Server-to-Client Streaming

است.

یعنی:

  • connection دائمی است

  • Server فعالانه data ارسال می‌کند

  • Client فقط listener است


تفاوت مهم SSE با Polling

در Polling:

Client: "چیزی تغییر کرده؟"

اما در SSE:

Server: "این هم update جدید."

یعنی برای اولین بار، Web واقعاً push-based شد.


جریان ارتباط در SSE

روند معمول به این شکل است:

1. Client opens connection 2. Connection stays alive 3. Server pushes events continuously 4. Client receives updates instantly

بدون reconnect مداوم،

بدون polling،

بدون requestهای تکراری.


نمونه ساده SSE

Client:

const events = new EventSource('/events'); events.onmessage = (event) => { console.log(event.data); };

Server:

data: New notification data: User joined data: Price updated

Server عملاً یک stream دائمی از eventها ارسال می‌کند.


چرا SSE مهم بود؟

چون نسبت به Long Polling:

  • ساده‌تر بود

  • latency پایین‌تری داشت

  • overhead کمتری داشت

  • realtime طبیعی‌تری ایجاد می‌کرد

بدون اینکه complexity کامل WebSocket را وارد سیستم کند.


مزایای معماری SSE

Lightweight بودن

SSE روی HTTP معمولی کار می‌کند.

نیازی به protocol جدید ندارد.


Push واقعی

Server می‌تواند بدون request جدید data ارسال کند.


Connection دائمی

برخلاف Polling، reconnect مداوم وجود ندارد.


مناسب برای Streaming

SSE برای data stream بسیار مناسب است.

مثلاً:

  • AI response streaming

  • Live feeds

  • Metrics

  • Notifications


سازگاری خوب با Infrastructure

چون SSE هنوز مبتنی بر HTTP است:

  • proxyها

  • firewallها

  • load balancerها

معمولاً راحت‌تر با آن کار می‌کنند.


محدودیت‌های معماری SSE

با وجود مزایا، SSE هنوز محدودیت‌های مهمی داشت.


مشکل شماره ۱: ارتباط یک‌طرفه

SSE فقط:

Server ---> Client

است.

Client نمی‌تواند روی همان connection data ارسال کند.

اگر Client بخواهد چیزی بفرستد، هنوز باید HTTP request جداگانه ارسال کند.


مشکل شماره ۲: Binary Data

SSE اساساً text-based است.

برای binary streaming مناسب نیست.


مشکل شماره ۳: Browser Connection Limits

در بعضی browserها تعداد connectionهای هم‌زمان محدود بود و SSE می‌توانست به bottleneck تبدیل شود.


مشکل شماره ۴: هنوز Stateful است

هر connection باز:

  • memory مصرف می‌کند

  • resource نگه می‌دارد

  • lifecycle دارد

پس scaling همچنان سخت‌تر از HTTP سنتی است.


Use Case های مناسب SSE

SSE برای سناریوهایی عالی است که:

  • ارتباط mostly one-way باشد

  • server بیشتر producer باشد

  • client فقط consumer باشد

مثلاً:

  • Live notifications

  • Activity feeds

  • Monitoring dashboards

  • Realtime analytics

  • AI token streaming

  • Stock tickers


چرا ChatGPT به SSE شبیه است؟

وقتی پاسخ ChatGPT کم‌کم stream می‌شود:

Hello... How are...

این دقیقاً مدلی شبیه SSE است.

Server به‌صورت incremental داده را push می‌کند.


از نگاه معماری

SSE اولین معماری mainstream در Web بود که:

  • واقعاً push-based شد

  • streaming واقعی ارائه داد

  • persistent connection داشت

اما هنوز یک محدودیت بزرگ باقی مانده بود:

ارتباط فقط یک‌طرفه بود.

Web کم‌کم به سمت اپلیکیشن‌هایی حرکت می‌کرد که نیاز داشتند:

  • Client و Server هم‌زمان data ارسال کنند

  • latency بسیار پایین باشد

  • ارتباط کاملاً realtime باشد

و اینجا بود که Web وارد دنیای: WebSocket شد؛

معماری‌ای که ارتباط بین Client و Server را از Request/Response به یک connection دائمی و bidirectional تبدیل کرد.

وقتی ارتباط واقعاً دوطرفه شد: WebSocket

تا قبل از WebSocket، Web هنوز تا حد زیادی بر پایه مدل سنتی Request/Response حرکت می‌کرد.

حتی SSE با وجود realtime بودن، هنوز یک محدودیت مهم داشت:

ارتباط فقط از سمت Server به Client بود.

اما نسل جدید اپلیکیشن‌ها به چیزی فراتر نیاز داشتند:

  • Chat applications

  • Online gaming

  • Live collaboration

  • Trading systems

  • Multiplayer apps

سیستم‌هایی که در آن‌ها:

  • Client و Server باید هم‌زمان داده ارسال کنند

  • latency باید بسیار پایین باشد

  • ارتباط باید دائمی و realtime باشد

و اینجا بود که معماری: WebSocket متولد شد.


WebSocket چگونه کار می‌کند؟

WebSocket ارتباط بین Client و Server را از مدل Request/Response به یک connection دائمی و دوطرفه تبدیل می‌کند.

Client <=======> Server

بعد از برقراری connection:

  • هر دو سمت می‌توانند هر زمان data ارسال کنند

  • connection باز باقی می‌ماند

  • نیازی به reconnect مداوم نیست


Upgrade از HTTP به WebSocket

نکته جالب اینجاست که WebSocket معمولاً با یک HTTP request شروع می‌شود.

Upgrade: websocket Connection: Upgrade

بعد از handshake اولیه، ارتباط از HTTP خارج می‌شود و وارد protocol مخصوص WebSocket می‌شود.

از این لحظه دیگر خبری از:

  • request

  • response

  • polling

  • reconnect مداوم

نیست.


معماری WebSocket

WebSocket یک معماری:

  • Persistent

  • Stateful

  • Bidirectional

  • Full-Duplex

است.

یعنی:

  • connection دائمی است

  • هر دو سمت active هستند

  • data می‌تواند هم‌زمان در هر دو جهت حرکت کند


تفاوت بنیادی با HTTP

در HTTP:

Request -> Response -> Close

اما در WebSocket:

connect ================= Active Stream

connection تبدیل به یک channel دائمی می‌شود.


چرا WebSocket انقلابی بود؟

چون برای اولین بار Web توانست رفتاری شبیه applicationهای desktop یا network protocolهای realtime پیدا کند.

یعنی:

  • latency بسیار پایین

  • realtime واقعی

  • event-driven communication

  • bidirectional messaging


نمونه ساده WebSocket

Client:

const socket = new WebSocket('ws://server'); socket.onmessage = (event) => { console.log(event.data); }; socket.send('Hello');

اینجا:

  • Server می‌تواند هر زمان message ارسال کند

  • Client هم هر زمان بخواهد data می‌فرستد

بدون request جدید.


مزایای معماری WebSocket

Realtime واقعی

تاخیر بسیار پایین است.


ارتباط دوطرفه

هر دو سمت می‌توانند producer و consumer باشند.


کاهش overhead

دیگر برای هر message:

  • HTTP headers

  • reconnect

  • handshake

تکرار نمی‌شود.


مناسب برای Event-Driven Systems

WebSocket ذاتاً event-oriented است.


Use Case های مناسب WebSocket

WebSocket برای سیستم‌هایی مناسب است که:

  • ارتباط دائمی نیاز دارند

  • latency بسیار مهم است

  • تبادل data دوطرفه است

مثل:

  • Chat systems

  • Multiplayer games

  • Collaborative editors

  • Trading platforms

  • Live dashboards

  • Presence systems

  • Realtime notifications


اما WebSocket هزینه دارد

و این دقیقاً جایی است که معماری اهمیت پیدا می‌کند.

بسیاری از تیم‌ها فقط به realtime بودن WebSocket نگاه می‌کنند، اما complexity معماری آن را نادیده می‌گیرند.


مشکل شماره ۱: Stateful Connections

برخلاف HTTP، حالا Server باید connectionها را نگه دارد.

100,000 active sockets

هر connection:

  • memory مصرف می‌کند

  • lifecycle دارد

  • باید مدیریت شود


مشکل شماره ۲: Scaling سخت‌تر می‌شود

در HTTP هر request مستقل بود.

اما حالا:

Client A -> Server 1 Client B -> Server 2

اگر Client A پیامی بفرستد و Client B روی node دیگری باشد، سیستم باید somehow message را بین serverها synchronize کند.

اینجاست که سیستم‌ها وارد دنیای:

  • Redis Pub/Sub

  • Kafka

  • NATS

  • Message Brokers

می‌شوند.


مشکل شماره ۳: Load Balancing پیچیده‌تر می‌شود

در HTTP:

  • هر request می‌تواند به هر server برود

اما در WebSocket:

  • connection دائمی است

  • server state نگه می‌دارد

پس Load Balancer باید:

  • sticky session داشته باشد

  • connection-aware باشد


مشکل شماره ۴: Connection Lifecycle

حالا باید این مسائل مدیریت شوند:

  • reconnect

  • heartbeat

  • timeout

  • dead connections

  • network interruption

و این complexity کمی نیست.


مشکل شماره ۵: Observability سخت‌تر می‌شود

در HTTP tracing ساده‌تر است چون requestها مستقل‌اند.

اما در WebSocket:

  • event stream طولانی داریم

  • state دائماً تغییر می‌کند

  • debugging سخت‌تر می‌شود


WebSocket همیشه انتخاب درست نیست

یکی از اشتباه‌های رایج این است که:

«هر realtimeای = WebSocket»

در حالی که بسیاری از سیستم‌ها اصلاً به bidirectional communication نیاز ندارند.

مثلاً:

  • Notification stream

  • AI output streaming

  • Live metrics

ممکن است با SSE ساده‌تر و scalableتر حل شوند.


از نگاه معماری

WebSocket نقطه‌ای بود که Web از مدل سنتی document-based فاصله گرفت و به سمت application-level communication حرکت کرد.

اما complexity آن هم به همان اندازه افزایش پیدا کرد.

در واقع WebSocket فقط یک protocol نیست؛

ورود به دنیای:

  • stateful systems

  • distributed coordination

  • realtime infrastructure

است.


نتیجه چه شد؟

با رشد سیستم‌های توزیع‌شده، کم‌کم مشخص شد که realtime communication تنها مسئله نیست.

سیستم‌ها نیاز داشتند:

  • سرویس‌ها با هم سریع‌تر صحبت کنند

  • streaming داخلی داشته باشند

  • communication typed و efficient باشد

  • latency بین serviceها کمتر شود

و اینجا بود که معماری‌های جدیدتری مثل: gRPC و بعد از آن:

  • Event Streaming

  • Message Queues

  • Realtime Sync Engines

وارد معماری سیستم‌های مدرن شدند.

جمع‌بندی

مسیر تکامل Web را می‌توان تا حد زیادی از زاویه «معماری ارتباط» نگاه کرد.

در ابتدا، Web بر پایه مدل ساده و قدرتمند HTTP Request/Response ساخته شد؛
مدلی stateless، scalable و مناسب برای دنیایی که بیشتر از document و page تشکیل شده بود.

اما با تبدیل شدن Web به یک platform برای applicationهای interactive، محدودیت‌های این معماری کم‌کم آشکار شد.

نیازهای جدیدی به وجود آمد:

  • realtime communication

  • live updates

  • low latency interactions

  • bidirectional messaging

  • persistent synchronization

و هرکدام از معماری‌هایی که بررسی کردیم، در واقع پاسخی به همین نیازها بودند.


Polling تلاش کرد realtime را روی HTTP شبیه‌سازی کند، اما به قیمت requestهای مکرر و مصرف بالای resource.

Long Polling قدمی به جلو بود و توانست ارتباط را طبیعی‌تر و کم‌هزینه‌تر کند، اما هنوز در چارچوب Request/Response باقی مانده بود.

SSE برای اولین بار مفهوم واقعی Push-based communication را وارد Web کرد و امکان streaming ساده و lightweight را فراهم ساخت.

و در نهایت، WebSocket ارتباط بین Client و Server را از یک مدل سنتی مبتنی بر request، به یک connection دائمی، realtime و bidirectional تبدیل کرد.


اما نکته مهم اینجاست که هیچ‌کدام از این معماری‌ها «بهترین» نیستند.

هر مدل ارتباطی، مجموعه‌ای از trade-off ها را به همراه دارد.

معمولاً هرچه:

  • realtime بیشتر شود،

  • latency کمتر شود،

  • و synchronization قوی‌تر شود،

complexity سیستم هم افزایش پیدا می‌کند.


به همین دلیل انتخاب معماری ارتباط، فقط یک تصمیم تکنیکال نیست؛
بلکه یک تصمیم معماری است که مستقیماً روی:

  • scalability

  • infrastructure

  • maintainability

  • observability

  • operational cost

  • developer experience

تأثیر می‌گذارد.


یکی از اشتباه‌های رایج در طراحی سیستم‌ها این است که تکنولوژی‌ها صرفاً بر اساس trend یا hype انتخاب شوند.

در حالی که در بسیاری از پروژه‌ها:

  • HTTP کاملاً کافی است،

  • Polling هنوز منطقی است،

  • یا SSE انتخاب ساده‌تر و مناسب‌تری نسبت به WebSocket محسوب می‌شود.

Realtime بودن همیشه ارزش complexity بیشتر را ندارد.


در نهایت، معماری ارتباط فقط درباره انتقال data نیست؛
بلکه درباره نحوه فکر کردن به interaction بین اجزای سیستم است.

اینکه:

  • چه کسی کنترل ارتباط را در دست دارد،

  • state کجا نگهداری می‌شود،

  • ارتباط چگونه scale می‌شود،

  • و سیستم تا چه اندازه باید realtime، resilient و distributed باشد.

و دقیقاً همین تصمیم‌ها هستند که تفاوت بین یک API ساده و یک سیستم realtime در مقیاس بزرگ را مشخص می‌کنند.


در مقاله بعدی، از دنیای ارتباطات Web فراتر می‌رویم و سراغ معماری ارتباط در سیستم‌های توزیع‌شده مدرن می‌رویم؛
جایی که مفاهیمی مثل:

  • gRPC

  • Event Streaming

  • Kafka

  • Message Queues

  • Realtime Synchronization

  • Distributed Messaging

نقش اصلی را بازی می‌کنند.

معماریارتباطمعماری نرم افزارتوسعه نرم افزارپروتکل
۲
۰
حامد پارسا
حامد پارسا
شاید از این پست‌ها خوشتان بیاید