使用 Flask 和 gunicorn 为服务器加载全局数据

Loading global data for server using Flask and gunicorn

我已经使用 Flask 框架实现了简单的 API,现在我正在尝试将它部署到 gunicorn 服务器。

我的服务器脚本如下所示:

app = Flask(__name__)

class Server(object):
   def __init__(self, data):
       self.data = data

@app.route("/api_method", methods=['GET', 'POST'])
def api_method():
     return server.data

if __name__ == '__main__':
     with smart_open(sys.argv[1]) as f:
         server = Server(f.read())
     app.run()

当我从控制台 运行 作为 "Flask app" 时一切正常,但是当我尝试在 gunicorn 下 运行 时它看不到服务器。我只能通过移动服务器创建来修复它,但我必须对路径进行硬编码。

有什么方法可以在 gunicorn 服务器启动时加载像我的服务器 class 这样的东西,然后在 API 方法中读取它吗?

这是我关于如何执行此操作的建议。

1. 如果需要以这种方式实例化,我不会依赖全局 server 变量。由于它是在应用程序创建后实例化的,因此最好以某种方式将其显式传递到应用程序中。这样你就可以从其他模块(blueprints, pluggable views等)访问它例如,你可以使用Flask的配置机制:

app.config['server'] = Server(f.read())

#...then in your view functions...

return app.config['server'].data

或者通过flask的globals object,g:

@app.before_request
def add_server_to_globals():
    with open(sys.argv[1]) as f:
        g.server = Server(f.read())

#...then in your view functions...
return g.server.data

或者通过 subclassing Flask 并通过初始化器传递它。

2. 现在你正在使用 gunicorn,你不能依赖 if __name__ == '__main__' 块,因为它只在你 运行文件,而不是在导入时。 Gunicorn 通过导入应用程序对象(参见 how APP_MODULE is explained in the docs)并将其传递给 gunicorn 的 WSGI 服务器来工作,因此您用于实例化 Server 的代码不会 运行.

unicorn 的另一个新要求是您不能依赖 sys.argv,因为这将是 gunicorn 的 sys.argv,这与您当前在 [=62= 时传入的任何参数不同]直接打开文件。

要替换 if __name__ == '__main__' 块,您可以像我上面那样使用 before_request 来 运行 在应用程序处理其第一个请求之前的一些代码。我建议使用环境变量或 config file 将您的参数传递给 Server class。例如:

@app.before_request
def add_server_to_globals():
    with open(os.environ['SERVER_FILE']) as f:
        g.server = Server(f.read())

然后 运行 您的应用程序设置了环境变量:

$ SERVER_FILE=blah.txt gunicorn app:app
[2015-02-24 10:10:20 -0800] [3217] [INFO] Starting gunicorn 19.2.1
...

3. 您还可以查看 flask 文档中描述的 "application factory" 模式来替换 if __name__ == '__main__' 块。但在这种情况下,它不是很有必要。如果您只想实例化 Server 对象一次(出于性能原因),则可以使用应用程序工厂。

Here's some fully working code based on your example.

Here's another example using an application factory.

简单:)

with smart_open(sys.argv[2]) as f:
    server = Server(f.read())
if __name__ == '__main__':
    app.run()

Steven Kryskalla 在他的回答中已经给出了逻辑。

但是请确保将 app.run() 保留在 if __name__=="__main__" 中,因为当 gunicorn 导入服务器 [= 时,您不希望它成为 运行 13=]

编辑: 实际上必须有 sys.argv[2],因为在 sys.argv[1] 中是服务器应用程序的路径。