آموزش

wp-cron وردپرس؛ چرا اجرا نمی‌شود و چطور با کرون واقعی جایگزینش کنیم

اگر زمان‌بندی انتشار پست‌ها عقب می‌افتد یا افزونه‌ها کار نمی‌کنند، مشکل از wp-cron است. راهنمای تشخیص و جایگزینی با کرون واقعی سرور.

آموزش

پستی را برای ساعت ۹ صبح زمان‌بندی کرده‌اید و ساعت ۱۱ است که هنوز منتشر نشده. یا افزونه‌ای که باید هر ساعت گزارش بفرستد، دیروز یک بار هم نفرستاده. اگر لاگ سرور را باز کنید و دنبال wp-cron.php بگردید، می‌بینید که یا هیچ رکوردی نیست، یا صدها رکورد پشت سر هم در یک ثانیه ثبت شده. این دو حالت، دو صورت یک مسئله‌اند: wp-cron وردپرس یک زمان‌بند واقعی نیست.

وردپرس هیچ کرون‌دامونی روی سرور ندارد. آنچه «wp-cron» نامیده می‌شود، یک فایل PHP است که با هر بازدید صفحه صدا زده می‌شود. یعنی زمان‌بندی وظایف شما گره خورده به ترافیک سایت، نه به ساعت سیستم. همین یک جمله، بیشتر مشکلاتی را که دنبالشان هستید توضیح می‌دهد.

چرا wp-cron روی سایت کم‌بازدید اجرا نمی‌شود

در فایل wp-includes/default-filters.php یک هوک هست که در هر بار لود شدن صفحه، تابع wp_cron() را صدا می‌زند. این تابع چک می‌کند آیا وظیفه‌ای سررسید شده یا نه. اگر سایت شما روزی ۵۰ بازدید دارد و همه‌شان هم در بازه‌های نامنظم می‌آیند، عملاً هیچ‌کس آن هوک را در لحظه درست اجرا نمی‌کند. نتیجه‌اش این است که پست زمان‌بندی‌شده با چند ساعت تأخیر منتشر می‌شود، ایمیل‌های تراکنشی دیر می‌رسند، و افزونه‌های پشتیبان‌گیری خودکار بعضی روزها کلاً اجرا نمی‌شوند.

علامت تشخیصی‌اش هم ساده است. یک اسکریپت کوچک بنویسید که _get_cron_array() را بخواند و زمان next_run هر وظیفه را چاپ کند. اگر آن زمان مدام در گذشته مانده و جلو نمی‌رود، یعنی هیچ بازدیدی آن را trigger نکرده. این را در مستندات و پایگاه دانش هم به شکل دیگری توضیح داده‌ایم، ولی خودِ تست سه خط کد است.

چرا روی سایت پربازدید کند می‌کند

حالا سایت پربازدید را در نظر بگیرید. اینجا مشکل برعکس است. هر بازدید صفحه، یک درخواست HTTP جداگانه به wp-cron.php می‌فرستد. آن فایل با define('DISABLE_WP_CRON', false) فعال، در هر اجرا کل آرایه کرون را می‌خواند، قفل می‌گیرد، و اگر وظیفه‌ای سررسید شده باشد اجرا می‌کند. روی سایتی با ۲۰۰ بازدید همزمان در دقیقه، این یعنی ۲۰۰ درخواست اضافه که هرکدام یک کانکشن PHP-FPM و یک کوئری به جدول wp_options مصرف می‌کند.

عددش را ببینید. یک درخواست به wp-cron.php که هیچ وظیفه‌ای برای اجرا ندارد، معمولاً بین ۸۰ تا ۱۵۰ میلی‌ثانیه طول می‌کشد و حدود ۱۵ تا ۳۰ مگابایت حافظه PHP می‌گیرد. اگر سایت شما در پیک ترافیک ۵۰ درخواست در ثانیه بگیرد، این یعنی ۵۰ کانکشن اضافه در ثانیه که هیچ کاری برای کاربر انجام نمی‌دهند. روی سروری با pm.max_children = 20، استخر PHP-FPM در چند ثانیه پر می‌شود و کاربران واقعی صف می‌ایستند. TTFB از ۳۰۰ میلی‌ثانیه می‌پرد به بالای دو ثانیه.

این‌جا اشتباه می‌کنند: خیلی‌ها فکر می‌کنند مشکل از خود وردپرس است و می‌روند سراغ کش پلاگین. کش صفحه را سریع می‌کند، ولی درخواست‌های wp-cron.php از مسیر کش عبور نمی‌کنند چون یک فایل PHP مستقل‌اند. سایت روی صفحه اصلی سریع به نظر می‌رسد، ولی لود سرور بالا مانده و هر چند ساعت یک بار ۵۰۲ می‌دهد. اگر در access.log بگردید، الگویش این است: صدها خط POST /wp-cron.php?doing_wp_cron پشت سر هم، همه با کد ۲۰۰ و زمان پاسخ بالا.

جایگزینی با کرون واقعی سرور

راه‌حل استاندارد این است که wp-cron داخلی را خاموش کنید و یک کرون‌جاب واقعی در سطح سیستم‌عامل بسازید. اول در wp-config.php این خط را اضافه کنید:

define('DISABLE_WP_CRON', true);

بعد با crontab -e روی کاربر وب‌سرور، این خط را اضافه کنید:

*/5 * * * * cd /var/www/html && /usr/bin/php wp-cron.php >/dev/null 2>&1

دو نکته در همین خط هست که معمولاً اشتباه می‌شود. اول، cd به مسیر وردپرس لازم است چون wp-cron.php مسیرها را نسبی می‌خواند. دوم، از باینری PHP همان نسخه‌ای استفاده کنید که سایت با آن اجرا می‌شود؛ اگر سرور چند نسخه PHP دارد و شما /usr/bin/php را صدا بزنید در حالی که سایت روی PHP 8.2 است، ممکن است خطای Fatal error: Uncaught Error بگیرید یا بدتر، بی‌صدا کار نکند. مسیر درست را با which php در همان محیط پیدا کنید.

بازه پنج دقیقه‌ای برای اکثر سایت‌ها درست است. اگر وظیفه‌ای دارید که باید دقیق‌تر اجرا شود، بازه را به یک دقیقه کاهش دهید، ولی حواستان باشد که هر اجرا یک پروسه PHP کامل بالا می‌آورد. روی سرور ضعیف، ۱۴۴۰ اجرا در روز خودش هزینه دارد.

جایگزین بهتر: WP-CLI

اگر WP-CLI نصب است، خط کرون را این‌طور بنویسید:

*/5 * * * * cd /var/www/html && /usr/local/bin/wp cron event run --due-now --quiet

این نسخه بهتر است چون فقط وظایف سررسیدشده را اجرا می‌کند، خروجی تمیزی دارد و با --quiet لاگ را شلوغ نمی‌کند. تفاوت عملی‌اش را وقتی می‌بینید که بخواهید یک وظیفه خاص را دستی تست کنید: wp cron event list و wp cron event run woocommerce_scheduled_sales دقیقاً همان چیزی است که برای دیباگ لازم دارید.

مقایسه دو روش

معیارwp-cron پیش‌فرضکرون واقعی سرور
وابستگی به ترافیکداردندارد
دقت زمان‌بندینامنظم، تا چند ساعت تأخیردقیق تا حد بازه کرون
هزینه روی ترافیک بالابالا، درخواست اضافه در هر بازدیدثابت، مستقل از بازدید
نیاز به دسترسی SSHندارددارد
مناسب برایسایت تازه، ترافیک کم، بدون SSHهر سایت جدی

اگر روی هاست اشتراکی بدون دسترسی SSH هستید، گزینه‌ای جز wp-cron پیش‌فرض ندارید مگر اینکه پنل هاست امکان تعریف Cron Job بدهد. در آن حالت بازه را روی ۱۵ دقیقه بگذارید و DISABLE_WP_CRON را true کنید. اگر کنترل کامل سرور دارید، هیچ دلیلی برای نگه داشتن wp-cron داخلی وجود ندارد. من روی هر سروری که خودم راه می‌اندازم، از روز اول این را خاموش می‌کنم. روی هاست لینوکس با دسترسی SSH این کار دو دقیقه وقت می‌برد.

چیزهایی که بعد از مهاجرت خراب می‌شوند

بعد از خاموش کردن wp-cron، دو چیز را چک کنید. اول، بعضی افزونه‌ها خودشان مستقیم wp-cron.php را صدا می‌زنند یا به هوک wp_loaded تکیه دارند؛ اگر بعد از تغییر دیدید یک افزونه دیگر کار نمی‌کند، لاگ PHP را برای Undefined index بگردید. دوم، اگر سایت روی چند سرور پشت لودبالانسر اجرا می‌شود، کرون را فقط روی یک سرور تعریف کنید. وگرنه هر سرور جداگانه وظایف را اجرا می‌کند و مثلاً یک ایمیل را چهار بار می‌فرستید.

یک نکته دیگر: بعد از مهاجرت، جدول wp_options را از نظر رکوردهای cron و doing_cron بررسی کنید. اگر یک اجرا وسط کار کرش کرده باشد، قفل doing_cron باقی می‌ماند و هیچ وظیفه‌ای دیگر اجرا نمی‌شود. پاک کردن دستی آن رکورد، مشکل را حل می‌کند. این حالت را روی سایتی دیده‌ام که سه هفته هیچ پشتیبان‌گیری‌ای نگرفته بود و کسی متوجه نشده بود.

برای اینکه بفهمید بعد از این تغییرات واقعاً چه اتفاقی در سرور می‌افتد، ارزشش را دارد که پایش آپتایم سایت را هم راه بیندازید؛ افت‌های کوتاه ناشی از پر شدن استخر PHP-FPM را فقط با مانیتورینگ مداوم می‌بینید، نه با باز کردن دستی سایت.

پرسش‌های پرتکرار

آیا خاموش کردن wp-cron به سایت آسیب می‌زند؟

نه، به شرطی که کرون واقعی را قبل از خاموش کردن تنظیم کرده باشید. اگر DISABLE_WP_CRON را true کنید و هیچ کرون‌جابی نسازید، هیچ وظیفه‌ای اجرا نمی‌شود و پست‌های زمان‌بندی‌شده هرگز منتشر نمی‌شوند. ترتیب درست این است: اول خط کرون را اضافه کنید، تست کنید که اجرا می‌شود، بعد wp-cron داخلی را خاموش کنید.

چطور بفهمم کرون‌جاب واقعی درست کار می‌کند؟

یک وظیفه تستی بسازید که یک رکورد در لاگ بنویسد، یا از wp cron event list استفاده کنید و ببینید زمان next_run بعد از هر اجرا جلو می‌رود. اگر زمان ثابت مانده، یعنی کرون اجرا نمی‌شود. لاگ کرون سیستم را هم با grep CRON /var/log/syslog چک کنید.

بازه مناسب برای کرون‌جاب چند دقیقه است؟

برای اکثر سایت‌ها پنج دقیقه کافی است. اگر افزونه فروشگاهی دارید که سفارش‌های معلق را منقضی می‌کند یا سیستم ارسال ایمیل صف‌بندی‌شده دارید، یک دقیقه منطقی‌تر است. کمتر از یک دقیقه در کرون استاندارد ممکن نیست و به کرون‌های سطح ثانیه نیاز دارد که وردپرس از آن‌ها پشتیبانی نمی‌کند.

روی هاست اشتراکی بدون SSH چه کار کنم؟

در پنل هاست دنبال بخش Cron Jobs بگردید؛ اکثر پنل‌ها این امکان را می‌دهند. اگر نبود، wp-cron پیش‌فرض را نگه دارید ولی بازه اجرای وظایف سنگین مثل پشتیبان‌گیری را کوتاه نکنید. سایت‌های کم‌بازدید در این حالت باید بدانند که زمان‌بندی‌شان دقیق نخواهد بود و به آن تکیه نکنند.

پشتیبانی سرورنت

تیم فنی و تحریریه‌ی سرورنت — تخصص در زیرساخت، شبکه و میزبانی وب.

هاست وردپرس
اشتراک‌گذاری:

دیدگاه‌ها ۰

هنوز دیدگاهی ثبت نشده؛ اولین نفر باشید!

دیدگاه خود را بنویسید

سرویس مرتبط

هاست وردپرس

استک اختصاصی وردپرس با LiteSpeed Enterprise و NVMe — نصب خودکار، آپدیت امن، استیجینگ و کشی که سایت شما را در صدر نتایج گوگل نگه می‌دارد.