如有不對,請詳細指正。
最近再研究uwsgi如何部署python app,看uwsgi的文檔,里面有太多的參數,但每個參數的解釋太蒼白,作為菜鳥的我實在是不懂。想搞清楚uwsgi的工作原因以及里面的一些參數的意義,只能通過英文的文檔和源碼來入手,雖然慢,但是理解更深刻。
本文根據代碼閱讀以及參照多種文檔,描述了uwsgi的啟動多進程+多線程工作原因,以及thunder_lock參數的作用:
- uwsgi是用c語言寫的一個webserver,可以啟動多個進程,進程里面可以啟動多個線程來服務。進程分為主進程和worker進程,worker里面可以有多個線程。
- 一開始進入main函數,啟動這個就是主進程了,uwsgi_setup函數(主進程)里面針對選項參數做一些處理,執行環境設置,執行一些hook,語言環境初始化(python),如果沒有設置延遲加載app,則app在主進程加載;如果設置了延遲加載,則在每一個worker進程中都會加載一次。uwsgi_setup函數還執行了插件的初始化(當前我只關心http、python:http是gateway類型的插件,這種插件是向外暴露http服務的,python的requests類型的插件,用來服務請求的)、tcp socket的綁定與監聽(這個是指http與work之間的通信,且它的端口是自動產生的)。最后uwsgi主進程fork了指定worker進程用來接收(accept)請求。雖然在setup中就fork了子進程,但是現在還沒有開始accept。
- wsgi_run函數就是真正的開始執行了,這個函數分為倆個部分,主進程執行部分和worker進程執行部分。主進程執行部分是一個無限循環,他可以執行特定的hook以及接收信號等,總之是用來管理worker進程以及一些定時或者事件觸發任務。worker部分:注冊型號處理函數,執行一些hook,循環接收(accept)請求。在啟動woker時可以根據--threads參數指定要產生的線程個數,否則只在當前進程啟動一個線程。這些線程循環接收請求並處理。
- 在worker中接收請求的函數wsgi_req_accept有一個鎖:thunder_lock,這個鎖用來串行化accept,防止“驚群”現象:參考這里
- 驚群現象出現在這樣的情況:主進程綁定並監聽socket,然后調用fork,在各個子進程進行accept。無論任何時候,只要有一個連接嘗試連接,所有的子進程都將被喚醒,但只有一個會連接成功,其他的會得到一個EAGAIN的錯誤,浙江導致巨大的CPU資源浪費,如果在進程中使用線程,這個問題被再度放大。一個解決方法是串行化accept,在accept前防止一個鎖。
