حقیقت درباره Async PHP: Fibers، epoll و PHP 8.6

من سال‌ها با Laravel کار کرده‌ام. از PHP همگام (sync) استفاده می‌کردم. یک درخواست می‌آید، فرآیند اجرا می‌شود و پاسخ ارسال می‌گردد. من هرگز به کد async نیاز نداشتم.

سپس درباره Polling API جدید در PHP 8.6 خواندم. این موضوع دیدگاه من را نسبت به همه چیز تغییر داد.

در اینجا آنچه درباره نحوه عملکرد async در پشت صحنه آموختم را آورده‌ام.

مشکل I/O

وقتی یک API را فراخوانی می‌کنید، فرآیند PHP شما منتظر می‌ماند. مثال: $response = Http::get('https://api.example.com');

اگر آن فراخوانی ۳۰۰ میلی‌ثانیه طول بکشد، CPU شما به مدت ۳۰۰ میلی‌ثانیه هیچ کاری انجام نمی‌دهد. در حالت خواب (sleep) باقی می‌ماند. این همان I/O مسدودکننده (blocking I/O) است.

اگر سه فراخوانی API داشته باشید:

  • API A: 300ms
  • API B: 400ms
  • API C: 200ms

مجموع ترتیبی (Sequential): 900ms. مجموع async: 400ms (زمان طولانی‌ترین فراخوانی).

Async به فرآیند شما اجازه می‌دهد تا در حین انتظار برای داده‌ها، کارهای دیگری انجام دهد.

Select در مقابل epoll

برای انجام async، باید بدانید کدام socket داده‌های آماده دارد.

  1. select() PHP از نسخه ۴ از stream_select() استفاده می‌کند. این تابع با درخواست از kernel برای نظارت بر لیستی از socketها کار می‌کند. مشکل: هر بار که داده‌ای می‌رسد، باید کل لیست را دوباره اسکن کنید. این یک هزینه اضافی (rescan tax) است. همچنین محدودیتی در حدود ۱۰۲۴ اتصال دارد.

  2. epoll (Linux) / kqueue (macOS) این‌ها ویژگی‌های kernel هستند. به جای اسکن کردن یک لیست، kernel یک لیستِ آماده (ready-list) نگه می‌دارد. این ویژگی فقط به شما می‌گوید کدام socketهای خاص آماده هستند. این روش بدون کار اضافی، تا هزاران اتصال را پشتیبانی می‌کند (scale می‌کند).

epoll یک ویژگی PHP نیست؛ بلکه یک ویژگی Linux است. Go، Rust و Node.js همگی از آن استفاده می‌کنند.

Fibers: دکمه توقف

PHP 8.1 قابلیت Fibers را معرفی کرد. فکر می‌کردم Fibers خودبه‌خود بیدار می‌شوند، اما این‌طور نیست.

یک Fiber مانند یک ویدیوی متوقف شده است. تا زمانی که کسی $fiber->resume() را فراخوانی نکند، متوقف می‌ماند.

یک Event Loop صرفاً تکه‌ای از کد PHP است که تصمیم می‌گیرد چه زمانی resume() فراخوانی شود.

Async I/O به سه بخش نیاز دارد:

  • توقف (Pause): Fibers (هسته PHP 8.1)
  • تصمیم‌گیری (Decide): Event Loop (کد ساده PHP)
  • دانستن (Know): Kernel Polling (epoll/kqueue)

قبل از PHP 8.6، زبان PHP بخش‌های "Pause" و "Decide" را داشت، اما بخش "Know" به select() قدیمی یا افزونه‌های (extensions) کندِ C وابسته بود.

PHP 8.6 این شکاف را پر می‌کند. این نسخه یک Polling API بومی را به هسته زبان می‌آورد. اکنون PHP می‌تواند مستقیماً از epoll یا kqueue بدون نیاز به افزونه‌های اضافی استفاده کند.

نکته نهایی

اگر از Laravel با PHP-FPM استفاده می‌کنید، امروز نیازی به تغییر هیچ‌چیز ندارید.

اما این را درک کنید: Async جادو نیست. فقط یک روش هوشمندانه برای مدیریت زمان انتظار است.

فقط مصرف‌کننده کد نباشید. یک اسکریپت ساده بنویسید. آن را خراب کنید. اینگونه است که واقعاً یاد می‌گیرید.

منبع: https://dev.to/alamriku/sync-php-developer-hisebe-async-php-bujhte-giye-yaa-shikhlaam-fibers-epoll-aar-php-86-462j