应用程序调度 — Flask 文档
应用调度
应用调度是在 WSGI 层面上组合多个 Flask 应用的过程。 您不仅可以组合 Flask 应用程序,还可以组合任何 WSGI 应用程序。 如果需要,这将允许您在同一个解释器中并排运行 Django 和 Flask 应用程序。 这是否有用取决于应用程序如何在内部工作。
与 作为包的大型应用程序 的根本区别在于,在这种情况下,您运行的是相同或不同的 Flask 应用程序,这些应用程序彼此完全隔离。 它们运行不同的配置并在 WSGI 级别分派。
使用本文档
下面的每个技术和示例都会产生一个 application
对象,该对象可以与任何 WSGI 服务器一起运行。 对于生产,请参阅 部署选项 。 对于开发,Werkzeug 通过 werkzeug.serving.run_simple()
提供了一个服务器:
from werkzeug.serving import run_simple
run_simple('localhost', 5000, application, use_reloader=True)
请注意,run_simple
不适用于生产。 使用生产 WSGI 服务器。 请参阅 部署选项 。
为了使用交互式调试器,必须在应用程序和简单服务器上都启用调试。 这是带有调试和 run_simple
的“hello world”示例:
from flask import Flask
from werkzeug.serving import run_simple
app = Flask(__name__)
app.debug = True
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
run_simple('localhost', 5000, app,
use_reloader=True, use_debugger=True, use_evalex=True)
组合应用
如果您有完全分离的应用程序,并且希望它们在同一个 Python 解释器进程中彼此相邻工作,则可以利用 werkzeug.wsgi.DispatcherMiddleware
。 这里的想法是每个 Flask 应用程序都是一个有效的 WSGI 应用程序,它们被调度程序中间件组合成一个更大的基于前缀调度的应用程序。
例如,您可以将主应用程序运行在 /
上,并将后端接口运行在 /backend
上:
from werkzeug.middleware.dispatcher import DispatcherMiddleware
from frontend_app import application as frontend
from backend_app import application as backend
application = DispatcherMiddleware(frontend, {
'/backend': backend
})
按子域分派
有时您可能希望使用具有不同配置的同一应用程序的多个实例。 假设应用程序是在一个函数内创建的,并且您可以调用该函数来实例化它,这真的很容易实现。 为了开发您的应用程序以支持在函数中创建新实例,请查看 Application Factories 模式。
一个非常常见的例子是为每个子域创建应用程序。 例如,您将网络服务器配置为将所有子域的所有请求分派到您的应用程序,然后使用子域信息创建特定于用户的实例。 一旦您将服务器设置为侦听所有子域,您就可以使用一个非常简单的 WSGI 应用程序来创建动态应用程序。
在这方面的完美抽象级别是 WSGI 层。 您编写自己的 WSGI 应用程序,该应用程序查看到来的请求并将其委托给您的 Flask 应用程序。 如果该应用程序尚不存在,则会动态创建并记住它:
from threading import Lock
class SubdomainDispatcher(object):
def __init__(self, domain, create_app):
self.domain = domain
self.create_app = create_app
self.lock = Lock()
self.instances = {}
def get_application(self, host):
host = host.split(':')[0]
assert host.endswith(self.domain), 'Configuration error'
subdomain = host[:-len(self.domain)].rstrip('.')
with self.lock:
app = self.instances.get(subdomain)
if app is None:
app = self.create_app(subdomain)
self.instances[subdomain] = app
return app
def __call__(self, environ, start_response):
app = self.get_application(environ['HTTP_HOST'])
return app(environ, start_response)
然后可以像这样使用这个调度器:
from myapplication import create_app, get_user_for_subdomain
from werkzeug.exceptions import NotFound
def make_app(subdomain):
user = get_user_for_subdomain(subdomain)
if user is None:
# if there is no user for that subdomain we still have
# to return a WSGI application that handles that request.
# We can then just return the NotFound() exception as
# application which will render a default 404 page.
# You might also redirect the user to the main page then
return NotFound()
# otherwise create the application for the specific user
return create_app(user)
application = SubdomainDispatcher('example.com', make_app)
按路径分派
通过 URL 上的路径分派非常相似。 而不是查看 Host
标头来找出子域,只需查看直到第一个斜杠的请求路径:
from threading import Lock
from werkzeug.wsgi import pop_path_info, peek_path_info
class PathDispatcher(object):
def __init__(self, default_app, create_app):
self.default_app = default_app
self.create_app = create_app
self.lock = Lock()
self.instances = {}
def get_application(self, prefix):
with self.lock:
app = self.instances.get(prefix)
if app is None:
app = self.create_app(prefix)
if app is not None:
self.instances[prefix] = app
return app
def __call__(self, environ, start_response):
app = self.get_application(peek_path_info(environ))
if app is not None:
pop_path_info(environ)
else:
app = self.default_app
return app(environ, start_response)
这与子域的最大区别在于,如果创建者函数返回 None
,则该子域会回退到另一个应用程序:
from myapplication import create_app, default_app, get_user_for_prefix
def make_app(prefix):
user = get_user_for_prefix(prefix)
if user is not None:
return create_app(user)
application = PathDispatcher(default_app, make_app)