可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm working on on a Flask app using Flask's built in dev server. I start it using Flask-Script. I want to switch to using Gunicorn as the web server. To do so, do I need to write some sort of integration code between Flask-Script and Gunicorn? Or is Flask-Script irrelevant to running the app using Gunicorn?
Thanks in advance!
Props to @sean-lynch. The following is working, tested code based on his answer.
The changes I made were:
Options that aren't recognized by Gunicorn are removed from sys.argv
in remove_non_gunicorn_command_line_args()
before trying to start the server. Otherwise Gunicorn throws an error with a message like this: error: unrecognized arguments: --port 5010
. I remove -p
because, even though it doesn't cause the error, that's only because Gunicorn thinks its the short form of its pidfile
option, which is obviously not what's intended.
GunicornServer.handle() signature modified to match the method it overrides i.e. Command.handle()
-
from flask_script import Command
from gunicorn.app.base import Application
class GunicornServer(Command):
description = 'Run the app within Gunicorn'
def __init__(self, host='127.0.0.1', port=8000, workers=6):
self.port = port
self.host = host
self.workers = workers
def get_options(self):
return (
Option('-t', '--host',
dest='host',
default=self.host),
Option('-p', '--port',
dest='port',
type=int,
default=self.port),
Option('-w', '--workers',
dest='workers',
type=int,
default=self.workers),
)
def handle(self, app, *args, **kwargs):
host = kwargs['host']
port = kwargs['port']
workers = kwargs['workers']
def remove_non_gunicorn_command_line_args():
import sys
args_to_remove = ['--port','-p']
def args_filter(name_or_value):
keep = not args_to_remove.count(name_or_value)
if keep:
previous = sys.argv[sys.argv.index(name_or_value) - 1]
keep = not args_to_remove.count(previous)
return keep
sys.argv = filter(args_filter, sys.argv)
remove_non_gunicorn_command_line_args()
from gunicorn import version_info
if version_info < (0, 9, 0):
from gunicorn.arbiter import Arbiter
from gunicorn.config import Config
arbiter = Arbiter(Config({'bind': "%s:%d" % (host, int(port)),'workers': workers}), app)
arbiter.run()
else:
class FlaskApplication(Application):
def init(self, parser, opts, args):
return {
'bind': '{0}:{1}'.format(host, port),
'workers': workers
}
def load(self):
return app
FlaskApplication().run()
manager.add_command('gunicorn', GunicornServer())
回答1:
As Dhaivat said, you can just use your Flask app directly with Gunicorn.
If you still want to use Flask-Script, you will need to create a custom Command
. I don't have any experience with Gunicorn, but I found a similar solution for Flask-Actions and ported it to Flask-Script, although be warned, it's untested.
from flask_script import Command, Option
class GunicornServer(Command):
description = 'Run the app within Gunicorn'
def __init__(self, host='127.0.0.1', port=8000, workers=4):
self.port = port
self.host = host
self.workers = workers
def get_options(self):
return (
Option('-H', '--host',
dest='host',
default=self.host),
Option('-p', '--port',
dest='port',
type=int,
default=self.port),
Option('-w', '--workers',
dest='workers',
type=int,
default=self.workers),
)
def handle(self, app, host, port, workers):
from gunicorn import version_info
if version_info < (0, 9, 0):
from gunicorn.arbiter import Arbiter
from gunicorn.config import Config
arbiter = Arbiter(Config({'bind': "%s:%d" % (host, int(port)),'workers': workers}), app)
arbiter.run()
else:
from gunicorn.app.base import Application
class FlaskApplication(Application):
def init(self, parser, opts, args):
return {
'bind': '{0}:{1}'.format(host, port),
'workers': workers
}
def load(self):
return app
FlaskApplication().run()
You can then either register it to replace Flask's local development server at python manage.py runserver
manager.add_command("runserver", GunicornServer())
or register as a new command such as python manage.py gunicorn
manager.add_command("gunicorn", GunicornServer())
Edit June 2016: With the latest version of Flask-Script, change the method handle
with __call__
. old flask-script vs new flask-script
回答2:
I wrote a better version of GunicornServer based on Sean Lynch's, the command now accept all gunicorn's arguments
from yourapp import app
from flask.ext.script import Manager, Command, Option
class GunicornServer(Command):
"""Run the app within Gunicorn"""
def get_options(self):
from gunicorn.config import make_settings
settings = make_settings()
options = (
Option(*klass.cli, action=klass.action)
for setting, klass in settings.iteritems() if klass.cli
)
return options
def run(self, *args, **kwargs):
from gunicorn.app.wsgiapp import WSGIApplication
app = WSGIApplication()
app.app_uri = 'manage:app'
return app.run()
manager = Manager(app)
manager.add_command("gunicorn", GunicornServer())
回答3:
Based the answer of the Sean, I also wrote a version more preferred to me.
@manager.option('-h', '--host', dest='host', default='127.0.0.1')
@manager.option('-p', '--port', dest='port', type=int, default=6969)
@manager.option('-w', '--workers', dest='workers', type=int, default=3)
def gunicorn(host, port, workers):
"""Start the Server with Gunicorn"""
from gunicorn.app.base import Application
class FlaskApplication(Application):
def init(self, parser, opts, args):
return {
'bind': '{0}:{1}'.format(host, port),
'workers': workers
}
def load(self):
return app
application = FlaskApplication()
return application.run()
you can run the gunicorn using command like thispython manager.py gunicorn
回答4:
Flask actually has docs to run Gunicorn here.
You have to remember that Gunicorn is a WSGI server with some niceties.
回答5:
I will elaborate further on the answer by @NinjaDQ. If you want to use app_uri
attribute for defining for example the flask application configuration file and custom command line arguments at the same time, you need to use WSGIApplication
. The problem is that this application overrides
the command line arguments thus it is necessary to ignore the sys.argv
.
from gunicorn.app.base import Application
class FlaskApplication(Application):
def init(self, parser, opts, args):
return {
"bind": "{0}:{1}".format(host, port),
"workers": 4
}
def chdir(self):
# chdir to the configured path before loading,
# default is the current dir
os.chdir(self.cfg.chdir)
# add the path to sys.path
sys.path.insert(0, self.cfg.chdir)
def load_wsgiapp(self):
self.chdir()
# load the app
return util.import_app(self.app_uri)
def load(self):
return self.load_wsgiapp()
# Important! Do not pass any cmd line arguments to gunicorn
sys.argv = sys.argv[:2]
wsgi_app = FlaskApplication()
wsgi_app.app_uri = "manage:create_app('{0}')".format(config_file)
return wsgi_app.run()
回答6:
Based on menghan's answer, receive all arguments from Application config.
from flask_script import Command, Option
class GunicornApp(Command):
def get_options(self):
from gunicorn.config import make_settings
settings = make_settings()
options = (
Option(*klass.cli, dest=klass.name, default=klass.default)
for setting, klass in settings.items() if klass.cli
)
return options
def __call__(self, app=None, *args, **kwargs):
from gunicorn.app.base import Application
class FlaskApplication(Application):
def init(self, parser, opts, args):
return kwargs
def load(self):
return app
FlaskApplication().run()