زمینه
این پروژه را سال ۲۰۲۲، زمانی که داشتم در جنگوی رویدادمحور عمیق میشدم، ساختم — سرویسی عمداً جمعوجور که به یک سؤال، خوب پاسخ میدهد: چطور دادهٔ بازار را بهصورت زنده به کلاینتهای متصل برسانیم، بدون آنکه هر کلاینت مدام یک API خارجی را بمباران کند؟
مسئله
کاربر میخواهد یک جفتارز را زیر نظر بگیرد و بداند آخرین کندل یکدقیقهای بالاتر یا پایینتر از قیمت هدف بسته شده است. راه سرراست — یعنی polling هر مرورگر به سمت صرافی — درخواستهای تکراری را چند برابر میکند و خیلی زود به سقف rate limit میخورد. نسخهٔ درست، زمانبندی را به سمت سرور میبرد و نتیجه را push میکند.
سازوکار
- مرورگر یک WebSocket (با Django Channels) باز میکند و نماد و قیمت هدف را میفرستد.
- Consumer یک تسک دورهای Celery Beat را بهصورت پویا ثبت میکند که به گروه کانال همان اتصال گره خورده است — زمانبندی اینجا داده است؛ توسط اتصال ساخته و مدیریت میشود، نه اینکه در کد ثابت باشد.
- هر دقیقه، یک worker سلری آخرین کندل یکدقیقهای را از Binance API میگیرد و قیمت بستهشدن را با هدف مقایسه میکند.
- نتیجه از طریق لایهٔ کانال Redis به گروه WebSocket میرسد و بهصورت پیام push در مرورگر مینشیند — بدون هیچ polling سمت کلاینت.
- فهرست نمادهای قابل معامله در Redis کش میشود تا رابط کاربری هرگز برای دادهٔ ایستا منتظر صرافی نماند.
تصمیمها و محدودیتهای صادقانه
- زمانبندی بهمثابهٔ داده. استفاده از زمانبندی مبتنی بر دیتابیسِ
django-celery-beatیعنی میتوان تسکها را در زمان اجرا و بهازای هر اتصال ساخت — الگویی که از نظر مفهومی به سیستمهای هشدارِ بهازای هر کاربر مقیاس مییابد. - تفکیک تمیز مسئولیتها. انتقال (Channels)، پردازش (Celery) و وضعیت (Redis) هر کدام در جای خودشان میمانند؛ consumer هیچوقت مستقیم با Binance حرف نمیزند.
- محدودیتهای شناختهشده، به اقتضای دامنهٔ پروژه: این یک دموی تکاپلیکیشنی است — نسخهٔ production باید تسکها را بهازای هر نماد یکتا کند، زمانبندیها را هنگام قطع اتصال پاک کند و دور کلاینت صرافی retry و backoff بگذارد. دیدن همین شکافها بخشی از چیزی است که این پروژه به من یاد داد.
چه چیزی را نشان میدهد
اسکلت یک سیستم مبتنی بر پیام — ورودی سوکت، کار زمانبندیشدهٔ پسزمینه و توزیع مبتنی بر صف — دقیقاً همان شکلی که بعدها پایپلاین بلادرنگ دادههای بازار را بهصورت حرفهای در ماهدا ساختم.