在上一篇認識wsgi走紅,我們瞭解了WSGI協議,知道了該協議在網絡中扮演的角色,並且使用Python自帶的WSGI模塊wsgiref編寫了一個簡單的WSGI server。wsgiref是用純Python寫的,性能不行,生產環境還是需要用uWSGI這一類高性能的WSGI server。這一篇我們使用uwsgi替代上一篇提到的自己編寫的WSGI server。
本章概覽
- 使用uWSGI運行簡單的WSGI application
- 使用processes,threads參數增強uWSGI的併發能力
- 使用status參數啓動uWSGI的監控
- 一個監視uWSGI狀態的小工具——uwsgitop
運行WSGI application
首先還是運行上一篇文章提到的hello.py
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return [b'<h1>Hello, World!</h1>']
注:名爲application的函數是uWSGI Python加載器搜索的默認函數名(也可以自定義)。
然後使用uWSGI啓動一個HTTP 服務/路由,將請求傳遞到我們的WSGI application
uwsgi --http: 9090 --wsgi-file hello.py
瀏覽器進行訪問,可以看到
在終端可以看到log信息:
注:當前端還有一個服務器或者要做基準測試的時候,不要使用--http,而要使用--http-socket。
注:信息中有一段“!!! no internal routing support, rebuild with pcre support !!!”,可重新安裝libpcre3 libpcre3-dev,然後重新安裝uwsgi解決
增加併發能力
默認情況下,uWSGI啓動一個進程一個線程,我們可以使用--processes選項和--threads選項來增加啓動的進程和線程。
注:WSGI架構解耦了服務端與應用端,但這樣的形式也決定了其不支持異步相應。這也是爲什麼tornado這種主打異步相應的框架不能使用uwsgi的原因。
uwsgi --http :9090 --wsgi-file hello.py --master --processes 4 --threads 2
這個命令會啓動4個進程(每個進程包含兩個線程),一個主進程(它將會在其他進程死掉時將其拉起)以及一個HTTP路由。
啓動監控
默認情況下,uWSGI啓動一個進程一個線程,我們可以使用--processes選項和--threads選項來增加啓動的進程和線程。
注:WSGI架構解耦了服務端與應用端,但這樣的形式也決定了其不支持異步相應。這也是爲什麼tornado這種主打異步相應的框架不能使用uwsgi的原因。
啓動監控
在生產環境中監控uWSGI的狀態非常重要。stats子系統將uWSGI的內部統計導出爲JSON。可以通過--status啓動
uwsgi --http :9090 --wsgi-file hello.py --master --processes 4 --threads 2 --stats 127.0.0.1:9191
然後可以使用telent訪問9191端口獲取狀態信息如下:
{
"version": "2.0.18",
"listen_queue": 0,
"listen_queue_errors": 0,
"signal_queue": 0,
"load": 0,
"pid": 1494,
"uid": 1000,
"gid": 1000,
"cwd": "/home/zhr/disk1/study/blog_code/uwsgi/p2_uwsgi",
"locks": [
{
"user 0": 0
},
{
"signal": 0
},
{
"filemon": 0
},
{
"timer": 0
},
{
"rbtimer": 0
},
{
"cron": 0
},
{
"rpc": 0
},
{
"snmp": 0
}
],
"sockets": [
{
"name": "127.0.0.1:39995",
"proto": "uwsgi",
"queue": 0,
"max_queue": 100,
"shared": 0,
"can_offload": 0
}
],
"workers": [
{
"id": 1,
"pid": 1495,
"accepting": 1,
"requests": 0,
"delta_requests": 0,
"exceptions": 0,
"harakiri_count": 0,
"signals": 0,
"signal_queue": 0,
"status": "idle",
"rss": 0,
"vsz": 0,
"running_time": 0,
"last_spawn": 1575690455,
"respawn_count": 1,
"tx": 0,
"avg_rt": 0,
"apps": [
{
"id": 0,
"modifier1": 0,
"mountpoint": "",
"startup_time": 0,
"requests": 0,
"exceptions": 0,
"chdir": ""
}
],
"cores": [
{
"id": 0,
"requests": 0,
"static_requests": 0,
"routed_requests": 0,
"offloaded_requests": 0,
"write_errors": 0,
"read_errors": 0,
"in_request": 0,
"vars": [],
"req_info": {}
},
{
"id": 1,
"requests": 0,
"static_requests": 0,
"routed_requests": 0,
"offloaded_requests": 0,
"write_errors": 0,
"read_errors": 0,
"in_request": 0,
"vars": [],
"req_info": {}
}
]
},
{
"id": 2,
"pid": 1496,
"accepting": 1,
"requests": 0,
"delta_requests": 0,
"exceptions": 0,
"harakiri_count": 0,
"signals": 0,
"signal_queue": 0,
"status": "idle",
"rss": 0,
"vsz": 0,
"running_time": 0,
"last_spawn": 1575690455,
"respawn_count": 1,
"tx": 0,
"avg_rt": 0,
"apps": [
{
"id": 0,
"modifier1": 0,
"mountpoint": "",
"startup_time": 0,
"requests": 0,
"exceptions": 0,
"chdir": ""
}
],
"cores": [
{
"id": 0,
"requests": 0,
"static_requests": 0,
"routed_requests": 0,
"offloaded_requests": 0,
"write_errors": 0,
"read_errors": 0,
"in_request": 0,
"vars": [],
"req_info": {}
},
{
"id": 1,
"requests": 0,
"static_requests": 0,
"routed_requests": 0,
"offloaded_requests": 0,
"write_errors": 0,
"read_errors": 0,
"in_request": 0,
"vars": [],
"req_info": {}
}
]
},
{
"id": 3,
"pid": 1497,
"accepting": 1,
"requests": 0,
"delta_requests": 0,
"exceptions": 0,
"harakiri_count": 0,
"signals": 0,
"signal_queue": 0,
"status": "idle",
"rss": 0,
"vsz": 0,
"running_time": 0,
"last_spawn": 1575690455,
"respawn_count": 1,
"tx": 0,
"avg_rt": 0,
"apps": [
{
"id": 0,
"modifier1": 0,
"mountpoint": "",
"startup_time": 0,
"requests": 0,
"exceptions": 0,
"chdir": ""
}
],
"cores": [
{
"id": 0,
"requests": 0,
"static_requests": 0,
"routed_requests": 0,
"offloaded_requests": 0,
"write_errors": 0,
"read_errors": 0,
"in_request": 0,
"vars": [],
"req_info": {}
},
{
"id": 1,
"requests": 0,
"static_requests": 0,
"routed_requests": 0,
"offloaded_requests": 0,
"write_errors": 0,
"read_errors": 0,
"in_request": 0,
"vars": [],
"req_info": {}
}
]
},
{
"id": 4,
"pid": 1498,
"accepting": 1,
"requests": 0,
"delta_requests": 0,
"exceptions": 0,
"harakiri_count": 0,
"signals": 0,
"signal_queue": 0,
"status": "idle",
"rss": 0,
"vsz": 0,
"running_time": 0,
"last_spawn": 1575690455,
"respawn_count": 1,
"tx": 0,
"avg_rt": 0,
"apps": [
{
"id": 0,
"modifier1": 0,
"mountpoint": "",
"startup_time": 0,
"requests": 0,
"exceptions": 0,
"chdir": ""
}
],
"cores": [
{
"id": 0,
"requests": 0,
"static_requests": 0,
"routed_requests": 0,
"offloaded_requests": 0,
"write_errors": 0,
"read_errors": 0,
"in_request": 0,
"vars": [],
"req_info": {}
},
{
"id": 1,
"requests": 0,
"static_requests": 0,
"routed_requests": 0,
"offloaded_requests": 0,
"write_errors": 0,
"read_errors": 0,
"in_request": 0,
"vars": [],
"req_info": {}
}
]
}
]
}
一個監視uWSGI狀態的小工具——uwsgitop
有一個類似於top命令的小工具——uwsgitop,可以用來實時監視uWSGI的狀態。
通過如下命令啓動uWSGI:
uwsgi --http :9090 --wsgi-file hello.py --master --processes 4 --threads 2 --stats 127.0.0.1:9191 --stats-http
然後輸入:uwsgitop http://127.0.0.1:9191
或者通過如下命令啓動uWSGI:
uwsgi --http :9090 --wsgi-file hello.py --master --processes 4 --threads 2 --stats /tmp/stats.socket
然後輸入:uwsgitop /tmp/stats.socket
都可以獲得下面這樣的實時監視狀態:
其中每個字段的介紹如下:
Field |
Description |
WID |
Worker ID |
% |
Worker usage |
PID |
Worker PID |
REQ |
Worker從最近一次啓動算起執行的請求次數 |
RPS |
每秒請求次數 |
EXC |
異常次數 |
SIG |
被管理的uWSGI信號 |
STATUS |
Worker是忙碌狀態還是空閒狀態 |
AVG |
平均請求時間 |
RSS |
Worker RSS(Resident Set Size,參閱linux內存管理) |
VSZ |
Worker VSZ(Virtual Memory Size,參閱linux內存管理) |
TX |
worker傳輸了多少數據 |
ReSpwn |
重啓次數(Respawn count) |
HC |
Harakiri 次數(Harakiri是可以設置一個請求超過多少秒後會被中斷) |
RunT |
Worker運行了多長時間 |
LastSpwn |
最後一次啓動時間 |