第十六章 性能(一)

       經過了各種測試,代碼的正確性和質量都有了很大保證,但是,部署前的準備工作尚未結束。雖然代碼能實現預期的效果,但在性能上未必是合格的。頁面加載時間太長會讓用戶失去興趣,所以儘早發現並修正性能問題是一件很重要的工作。我們可以從請求響應的流程中的下面幾個環節來進行優化:

  • 函數執行
  • 數據庫查詢
  • 模板渲染
  • 頁面資源加載

       我們可以Flask-DebugToolBar的性能分析功能對代碼進行分析,當然也可以使用Werkzeug的源碼分析器(稍後介紹),找出運行最慢的函數。那麼如何對這些函數進行優化呢?除了在代碼層面進行優化外,我們通常會使用異步任務隊列把它們放到後臺處理,這樣可以避免阻塞請求響應的處理。常用的Python任務隊列有Celery和更輕量的Redis-Queue等,其中Celery還支持週期任務和定時任務。

一. 在日誌中記錄影響性能的緩慢數據庫查詢

       如果應用的性能隨着時間的推移不斷降低,很可能是因爲數據庫查詢變慢了,隨着數據庫規模的增長,這一情況會變得更糟。優化數據庫有時很簡單,只需添加更多的索引即可;有時卻很複雜,需要在應用和數據庫之間加入緩存。多數數據庫查詢語言都提供了explain語句,用於顯示數據庫執行查詢時採取的步驟,從這些步驟中我們經常能發現數據庫索引設計的不足之處。

      一次請求往往可能要執行多條數據庫查詢,所以經常很難分辨那一條查詢較慢。Flask-SQLAlchemy提供可一個選項,可以記錄一次請求中和數據庫查詢有關的統計數據。

app/main/views.py:報告緩慢的數據庫查詢

from flask_sqlalchemy import get_debug_queries

@main.after_app_request
def after_request(response):
    for query in get_debug_queries():
        if query.duration >= current_app.config['FLASKY_SLOW_DB_QUERY_TIME']:
            current_app.logger.warning('Slow query:{}\nParameters:{}\nDuration:{}\nContext:{}\n'.
                                       format(query.statement, query.parameters, query.duration, query.context))
    return response

        這個功能使用after_app_request處理程序實現,它和before_app_request處理程序的工作方式類似,只不過在視圖函數處理完請求之後執行。Flask把響應對象傳給after_app_request處理程序,以防需要修改響應。本例中,我們不需要修改response對象,只是獲取Flask-SQLAlchemy記錄的查詢時間,把緩慢的查詢寫入日誌(應用的日誌需要通過app.logger設置),然後再返回響應,發送給客戶端。

       get_debug_queries()函數返回一個列表,其元素是請求中執行的查詢。Flask-SQLAlchemy記錄的查詢信息如下:

名稱 說明
statement SQL語句
parameters SQL語句使用的參數
start_time 執行查詢時的實踐
end_time 返回查詢結果時的時間
duration 查詢持續的時間,單位爲秒
context 表示查詢在源碼中所處的位置

       這裏設置的日誌等級是告警,不過有時候更適合把緩慢數據庫查詢視作錯誤。默認情況下,get_debug_queries()函數只在調試模式中可用。但是性能問題很少發生在開發階段,因爲開發時使用的數據量較小。因此,在生產環境中使用該選項才能發揮它的作用。若想在生產環境中監控數據庫性能,我們必須修改如下配置:

class Config:
    ...
    SQLALCHEMY_RECORD_QUERIES = True
    FLASK_SLOW_DB_QUERY_TIME = 0.5
    ...

       當發現緩慢查詢時,Flask應用的日誌記錄器會寫入一條記錄。若想保存這些日誌記錄,必須配置日誌記錄器。 關於日誌記錄器的配置我們將在部署章節展開介紹。這裏小編使用Flask-DebugToolBar查看記錄的緩慢查詢日誌(爲了看到效果,把FLASKY_SLOW_DB_QUERY_TIME 設置爲了 0.0005,即查詢時間大於0.5ms時將記錄一條日誌):

二. 分析源碼

       性能的另一個可能誘因是高CPU消耗,由執行大量運算的函數導致。源碼分析器能找出應用中最慢的部分。分析器監視運行中的應用,記錄調用的函數以及運行各函數所消耗的時間,然後生成一份詳細的報告,指出運行最慢的函數。一般只在開發環境中分析源碼,因爲源碼分析器會導致應用運行速度比常規情況下慢得多。

       Flask使用的Web開發服務器由Werkzeug提供,可根據需要爲每條請求啓用python分析器。

fasky.py:在請求分析器的監視下運行應用

import sys

from werkzeug.middleware.profiler import ProfilerMiddleware

from app import create_app

if __name__ == "__main__":
    app = create_app('development')
    try:
        restrictions = int(sys.argv[1])
    except(IndexError, TypeError):
        restrictions = 25
    app.wsgi_app = ProfilerMiddleware(app.wsgi_app, restrictions=[restrictions])

    app.run(debug=False)

(注:新版本的Flask似乎不建議自定義命令中調用app.run(),所以小編略微調整了一點點架構,可在github源碼倉庫中查看。)

通過運行"python flask.py"在分析器模式下運行應用。(不使用分析器,直接flask run運行即可。) 使用app的wsgi_app屬性,把Werkzeug的ProfilerMiddleware中間件依附到應用上。這裏通過中間件捕獲分析數據,注意這裏以app.run()方法,以編程的方式啓動應用。

終端輸出如下:

      但了使用ProfilerMiddleware中間件外,目前更流行的一種做法是使用Flask-DebugToolbar,可以直接在WEB界面查看結果,通過如下方式安裝:

pipenv install flask-debugtoolbar

然後進行擴展的初始化:

extensions.py:創建擴展

from flask_debugtoolbar import DebugToolbarExtension

debug_toolbar = DebugToolbarExtension()

app/__init__.py:完成擴展初始化

from app.extensions import bootstrap, mail, moment, db, login_manager, page_down, debug_toolbar, migrate

def create_app(config_name=None):
    if not config_name:
        config_name = os.environ.get('FLASK_CONFIG') or 'default'
    app = Flask('app')
    app.config.from_object(config[config_name])  # 使用對象始化app.config
    config[config_name].init_app(app=app)

    register_extensions(app)
    register_blueprints(app)
    register_command(app)
    register_shell_context(app)

    return app
...

def register_extensions(app):
    bootstrap.init_app(app)
    mail.init_app(app)
    moment.init_app(app)
    db.init_app(app)
    login_manager.init_app(app)
    page_down.init_app(app)
    debug_toolbar.init_app(app)
    migrate.init_app(app, db)

...

config.py:添加和Debugtoolbar相關的配置:

class Config:
    ...
    # debug toolbar默認會攔截重定向
    DEBUG_TB_INTERCEPT_REDIRECTS = False
    # 控制debug toolbar的開關
    # DEBUG_TB_ENABLED = False

啓動程序後,打開右側懸浮的sidebar,Profile源碼分析器默認關閉,點擊Profile後重新刷新頁面即可打開Profile:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章