在 Flask 应用开发中,使用工厂函数创建应用实例,并借助 uWSGI 服务进行部署,是常见且高效的组合。
然而,在实际操作过程中,uWSGI 配置文件与应用启动函数之间的关系复杂,容易引发各种问题。
本文将详细探讨其中的关键要点和注意事项,帮助开发者顺利完成 Flask 应用的部署。
一、Flask 应用工厂函数简介
Flask 应用工厂函数是一种创建 Flask 应用实例的设计模式。通过将应用的创建逻辑封装在函数中,我们可以实现更灵活的配置和复用。例如:
python">from flask import Flask
# 创建Flask应用
def create_app():
app = Flask(__name__)
# 加载配置
app.config.from_object('config.Config')
# 初始化扩展
from models import db
db.init_app(app)
# 注册蓝图
from api import register_blueprints
register_blueprints(app)
@app.route('/')
def index():
return "Hello, Flask!"
return app
if __name__ == '__main__':
app = create_app()
app.run()
在上述代码中,create_app
函数负责创建 Flask 应用实例,配置应用,初始化数据库,并注册蓝图。这种方式使得应用的创建和配置分离,便于管理和维护。
二、uWSGI 配置文件详解
uWSGI 是一个高性能的 Web 服务器,支持多种协议和功能。在部署 Flask 应用时,需要正确配置 uWSGI 配置文件(通常为.ini
格式)。以下是一个典型的 uWSGI 配置文件示例:
[uwsgi] # 项目根目录 chdir = /home/flask_pro # 应用模块和可调用对象 module = app:create_app callable = app # 进程和线程设置 master = true processes = 4 threads = 2 # 网络设置 socket = 127.0.0.1:5000 # 其他设置 vacuum = true harakiri = 60 pidfile = uwsgi.pid daemonize = uwsgi.log
chdir
:指定项目的根目录,uWSGI 会在该目录下查找应用模块。module
:指定要加载的应用模块和可调用对象。格式为<模块名>:<可调用对象>
,这里的app
是包含create_app
函数的 Python 模块名,create_app
是创建 Flask 应用实例的函数。callable
:指定module
中返回的可调用对象,即 Flask 应用实例。在上述示例中,create_app
函数返回的 Flask 应用实例名为app
。master
:设置为true
时,uWSGI 会启动一个主进程来管理子进程,提高稳定性。processes
和threads
:分别设置 uWSGI 的进程数和每个进程的线程数,用于控制应用的并发处理能力。socket
:指定 uWSGI 监听的 IP 地址和端口号,用于接收外部请求。vacuum
:设置为true
时,uWSGI 在退出时会自动清理环境,如删除临时文件。harakiri
:设置请求的超时时间,单位为秒。pidfile
和daemonize
:分别指定 uWSGI 进程 ID 文件和日志文件的路径,用于记录进程信息和日志。
三、uWSGI 配置文件与 app 启动函数的关系
- 入口函数的匹配:uWSGI 通过
module
和callable
配置来定位 Flask 应用的入口函数。module
指定包含应用创建逻辑的模块,callable
指定模块中返回 Flask 应用实例的函数或对象。在使用工厂函数创建应用时,callable
通常是工厂函数返回的应用实例。 - 参数传递问题:在配置 uWSGI 时,容易混淆应用创建函数和 WSGI 应用接口。WSGI 规范要求应用入口函数接受
environ
和start_response
两个参数,但 Flask 应用工厂函数通常不需要这两个参数。如果在工厂函数中错误地添加了这两个参数,可能会导致 “TypeError: 'Flask' object is not iterable” 错误。因为工厂函数返回的 Flask 应用实例本身不是可迭代的,而 WSGI 期望的是一个可迭代对象来生成响应内容。正确的做法是,在工厂函数中返回 Flask 应用实例,然后通过 uWSGI 的配置来正确调用应用。 - 应用配置与环境变量:uWSGI 配置文件中的
chdir
和 Flask 应用工厂函数中的配置加载逻辑密切相关。chdir
指定的项目根目录决定了 Flask 应用查找配置文件、静态文件和模板文件的路径。在工厂函数中,通常会使用app.config.from_object
方法加载配置,配置文件的路径和名称应与项目结构和 uWSGI 配置相匹配。此外,还可以通过环境变量来传递配置信息,提高应用的灵活性和可扩展性。
四、部署过程中的问题及解决方法
通常根据网上经验,按照根据上面的app代码和uWSGI配置文件部署应该没有问题,但是实际部署到服务器上,启动后发现app并没有正确启动成功,日志显示报错如下:
错误信息 “TypeError: create_app() takes 0 positional arguments but 2 were given”,
表示 create_app()
函数被调用时传入了两个位置参数,但该函数不接受任何位置参数。
在 uWSGI 中,如果 callable
参数指定的函数不接受任何参数,那么在配置文件中就不应该提供任何额外的参数。
uWSGI 默认会将 environ
和 start_response
两个参数传递给 WSGI 应用程序。这可能是导致错误的原因。
[INFO] bound built-in connection pool when new client. maxsize=10,10 WSGI app 0 (mountpoint='') ready in 4 seconds on interpreter 0x26ced20 pid: 3592 (default app) *** uWSGI is running in multiple interpreter mode *** spawned uWSGI master process (pid: 3592) spawned uWSGI worker 1 (pid: 3595, cores: 2) spawned uWSGI worker 2 (pid: 3596, cores: 2) spawned uWSGI worker 3 (pid: 3597, cores: 2) spawned uWSGI worker 4 (pid: 3598, cores: 2) TypeError: create_app() takes 0 positional arguments but 2 were given
然而不是随便将将 environ
和 start_response
两个参数加入到create_app(environ,
)函数中的,这样修改,使用uWSGI启动后,又会出现新的报错:start_response
“TypeError: 'Flask' object is not iterable” 错误。
因为工厂函数返回的 Flask 应用实例本身不是可迭代的,而 WSGI 期望的是一个可迭代对象来生成响应内容。
经过查询网上资料以及多个AI平台分析问题,多次测试验证,最终解决方案如下:
1、修改flask应用启动代码:
增加一个符合 WSGI 规范的应用入口函数。
python">from flask import Flask
# 创建Flask应用
def create_app():
print("开始创建Flask应用...")
app = Flask(__name__)
# 加载配置
app.config.from_object('config.Config')
# 初始化扩展
from models import db
db.init_app(app)
# 注册蓝图
from api import register_blueprints
register_blueprints(app)
@app.route('/')
def index():
return "Hello, Flask!"
return app
# 定义一个符合 WSGI 规范的应用入口函数
def application(environ, start_response):
app = create_app()
return app.wsgi_app(environ, start_response)
if __name__ == '__main__':
app = create_app()
app.run()
2、修改uWSGI 配置文件:
修改 module:指定要加载的应用模块和可调用对象。格式为<模块名>:<可调用对象>。
修改 callable:指定module中返回的可调用对象。
[uwsgi] # 这里是你的项目根目录路径 chdir = /home/项目根目录路径 # 模块名,这里用 app;app:application 是指定一个 Python 的可执行文件,它包括符合 WSGI 规范的代码 module = app:application # 因为 application 是启动整个服务的入口,所以是 application callable = application # ... 其他配置不变 ...
3、使用uWSGI启动app:
================uwsgi========================== # 启动 uwsgi 命令 cd /home/flask_pro # 项目文件夹,uwsgi.ini 配置文件 下执行 uwsgi --ini uwsgi.ini # 停止 uwsgi 命令 uwsgi --stop uwsgi.pid # 查端是否启动成功 ps -ef|grep uwsgi ================uwsgi==========================
启动成功,打开网址验证下你的服务接口。
[INFO] generate built-in connection pool success. maxsize=10,10 [INFO] bound built-in connection pool when new client. maxsize=10,10 [INFO] bound built-in connection pool when new client. maxsize=10,10 [INFO] bound built-in connection pool when new client. maxsize=10,10 [INFO] bound built-in connection pool when new client. maxsize=10,10 [INFO] bound built-in connection pool when new client. maxsize=10,10 WSGI app 0 (mountpoint='') ready in 1 seconds on interpreter 0x2656cf0 pid: 3772 (default app) *** uWSGI is running in multiple interpreter mode *** spawned uWSGI master process (pid: 3772) spawned uWSGI worker 1 (pid: 3775, cores: 2) spawned uWSGI worker 2 (pid: 3776, cores: 2) spawned uWSGI worker 3 (pid: 3777, cores: 2) spawned uWSGI worker 4 (pid: 3778, cores: 2) 开始创建Flask应用...
五、总结
在 Flask 应用开发中,使用工厂函数创建应用实例并结合 uWSGI 进行部署,可以提高应用的灵活性、可维护性和性能。
在配置 uWSGI 时,需要特别注意配置文件与应用启动函数之间的关系,确保正确指定应用入口函数、处理参数传递问题,并合理设置进程、线程和网络参数。
同时,通过查看日志和解决常见问题,可以快速完成应用的部署和调试。希望本文对您在 Flask 应用部署方面有所帮助,祝您开发顺利!
如果你觉得文章内容对你有所启发,不妨点赞、关注支持一下。要是你在实际操作中遇到了不一样的情况,或是有独特的见解和经验,欢迎在评论区分享交流,大家共同进步。