Flask에서 매시간 실행되도록 함수를 예약하는 방법은 무엇입니까?


98

cron명령에 대한 액세스 권한이없는 Flask 웹 호스팅이 있습니다 .

매시간 파이썬 함수를 어떻게 실행할 수 있습니까?

답변:


104

APScheduler 패키지 (v3.5.3) BackgroundScheduler()에서 사용할 수 있습니다 .

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가 디버그 모드에있을 때 시작됩니다. 자세한 내용은 질문을 확인하십시오 .


1
@ user5547025 일정이 어떻게 작동합니까? schedule.py에 내용을 넣었다고 가정하면 어떻게 자동으로 실행됩니까?
Kishan Mehta 2017 년

2
user5547025가 제안한 일정은 마스터 스레드를 차단할 수있는 동기 작업을위한 것이라고 생각합니다. 차단되지 않도록 작업자 스레드를 스핀 업해야합니다.
Simon

1
경우 flask있었다 App.runonce또는 App.runForNseconds당신이 사이를 전환 할 수 schedule플라스크 러너,하지만 지금 유일한 방법이 사용되도록 이것은 사실이 아니다
lurscher

감사합니다! if name __ == "__ main " 아래에이 함수를 어디에 삽입해야 합니까? 또한 print_date_time 함수를 우리의 함수로 바꿀 수 있습니까?
Ambleu

스케줄러를 매일 한 번 실행하는 방법은 무엇입니까?
arun kumar

57

당신의 사용을 만들 수있는 APScheduler당신의 플라스크 응용 프로그램과의 인터페이스를 통해 작업을 실행 :

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
초심자 질문을해도 될까요? 왜 lambda안에 atexit.register있습니까?
Pygmalion

2
때문에이 atexit.register호출하는 기능이 필요합니다. 방금 통과했다면 호출 결과 (아마도 )를 cron.shutdown(wait=False)전달합니다 . 그래서 그 대신, 우리는 대신에 이름을 부여하고 사용하는 제로 인수 기능을 전달하고 문을 하고 우리가 대신 인라인에 등록 (제로 인수 함수 인 표현 .)cron.shutdownNone def shutdown(): cron.shutdown(wait=False)atexit.register(shutdown)lambda:
숀 비에이라

감사. 그래서 문제는 내가 옳다고 이해한다면 함수에 인자를 전달하고 싶다는 것입니다.
Pygmalion

51

나는 응용 프로그램 스케줄러의 개념이 조금 새롭지 만 여기서 APScheduler v3.3.1 에서 찾은 것은 약간 다릅니다. 최신 버전의 경우 패키지 구조, 클래스 이름 등이 변경되었다고 생각하므로 최근에 만든 새로운 솔루션을 기본 Flask 응용 프로그램과 통합했습니다.

#!/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()

이 예제에 대한 업데이트에 관심이있는 사람이 있다면 이 요지를 여기에 남겨 두겠습니다 .

다음은 향후 읽기를위한 몇 가지 참고 자료입니다.


2
이것은 훌륭하게 작동하며 더 많은 사람들 이이 스레드를 볼 때 더 높은 순위로 선정되기를 바랍니다.
Mwspencer

1
PythonAnywhere와 같은 웹에있는 응용 프로그램에서 이것을 사용해 보셨습니까?
Mwspencer

1
감사합니다, @Mwspencer. 예, 사용했으며 잘 작동합니다. :)에서 제공하는 더 많은 옵션을 탐색하는 것이 좋지만 apscheduler.schedulers.background애플리케이션에 다른 유용한 시나리오가 발생할 수 있기 때문입니다. 문안 인사.
ivanleoncz 2018

2
앱이있을 때 스케줄러를 종료하는 것을 잊지 마십시오
Hanynowsky

1
여보세요! gunicorn 작업자가 여러 명인 상황에 대해 조언을 해주실 수 있습니까? 내 말은, 스케줄러는 작업 자당 한 번 실행됩니까?
ElPapi42

13

APScheduler의 BackgroundScheduler 를 사용하여 간격 작업을 Flask 앱에 통합 할 수 있습니다. 다음은 블루 프린트와 앱 팩토리 ( init .py) 를 사용하는 예입니다 .

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()

도움이되기를 바랍니다 :)

참고 :

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

1
나는 return 문은 예외 제기하지 않을 것을 확신합니다
타마스 Hegedus

12

간단한 솔루션을 위해 다음과 같은 경로를 추가 할 수 있습니다.

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

그런 다음 이 엔드 포인트에 주기적으로 POST 하는 unix cron 작업추가하십시오 . 예를 들어 터미널 유형에서 1 분에 한 번 실행하고 crontab -e다음 줄을 추가합니다.

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

(작업이 실행될 때 PATH가 없기 때문에 curl 경로가 완료되어야합니다. 시스템에서 curl을 수행 할 전체 경로는에서 찾을 수 있습니다. which curl)

작업을 수동으로 테스트하기 쉽고 추가 종속성이 없으며 특별한 작업이 없기 때문에 이해하기 쉽습니다.

보안

크론 작업을 암호로 보호하려면을 수행 pip install Flask-BasicAuth한 다음 앱 구성에 자격 증명을 추가 할 수 있습니다.

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

작업 엔드 포인트를 비밀번호로 보호하려면 다음을 수행하십시오.

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

그런 다음 크론 작업에서 호출하려면 :

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

1
당신은 천재입니다! 정말 편리한 팁입니다.
Sharl Sherif


4

on / off 제어와 run_job ()에 대한 매개 변수를 사용하여 스케줄 및 다중 처리를 사용하는 완전한 예제는 리턴 코드가 단순화되고 간격이 10 초로 설정되어 every(2).hour.do()2 시간으로 변경됩니다 . 일정은 매우 인상적이며 표류하지 않으며 일정을 잡을 때 100ms 이상 떨어져있는 것을 본 적이 없습니다. 종료 방법이 있으므로 스레딩 대신 다중 처리를 사용합니다.

#!/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)

다음을 실행하여이를 테스트합니다.

$ 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

타이머가 켜져있는 매 10 초마다 콘솔에 타이머 메시지가 표시됩니다.

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

다중 처리 전문가는 아니지만 이것을 사용하면 피클 오류가 발생할 가능성이 큽니다.
Patrick Mutuku

@PatrickMutuku, 디지털 직렬화 (쿠키, 임시 파일)에서 볼 수있는 유일한 문제는 비동기 및 웹 소켓이지만 Flask는 API가 아닙니다 . github.com/kennethreitz/responder보십시오 . Flask는 apache wsgi의 간단한 프런트 엔드로 순수 REST에서 탁월합니다.
MortenB

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.