问题描述
我有一个连接到Cassandra的Flask应用程序。当我在Gunicorn下运行这个应用程序并且将Gunicorn作为一个flask-script命令 python manage.py gunicorn
时,它会挂起。但是当我在 gunicorn manage:app
命令行上运行这个相同的应用程序时,它会成功。为什么?
解释
要求。
如果Cassandra会话(连接池)是在 fork之前(例如,在使用应用程序工厂模式创建应用程序的时候)在之前创建的,则工作人员在使用Cassandra时会遇到问题。 DataStax建议每个工作人员都有自己的会话,因此您需要推迟会话创建,直到之后的分支。
当你将Gunicorn和Flask捆绑在一起作为一个自定义应用程序时,这是一个问题,但是大概在命令行上,Gunicorn显然可以在创建Flask应用程序之前进行初始化和分叉。
示例
要查看这两个行为,请手动将bad = False更改为bad = True。从cassandra.cluster导入群集
从flask中导入群集
从flask_script中导入
import gunicorn.app.base中的命令,管理器
import BaseApplication
from gunicorn.six import iteritems
class CassandraClient:
def __init __(self,bad = False):
self.session = None
self。 cluster = None
if bad:
self.connect()
def connect(self):
self.cluster = Cluster(['127.0.0.1'] )
self.session = self.cluster.connect('keyspace')
def execute(self):
如果不是self.session:
self.connect ()
query ='''
从system.local选择now()
'''
返回self.session.execute(query)[0]
class GunicornApp(BaseApplication):
'''
将Gunicorn和Flask捆绑在一起,这样我们可以把它叫做
flask-script命令。
http://docs.gunicorn.org/en/stable/custom.html
'''
def __init __(self,app,options = None ):
self.options = options或{}
self.application = app $ b $ super(GunicornApp,self).__ init __()
$ b $ def load_config(self) :
config = dict(
[key,value)for iteritems(self.options)
如果key为self.cfg.settings且值不是None])
为键值,iteritems(config):
self.cfg.set(key.lower(),value)
def load(self):
return self.application
class GunicornCommand(命令):
'''
模拟关于flask_script.Server
'''
def __init __(self,app,options):
self.app = app
self.options = options
$ b def __call __(self,* args,** kwargs):
GunicornApp(self.app,self.options).run()
app = Flask(__ name__)
app.cassandra = CassandraClient()
@ app.route('/')
def hello():
return str(app.cassandra。执行())
if __name__ =='__main__':
manager = Manager(app)
gunicorn_options = {
'bind':'
port:8000,
'workers':4
}
manager.add_command(gunicorn,GunicornCommand(app,gunicorn_options))
manager.run()
版本
Flask == 0.12.1
Flask-Script == 2.0.5
gunicorn == 19.7.1
cassandra-driver == 3.8。 1
参考文献
I have a Flask app that connects to Cassandra. When I run this app under Gunicorn and invoke Gunicorn as a flask-script command python manage.py gunicorn
, it hangs. But when I run this same app on the command line as gunicorn manage:app
, it succeeds. Why?
Explanation
Gunicorn forks off workers to handle incoming requests. If the Cassandra session (connection pool) is created before the worker fork (e.g., during app creation using an application factory pattern), the workers will have problems using Cassandra. DataStax recommends that each worker get its own session, and so you need to defer session creation until after the fork. This is a problem when you bundle Gunicorn and Flask together as a custom application, but presumably on the command line Gunicorn can obviously initialize and fork fully before creating the Flask app.
Example
To see the two behaviors, manually change bad=False to bad=True.
from cassandra.cluster import Cluster
from flask import Flask
from flask_script import Command, Manager
from gunicorn.app.base import BaseApplication
from gunicorn.six import iteritems
class CassandraClient:
def __init__(self, bad=False):
self.session = None
self.cluster = None
if bad:
self.connect()
def connect(self):
self.cluster = Cluster(['127.0.0.1'])
self.session = self.cluster.connect('keyspace')
def execute(self):
if not self.session:
self.connect()
query = '''
select now()
from system.local
'''
return self.session.execute(query)[0]
class GunicornApp(BaseApplication):
'''
Bundle Gunicorn and Flask together, so that we can call it as a
flask-script command.
http://docs.gunicorn.org/en/stable/custom.html
'''
def __init__(self, app, options=None):
self.options = options or {}
self.application = app
super(GunicornApp, self).__init__()
def load_config(self):
config = dict(
[(key, value) for key, value in iteritems(self.options)
if key in self.cfg.settings and value is not None])
for key, value in iteritems(config):
self.cfg.set(key.lower(), value)
def load(self):
return self.application
class GunicornCommand(Command):
'''
Modeled off of flask_script.Server
'''
def __init__(self, app, options):
self.app = app
self.options = options
def __call__(self, *args, **kwargs):
GunicornApp(self.app, self.options).run()
app = Flask(__name__)
app.cassandra = CassandraClient()
@app.route('/')
def hello():
return str(app.cassandra.execute())
if __name__ == '__main__':
manager = Manager(app)
gunicorn_options = {
'bind': '127.0.0.1',
'port': 8000,
'workers': 4
}
manager.add_command("gunicorn", GunicornCommand(app, gunicorn_options))
manager.run()
Versions
Flask==0.12.1
Flask-Script==2.0.5
gunicorn==19.7.1
cassandra-driver==3.8.1
References
- http://docs.gunicorn.org/en/stable/custom.html
- https://datastax.github.io/python-driver/faq.html
- How to use Flask-Script and Gunicorn
- https://groups.google.com/a/lists.datastax.com/forum/#!topic/python-driver-user/XuSjjWVnE9Y
这篇关于当我通过Gunicorn和Flask连接到Cassandra的时候为什么会挂起Cassandra?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!