Flask 應用調度

2021-08-11 09:47 更新

應用調度指的是在 WSGI 層次合并運行多個 Flask 的應用的進程。您不能將 Flask 與更大的東西合并,但是可以和 WSGI 應用交叉。這甚至允許您將 Django 和 Flask 的應用運行在同一個解釋器下。這么做的用處依賴于 這個應用內部是如何運行的。

模塊方式 的區(qū)別在于,此時您運行的不 同 Flask 應用是相互之間完全獨立的,他們運行在不同的配置,而且在 WSGI 層調度。

如何使用此文檔

下面的所有技巧和例子都將最終得到一個 application 對象,這個對象 可以在任何 WSGI 服務器上運行。在生產環(huán)境下,請參看 部署選擇 相關章節(jié)。在開發(fā)時,Werkzeug 提供了一個提供了一個內置的開發(fā)服務器, 可以通過 werkzeug.serving.run_simple() 函數使用:

from werkzeug.serving import run_simple
run_simple('localhost', 5000, application, use_reloader=True)

注意,run_simple 函數不是為生產 用途設計的,發(fā)布應用時可以使用 成熟的 WSGI 服務器 。

為了能使用交互式調試器,調試必須在應用和簡易開發(fā)服務器兩邊都被激活。 下面是一個帶有調試功能的 “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 應用對象,而且他們在 調度中間層當中被合并進入一個規(guī)模更大的應用,并通過前綴來實現調度。

例如,您可以使您的主應用運行在 / 路徑,而您的后臺 接口運行在 /backend 路徑:

from werkzeug.wsgi import DispatcherMiddleware
from frontend_app import application as frontend
from backend_app import application as backend

application = DispatcherMiddleware(frontend, {
    '/backend':     backend
})

通過子域名調度

有時,您希望使用對一個應用使用不同的配置,對每個配置運行一個實例,從而有 多個實例存在。假設應用對象是在函數中生成的,您就可以調用這個函數并實例化 一個實例,這相當容易實現。為了使您的應用支持在函數中創(chuàng)建新的對象,請先參考 應用程序的工廠函數 模式。

一個相當通用的例子,那就是為不同的子域名創(chuàng)建不同的應用對象。比如 您將您的Web服務器設置為將所有的子域名都分發(fā)給您的引用,而您接下來 使用這些子域名信息創(chuàng)建一個針對特定用戶的實例。一旦您使得您的服務器 偵聽所有的子域名請求,那么您就可以使用一個非常簡單的 WSGI 對象 來進行動態(tài)的應用程序構造。

實現此功能最佳的抽象層就是 WSGI 層。您可以編寫您自己的 WSGI 程序來 檢查訪問請求,然后分發(fā)給您的 Flask 應用。如果您的應用尚未存在,那么 就創(chuàng)建一個并且保存下來:

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 路徑分發(fā)請求跟前面的方法很相似。只需要簡單檢查請求路徑當中到第一個 斜杠之前的部分,而不是檢查用來確定子域名的 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)

這種例子與之前子域名調度那里的區(qū)別是,這里如果創(chuàng)建應用對象的函數返回了 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)


以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號