def create_app(config_name='default'):
"""应用工厂函数"""
# 在应用启动时立即配置日志
# 这确保了所有后续的日志记录都使用我们定义的滚动文件处理器
from tools.log_config import setup_logging
setup_logging()
app = Flask(__name__, instance_relative_config=True)
CORS(app)
# 加载配置
# 处理 Worker 子进程中可能出现的 "No module named 'config'" 问题
try:
from config import config
except ModuleNotFoundError:
# 动态将项目根目录加入 sys.path 后重试
current_dir = os.path.abspath(os.path.dirname(__file__))
project_root_retry = os.path.abspath(os.path.join(current_dir, os.pardir))
if project_root_retry not in sys.path:
sys.path.insert(0, project_root_retry)
from config import config # 再次尝试导入
app.config.from_object(config[config_name])
# 确保JSON序列化配置正确,解决中文乱码问题 - 适用于Flask 3.x
app.config['JSON_AS_ASCII'] = False
app.config['JSONIFY_PRETTYPRINT_REGULAR'] = True
app.config['JSON_SORT_KEYS'] = False
# Flask 3.x的正确配置方式
app.json.ensure_ascii = False
app.json.sort_keys = False
# 初始化数据库
db.init_app(app)
# 为 gevent 环境配置数据库线程安全
with app.app_context():
# 确保数据库引擎使用线程安全模式
from sqlalchemy import event
@event.listens_for(db.engine, "connect")
def set_pragma(dbapi_connection, connection_record):
# 只在数据库是 SQLite 时执行 PRAGMA 指令
if db.engine.name == 'sqlite':
cursor = dbapi_connection.cursor()
cursor.execute("PRAGMA journal_mode=WAL")
cursor.execute("PRAGMA synchronous=NORMAL")
cursor.execute("PRAGMA temp_store=memory")
cursor.execute("PRAGMA mmap_size=268435456") # 256MB
cursor.close()
# 配置Swagger UI - 简化配置
swagger_template = {
"swagger": "2.0",
"info": {
"title": "异步调度服务器",
"description": "基于Flask和Celery的异步任务调度服务",
"version": "1.0.0"
},
"host": "127.0.0.1:5000",
"basePath": "/",
"schemes": ["http"]
}
swagger_config = {
"headers": [],
"specs": [
{
"endpoint": 'apispec_1',
"route": '/apispec_1.json',
"rule_filter": lambda rule: True, # all in
"model_filter": lambda tag: True, # all in
}
],
"static_url_path": "/flasgger_static",
"swagger_ui": True,
"specs_route": "/apidocs/",
"uiversion": 3
}
Swagger(app, template=swagger_template, config=swagger_config)
# 添加全局响应处理器,确保中文编码正确
@app.after_request
def after_request(response):
# 确保JSON响应使用UTF-8编码
if response.content_type and response.content_type.startswith('application/json'):
response.headers['Content-Type'] = 'application/json; charset=utf-8'
return response
# 注册teardown函数
@app.teardown_appcontext
def shutdown_session(exception=None):
db.session.remove()
# 在应用上下文中创建所有数据表
with app.app_context():
# 导入所有模型,以便 SQLAlchemy 能够正确创建它们
from app.models import task, log # noqa
db.create_all()
return app
def create_celery_app(app): # todo 从app.config读取
"""创建独立的Celery应用"""
# 在 Celery app 创建时,也配置一次日志
# 这对 beat 调度器或直接调用任务的场景很重要
from tools.log_config import setup_logging
setup_logging()
# 创建任务上下文
class ContextTask(Task):
def __call__(self, *args, **kwargs):
with app.app_context():
return self.run(*args, **kwargs)
celery = Celery(app.import_name, task_cls=ContextTask)
# 基础配置
celery.conf.update(
broker_url='redis://127.0.0.1:6379/0',
result_backend='redis://127.0.0.1:6379/1',
# broker_url=app.config['CELERY_BROKER_URL'],
# result_backend=app.config['CELERY_RESULT_BACKEND'],
task_serializer='json',
result_serializer='json',
accept_content=['json'],
timezone='UTC',
enable_utc=True,
# Windows兼容性配置 - 使用gevent
# worker_pool='gevent', # 移除此行
worker_concurrency=20,
# 添加任务发现配置
task_always_eager=False, # 确保任务异步执行
result_expires=3600, # 结果保存1小时
# 添加队列定义,确保只使用celery队列
task_default_queue='celery',
task_create_missing_queues=True,
# 让 Celery 不要覆盖我们自定义的 root logger,直接复用现有 handler
worker_hijack_root_logger=False,
# 将标准输出/错误也重定向到 logging,确保 print() 内容写入日志
worker_redirect_stdouts=True,
worker_redirect_stdouts_level='INFO',
# 可靠性配置:防止任务丢失
task_acks_late=True, # 任务完成后才确认,防止 Worker 崩溃导致任务丢失
worker_prefetch_multiplier=1, # 减少预取,确保任务均匀分配
task_reject_on_worker_lost=True, # Worker 丢失时拒绝任务,重新排队
)
celery.set_default()
return celery
# 创建全局Celery实例
flask_app = create_app()
celery_app = create_celery_app(flask_app)
@celery_app.task
def long_task(data):
sleep(10)
return len(data)
优化以上代码便于flask编写蓝图和路由,可以拆分为几个python文件
最新发布