Flask'ta her saat çalışacak bir işlevi nasıl programlayabilirim?


Yanıtlar:


115

Sen kullanabilirsiniz BackgroundScheduler()gelen APScheduler paketi (v3.5.3):

import time
import atexit

from apscheduler.schedulers.background import BackgroundScheduler


def print_date_time():
    print(time.strftime("%A, %d. %B %Y %I:%M:%S %p"))


scheduler = BackgroundScheduler()
scheduler.add_job(func=print_date_time, trigger="interval", seconds=3)
scheduler.start()

# Shut down the scheduler when exiting the app
atexit.register(lambda: scheduler.shutdown())

Flask hata ayıklama modundayken bu programlayıcılardan ikisinin başlatılacağını unutmayın. Daha fazla bilgi için bu soruya göz atın.


1
@ user5547025 İçerikleri program.py'ye koyduğumu varsayarsak program nasıl çalışır otomatik olarak nasıl çalışacak?
Kishan Mehta

2
Bence user5547025'in önerdiği zamanlama, ana iş parçacığını engelleyebilecek eşzamanlı görevler içindir. Engellememesi için bir işçi iş parçacığını döndürmeniz gerekecek.
Simon

1
Eğer flaskbir App.runonceya da şişe koşucusu App.runForNsecondsarasında geçiş yapabilseydin schedule, ama durum böyle değil, bu yüzden şimdilik tek yol bunu kullanmak
lurscher

Bunun için teşekkürler! İsim __ == "__ main " ise bu işlevi nereye ekleyebilirim ? Ayrıca print_date_time işlevini işlevimizle değiştirebiliriz değil mi?
Ambleu

Her gün bir kez programlayıcı nasıl çalıştırılır?
arun kumar

61

APSchedulerFlask uygulamanızda faydalanabilir ve işlerinizi arayüzü aracılığıyla çalıştırabilirsiniz:

import atexit

# v2.x version - see https://stackoverflow.com/a/38501429/135978
# for the 3.x version
from apscheduler.scheduler import Scheduler
from flask import Flask

app = Flask(__name__)

cron = Scheduler(daemon=True)
# Explicitly kick off the background thread
cron.start()

@cron.interval_schedule(hours=1)
def job_function():
    # Do your work here


# Shutdown your cron thread if the web process is stopped
atexit.register(lambda: cron.shutdown(wait=False))

if __name__ == '__main__':
    app.run()

1
Acemi bir soru sorabilir miyim? Neden var lambdaiçinde atexit.register?
Pygmalion

2
Çünkü atexit.registerçağırmak için bir işleve ihtiyaç var. Eğer cron.shutdown(wait=False)geçersek, aramanın sonucunu geçirmiş oluruz cron.shutdown(muhtemelen budur None). Bunun yerine, bunun yerine bir ad vererek ve bir kullanmanın sıfır argüman fonksiyonunu geçmesi ve deyimi def shutdown(): cron.shutdown(wait=False) ve atexit.register(shutdown)bunun yerine satır içi ile kayıt lambda:(sıfır argüman fonksiyonu olan ifade .)
Sean Vieira

Teşekkürler. Öyleyse sorun şu ki, doğru anladıysam, fonksiyona argüman iletmek istiyoruz.
Pygmalion

55

Uygulama zamanlayıcıları konusunda biraz yeniyim, ancak burada APScheduler v3.3.1 için bulduğum şey biraz farklı. En yeni sürümler için paket yapısının, sınıf adlarının vb. Değiştiğine inanıyorum, bu yüzden buraya yakın zamanda temel bir Flask uygulamasıyla entegre olarak yaptığım yeni bir çözümü koyuyorum:

#!/usr/bin/python3
""" Demonstrating Flask, using APScheduler. """

from apscheduler.schedulers.background import BackgroundScheduler
from flask import Flask

def sensor():
    """ Function for test purposes. """
    print("Scheduler is alive!")

sched = BackgroundScheduler(daemon=True)
sched.add_job(sensor,'interval',minutes=60)
sched.start()

app = Flask(__name__)

@app.route("/home")
def home():
    """ Function for test purposes. """
    return "Welcome Home :) !"

if __name__ == "__main__":
    app.run()

Ben de bu Gist'i burada bırakıyorumBu örnek için güncellemelere ilgi duyan varsa, .

Gelecekteki okumalar için bazı referanslar:


2
Bu harika çalışıyor, umarım daha fazla insan bu konuyu gördükçe en üst sıralarda oylanır.
Mwspencer

1
Bunu, PythonAnywhere gibi web'de bulunan bir uygulamada kullanmayı denediniz mi?
Mwspencer

1
Teşekkürler @Mwspencer. Evet, kullandım ve iyi çalışıyor :), tarafından sağlanan daha fazla seçeneği keşfetmenizi tavsiye etmeme rağmen apscheduler.schedulers.background, uygulamanız için başka faydalı senaryolarla karşılaşmanız mümkün olabilir. Saygılarımızla.
ivanleoncz

2
Uygulama mevcut olduğunda planlayıcıyı kapatmayı unutmayın
Hanynowsky

1
Merhaba! Birden fazla silahlı mısır işçisinin olduğu bir durum için tavsiyelerde bulunabilir misiniz? Yani, planlayıcı çalışan başına bir kez mi çalıştıracak?
ElPapi 42

13

Aralıklı işi Flask uygulamanıza entegre etmek için APScheduler BackgroundScheduler'ı kullanmayı deneyebilirsiniz . Aşağıda, plan ve uygulama fabrikası ( init .py) kullanan örnek verilmiştir :

from datetime import datetime

# import BackgroundScheduler
from apscheduler.schedulers.background import BackgroundScheduler
from flask import Flask

from webapp.models.main import db 
from webapp.controllers.main import main_blueprint    

# define the job
def hello_job():
    print('Hello Job! The time is: %s' % datetime.now())

def create_app(object_name):
    app = Flask(__name__)
    app.config.from_object(object_name)
    db.init_app(app)
    app.register_blueprint(main_blueprint)
    # init BackgroundScheduler job
    scheduler = BackgroundScheduler()
    # in your case you could change seconds to hours
    scheduler.add_job(hello_job, trigger='interval', seconds=3)
    scheduler.start()

    try:
        # To keep the main thread alive
        return app
    except:
        # shutdown if app occurs except 
        scheduler.shutdown()

Umarım yardımcı olur :)

Referans:

  1. https://github.com/agronholm/apscheduler/blob/master/examples/schedulers/background.py

1
Bir dönüş ifadesinin asla bir istisna yaratmayacağından eminim
Tamas Hegedus

13

Basit bir çözüm için aşağıdaki gibi bir rota ekleyebilirsiniz:

@app.route("/cron/do_the_thing", methods=['POST'])
def do_the_thing():
    logging.info("Did the thing")
    return "OK", 200

Ardından , periyodik olarak bu uç noktaya POST gönderen bir unix cron işi ekleyin . Örneğin, terminal türünde dakikada bir çalıştırmak crontab -eve şu satırı eklemek için:

* * * * * /opt/local/bin/curl -X POST https://YOUR_APP/cron/do_the_thing

(İş çalışırken YOLUNUZ olmayacağından rotasyon yolunun tamamlanması gerektiğini unutmayın. Sisteminizde curl için tam yolu şu şekilde bulabilirsiniz: which curl )

İşi manuel olarak test etmenin kolay olması, fazladan bağımlılıkları olmaması ve özel bir şey olmadığı için anlaşılması kolay olması nedeniyle bunu seviyorum.

Güvenlik

Cron işinizi parola ile korumak isterseniz, bunu yapabilir pip install Flask-BasicAuthve ardından kimlik bilgilerini uygulama yapılandırmanıza ekleyebilirsiniz:

app = Flask(__name__)
app.config['BASIC_AUTH_REALM'] = 'realm'
app.config['BASIC_AUTH_USERNAME'] = 'falken'
app.config['BASIC_AUTH_PASSWORD'] = 'joshua'

İş uç noktasını parola ile korumak için:

from flask_basicauth import BasicAuth
basic_auth = BasicAuth(app)

@app.route("/cron/do_the_thing", methods=['POST'])
@basic_auth.required
def do_the_thing():
    logging.info("Did the thing a bit more securely")
    return "OK", 200

Sonra onu cron işinizden çağırmak için:

* * * * * /opt/local/bin/curl -X POST https://falken:joshua@YOUR_APP/cron/do_the_thing

1
Sen bir dahisin! gerçekten kullanışlı bir ipucu.
Sharl Sherif


4

Açma ve kapama kontrolü ve run_job () parametresi ile zamanlama ve çoklu işlemenin kullanıldığı eksiksiz bir örnek, dönüş kodları basitleştirilmiştir ve aralık 10 saniye olarak ayarlanmıştır, 2 saat olarak değiştirilmiştir every(2).hour.do(). Program oldukça etkileyici, sürüklenmiyor ve planlarken 100 ms'den fazla kapalı olduğunu hiç görmedim. Sonlandırma yöntemine sahip olduğu için iş parçacığı yerine çoklu işlemenin kullanılması.

#!/usr/bin/env python3

import schedule
import time
import datetime
import uuid

from flask import Flask, request
from multiprocessing import Process

app = Flask(__name__)
t = None
job_timer = None

def run_job(id):
    """ sample job with parameter """
    global job_timer
    print("timer job id={}".format(id))
    print("timer: {:.4f}sec".format(time.time() - job_timer))
    job_timer = time.time()

def run_schedule():
    """ infinite loop for schedule """
    global job_timer
    job_timer = time.time()
    while 1:
        schedule.run_pending()
        time.sleep(1)

@app.route('/timer/<string:status>')
def mytimer(status, nsec=10):
    global t, job_timer
    if status=='on' and not t:
        schedule.every(nsec).seconds.do(run_job, str(uuid.uuid4()))
        t = Process(target=run_schedule)
        t.start()
        return "timer on with interval:{}sec\n".format(nsec)
    elif status=='off' and t:
        if t:
            t.terminate()
            t = None
            schedule.clear()
        return "timer off\n"
    return "timer status not changed\n"

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

Bunu sadece yayınlayarak test edersiniz:

$ curl http://127.0.0.1:5000/timer/on
timer on with interval:10sec
$ curl http://127.0.0.1:5000/timer/on
timer status not changed
$ curl http://127.0.0.1:5000/timer/off
timer off
$ curl http://127.0.0.1:5000/timer/off
timer status not changed

Zamanlayıcının açık olduğu her 10 saniyede bir konsola bir zamanlayıcı mesajı gönderir:

127.0.0.1 - - [18/Sep/2018 21:20:14] "GET /timer/on HTTP/1.1" 200 -
timer job id=b64ed165-911f-4b47-beed-0d023ead0a33
timer: 10.0117sec
timer job id=b64ed165-911f-4b47-beed-0d023ead0a33
timer: 10.0102sec

Çoklu işlem konusunda uzman değilim, ancak bunu kullanırsanız büyük olasılıkla turşu hataları alırsınız.
Patrick Mutuku

@PatrickMutuku, dijital serileştirmede (çerezler, geçici dosyalar) gördüğüm tek sorun zaman uyumsuz ve web soketleridir, ancak Flask sizin api'niz değildir, github.com/kennethreitz/responder adresine bakın . Flask, apache wsgi'de basit bir ön uç ile saf REST'te mükemmeldir.
MortenB

2

Basit bir apscheduler yerine flask kullanmayı denedim, yüklemeniz gereken şey

pip3 kurulum flask_apscheduler

Kodumun bir örneği aşağıdadır:

from flask import Flask
from flask_apscheduler import APScheduler

app = Flask(__name__)
scheduler = APScheduler()

def scheduleTask():
    print("This test runs every 3 seconds")

if __name__ == '__main__':
    scheduler.add_job(id = 'Scheduled Task', func=scheduleTask, trigger="interval", seconds=3)
    scheduler.start()
    app.run(host="0.0.0.0")

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.