如何使用Flask-Script和Gunicorn

20 投票
7 回答
20841 浏览
提问于 2025-04-17 13:57

我正在开发一个Flask应用,使用Flask自带的开发服务器。我是通过Flask-Script来启动它的。现在我想换成Gunicorn作为网络服务器。为了做到这一点,我需要写一些代码来连接Flask-Script和Gunicorn吗?还是说Flask-Script在用Gunicorn运行应用时就没用了?

提前谢谢你们!

感谢@sean-lynch。以下是基于他回答的有效代码,经过测试可用。我做的修改有:

  • 在尝试启动服务器之前,我在remove_non_gunicorn_command_line_args()中移除了sys.argv中Gunicorn不认识的选项。否则,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 个回答

6

根据Sean的回答,我也写了一个我更喜欢的版本。

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

你可以用这样的命令来运行gunicorn:python manager.py gunicorn

10

我根据Sean Lynch的版本,写了一个更好的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())
16

正如Dhaivat所说,你可以直接用Gunicorn来运行你的Flask应用。

如果你还是想用Flask-Script,你需要创建一个自定义的Command。我对Gunicorn没有经验,但我找到了一种类似的解决方案,是为Flask-Actions做的,然后把它移植到了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()

然后你可以选择把它注册为Flask的本地开发服务器,这样你就可以用python manage.py runserver来启动。

manager.add_command("runserver", GunicornServer())

或者你可以把它注册为一个新命令,比如用python manage.py gunicorn来启动。

manager.add_command("gunicorn", GunicornServer())

编辑于2016年6月: 在最新版本的Flask-Script中,把方法handle改成__call__。你可以对比一下旧版flask-script新版flask-script

撰写回答