Диспетчеризация приложений

Диспетчеризация приложений - это процесс объединения нескольких приложений Flask на уровне WSGI. Вы можете объединять не только приложения 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. См. раздел Развертывание в производство.

Чтобы использовать интерактивный отладчик, отладка должна быть включена как на приложении, так и на простом сервере. Вот пример «hello world» с отладкой и 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__':
    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
})

Отправка по поддоменам

Иногда вам может понадобиться использовать несколько экземпляров одного и того же приложения с разными конфигурациями. Если приложение создается внутри функции, и вы можете вызвать эту функцию для его инстанцирования, это очень просто реализовать. Для того чтобы разработать приложение, поддерживающее создание новых экземпляров в функциях, рассмотрите паттерн Заводы по производству приложений.

Очень распространенным примером является создание приложений для каждого поддомена. Например, вы настраиваете свой веб-сервер на отправку всех запросов для всех поддоменов в ваше приложение, а затем используете информацию о поддоменах для создания экземпляров для конкретных пользователей. Как только вы настроите свой сервер на прослушивание всех поддоменов, вы можете использовать очень простое приложение WSGI для динамического создания приложений.

Идеальным уровнем абстракции в этом отношении является уровень WSGI. Вы пишете свое собственное приложение WSGI, которое рассматривает приходящий запрос и делегирует его вашему приложению Flask. Если такого приложения еще не существует, оно динамически создается и запоминается:

from threading import Lock

class SubdomainDispatcher:

    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:

    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)
Вернуться на верх