Yıllarca cron job yazan biri olarak şunu söyleyebilirim: cron, sistem yöneticisinin en sadık dostudur. Ama her dost gibi, onun da sinir bozucu kısıtlamaları var. Özellikle “bu işi her 30 saniyede bir çalıştırmam lazım” dediğin anda cron sana soğuk bir yüz gösterir. Çünkü cron’un bildiği en küçük zaman birimi bir dakikadır. Bu yazıda dakika altı görevlerin neden gerekli olduğunu, cron’u bu sınırın içinde zorlamak için kullanılan hileleri ve daha temiz alternatif çözümleri inceleyeceğiz.
Cron Neden Dakika Altında Çalışamaz?
Cron’un mimarisi 1970’lere dayanıyor. O dönemde sistem kaynakları kıymetliydi ve birinin her 10 saniyede bir bir şeyler çalıştırmasına gerek yoktu. Cron daemon’ı, sistem saatini her dakika kontrol eder ve o dakikaya uyan görevleri tetikler. Bu tasarım bilinçli bir tercihti, bir bug değil.
Crontab sözdiziminin beş alanı var: dakika, saat, gün, ay, haftanın günü. Saniye alanı hiç yok. Dolayısıyla /30 gibi bir şey standart cron’da çalışmaz.
Peki gerçek dünyada neden dakika altı görevlere ihtiyaç duyuyoruz?
- Servis sağlık kontrolleri: Bir web servisinin her 15 saniyede bir yanıt verip vermediğini kontrol etmek
- Log akışı işleme: Sürekli gelen log satırlarını küçük aralıklarla parse etmek
- Metrik toplama: CPU, bellek, disk I/O gibi anlık metrikleri sık aralıklarla kaydetmek
- Kuyruk işleme: Mesaj kuyruklarındaki öğeleri hızlı tüketmek
- IoT veri toplama: Sensörlerden saniye bazında veri okumak
Cron ile Dakika Altı Görev: Eski Ama İşe Yarayan Hack
Bazı durumlar için cron’u hacklemeye çalışmak mantıklı olabilir. En yaygın yöntem, bir dakika içinde birden fazla komutu sleep ile arka arkaya çalıştırmaktır.
# Her 20 saniyede bir çalıştırmak için crontab girişi
* * * * * /opt/scripts/check_service.sh
* * * * * sleep 20 && /opt/scripts/check_service.sh
* * * * * sleep 40 && /opt/scripts/check_service.sh
Bu yöntem çirkin görünüyor, çünkü öyle. Ama küçük ve kritik olmayan görevler için işe yarar. Sorunları şunlar:
- Bakımı zorlaşır: 10 saniyelik aralık istediğinde 6 satır yazman gerekir
- Görev süresi garanti edilemez:
check_service.sh25 saniye sürerse örtüşme olur - Crontab okunaksız bir hal alır
- Hata yönetimi yok
Bunu biraz daha düzgün yapmak istiyorsan, wrapper bir script yazabilirsin:
#!/bin/bash
# /opt/scripts/run_every_n_seconds.sh
# Kullanim: ./run_every_n_seconds.sh 15 /path/to/script.sh
INTERVAL=$1
SCRIPT=$2
RUNTIME=60 # Bir dakika boyunca calistir
end_time=$((SECONDS + RUNTIME))
while [ $SECONDS -lt $end_time ]; do
$SCRIPT &
sleep $INTERVAL
done
Crontab’a şu şekilde eklersin:
* * * * * /opt/scripts/run_every_n_seconds.sh 15 /opt/scripts/check_service.sh
Bu yaklaşım biraz daha temiz ama hala ciddi bir alternatif değil. Çünkü dakika sınırında yeniden başlama anında küçük bir kaymalar olabilir ve görev durumu yönetimi hala çözümsüz.
Systemd Timers: Modern Linux’un Cevabı
Eğer sisteminde systemd varsa (ve muhtemelen vardır, 2024’te Red Hat tabanlı veya Debian tabanlı modern bir dağıtım kullanıyorsan kesinlikle var), systemd timer birimleri cron’un çok güçlü bir alternatifidir.
Systemd timer’lar iki dosyadan oluşur: bir .service birimi ve bir .timer birimi.
Önce servisi tanımlayalım:
# /etc/systemd/system/servis-kontrol.service
[Unit]
Description=Servis Saglik Kontrolu
After=network.target
[Service]
Type=oneshot
User=monitoring
ExecStart=/opt/scripts/check_service.sh
StandardOutput=journal
StandardError=journal
Sonra timer birimini:
# /etc/systemd/system/servis-kontrol.timer
[Unit]
Description=Her 15 Saniyede Servis Kontrolu
Requires=servis-kontrol.service
[Timer]
OnBootSec=30s
OnUnitActiveSec=15s
AccuracySec=1s
[Install]
WantedBy=timers.target
Etkinleştirip başlatmak için:
sudo systemctl daemon-reload
sudo systemctl enable servis-kontrol.timer
sudo systemctl start servis-kontrol.timer
# Durumu kontrol etmek icin
systemctl status servis-kontrol.timer
systemctl list-timers --all
OnUnitActiveSec=15s ifadesi, son çalışmanın bitmesinden 15 saniye sonra tekrar çalıştır anlamına gelir. Bu, örtüşme sorununu otomatik olarak çözer. Eğer görev 20 saniye sürerse, bir sonraki çalışma 35. saniyede başlar, 15. saniyede değil.
AccuracySec=1s parametresi önemli: varsayılan değer 1 dakikadır ve bu, systemd’nin zamanlama hassasiyetini gevşetmesine izin verir. Sık çalışan görevler için bunu 1s yapman gerekir.
Systemd timer’larının cron’a göre avantajları:
- Saniye bazında hassasiyet destekler
- Journald entegrasyonu sayesinde tüm çıktılar otomatik loglanır
- Bağımlılık yönetimi mevcut (network.target gibi)
- Kaçırılan görevler için
Persistent=trueile telafi mekanizması var - Kaynak kısıtlama yapılabilir (CPU, bellek limitleri)
systemctl statusile anlık durum izlenebilir
Anacron ve Fcron: Hafif Alternatifler
Anacron, özellikle her zaman açık olmayan sistemler için tasarlanmıştır. Laptop’lar ve masaüstü sistemlerde çok kullanılır. Saniye bazında çalışmaz ama kaçırılan günlük/haftalık görevleri tazeler.
Fcron ise hem cron hem anacron özelliklerini birleştirir ve saniye bazında çalışmayı destekler:
# fcrontab ornegi - her 30 saniyede bir
@30s /opt/scripts/metric_collector.sh
# Her 45 saniyede bir, bir onceki bitmeden baslamaz
@serial 45s /opt/scripts/db_health_check.sh
Fcron’u Ubuntu/Debian’a kurmak için:
sudo apt-get install fcron
sudo systemctl enable fcron
sudo systemctl start fcron
# fcrontab duzenlemek icin
fcrontab -e
Fcron hafif bir araç olduğu için embedded sistemlerde veya container ortamlarında tercih edilebilir. Ama production Linux sunucularında systemd timer’lar genellikle daha iyi bir tercih.
Run-Parts ve Wrapper Scriptler ile Yönetim
Birden fazla dakika altı görevi yönetiyorsan, onları organize etmek için bir wrapper sistemi kurabilirsin. Örneğin tüm sık çalışan scriptleri bir dizinde toplayıp tek bir koordinatör script ile yönetmek:
#!/bin/bash
# /opt/scripts/coordinator.sh
# Bu script systemd tarafindan her 10 saniyede bir cagrilir
SCRIPT_DIR="/opt/scripts/frequent"
LOCK_DIR="/var/run/coordinator"
LOG_FILE="/var/log/coordinator.log"
mkdir -p "$LOCK_DIR"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
}
run_script() {
local script="$1"
local script_name=$(basename "$script")
local lock_file="$LOCK_DIR/$script_name.lock"
# Onceki calisma hala devam ediyorsa atla
if [ -f "$lock_file" ]; then
pid=$(cat "$lock_file")
if kill -0 "$pid" 2>/dev/null; then
log "SKIP: $script_name hala calisiyor (PID: $pid)"
return
fi
fi
# Lock olustur ve scripti calistir
echo $$ > "$lock_file"
log "START: $script_name"
if "$script" >> "$LOG_FILE" 2>&1; then
log "OK: $script_name"
else
log "ERROR: $script_name (cikis kodu: $?)"
fi
rm -f "$lock_file"
}
# Dizindeki tum scriptleri calistir
for script in "$SCRIPT_DIR"/*.sh; do
[ -x "$script" ] && run_script "$script" &
done
wait
Bu coordinator script’i systemd timer ile her 10 saniyede bir çalıştırabilirsin. Script dizinine yeni bir .sh dosyası eklediğinde otomatik olarak devreye girer.
Gerçek Dünya Senaryosu: Redis Kuyruk İşleme
Bir e-ticaret sisteminde siparişler Redis kuyruğuna düşüyor ve hızla işlenmesi gerekiyor diyelim. Cron ile bunu her dakika çalıştırsan, kullanıcı en fazla 60 saniye beklemek zorunda kalır. Bu kabul edilemez.
Systemd timer ile çözüm:
# /etc/systemd/system/order-processor.service
[Unit]
Description=Siparis Kuyrugu Isleyici
After=network.target redis.service
Requires=redis.service
[Service]
Type=oneshot
User=appuser
WorkingDirectory=/var/www/app
ExecStart=/usr/bin/python3 /var/www/app/process_orders.py
TimeoutStartSec=25s
MemoryMax=512M
CPUQuota=50%
StandardOutput=journal
StandardError=journal
SyslogIdentifier=order-processor
# /etc/systemd/system/order-processor.timer
[Unit]
Description=Siparis Isleyici Timer
Requires=order-processor.service
[Timer]
OnBootSec=60s
OnUnitActiveSec=10s
AccuracySec=1s
[Install]
WantedBy=timers.target
Bu yapıda MemoryMax=512M ve CPUQuota=50% ile kuyruk işleyici diğer servislerden kaynak çalmaz. TimeoutStartSec=25s ile de 10 saniyelik döngüde işlem taşması önlenir.
Supervisor ve Process Manager Alternatifi
Bazen ihtiyaç duyulan şey aslında periyodik görev değil, sürekli çalışan bir süreçtir. Eğer her 5 saniyede bir bir şeyler kontrol ediyorsan, belki de o işi bir daemon olarak yazman gerekiyor.
Supervisor bu konuda çok kullanışlıdır:
# /etc/supervisor/conf.d/metric-collector.conf
[program:metric-collector]
command=/opt/scripts/metric_collector_daemon.sh
autostart=true
autorestart=true
startretries=5
user=monitoring
stdout_logfile=/var/log/metric-collector.log
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=5
stderr_logfile=/var/log/metric-collector-error.log
stderr_logfile_maxbytes=10MB
Daemon script’in içinde döngü:
#!/bin/bash
# /opt/scripts/metric_collector_daemon.sh
INTERVAL=5
LOG_FILE="/var/log/metrics/collector.log"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
# Sinyal yakalama - temiz kapanma icin
cleanup() {
log "Daemon durduruluyor..."
exit 0
}
trap cleanup SIGTERM SIGINT
log "Metric collector basladi (PID: $$)"
while true; do
start_time=$(date +%s%N)
# Metrikleri topla
cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
mem_usage=$(free | awk 'NR==2{printf "%.2f", $3*100/$2}')
disk_io=$(iostat -d 1 1 | awk 'NR==4{print $4}')
log "CPU: ${cpu_usage}% | MEM: ${mem_usage}% | DISK_IO: ${disk_io}"
# Metrik dosyasina yaz
echo "$(date +%s),${cpu_usage},${mem_usage},${disk_io}" >> /var/log/metrics/data.csv
# Calisma suresini hesapla ve buna gore bekle
end_time=$(date +%s%N)
elapsed=$(( (end_time - start_time) / 1000000 ))
sleep_time=$(echo "scale=3; ($INTERVAL * 1000 - $elapsed) / 1000" | bc)
[ $(echo "$sleep_time > 0" | bc) -eq 1 ] && sleep "$sleep_time"
done
Bu yaklaşım, gerçek anlamda düzenli aralıklar sağlar çünkü çalışma süresini hesaba katar.
Windows Tarafında: Task Scheduler’ın Kısıtları
Linux’ta bu kadar konuştuk, Windows tarafında da durum benzer. Windows Task Scheduler arayüzü saniye bazında tetiklenmeyi doğrudan desteklemiyor gibi görünse de, aslında XML ile tanımladığında saniye bazında çalıştırma mümkün:
<!-- C:Taskssaniye-bazli-gorev.xml -->
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.4">
<Triggers>
<TimeTrigger>
<Repetition>
<Interval>PT30S</Interval>
<StopAtDurationEnd>false</StopAtDurationEnd>
</Repetition>
<StartBoundary>2024-01-01T00:00:00</StartBoundary>
<Enabled>true</Enabled>
</TimeTrigger>
</Triggers>
<Actions>
<Exec>
<Command>powershell.exe</Command>
<Arguments>-File "C:Scriptscheck-service.ps1"</Arguments>
</Exec>
</Actions>
</Task>
PowerShell ile bu görevi oluşturmak:
# Her 30 saniyede bir calisan Windows gorevi olustur
$action = New-ScheduledTaskAction -Execute "powershell.exe" `
-Argument '-NonInteractive -File "C:Scriptscheck-service.ps1"'
$trigger = New-ScheduledTaskTrigger -RepetitionInterval (New-TimeSpan -Seconds 30) `
-Once -At (Get-Date)
$settings = New-ScheduledTaskSettingsSet `
-ExecutionTimeLimit (New-TimeSpan -Seconds 25) `
-MultipleInstances IgnoreNew
Register-ScheduledTask -TaskName "ServisKontrol-30s" `
-Action $action `
-Trigger $trigger `
-Settings $settings `
-RunLevel Highest `
-Force
ExecutionTimeLimit burada kritik: 30 saniyede bir çalışan bir görevin 25 saniye zaman sınırı varsa, örtüşme otomatik olarak önlenir.
Hangi Aracı Ne Zaman Seçmeli
Her senaryoya uyan tek bir çözüm yok. Tercihini yaparken şu kriterlere bakabilirsin:
- Cron (sleep ile hack): Çok basit, kritik olmayan, 20+ saniyelik aralıklar için geçici çözüm. Tavsiye edilmez ama hızlı fix olarak işe yarar.
- Systemd timer: Modern Linux sunucularda birincil tercih olmalı. Saniye bazında çalışma, log entegrasyonu, kaynak yönetimi. Özellikle production ortamlar için ideal.
- Fcron: Systemd’nin olmadığı ortamlar, hafif container’lar veya saniye bazında cron benzeri sözdizimi isteyenler için.
- Supervisor + daemon script: Sürekli çalışması gereken, kısa aralıklarda tekrarlayan görevler için. Özellikle 10 saniyenin altındaki aralıklar.
- Mesaj kuyruğu (RabbitMQ, Redis Streams): Kuyruk tabanlı tetiklenme gereken, yük dengeleme ve ölçekleme ihtiyacı olan görevler için. Bu artık “zamanlama” değil, “event-driven” mimariye geçiş demek.
Gözetlenmesi Gereken Tuzaklar
Dakika altı görevler yazarken dikkat edilmesi gereken birkaç önemli nokta var:
Görev örtüşmesi: En yaygın sorun. Görev 15 saniyede bir çalışıyor ama 20 saniye sürüyorsa önceki bitmeden yenisi başlar. Lock mekanizması şart.
Log şişmesi: Sık çalışan bir görev çok hızlı log üretir. Log rotation ve boyut sınırı kesinlikle ayarlanmalı.
Kaynak tüketimi: Her 5 saniyede bir veritabanı sorgusu yapan bir script, günde 17.000’den fazla sorgu demek. Bunu test ortamında ölçmeden production’a alma.
Alarm yorgunluğu: Monitoring sistemin bu görevi takip ediyorsa ve görev sık sık küçük gecikmeler yaşıyorsa, gereksiz alarm üretebilir. Threshold’ları sık çalışan görevlere göre ayarla.
Zaman dilimleri: Özellikle OnCalendar ile tanımlanan systemd timer’larda zaman dilimi farkları beklenmedik davranışlara yol açabilir. TZ=UTC ile çalışmayı tercih et.
Sonuç
Cron’un dakika sınırı onlarca yıllık bir tasarım kısıtı ve büyük ihtimalle yakın gelecekte değişmeyecek. Ama bu, cron’un kullanışlı olmadığı anlamına gelmiyor; sadece doğru aracı doğru iş için seçmen gerektiği anlamına geliyor.
Dakika altı görevler için pratik tavsiyem şu: Eğer modern bir Linux sunucu yönetiyorsan, direkt systemd timer’a geç. Karmaşık görünüyor ama bir kez kurduğunda journald entegrasyonu, kaynak kısıtlama ve bağımlılık yönetimi ile cron’dan çok daha iyi bir deneyim sunuyor. Eğer gerçekten çok kısa aralıklar (5 saniye ve altı) gerekiyorsa, supervisor ile daemon script yaklaşımı daha sağlıklı. Periyodik tetiklenme yerine olay tabanlı mimari de ciddi şekilde değerlendirilmeli; çünkü “her X saniyede kontrol et” mantığı çoğu zaman “bir şey olduğunda tepki ver” mantığına dönüştürülebilir ve bu genellikle çok daha verimli.
Araçları tanı, kısıtları bil, senaryoya uygun seç. Sysadmin’in işi bu.