2016-09-18 100 views
2

我正在開發使用Python 3.5,金字塔1.7,uWSGI 2.0.11和SQLAlchemy的1.0.9一個Web應用程序。我聽說在多個worker使用uWSGI時,我們應該使用uWSGI postfork函數連接到SQLAlchemy數據庫。否則的SQLAlchemy將分享不同的叉之間的連接池造成的問題:如何配置金字塔+ uWSGI + SQLAlchemy的

根據這一意見,我已經在我的金字塔的應用程序上的文件my_app/__ini__.py添加該代碼,用於在postfork事件後創建連接引擎:

def main(global_config, **settings): 

    try: 
     from uwsgidecorators import postfork 
    except ImportError: 
     # We're not in a uWSGI context, no need to hook dbs connection 
     # to the postfork event. 
     engine = engine_from_config(settings, prefix='sqlalchemy.') 

    else: 
     @postfork 
     def init(): 
      """ Initialize dbs connexions in the context. 
       Ensures that a new connexion is returned for every new request. 
      """ 
      global engine 
      engine = engine_from_config(settings, prefix='sqlalchemy.') 


    # Retrieves database connection 
    def get_db(request): 
     global engine 
     connection = engine.connect() 
     def disconnect(request): 
      connection.close() 
     request.add_finished_callback(disconnect) 
     return connection 

    config = Configurator(settings=settings, root_factory=my_factory) 
    config.add_request_method(get_db, 'db', reify=True) 
    config.scan() 
    return config.make_wsgi_app() 

以前有經驗的人可否確認這是否是在uWSGI中使用預加工的正確方法?我有點困惑,因爲我不太明白在引擎創建期間或者在致電engine.connect()

回答

0

時定義的特定池的連接「我聽說與多個工人一起使用uWSGI時,我們應該使用uWSGI postfork函數連接到SQLAlchemy數據庫,否則SQLAlchemy將在不同的叉子之間共享連接池,導致問題。「 [來源請求] :)

以我的經驗,股票標準SQLAlchemy的設置都設有帶UWSGI的多進程和多線程模型和應用程序並不需要知道UWSGI在所有沒有問題。

SQLAlchemy的Session對象配置爲scoped_session時,它是一個線程本地的,因此雖然看起來您在線程之間共享一個全局變量,但該變量實際上代表了每個線程中的單獨連接/事務。

如果使用UWSGI preforking thereads,而不是您可以使用scoped_sessionscopefunc參數,使其返回人均不同的連接 - 我會想象你可以使用uwsgi.worker_id()作爲哈希鍵。

我也不太明白你想用def get_db()實現什麼,但它看起來高度可疑 - 看起來你打開和關閉每個請求的新連接,即ewww。 .. :)我建議你看看stock Pyramid scaffolds之一,它說明了如何用金字塔配置SQLAlchemy。神奇的詞是「ZopeTransactionExtension」,如圖所示here

+0

嗨@Sergey,謝謝你的回覆:) uWSGI默認使用[Preforking](http://uwsgi-docs.readthedocs.io/en/latest/articles/TheArtOfGracefulReloading.html#preforking-vs-lazy-apps -vs-lazy),它基本上只加載一次應用程序並與其他工作人員共享結果,這就是爲什麼你可能會遇到與SQLAlchemy或Cassandra共享連接的問題。 (只是增加了參考)。這就是爲什麼人們通常使用** lazy-apps **選項來配置uWSGI以避免這種行爲。據我所知使用SQL鍊金術ORM就像你提出的方式比核心實現方式慢:) – Ander

+0

@Ander:我已經添加了一段關於指定自定義範圍的答案,這應該解決您的問題與preforking。我從來沒有建議使用ORM(實際上不是_that_慢),我建議使用正確配置的Session,它是連接/事務的抽象,連接從池中檢出,而不是在每個請求上重新連接。如果你認爲你自己種植的自行車改造方法會比使用會話更快,那麼你會驚喜:) – Sergey

+0

mmh ..糾正我,如果我錯了,但我相信會議只能用於ORM SQLAlchemy(它們是從** sqlalchemy.orm **導入的),並且SQLAlchemy核心文檔中沒有會話。目前我的應用程序太大了,無法從Core移植到ORM實現。我自己開發的方法只是我在以前的[aiopyramid項目](http://aiopyramid.readthedocs.io/)中使用的,以避免與SQLAlchemy產生異步問題。我不相信是最好的,但迄今爲止它工作得非常出色:) – Ander

1

必須做一個,只有一個,事情分叉後「scoped_session」 - 呼籲引擎dispose()方法。

作爲每SQLAlchemy的的文檔:http://docs.sqlalchemy.org/en/latest/core/connections.html#engine-disposal

當程序使用多處理或叉(),以及發動機對象被複制到子進程,引擎。應調用dispose(),以便引擎創建本地到該分支的全新數據庫連接。數據庫連接通常不跨越進程邊界。

從內存中,我的代碼看起來是這樣的:

engine = engine_from_config(settings, prefix='sqlalchemy.') 

try: 
    import uwsgi 

    def postfork(): 
     engine.dispose() 
    uwsgi.post_fork_hook = postfork 

except ImportError: 
    pass 

scoped_session帶關閉的東西交易/釋放池中的連接 - 沒有dispose,你運行多個工人管理的風險相同的db連接獨立。

tldr;叉安全和線程安全是Python中的不同概念。 (另請參閱http://www.dctrwatson.com/2010/09/python-thread-safe-does-not-mean-fork-safe/)。