如何在Python Flask中异步执行定时API任务而不阻塞主服务?
问题描述
我通过football-data.org API获取赛事数据,为规避请求限制加入了定时逻辑,但将该代码整合到Flask网站主文件后,time.sleep()会阻塞其他核心功能的执行。请问是否有办法实现这些任务与核心功能同时运行而不造成阻塞?
原代码如下:
from flask import Flask, redirect, render_template, flash, request, url_for from pymongo import MongoClient import http.client import json import time import requests app = Flask(__name__) cluster = MongoClient("mongodb://localhost:27017/footballScoreDB") db = cluster["footballScorePy"] collection = db["footballScore"] def get_matches(league): status = ["FINISHED", "LIVE", "SCHEDULED"] x = 0 while(x < 3): uri = "https://api.football-data.org/v4/competitions/" + league + "/matches?status=" + status[x] headers = { 'X-Auth-Token': 'api key' } response = requests.get(uri, headers=headers) matches = response.json()["matches"] length = response.json()["resultSet"]["count"] i=0 collection.delete_many({"league": league , "status": status[x]}) while(i != length): awayTeam = matches[i]["awayTeam"]["name"] homeTeam = matches[i]["homeTeam"]["name"] homeTeamCrest = matches[i]["homeTeam"]["crest"] awayTeamCrest = matches[i]["awayTeam"]["crest"] date = matches[i]["utcDate"] winner = matches[i]["score"]["winner"] awayScore = matches[i]["score"]["fullTime"]["away"] awayHalfTimeScore = matches[i]["score"]["halfTime"]["away"] homeScore = matches[i]["score"]["fullTime"]["home"] homeHalfTimeScore = matches[i]["score"]["halfTime"]["home"] if status == "FINISHED": post = { "homeTeam": homeTeam, "awayTeam": awayTeam, "date": date, "homeScore": homeScore, "awayScore": awayScore, "homeHalfTimeScore": homeHalfTimeScore, "awayHalfTimeScore": awayHalfTimeScore, "winner": winner, "status": status[x], "league": league, "awayTeamCrest": awayTeamCrest, "homeTeamCrest": homeTeamCrest } elif status == "LIVE": post = { "homeTeam": homeTeam, "awayTeam": awayTeam, "date": "LIVE", "homeScore": homeScore, "awayScore": awayScore, "homeHalfTimeScore": 0, "awayHalfTimeScore": 0, "winner": 0, "status": status[x], "league": league, "awayTeamCrest": awayTeamCrest, "homeTeamCrest": homeTeamCrest } else : post = { "homeTeam": homeTeam, "awayTeam": awayTeam, "date": date, "homeScore": 0, "awayScore": 0, "homeHalfTimeScore": 0, "awayHalfTimeScore": 0, "winner": 0, "status": status[x], "league": league, "awayTeamCrest": awayTeamCrest, "homeTeamCrest": homeTeamCrest } if collection.find_one(post) == False : collection.insert_one(post) i+=1 x+=1 @app.route("/") def home(): return "testing" if __name__ == "__main__": app.run(debug=True, port="4000") while True: get_matches("PL") get_matches("SA") get_matches("DED") time.sleep(60)
解决方案
方法1:使用Python内置threading模块
将定时任务放到独立线程中运行,避免阻塞Flask主进程。同时修复原代码中的两处逻辑错误:
- 状态判断时误用整个
status列表,应改为当前循环的status[x] - 数据库存在性判断错误,
find_one返回None而非False
修改后的代码:
from flask import Flask, redirect, render_template, flash, request, url_for from pymongo import MongoClient import requests import time import threading app = Flask(__name__) cluster = MongoClient("mongodb://localhost:27017/footballScoreDB") db = cluster["footballScorePy"] collection = db["footballScore"] def get_matches(league): status = ["FINISHED", "LIVE", "SCHEDULED"] x = 0 while x < 3: uri = f"https://api.football-data.org/v4/competitions/{league}/matches?status={status[x]}" headers = {'X-Auth-Token': 'api key'} response = requests.get(uri, headers=headers) matches = response.json()["matches"] length = response.json()["resultSet"]["count"] i = 0 collection.delete_many({"league": league, "status": status[x]}) while i != length: awayTeam = matches[i]["awayTeam"]["name"] homeTeam = matches[i]["homeTeam"]["name"] homeTeamCrest = matches[i]["homeTeam"]["crest"] awayTeamCrest = matches[i]["awayTeam"]["crest"] date = matches[i]["utcDate"] winner = matches[i]["score"]["winner"] awayScore = matches[i]["score"]["fullTime"]["away"] awayHalfTimeScore = matches[i]["score"]["halfTime"]["away"] homeScore = matches[i]["score"]["fullTime"]["home"] homeHalfTimeScore = matches[i]["score"]["halfTime"]["home"] if status[x] == "FINISHED": post = { "homeTeam": homeTeam, "awayTeam": awayTeam, "date": date, "homeScore": homeScore, "awayScore": awayScore, "homeHalfTimeScore": homeHalfTimeScore, "awayHalfTimeScore": awayHalfTimeScore, "winner": winner, "status": status[x], "league": league, "awayTeamCrest": awayTeamCrest, "homeTeamCrest": homeTeamCrest } elif status[x] == "LIVE": post = { "homeTeam": homeTeam, "awayTeam": awayTeam, "date": "LIVE", "homeScore": homeScore, "awayScore": awayScore, "homeHalfTimeScore": 0, "awayHalfTimeScore": 0, "winner": 0, "status": status[x], "league": league, "awayTeamCrest": awayTeamCrest, "homeTeamCrest": homeTeamCrest } else: post = { "homeTeam": homeTeam, "awayTeam": awayTeam, "date": date, "homeScore": 0, "awayScore": 0, "homeHalfTimeScore": 0, "awayHalfTimeScore": 0, "winner": 0, "status": status[x], "league": league, "awayTeamCrest": awayTeamCrest, "homeTeamCrest": homeTeamCrest } if not collection.find_one(post): collection.insert_one(post) i += 1 x += 1 def scheduled_task(): while True: get_matches("PL") get_matches("SA") get_matches("DED") time.sleep(60) @app.route("/") def home(): return "testing" if __name__ == "__main__": # 启动定时任务线程,daemon=True表示随主进程退出而终止 task_thread = threading.Thread(target=scheduled_task, daemon=True) task_thread.start() # 启动Flask服务 app.run(debug=True, port="4000")
方法2:使用APScheduler扩展(适合复杂定时需求)
如果需要更灵活的定时配置(如Cron表达式、指定时间点执行),可以使用APScheduler这个Flask常用的定时任务扩展。
步骤1:安装依赖
pip install apscheduler
步骤2:修改代码整合APScheduler
from flask import Flask from pymongo import MongoClient import requests from apscheduler.schedulers.background import BackgroundScheduler app = Flask(__name__) cluster = MongoClient("mongodb://localhost:27017/footballScoreDB") db = cluster["footballScorePy"] collection = db["footballScore"] def get_matches(league): # 内容同方法1中修正后的get_matches函数,此处省略重复代码 status = ["FINISHED", "LIVE", "SCHEDULED"] x = 0 while x < 3: uri = f"https://api.football-data.org/v4/competitions/{league}/matches?status={status[x]}" headers = {'X-Auth-Token': 'api key'} response = requests.get(uri, headers=headers) matches = response.json()["matches"] length = response.json()["resultSet"]["count"] i = 0 collection.delete_many({"league": league, "status": status[x]}) while i != length: awayTeam = matches[i]["awayTeam"]["name"] homeTeam = matches[i]["homeTeam"]["name"] homeTeamCrest = matches[i]["homeTeam"]["crest"] awayTeamCrest = matches[i]["awayTeam"]["crest"] date = matches[i]["utcDate"] winner = matches[i]["score"]["winner"] awayScore = matches[i]["score"]["fullTime"]["away"] awayHalfTimeScore = matches[i]["score"]["halfTime"]["away"] homeScore = matches[i]["score"]["fullTime"]["home"] homeHalfTimeScore = matches[i]["score"]["halfTime"]["home"] if status[x] == "FINISHED": post = { "homeTeam": homeTeam, "awayTeam": awayTeam, "date": date, "homeScore": homeScore, "awayScore": awayScore, "homeHalfTimeScore": homeHalfTimeScore, "awayHalfTimeScore": awayHalfTimeScore, "winner": winner, "status": status[x], "league": league, "awayTeamCrest": awayTeamCrest, "homeTeamCrest": homeTeamCrest } elif status[x] == "LIVE": post = { "homeTeam": homeTeam, "awayTeam": awayTeam, "date": "LIVE", "homeScore": homeScore, "awayScore": awayScore, "homeHalfTimeScore": 0, "awayHalfTimeScore": 0, "winner": 0, "status": status[x], "league": league, "awayTeamCrest": awayTeamCrest, "homeTeamCrest": homeTeamCrest } else: post = { "homeTeam": homeTeam, "awayTeam": awayTeam, "date": date, "homeScore": 0, "awayScore": 0, "homeHalfTimeScore": 0, "awayHalfTimeScore": 0, "winner": 0, "status": status[x], "league": league, "awayTeamCrest": awayTeamCrest, "homeTeamCrest": homeTeamCrest } if not collection.find_one(post): collection.insert_one(post) i += 1 x += 1 def scheduled_task(): get_matches("PL") get_matches("SA") get_matches("DED") @app.route("/") def home(): return "testing" if __name__ == "__main__": # 初始化后台调度器 scheduler = BackgroundScheduler() # 添加定时任务:每60秒执行一次scheduled_task函数 scheduler.add_job(scheduled_task, 'interval', seconds=60) # 启动调度器 scheduler.start() # 启动Flask服务 app.run(debug=True, port="4000")
注意事项
- 生产环境中不要使用
app.run()启动Flask,应使用Gunicorn、uWSGI等生产级服务器,同时确保定时任务的启动方式与服务器兼容。 - PyMongo客户端本身是线程安全的,无需额外处理数据库操作的线程同步问题。
- 严格遵守football-data.org的API请求限制,避免因请求过于频繁被封禁。
内容的提问来源于stack exchange,提问作者user16462824
相关产品推荐
相关产品推荐

