如何使用Flask-Script和Gunicorn

时间:2013-01-28 16:30:17

标签: python flask gunicorn

我正在使用Flask的内置开发服务器开发Flask应用程序。我是用Flask-Script开始的。我想切换到使用Gunicorn作为Web服务器。为此,我需要在Flask-Script和Gunicorn之间编写某种集成代码吗?或者Flask-Script与使用Gunicorn运行应用程序无关?

提前致谢!

支持@sean-lynch的道具。以下是基于他的答案的工作,测试代码。 我所做的改变是:

  • 在尝试启动服务器之前,Gunicorn无法识别的选项将从sys.argv中的remove_non_gunicorn_command_line_args()中删除。否则Gunicorn会抛出一条错误消息:error: unrecognized arguments: --port 5010。我删除了-p,因为即使它没有导致错误,也只是因为Gunicorn认为它是pidfile选项的缩写形式,这显然不是预期的。

  • 修改了GunicornServer.handle()签名以匹配它覆盖的方法,即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())

7 个答案:

答案 0 :(得分:13)

正如Dhaivat所说,你可以直接使用你的Flask应用程序与Gunicorn。

如果您仍想使用Flask-Script,则需要创建自定义Command。我对Gunicorn没有任何经验,但是我发现Flask-Actions有一个类似的solution并将它移植到Flask-Script,虽然被警告,但它没有经过测试。

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()

然后您可以注册它以在python manage.py runserver

替换Flask的local development server
manager.add_command("runserver", GunicornServer())

或注册为python manage.py gunicorn

等新命令
manager.add_command("gunicorn", GunicornServer())

2016年6月编辑:使用最新版本的Flask-Script,将方法handle更改为__call__old flask-script vs new flask-script

答案 1 :(得分:8)

我写了一个基于肖恩林奇的更好版本的GunicornServer,命令现在接受所有gunicorn的论点

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())

答案 2 :(得分:3)

根据肖恩的回答,我还写了一个更适合我的版本。

@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()

你可以使用像python manager.py gunicorn

这样的命令来运行gunicorn

答案 3 :(得分:2)

Flask实际上有运行Gunicorn的文档here

你必须记住,Gunicorn是一个带有一些细节的WSGI服务器。

答案 4 :(得分:1)

感谢@menghan的解决方案和@OutOfFoodException的改进。但是请记住,对于gunicorn=20.0.4使用-b, --bind documentation来绑定服务器套接字。结果命令对我来说是这样的:

python3 manage.py gunicorn -b 0.0.0.0:5000
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())

答案 5 :(得分:0)

我将在@NinjaDQ上进一步阐述答案。如果要使用WSGIApplication属性同时定义烧瓶应用程序配置文件和自定义命令行参数,则需要使用overrides。问题是这个应用程序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()

note:

答案 6 :(得分:0)

根据menghan的回答,从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()