Tôi có một lưu trữ web Flask không có quyền truy cập vào cron
lệnh.
Làm cách nào tôi có thể thực thi một số hàm Python mỗi giờ?
Tôi có một lưu trữ web Flask không có quyền truy cập vào cron
lệnh.
Làm cách nào tôi có thể thực thi một số hàm Python mỗi giờ?
Câu trả lời:
Bạn có thể sử dụng BackgroundScheduler()
từ gói APScheduler ( 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())
Lưu ý rằng hai trong số các bộ lập lịch này sẽ được khởi chạy khi Flask ở chế độ gỡ lỗi. Để biết thêm thông tin, hãy kiểm tra câu hỏi này .
flask
có một App.runonce
hoặc App.runForNseconds
bạn có thể chuyển đổi giữa schedule
và Á hậu bình, nhưng đây không phải là trường hợp, vì vậy cách duy nhất cho bây giờ đang sử dụng này
Bạn có thể sử dụng APScheduler
ứng dụng Flask của mình và thực hiện công việc của mình thông qua giao diện của nó:
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()
lambda
bên trong atexit.register
?
atexit.register
cần một hàm để gọi. Nếu chúng tôi vừa vượt qua, cron.shutdown(wait=False)
chúng tôi sẽ chuyển kết quả của việc gọi cron.shutdown
(có thể là như vậy None
). Vì vậy, thay vào đó, chúng tôi vượt qua một hàm không có đối số, và thay vì cho nó một cái tên và sử dụng một tuyên bố def shutdown(): cron.shutdown(wait=False)
và atexit.register(shutdown)
chúng tôi thay vì đăng ký nó inline với lambda:
(mà là một chức năng không có đối số biểu hiện .)
Tôi hơi mới với khái niệm về bộ lập lịch ứng dụng, nhưng những gì tôi tìm thấy ở đây cho APScheduler v3.3.1 , nó hơi khác một chút. Tôi tin rằng đối với các phiên bản mới nhất, cấu trúc gói, tên lớp, v.v., đã thay đổi, vì vậy tôi đang đặt ở đây một giải pháp mới mà tôi đã thực hiện gần đây, được tích hợp với ứng dụng Flask cơ bản:
#!/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()
Tôi cũng đang để Gist này ở đây , nếu ai đó quan tâm đến các bản cập nhật cho ví dụ này.
Dưới đây là một số tài liệu tham khảo, cho các bài đọc trong tương lai:
apscheduler.schedulers.background
vì có thể bạn có thể gặp phải các tình huống hữu ích khác cho ứng dụng của mình. Trân trọng.
Bạn có thể thử sử dụng BackgroundScheduler của APScheduler để tích hợp công việc ngắt quãng vào ứng dụng Flask của mình. Dưới đây là ví dụ sử dụng bản thiết kế và nhà máy ứng dụng ( 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()
Hy vọng nó giúp :)
Tham khảo:
Đối với một giải pháp đơn giản, bạn có thể thêm một tuyến đường như
@app.route("/cron/do_the_thing", methods=['POST'])
def do_the_thing():
logging.info("Did the thing")
return "OK", 200
Sau đó, thêm một công việc unix cron mà POST đến điểm cuối này theo định kỳ. Ví dụ: chạy nó mỗi phút một lần, trong loại thiết bị đầu cuối crontab -e
và thêm dòng này:
* * * * * /opt/local/bin/curl -X POST https://YOUR_APP/cron/do_the_thing
(Lưu ý rằng đường dẫn để cuộn phải hoàn chỉnh, vì khi công việc chạy nó sẽ không có PATH của bạn. Bạn có thể tìm hiểu đường dẫn đầy đủ để cuộn trên hệ thống của mình bằng cách which curl
)
Tôi thích điều này vì nó dễ dàng để kiểm tra công việc theo cách thủ công, nó không có phụ thuộc bổ sung và vì không có bất kỳ điều gì đặc biệt xảy ra nên rất dễ hiểu.
Nếu bạn muốn đặt mật khẩu bảo vệ công việc cron của mình, bạn có thể pip install Flask-BasicAuth
, sau đó thêm thông tin đăng nhập vào cấu hình ứng dụng của bạn:
app = Flask(__name__)
app.config['BASIC_AUTH_REALM'] = 'realm'
app.config['BASIC_AUTH_USERNAME'] = 'falken'
app.config['BASIC_AUTH_PASSWORD'] = 'joshua'
Để bảo vệ bằng mật khẩu cho điểm cuối công việc:
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
Sau đó, để gọi nó từ công việc cron của bạn:
* * * * * /opt/local/bin/curl -X POST https://falken:joshua@YOUR_APP/cron/do_the_thing
Một giải pháp thay thế khác có thể là sử dụng Flask-APScheduler chơi tốt với Flask, ví dụ:
Thông tin thêm tại đây:
Một ví dụ hoàn chỉnh bằng cách sử dụng lịch trình và đa xử lý, với điều khiển bật và tắt và tham số run_job (), mã trả về được đơn giản hóa và khoảng thời gian được đặt thành 10 giây, thay đổi thành 2 giờ every(2).hour.do()
. Lịch trình khá ấn tượng, nó không bị trôi và tôi chưa bao giờ thấy nó giảm quá 100ms khi lên lịch. Sử dụng đa xử lý thay vì phân luồng vì nó có phương thức kết thúc.
#!/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)
Bạn kiểm tra điều này bằng cách chỉ phát hành:
$ 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
Cứ sau 10 giây, bộ hẹn giờ được bật, nó sẽ đưa ra thông báo hẹn giờ đến bảng điều khiển:
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
Bạn có thể muốn sử dụng một số cơ chế hàng đợi với bộ lập lịch như bộ lập lịch RQ hoặc một cái gì đó nặng hơn như Cần tây (hầu hết có thể là quá mức cần thiết).