Gereksinimler ve Ön Hazırlık
Bu uygulama için Python 3.10 veya daha yeni bir sürümün yüklü olması önerilir. Görev zamanlama süreçlerinde en güvenilir ve popüler kütüphanelerden biri olan apscheduler (Advanced Python Scheduler) kütüphanesini kullanacağız. Bu kütüphane, bellek içi (in-memory) veya veritabanı destekli görev yönetimine olanak tanır.
Gerekli kütüphaneyi yüklemek için terminalinizde aşağıdaki komutu çalıştırın:
pip install apscheduler
Kurulum tamamlandıktan sonra, projenizin bağımlılıklarını yönetmek için bir sanal ortam (virtual environment) kullanmanız, sistem genelindeki kütüphanelerle çakışmaları önlemek adına en iyi pratiktir.
Temel Zamanlayıcı Mimarisi ve İlk Görev
Dinamik bir zamanlayıcı oluşturmanın ilk adımı, zamanlayıcıyı (scheduler) başlatmak ve ona temel bir görev atamaktır. BlockingScheduler, tek başına çalışan uygulamalar için uygundur; ancak dinamik yapılar için BackgroundScheduler kullanmak, uygulamanızın ana akışını engellemeden görevleri yönetmenizi sağlar.
Aşağıdaki örnekte, her 10 saniyede bir çalışan basit bir fonksiyon tanımlıyoruz:
from apscheduler.schedulers.background import BackgroundScheduler
import time
def basit_gorev():
print("Görev çalıştırıldı: Güncel zaman - " + time.strftime("%H:%M:%S"))
scheduler = BackgroundScheduler()
scheduler.add_job(basit_gorev, 'interval', seconds=10)
scheduler.start()
try:
while True:
time.sleep(1)
except (KeyboardInterrupt, SystemExit):
scheduler.shutdown()
Bu kodda add_job metodu ile zamanlayıcıya bir görev ekledik. interval parametresi, görevin belirli bir zaman aralığıyla tekrarlanacağını belirtir. try-except bloğu, programın Ctrl+C ile güvenli bir şekilde kapatılmasını sağlar.
Dinamik Görev Yönetimi: Çalışma Zamanında Görev Ekleme
Uygulamanızı dinamik kılan özellik, program çalışırken dışarıdan bir komut veya olayla yeni görevler ekleyebilmektir. Bunun için zamanlayıcıyı bir sınıf içerisinde sarmalamak ve görevleri yönetmek için bir arayüz oluşturmak en temiz yaklaşımdır.
Aşağıdaki kod örneği, sınıf tabanlı bir yapıda dinamik görev ekleme mantığını gösterir:
class GorevYoneticisi:
def __init__(self):
self.scheduler = BackgroundScheduler()
self.scheduler.start()
def gorev_ekle(self, gorev_id, fonksiyon, saniye):
self.scheduler.add_job(
fonksiyon,
'interval',
seconds=saniye,
id=gorev_id,
replace_existing=True
)
print(f"Görev {gorev_id} eklendi.")
# Kullanım
yonetici = GorevYoneticisi()
yonetici.gorev_ekle("yedekleme_1", lambda: print("Yedek alınıyor..."), 30)
Burada id parametresi kritik öneme sahiptir. Aynı ID ile tekrar görev eklediğinizde replace_existing=True parametresi sayesinde eski görev güncellenir, böylece çakışmaların önüne geçilir.
Zamanlayıcı Yöntemlerinin Karşılaştırılması
Python projelerinde görev zamanlama için kullanılan farklı yöntemlerin avantaj ve dezavantajlarını anlamak, projenizin ölçeklenebilirliği için önemlidir.
| Yöntem | Avantaj | Dezavantaj |
|---|---|---|
| APScheduler | Dinamik, esnek, veritabanı desteği | Harici kütüphane gerektirir |
| Cron (Linux) | İşletim sistemi seviyesinde, çok kararlı | Dinamik değil, Python içinden yönetimi zor |
| Celery | Dağıtık sistemler için mükemmel | Karmaşık kurulum (Redis/RabbitMQ) |
Görevleri Durdurma ve Silme İşlemleri
Dinamik bir sistemde sadece görev eklemek yetmez; aynı zamanda tamamlanan veya artık ihtiyaç duyulmayan görevleri temizlemek gerekir. remove_job fonksiyonu ile bu işlemi kolayca gerçekleştirebilirsiniz.
def gorevi_sil(yonetici, gorev_id):
try:
yonetici.scheduler.remove_job(gorev_id)
print(f"Görev {gorev_id} başarıyla kaldırıldı.")
except Exception as e:
print(f"Görev silinirken hata oluştu: {e}")
# Silme işlemi
gorevi_sil(yonetici, "yedekleme_1")
Bu yapı, özellikle kullanıcıların kendi zamanladığı görevleri yönetebildiği web uygulamalarında veya dashboard sistemlerinde oldukça kullanışlıdır.
Güvenlik Uyarısı: Dinamik olarak çalıştırılan görevlerde, kullanıcıdan gelen girdileri (örneğin görev zamanı veya fonksiyon parametreleri) doğrudan çalıştırmayın. Kod enjeksiyonu riskine karşı mutlaka girdi validasyonu (doğrulama) yapın ve sadece beyaz listede (whitelist) bulunan fonksiyonların çalıştırılmasına izin verin.
Kalıcı Görevler: Veritabanı Entegrasyonu
Uygulamanız kapandığında zamanlayıcının hafızası silinir. Görevlerin uygulama yeniden başlatıldığında da devam etmesini istiyorsanız, bir veritabanı (JobStore) kullanmalısınız. SQLAlchemy kullanarak görevleri veritabanında saklayabilirsiniz.
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
jobstores = {
'default': SQLAlchemyJobStore(url='sqlite:///gorevler.sqlite')
}
scheduler = BackgroundScheduler(jobstores=jobstores)
scheduler.start()
Bu yapı sayesinde, görev bilgileriniz bir SQLite dosyasında tutulur ve uygulamanız çökse veya kapansa bile, yeniden başlatıldığında veritabanındaki görevler otomatik olarak yüklenir.
Sıkça Sorulan Sorular
APScheduler ile Celery arasındaki fark nedir?
APScheduler, tek bir sunucu üzerinde çalışan Python uygulamaları için idealdir ve hafif bir yapıya sahiptir. Celery ise görevleri farklı sunuculara dağıtmak (distributed task queue) ve yüksek trafikli sistemlerde ölçeklenmek için tasarlanmıştır.
Zamanlayıcıda hata oluşursa ne olur?
APScheduler, görevde bir hata oluştuğunda varsayılan olarak loglama yapar ancak zamanlayıcı durmaz. Kritik görevler için try-except blokları ile hata yönetimi yapmanız ve gerekirse hata durumunda bildirim göndermeniz önerilir.
Dinamik görevlerde zaman dilimi (timezone) yönetimi nasıl yapılır?
Zamanlayıcıyı başlatırken timezone parametresini kullanarak (örneğin timezone='Europe/Istanbul') yerel saat diliminize göre görevlerin çalışmasını sağlayabilirsiniz.
Aynı anda binlerce görev çalıştırabilir miyim?
APScheduler binlerce görevi yönetebilir ancak her görev ağır bir işlem yapıyorsa, thread (iş parçacığı) havuzunu artırmanız veya ProcessPoolExecutor kullanarak çoklu işlem mimarisine geçmeniz gerekir.
Görevlerin çalışma geçmişini nasıl takip ederim?
APScheduler'ın events modülünü kullanarak, görevlerin başlama, bitme veya hata alma durumlarını dinleyebilir ve bu logları bir veritabanına veya dosyaya kaydedebilirsiniz.
Sorumluluk Reddi: Bu makalede paylaşılan kod örnekleri eğitim amaçlıdır. Üretim ortamında (production) kullanmadan önce, görevlerin hata toleransını, kaynak kullanımını ve güvenlik açıklarını (özellikle kullanıcı girdisi içeren kısımları) profesyonel standartlara göre test etmeniz sorumluluğunuzdadır.
Dağıtık Sistemlerde Zamanlayıcı Ölçeklendirme ve Performans Optimizasyonu
Uygulamanızdaki görev sayısı arttıkça, tek bir Python süreci (process) üzerinde çalışan zamanlayıcı darboğaz yaşamaya başlar. Özellikle I/O yoğunluklu görevlerde veya çok sayıda eşzamanlı (concurrent) işlem gerektiren durumlarda, zamanlayıcıyı dağıtık bir yapıya taşımak kaçınılmazdır. Performansı optimize etmek için şu stratejileri izleyebilirsiniz:
- Executor Seçimi: Varsayılan
ThreadPoolExecutoryerine, CPU yoğunluklu işlemler içinProcessPoolExecutorkullanarak Python'un Global Interpreter Lock (GIL) kısıtlamasını aşabilirsiniz. - Görev Gruplama: Çok sayıda küçük görevi tek tek zamanlamak yerine, bunları bir kuyruk yapısına (Redis gibi) alıp, zamanlayıcıyı sadece bu kuyruğu tetikleyecek bir "tetikleyici" olarak kullanın.
- Veritabanı İndeksleme: Kalıcı görevler için kullandığınız SQL tablolarında
next_run_timesütununa mutlaka indeks ekleyin. Aksi takdirde, binlerce görev arasından bir sonraki çalışacak olanı bulmak sorgu süresini ciddi oranda uzatacaktır.
from apscheduler.schedulers.background import BackgroundScheduler
from concurrent.futures import ProcessPoolExecutor
# CPU yoğunluklu görevler için ProcessPoolExecutor kullanımı
executors = {
'default': {'type': 'threadpool', 'max_workers': 20},
'processpool': ProcessPoolExecutor(max_workers=5)
}
scheduler = BackgroundScheduler(executors=executors)
scheduler.start()
Zamanlayıcı Uygulamalarında Hata Ayıklama ve İzleme (Monitoring)
Dinamik zamanlayıcılarda en büyük zorluk, arka planda sessizce başarısız olan görevleri tespit etmektir. Bir görev hata aldığında uygulamanın çökmemesi için mutlaka bir job_listener mekanizması kurmalısınız. Bu yapı, her görevin sonucunu (başarılı/başarısız) loglamanıza veya bir hata takip sistemine (Sentry gibi) göndermenize olanak tanır.
Görev Durumlarını İzleme
Aşağıdaki kod bloğu, zamanlayıcıdaki her bir görevin bitiş durumunu yakalayan basit bir dinleyiciyi göstermektedir:
from apscheduler.events import EVENT_JOB_EXECUTED, EVENT_JOB_ERROR
def job_listener(event):
if event.exception:
print(f"Görev başarısız oldu: {event.job_id}. Hata: {event.exception}")
else:
print(f"Görev başarıyla tamamlandı: {event.job_id}")
# Dinleyiciyi zamanlayıcıya ekleyin
scheduler.add_listener(job_listener, EVENT_JOB_EXECUTED | EVENT_JOB_ERROR)
Bu yöntemle, sisteminizdeki "zombi görevleri" (çalışmayan ama sistemde var görünen) anında tespit edebilir ve yönetim paneliniz üzerinden kullanıcıya hata bildirimleri gönderebilirsiniz. Ayrıca, kritik görevler için max_instances parametresini kullanarak aynı görevin üst üste binmesini engelleyerek sistem kaynaklarını koruma altına almanız önerilir.
Sonuç
Python ile dinamik bir görev zamanlayıcı oluşturmak, uygulamanızın otomasyon kapasitesini ciddi oranda artırır. Bu rehberde, apscheduler kütüphanesinin temellerinden başlayarak, dinamik görev ekleme, silme ve veritabanı ile kalıcılık sağlama konularını ele aldık. Bir sonraki adım olarak, bu zamanlayıcıyı bir FastAPI veya Flask uygulamasına entegre ederek, kullanıcıların arayüz üzerinden görevlerini yönetebileceği bir "Görev Paneli" geliştirmeyi deneyebilirsiniz.


Yorumlar (0)
Yorum Yaz