werkzeug
.Диспетчеризация приложений построенных на фреймворке Flask - это процесс объединения нескольких приложений Flask на уровне WSGI. Можно комбинировать не только приложения Flask, но и любое приложение WSGI. Такое поведение позволяет запускать приложения Django и Flask в одном интерпретаторе бок о бок. Полезность этого зависит от того, как приложения взаимодействуют друг с другом внутри.
Принципиальное отличие от больших приложений, построенных как пакеты, заключается в том, что диспетчер запускает одни и те же или разные приложения Flask, которые полностью изолированы друг от друга. Они работают в разных конфигурациях и управляются на уровне WSGI.
Каждый из приведенных ниже примеров приводит к созданию объекта приложения, который запускается с любым сервером WSGI. Для разработки Werkzeug
предоставляет сервер через werkzeug.serving.run_simple()
:
from werkzeug.serving import run_simple run_simple('localhost', 5000, application, use_reloader=True)
Обратите внимание, что run_simple()
не предназначен для использования на боевом сервере.
Чтобы использовать интерактивный отладчик, функции отладки должны быть включены как в экземпляре приложения, так и в аргументах run_simple()
. Смотрим пример:
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__': # передаем аргумент use_debugger=True run_simple('localhost', 5000, app, use_reloader=True, use_debugger=True, use_evalex=True)
werkzeug
.Если есть полностью разделенные приложения и необходимо, чтобы они работали рядом друг с другом в одном процессе интерпретатора Python, то можно воспользоваться классом werkzeug.wsgi.DispatcherMiddleware
. Идея здесь в том, что каждое приложение Flask является допустимым приложением WSGI, и они объединяются промежуточным программным обеспечением (диспетчером) в более крупное приложение, которое задействуется на основе префикса URL-адреса.
Например, можно запустить основное приложение по адресу /
и внутренний интерфейс по URL-адресу /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 })
Иногда необходимо использовать несколько экземпляров одного и того же приложения с разными конфигурациями. Предполагая, что приложение создается внутри функции, то можно вызывать эту функцию для создания экземпляра приложения. Чтобы разработать такое приложение необходимо использовать паттерн "фабрика приложений".
Очень распространенный пример - создание приложений для каждого поддомена. Например, есть веб-сервер, который настроен для проксирования всех запросов, всех поддоменов в приложение Flask, а в приложении используется информация о поддомене для создания экземпляров приложения, специфичных для пользователя. После того, как веб-сервер настроен для прослушивания всех поддоменов, можно использовать простой WSGI для создания динамического приложения Flask.
В этом отношении идеальным уровнем абстракции является уровень 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: # Если для этого поддомена нет пользователя, то все равно нужно # возвратить приложение WSGI, которое обрабатывает этот запрос. # Затем можно просто вернуть исключение `NotFound()` как приложение, # которое будет отображать страницу 404 по умолчанию. # Можно также перенаправить пользователя на главную страницу, # а затем вернуть `NotFound()` # в противном случае создается приложение для конкретного пользователя 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)
Разница между запуском приложения на основе URL-адреса и поддомена заключается в том, что если функция make_app()
возвращает None
, то запускается приложение default_app
:
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)