【问题标题】:How to use Flask-Script and Gunicorn如何使用 Flask-Script 和 Gunicorn
【发布时间】:2013-01-12 01:25:15
【问题描述】:

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

提前致谢!

@sean-lynch 的道具。以下是根据他的回答工作的、经过测试的代码。 我所做的更改是:

  • 在尝试启动服务器之前,从 sys.argv 中的 remove_non_gunicorn_command_line_args() 中删除 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())

【问题讨论】:

  • 我认为这会更容易,在你的环境中安装 gunicorn,然后你可以创建一个 gunicorn.conf 文件,你可以在其中声明 gunicorn 配置(工人数量,绑定端口,pid,等等...),然后调用 gunicorn -c gunicorn.conf runserver:app (yourrunappfile:yourapp variable)

标签: python flask gunicorn


【解决方案1】:

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

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

【讨论】:

  • 如果您使用的是flask_script,那将无济于事。问题仍然存在,您需要传递其他参数。
【解决方案2】:

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

然后您可以注册它以替换 Flask 的 local development server python manage.py runserver

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

或注册为新命令,如python manage.py gunicorn

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

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

【讨论】:

  • 还没有。但这足以为我指明正确的方向。如果我发现我需要对您提供的代码进行更改,我会在这里发布。
  • 按承诺发布。再次感谢您的帮助。
  • 这是我的错误报告:TypeError: &lt;gunicorn.Gunicorn object at 0x7f15bb51ef90&gt;: run() got an unexpected keyword argument 'host'
  • 我收到以下错误:TypeError: &lt;__main__.GunicornServer object at 0x106a084d0&gt;: __call__() got an unexpected keyword argument 'proxy_protocol'
  • 你好,我想问一下如何将这个链接到运行guincorn的方式为linux service
【解决方案3】:

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

【讨论】:

【解决方案4】:

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

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

你可以使用类似这样的命令运行 gunicornpython manager.py gunicorn

【讨论】:

  • 我在运行命令时没有看到任何错误,但是我看不到任何服务器在 127.0.0.1:6969 上运行。上面的自定义命令对你有用吗?还是未经测试?
【解决方案5】:

我将进一步详细说明@NinjaDQ 的答案。如果您想同时使用app_uri 属性来定义例如flask 应用程序配置文件和自定义命令行参数,您需要使用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()

【讨论】:

    【解决方案6】:

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

    【讨论】:

      【解决方案7】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-08-10
        • 2014-09-14
        • 2018-08-03
        • 1970-01-01
        • 1970-01-01
        • 2018-06-15
        • 2020-08-23
        • 2018-01-31
        相关资源
        最近更新 更多