مهندسیApr 17, 2026·۸ دقیقه مطالعه

زنده ماندن پس از قطعی: کلاینت WebSocket که دروغ نمی‌گوید

بخش سخت مصرف فید بازار، مسیر خوش‌بینانه نیست. بستن درِ لپ‌تاپ است، سوییچ گوشی از وای‌فای به LTE، پراکسی شرکتی که اتصال‌های بیکار را بی‌صدا می‌بندد. کلاینتی که فقط روی لینک پایدار کار می‌کند، دیر یا زود قیمت یخ‌زده‌ای به کاربر نشان می‌دهد و اسمش را «زنده» می‌گذارد. این نظمی است که در کلاینت‌های خودمان اعمال می‌کنیم.

به ضربان قلب دقیق جواب بدهید

سرور کرای‌هاب هر اتصال را با فاصله منظم ping می‌کند. کلاینت باید با فریم pong و echo همان مقدار جواب بدهد — و طبق پروتکل ما، مقدار echo شده باید رشته باشد:

ws.send(JSON.stringify({ op: "pong", data: { pong: String(ping) } }));

این جزئیات تایپی بیش از هر چیز دیگری در صندوق پشتیبانی ما دردسر ساخته است. عدد بفرستید، همه چیز به‌ظاهر درست است — تا اینکه سرور که pong معتبری نشنیده، چند ثانیه بعد اتصال را می‌بندد. خطا دور از علتش ظاهر می‌شود و همین آزاردهنده‌اش می‌کند. فوری جواب بدهید، دقیق echo کنید، حواس‌تان به نوع داده باشد.

اتصال مجدد، جدی

سه قاعده برای حلقه reconnect:

  1. Backoff نمایی با سقف. از حدود یک ثانیه شروع کنید، هر تلاش دو برابر، سقف حدود سی ثانیه. jitter اضافه کنید — اگر موقع یک اختلال شبکه ده هزار تب باز دارید، ده هزار reconnect هم‌زمان نمی‌خواهید.
  2. Backoff را فقط بعد از اثبات اتصال ریست کنید — با فریم خوش‌آمد یا ACK موفق subscribe، نه صرفِ باز شدن سوکت. بعضی تجهیزات میانی می‌گذارند اتصال باز شود و بعد گرسنه‌اش می‌کنند.
  3. حالت retry را هرگز به‌عنوان خطا نمایش ندهید. کاربر روی وای‌فای هتل بنر قرمز نمی‌خواهد؛ می‌خواهد UI آرام بگوید «در حال اتصال مجدد» و بعد واقعا وصل شود.

اشتراک مجدد از روی state، نه تاریخچه

در هر بار اتصال، کلاینت باید اشتراک‌هایش را از state فعلی اپلیکیشن بسازد — جفت‌ارزها و کانال‌هایی که UI همین حالا لازم دارد — نه اینکه لاگ پیام‌های subscribe قبلی را بازپخش کند. اشتراکِ state-محور، reconnect را idempotent می‌کند: قطعی هر قدر آشفته، جلسه بعدی از حقیقت شروع می‌شود. یک دسته کامل از باگ‌ها هم حذف می‌شود — مثل وقتی کاربر وسط قطعی جفت‌ارز را عوض کرده و جفت قدیمی از گور برمی‌گردد.

تا سوکت پایین است، یک snapshot از REST برای هر پنل قابل‌مشاهده، فاصله را پر می‌کند تا کاربر به‌جای قیمت به اسپینر خیره نماند.

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

و بخشی که بیشتر کلاینت‌ها جا می‌اندازند: timestamp آخرین تیک هر اشتراک را نگه دارید. اگر بیش از آهنگ طبیعی کانال چیزی نرسیده، بگویید — قیمت را کم‌رنگ کنید، چیپ اتصال مجدد را نشان بدهید، هر کاری جز رندر کردن عدد کهنه به‌عنوان قیمت لحظه‌ای. فیدی که «کهنه» را می‌پذیرد قابل‌اعتماد است. فیدی که هیچ‌وقت چیزی نمی‌پذیرد، فقط گاهی اشتباه است.

هیچ‌کدام از این الگوها عجیب نیستند. کنار هم، فرقِ بین یک دمو و یک ترمینال‌اند.