開發與維運

uWsgi的安裝和配置筆記

安裝

簡單如下

pip install uwsgi

配置

以ini文件配置,摘取部分常用的,配置實在太“靈活”了

[uwsgi]
#默認情況下Python plugin不會初始化GIL,也就是說app生成的線程不會執行,如果需要線程,需要配置開啟
enable-threads = true

#綁定本地ip和端口,如果直接":port"的話,綁定的ip是0.0.0.0,這是uwsgi方式,如果前端webserver不支持uwsgi的時候用http-socket
socket = 127.0.0.1:15000

#stats服務綁定配置,使用時:uwsgitop 127.0.0.1:3167,非必須配置
stats = 127.0.0.1:3167

#設置python的虛擬環境目錄,我這裡用的anaconda3創建的虛擬環境
virtualenv = /home/**/anaconda3/envs/**

#設置一個系統環境變量ALITA_CONFIG,這裡的變量名字自己起,在項目啟動時可以用來傳給python配置路徑,非必須,可以在代碼中配置
env = ALITA_CONFIG=../config/dev/dev.py

#配置wsgi文件路徑和名稱
wsgi-file = wsgi.py

#設置WSGI回調,默認“application”
callable = application

# 在每個worker而不是master中加載應用,這樣可以保證每個worker在一個一致的並且乾淨的環境中運行
lazy-apps = true

# 啟動主進程,來管理其他進程,其它的uwsgi進程都是這個master進程的子進程,如果kill這個master進程,相當於重啟所有的uwsgi進程。
master = true

#生成指定數目的worker/進程,和workers配置項一樣
processes = 2 

#設置用於uwsgi包解析的內部緩存區大小為64k。默認是4k  
buffer-size = 32768 

#當服務器退出的時候自動刪除unix socket文件和pid文件  
vacuum = true

#為每個工作進程設置請求數的上限。當一個工作進程處理的請求數達到這個值,那麼該工作進程就會被回收重用(重啟)。你可以使用這個選項來默默地對抗內存洩漏  
max-requests = 20000

#當一個工作進程的虛擬內存佔用超過了限制的大小,那麼該進程就會被回收重用(重啟)
reload-on-as = 128

#跟reload-on-as的效果類似,不過這個選項控制的是物理內存。你可以同時使用這2個選項,如下配置會導致超過96M物理內存的工作進程重啟,當工作進程因此重啟時,本次請求的響應不會受影響,返回正常結果
reload-on-rss: 96

#設置在平滑的重啟(直到接收到的請求處理完才重啟)一個工作子進程中,等待這個工作結束的最長秒數。這個配置會使在平滑地重啟工作子進程中,如果工作進程結束時間超過了8秒就會被強行結束(忽略之前已經接收到的請求而直接結束)
reload-mercy = 8

#如果一個請求花費的時間超過了這個harakiri超時時間,那麼這個請求都會被丟棄,並且當前處理這個請求的工作進程會被回收再利用(即重啟),如下設置會使uwsgi丟棄所有需要20秒以上才能處理完成的請求
harakiri = 20

#當一個請求被harakiri殺掉以後,你將在uWSGI日誌中得到一條消息。激活這個選項會打印出額外的信息
harakiri-verbose = true

#設置socket的監聽隊列大小(默認:100),每一個socket都有一個相關聯的隊列,請求會被放入其中等待進程來處理。當這個隊列慢的時候,新來的請求就會被拒絕。隊列大小的最大值依賴於系統內核
listen = 1024

#這個鎖用來串行化accept,防止“驚群”現象
thunder-lock = true

#指定pid文件
pidfile = /tmp/**.pid

#守護進程的方式,如果用supervisor啟動時不要配置這個,跟supervisor裡面的日誌衝突
daemonize = /home/***/logs/uwsgi.log

#日誌格式設置
log-format = %(addr) - %(pid) [%(ltime)] "%(method) %(uri) %(proto)" %(status) %(hsize) %(rsize) "%(referer)" "%(uagent)" %(switches) %(micros)

nginx 配置

使用 TCP socket 的配置

uwsgi_pass 127.0.0.1:3031;
include uwsgi_params;

或者使用uWSGI socket文件方式

uwsgi_pass unix:///tmp/uwsgi.sock;
include uwsgi_params;

性能監控

假設按照上面配置開了state如下

#stats服務綁定配置,使用時:uwsgitop 127.0.0.1:3167,非必須配置
stats = 127.0.0.1:3167

當運行起uWsgi程序後,可以通過curl 127.0.0.1:3167看到運行的情況,當然,返回的json結構數據太長,有個工具類似linux的top的工具,看起來清爽多了

#安裝監控工具
pip install uwsgitop

#使用
uwsgitop 127.0.0.1:3167

每一列說明如下

Field Description
WID Worker ID
% Worker usage
PID Worker PID
REQ Number of requests the worker executed since last (re)spawn
RPS Requests per second
EXC Exceptions
SIG Managed uwsgi signals
STATUS Worker is busy or free to use?
AVG Average request time
RSS Worker RSS (Resident Set Size, see linux memory management)
VSZ Worker VSZ (Virtual Memory Size, see linux memory management)
TX How much data was transmitted by the worker
ReSpwn Respawn count
HC Harakiri count
RunT How long the worker has been running
LastSpwn Last spawn time

注意事項

  • http和http-socket完全不同。http將生成一個額外的進程,將請求轉發給workers(一種屏蔽形式,和apache、nginx差不多),而http-socket將workers設置為以本地方式使用http協議。如果直接向公眾公開uWSGI,用--http;如果將其代理到webserver後面並使用http協議,就用--http-socket,當然,官方是建議使用uwsgi協議的。
  • 進程數和線程數配置沒有一定的規則,除了拍腦袋使用cpu核數*2,uwsgitop是個不錯的健康監控工具,幫助來確定什麼配置更合適
  • 配置memory-report,勤檢查worker內存佔用情況,
  • 如果用UNIX sockets的話,注意文件權限
  • 不要以root用戶運行uWSGI,如果在root下運行的話,可以配置一下uid和gid指定一下運行在那個用戶和用戶組下
  • 默認情況下uWSGI是先加載app,然後才fork worker的,這樣會導致比如在flask下使用orm會有獲取session失敗的情況,所以配置一下lazy-apps,讓每個worker獨立加載app,這樣每個app的環境是獨立且乾淨的,雖然佔用內存要多些,但穩定
  • 默認情況下,Python插件不會初始化GIL。這意味著你的應用程序生成的線程將不會運行。如果需要線程,請記住使用enable-threads啟用它們
  • 默認情況下,uWSGI為每個請求的頭分配一個非常小的緩衝區(4096字節)。如果在日誌中開始收到 “invalid request block size” ,則可能意味著需要更大的緩衝區。使用buffer-size選項增加它(最多65535個)
  • 如果(Linux)服務器似乎有很多空閒的工作線程,但性能仍然低,查看下ip_conntrack_max系統變量的值,並將其增大以查看是否有幫助
  • py-autoreload這個配置只在開發的時候使用,生產模式下就算了,挺耗性能的

Leave a Reply

Your email address will not be published. Required fields are marked *