带有 gaiohttp worker 的 gunicorn 总是用 flask app 返回 404

gunicorn with gaiohttp worker always returning 404 with flask app

我是 运行 一个在 nginx 代理后面使用 gunicorn 的 flask 应用程序,我正在尝试让 gaiohttp worker 工作。 The app just returns 404 for all URLS when the gaiohttp worker is selected

当使用 sync 或 gevent worker 时,一切正常。也不要 运行 直接连接到 gunicorn 和 gaiohttp 即不使用 nginx 它工作正常。

我已经阅读了我能找到的所有内容。

我错过了什么吗? 运行 在 nginx 代理后面时 gaiohttp worker 是否有效?

我的 nginx 配置:

location /app {
    proxy_pass http://127.0.0.1:9002;
    rewrite    /app(.*) /  break;
    proxy_redirect     off;
    proxy_buffering on;
    proxy_pass_header Server;
    proxy_set_header X-Scheme $scheme;
    proxy_set_header   Host             $host;
    proxy_set_header   X-Real-IP        $remote_addr;
    proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
    proxy_set_header   X-Forwarded-Host $server_name;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Script-Name /app;
    }

独角兽:

/usr/bin/gunicorn --workers 2 -k gaiohttp -b 127.0.0.1:9002 app:app

使用最新版本的 gunicorn 等等

我设法解决了这个问题。

此处的行导致了 nginx 配置中的问题:

rewrite /app(.*) / break;

我的 Flask 应用程序中需要一个代理中间件来正确处理反向代理。

class ReverseProxied(object):
'''Wrap the application in this middleware and configure the
front-end server to add these headers, to let you quietly bind
this to a URL other than / and to an HTTP scheme that is
different than what is used locally.

In nginx:
location /myprefix {
    proxy_pass http://192.168.0.1:5001;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Scheme $scheme;
    proxy_set_header X-Script-Name /myprefix;
    }

:param app: the WSGI application
'''
def __init__(self, app):
    self.app = app

def __call__(self, environ, start_response):
    script_name = environ.get('HTTP_X_SCRIPT_NAME', '')
    if script_name:
        server = environ.get('HTTP_X_FORWARDED_SERVER', '')
        if server:
            environ['HTTP_HOST'] = server
        environ['SCRIPT_NAME'] = script_name
        path_info = environ['PATH_INFO']
        if path_info.startswith(script_name):
            environ['PATH_INFO'] = path_info[len(script_name):]

    scheme = environ.get('HTTP_X_SCHEME', '')
    if scheme:
        environ['wsgi.url_scheme'] = scheme
    return self.app(environ, start_response)

在应用的 __init__.py 中:app.wsgi_app = ReverseProxied(app.wsgi_app)

另一种解决方案是为所有 Flask 路由添加前缀。

子安装在另一个 WSGI 容器内

假设您要在 WSGI 容器(mod_wsgi、uwsgi、gunicorn 等)中 运行 这个应用程序;您实际上需要 在那个前缀 处挂载应用程序作为该 WSGI 容器的子部分(任何会说 WSGI 的东西)并将您的 APPLICATION_ROOT 配置值设置为你的前缀:

app.config["APPLICATION_ROOT"] = "/app"

@app.route("/")
def index():
    return "The URL for this page is {}".format(url_for("index"))

# Will return "The URL for this page is /app"

设置 APPLICATION_ROOT 配置值只是将 Flask 的会话 cookie 限制为该 URL 前缀。 Flask 和 Werkzeug 出色的 WSGI 处理能力将自动为您处理其他一切。

正确安装您的应用程序的示例

如果您不确定第一段的含义,请看一下这个安装了 Flask 的示例应用程序:

from flask import Flask, url_for
from werkzeug.serving import run_simple
from werkzeug.wsgi import DispatcherMiddleware

app = Flask(__name__)
app.config['APPLICATION_ROOT'] = '/app'

@app.route('/')
def index():
    return 'The URL for this page is {}'.format(url_for('index'))

def simple(env, resp):
    resp(b'200 OK', [(b'Content-Type', b'text/plain')])
    return [b'Hello WSGI World']

app.wsgi_app = DispatcherMiddleware(simple, {'/app': app.wsgi_app})

if __name__ == '__main__':
    app.run('localhost', 5000)

代理对应用程序的请求

另一方面,如果您将 运行将您的 Flask 应用程序置于其 WSGI 容器的根目录并代理对它的请求(例如,如果它正在使用 FastCGI,或者如果nginx 正在 proxy_pass-ing 向您的独立 uwsgi / gevent 服务器请求子端点,然后您可以:

  • 使用蓝图,正如 Miguel 在 his answer 中指出的那样。
  • 使用werkzeug中的DispatcherMiddleware(或su27's answer中的PrefixMiddleware)来挂载你的应用程序在您使用的独立 WSGI 服务器中。 (有关要使用的代码,请参阅上面的 正确安装您的应用程序 的示例)。