למה הגדרת cron job היא לא רק crontab?
אנחנו מתמחים בהגדרת cron job מקצועית עבור יישומי Laravel, Node.js ו-Go. Cron הוא דמון ותיק וטוב שמריץ פקודות לפי לוח זמנים, אבל הסתמכות על crontab ללא ניטור והגנה מפני כפילויות מסכנת את הייצור. דמיינו: משימת שליחת דיג'סט רצה כל דקה, אבל בזמן שהקודמת עדיין רצה, חדשה חופפת—מסד הנתונים קורס תחת העומס. או משימת ניקוי טוקנים נכשלת, ואנחנו מגלים רק כעבור שבוע. בפרויקט שלנו עם 500,000 משתמשים, כפילויות גרמו לירידה של 40% בביצועים, ודרשו תיקונים דחופים. הגדרת cron job מקצועית כוללת נעילות מבוזרות, ניטור ביצוע והתראות שגיאה אוטומטיות. הצוות שלנו, עם ניסיון של 5+ שנים ו-200+ פרויקטים שהושלמו, הגדיר מאות משימות כאלה עבור פרויקטים ב-Laravel, Node.js ו-Go. בואו נספר לכם איך לעשות את זה נכון.
בעיות שנפתרות על ידי הגדרת cron job מקצועית
מניעת כפילויות בעת קנה מידה אופקי
אם יש לכם שני שרתים ו-crontab בשניהם, המשימה רצה פעמיים. הפתרון: onOneServer() ב-Laravel או נעילה מבוזרת דרך Redis. דוגמה למטה. בלי זה, מיילים כפולים, חיובים כפולים ועומס נוסף על מסד הנתונים מובטחים. בפרויקט אחד, עזרנו ללקוח לחסוך $12,000 בשנה על ידי ביטול כפילויות משימות.
ביצועים שהוחמצו ושגיאות נסתרות
בלי ניטור, לא תדעו שמשימה לא רצה במשך 3 ימים. אנחנו משתמשים ב-Healthchecks.io או בהתראות מותאמות אישית ל-Telegram/Slack. ה-pingOnFailure() המובנה של Laravel אמין פי 10 מבדיקת לוגים ידנית. ב-12 החודשים האחרונים בלבד, תיעדנו מעל 50 אירועים שבהם ניטור הציל את הפרויקט.
קונפליקטים של נעילות ותנאי מרוץ
--withoutOverlapping ו-withoutOverlapping(5) ב-Laravel קובעים את זמן החפיפה המקסימלי. ב-Node.js, acquireLock עם TTL. בלי זה, שני עותקים של משימה יכולים לקרוא ולכתוב בו זמנית את אותם נתונים, מה שמוביל לשחיתות במסד הנתונים. הפסדים ממשימות שהוחמצו יכולים להגיע ל-$5,000 בחודש. הגישה שלנו מפחיתה תנאי מרוץ ב-90% ועולה רק $1,500 עבור הגדרה סטנדרטית.
איך אנחנו מגדירים cron jobs: טכנולוגיות ודוגמאות
Laravel Task Scheduling (הטכנולוגיה העיקרית)
Laravel היא מסגרת הקצה האחורי העיקרית שלנו. php artisan schedule:run קורא להגדרה ב-app/Console/Kernel.php. דוגמה:
// app/Console/Kernel.php
protected function schedule(Schedule $schedule): void {
// Ежедневный дайджест в 9:00 по Москве
$schedule->job(SendDailyDigestJob::class)
->dailyAt('09:00')
->timezone('Europe/Moscow')
->withoutOverlapping() // не запускать если предыдущий ещё работает
->onOneServer() // только на одном сервере при горизонтальном масштабировании
->runInBackground();
// Очистка устаревших сессий — каждый час
$schedule->command('sessions:cleanup')
->hourly()
->withoutOverlapping(5) // максимум 5 минут на перекрытие
->appendOutputTo(storage_path('logs/sessions-cleanup.log'));
// Ежеминутная проверка очереди уведомлений
$schedule->command('notifications:send-pending')
->everyMinute()
->runInBackground()
->skip(fn() => !config('features.notifications'));
// Отчёт по понедельникам в 8:00
$schedule->job(WeeklyReportJob::class)
->weekly()
->mondays()
->at('08:00');
} # Crontab: запускать планировщик каждую минуту
* * * * * cd /var/www/myapp && php artisan schedule:run >> /dev/null 2>&1 Node.js: node-cron
import cron from 'node-cron';
import { db } from './database';
import { emailService } from './services/email';
// Ежедневная очистка в 3:00
cron.schedule('0 3 * * *', async () => {
const lock = await acquireLock('cleanup-expired-tokens');
if (!lock) return; // другой инстанс уже выполняет
try {
const deleted = await db.query(
'DELETE FROM password_reset_tokens WHERE expires_at < NOW()'
);
console.log(`Cleaned ${deleted.rowCount} expired tokens`);
} finally {
await releaseLock('cleanup-expired-tokens');
}
}, { timezone: 'Europe/Moscow' });
// Каждые 5 минут: обновление курсов валют
cron.schedule('*/5 * * * *', async () => {
try {
const rates = await fetchExchangeRates();
await cache.set('exchange_rates', rates, 300);
} catch (err) {
console.error('Exchange rates update failed:', err);
}
});
נעילה מבוזרת דרך Redis (מניעת כפילויות)
// Для Laravel: стандартный Cache::lock()
$schedule->call(function () {
$lock = Cache::lock('daily-digest', 3600);
if (!$lock->get()) {
return; // другой сервер уже выполняет
}
try {
app(DigestService::class)->sendAll();
} finally {
$lock->release();
}
})->dailyAt('09:00')->onOneServer(); ניטור: Healthchecks.io / Laravel Health
// Уведомление об ошибках планировщика
$schedule->job(SendDailyDigestJob::class)
->dailyAt('09:00')
->pingOnSuccess('https://hc-ping.com/success-uuid')
->pingOnFailure('https://hc-ping.com/fail-uuid')
->emailOutputOnFailure('[email protected]'); מידע נוסף על נעילות
כדי למנוע כפילויות ב-Laravel, השתמשו ב-`->onOneServer()` יחד עם נעילה מבוזרת. ב-node-cron וב-go-cron, יש ליישם נעילה ידנית דרך Redis או etcd.השוואת גישות: Laravel Schedule לעומת node-cron לעומת go-cron
| קריטריון | Laravel Schedule | node-cron | go-cron |
|---|---|---|---|
| ניטור מובנה | ✅ Ping על הצלחה/כישלון | ❌ דורש חיצוני | ❌ דורש חיצוני |
| נעילה מבוזרת | דרך Redis / מסד נתונים | דרך Redis | דרך etcd |
| קלות תחזוקה | גבוהה (artisan) | בינונית | נמוכה |
| הגנה מפני כפילויות | // app/Console/Kernel.php protected function schedule(Schedule $schedule): void { // Ежедневный дайджест в 9:00 по Москве $schedule->job(SendDailyDigestJob::class) ->dailyAt('09:00') ->timezone('Europe/Moscow') ->withoutOverlapping() // не запускать если предыдущий ещё работает ->onOneServer() // только на одном сервере при горизонтальном масштабировании ->runInBackground(); // Очистка устаревших сессий — каждый час $schedule->command('sessions:cleanup') ->hourly() ->withoutOverlapping(5) // максимум 5 минут на перекрытие ->appendOutputTo(storage_path('logs/sessions-cleanup.log')); // Ежеминутная проверка очереди уведомлений $schedule->command('notifications:send-pending') ->everyMinute() ->runInBackground() ->skip(fn() => !config('features.notifications')); // Отчёт по понедельникам в 8:00 $schedule->job(WeeklyReportJob::class) ->weekly() ->mondays() ->at('08:00'); } + # Crontab: запускать планировщик каждую минуту * * * * * cd /var/www/myapp && php artisan schedule:run >> /dev/null 2>&1 | ידני | ידני |
טעויות נפוצות והשלכותיהן
| טעות | השלכה | פתרון |
|---|---|---|
שכחת import cron from 'node-cron'; import { db } from './database'; import { emailService } from './services/email'; // Ежедневная очистка в 3:00 cron.schedule('0 3 * * *', async () => { const lock = await acquireLock('cleanup-expired-tokens'); if (!lock) return; // другой инстанс уже выполняет try { const deleted = await db.query( 'DELETE FROM password_reset_tokens WHERE expires_at < NOW()' ); console.log(`Cleaned ${deleted.rowCount} expired tokens`); } finally { await releaseLock('cleanup-expired-tokens'); } }, { timezone: 'Europe/Moscow' }); // Каждые 5 минут: обновление курсов валют cron.schedule('*/5 * * * *', async () => { try { const rates = await fetchExchangeRates(); await cache.set('exchange_rates', rates, 300); } catch (err) { console.error('Exchange rates update failed:', err); } }); | המשימה חוסמת את המתזמן, משימות הבאות מתעכבות | הוספת // Для Laravel: стандартный Cache::lock() $schedule->call(function () { $lock = Cache::lock('daily-digest', 3600); if (!$lock->get()) { return; // другой сервер уже выполняет } try { app(DigestService::class)->sendAll(); } finally { $lock->release(); } })->dailyAt('09:00')->onOneServer(); |
לא הגדרת // Уведомление об ошибках планировщика $schedule->job(SendDailyDigestJob::class) ->dailyAt('09:00') ->pingOnSuccess('https://hc-ping.com/success-uuid') ->pingOnFailure('https://hc-ping.com/fail-uuid') ->emailOutputOnFailure('[email protected]'); | כפילויות בשרתים מרובים | שימוש ב-onOneServer() + נעילה מבוזרת |
| התעלמות מניטור | שגיאות לא מתגלות במשך שבועות | חיבור Healthchecks.io או אימייל |
| TTL נעילה קטן מדי | הנעילה משתחררת מוקדם, כפילויות | הגדרת TTL = זמן ביצוע מקסימלי + מרווח ביטחון |
למה ניטור חשוב
בלי ניטור, אתם מסתכנים בגילוי בעיה מאוחר מדי. מניסיוננו, לקוח לא ידע שמשימת הפקת הדוחות שלו לא רצה במשך שבועיים—הנתונים היו מיושנים ב-30%. לאחר יישום ניטור עם התראות Telegram, זמן התגובה ירד ל-15 דקות, ועלויות פתרון התקלות ירדו ב-60%. אנחנו ממליצים לשלוח ping לנקודת בדיקה כל דקה—זה מכסה 99% מהתרחישים.
מה כלול בעבודה (תוצרים)
- קוד משימה עם הגנה מפני כפילויות (נעילה מבוזרת).
- ניטור ביצוע (Healthchecks.io או מותאם אישית).
- התראות שגיאה (Slack, Telegram, אימייל).
- תיעוד לתחזוקה והוספת משימות חדשות.
- הדרכה לצוות הלקוח.
לוח זמנים ועלות משוערים
- הגדרה פשוטה (2-3 משימות): מיום אחד, עלות אופיינית $1,500–$3,000.
- מורכבת עם ניטור ונעילות: מיומיים, עלות אופיינית $3,000–$5,000.
תהליך
- ניתוח — קביעה אילו משימות נדרשות: ניקוי מטמון, שליחת מיילים, הפקת דוחות.
- עיצוב — הגדרת מרווחים, נעילות, ניטור.
-
יישום — כתיבת קוד עם טיפול בשגיאות ו-
->withoutOverlapping(). - בדיקות — הרצה בסביבת staging, בדיקת לוגים.
- פריסה — הגדרת crontab ו-healthchecks.
המהנדסים שלנו מבטיחים שהמתזמן שלכם יפעל ביציבות. אם אתם רוצים להימנע מבעיות כפילויות, הזמינו הגדרת cron job מקצועית. צרו קשר להערכת פרויקט.







