TL;DR
- uWSGIでサーバーの状態(Stats)を見るには、uWSGI Stats Serverを使えばよい
-
uwsgi
の起動オプションとして設定すれば、利用可能 - データはソケット(HTTP含む)で取得できる
- ただし、取得できる項目がどういった意味なのかは特にドキュメンテーションされていない様子…
- ソースコードを見て把握するしかなさそう
モチベーション
uWSGIを使ってアプリケーションをデプロイ、公開する際に、uWSGIからメトリクスを取得したい、というのが動機です。
で、ページを見つけたはいいものの、取得できる項目のサンプルはありますが、それがどういう意味家はドキュメントには見当たらず、日本語情報もなさそうです…。
といって、このページでそれを解説するわけでもないのですが。
そもそも、uWSGIでメトリクスを取得するには?
前述のドキュメントに記載してある、Stats Serverを使う方法と
Metrics subsystemを使ってプラグインを書く方法があるようです。
たとえば、DatadogはこのMetrics subsystemを使って、取得したメトリクスをDatadogに送信するプラグインを作成しています。
ええ、C言語で書かれていますね…。
今回は、簡単にメトリクスを取得できるStats Serverを使用します。
環境
今回は、こちらの環境で確認します。
$ python3 -V
Python 3.6.7
$ uwsgi --version
2.0.18
Stats Serverを有効にする
今回のuWSGIのメトリクスを取得するにあたって、特に「処理中のリクエスト数(多重度)」を知りたいという要求があったので、以下のようにちょっとスリープを入れるWSGIスクリプトを用意しました。
app.py
import time
def application(env, start_response):
print('waiting...')
time.sleep(10)
start_response('200 OK', [('Content-Type', 'text/plain')])
return [b'Hello WSGI!!']
こちらを使って、uWSGIを起動します。
$ uwsgi --http :8000 --stats :9000 --wsgi-file app.py
これで、通常のHTTPリクエストは8000
ポートで受け付け、Statsは9000
ポートで取得できるようになっています。
ドキュメントを見ていると、他にUnixドメインソケットも利用できそうな感じですね。
--stats 127.0.0.1:1717
--stats /tmp/statsock
--stats :5050
--stats @foobar
# Any of the above socket types can also return stats using HTTP
--stats 127.0.0.1:1717 --stats-http
確認してみましょう。先程の起動方法ではTCP待ち受けるようにStats Serverを起動しているので、nc
コマンドで確認します。
$ nc localhost 9000
{
"version":"2.0.18",
"listen_queue":0,
"listen_queue_errors":0,
"signal_queue":0,
"load":0,
"pid":92625,
"uid":1000,
"gid":1000,
"cwd":"/path",
"locks":[
{
"user 0":0
},
{
"signal":0
},
{
"filemon":0
},
{
"timer":0
},
{
"rbtimer":0
},
{
"cron":0
},
{
"rpc":0
},
{
"snmp":0
}
],
"sockets":[
{
"name":"127.0.0.1:46175",
"proto":"uwsgi",
"queue":0,
"max_queue":100,
"shared":0,
"can_offload":0
}
],
"workers":[
{
"id":1,
"pid":92626,
"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":1557739876,
"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": {
}
}
]
}
]
}
こんな感じで情報が取得できました。
ちなみに、HTTPで取得したい場合は--stats-http
を付与します。
$ uwsgi --http :8000 --stats :9000 --stats-http --wsgi-file app.py
これで、curl
などで取得できるようになります。
$ curl localhost:9000
{
"version":"2.0.18",
"listen_queue":0,
"listen_queue_errors":0,
"signal_queue":0,
"load":0,
"pid":92775,
"uid":1000,
"gid":1000,
"cwd":"/home/ippei/projects/charon/IdeaProjects/uwsgi-stats",
"locks":[
{
"user 0":0
},
{
"signal":0
},
{
"filemon":0
},
{
"timer":0
},
{
"rbtimer":0
},
{
"cron":0
},
{
"rpc":0
},
{
"snmp":0
}
],
"sockets":[
{
"name":"127.0.0.1:41235",
"proto":"uwsgi",
"queue":0,
"max_queue":100,
"shared":0,
"can_offload":0
}
],
"workers":[
{
"id":1,
"pid":92776,
"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":1557740070,
"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,
$ curl localhost:9000
{
"version":"2.0.18",
"listen_queue":0,
"listen_queue_errors":0,
"signal_queue":0,
"load":0,
"pid":92775,
"uid":1000,
"gid":1000,
"cwd":"/path",
"locks":[
{
"user 0":0
},
{
"signal":0
},
{
"filemon":0
},
{
"timer":0
},
{
"rbtimer":0
},
{
"cron":0
},
{
"rpc":0
},
{
"snmp":0
}
],
"sockets":[
{
"name":"127.0.0.1:41235",
"proto":"uwsgi",
"queue":0,
"max_queue":100,
"shared":0,
"can_offload":0
}
],
"workers":[
{
"id":1,
"pid":92776,
"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":1557740070,
"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": {
}
}
]
}
]
}"routed_requests":0,
"offloaded_requests":0,
"write_errors":0,
"read_errors":0,
"in_request":0,
"vars":[
],
"req_info": {
}
}
]
}
]
}
今回は、このまま--stats-http
を付けたままにしましょう。
ちょっと簡易的に、curl
をバックグラウンドで動かして5回連続してリクエストを投げてみます。
$ curl localhost:8000 &
$ curl localhost:8000 &
$ curl localhost:8000 &
$ curl localhost:8000 &
$ curl localhost:8000 &
この待っている間に、Statsを取得してみます。
$ curl localhost:9000
{
"version":"2.0.18",
"listen_queue":4,
"listen_queue_errors":0,
"signal_queue":0,
"load":4,
"pid":92775,
"uid":1000,
"gid":1000,
"cwd":"/path",
"locks":[
{
"user 0":0
},
{
"signal":0
},
{
"filemon":0
},
{
"timer":0
},
{
"rbtimer":0
},
{
"cron":0
},
{
"rpc":0
},
{
"snmp":0
}
],
"sockets":[
{
"name":"127.0.0.1:41235",
"proto":"uwsgi",
"queue":4,
"max_queue":100,
"shared":0,
"can_offload":0
}
],
"workers":[
{
"id":1,
"pid":92776,
"accepting":1,
"requests":9,
"delta_requests":9,
"exceptions":0,
"harakiri_count":0,
"signals":0,
"signal_queue":0,
"status":"busy",
"rss":0,
"vsz":0,
"running_time":90069190,
"last_spawn":1557740070,
"respawn_count":1,
"tx":513,
"avg_rt":9986221,
"apps":[
{
"id":0,
"modifier1":0,
"mountpoint":"",
"startup_time":0,
"requests":10,
"exceptions":0,
"chdir":""
}
],
"cores":[
{
"id":0,
"requests":9,
"static_requests":0,
"routed_requests":0,
"offloaded_requests":0,
"write_errors":0,
"read_errors":0,
"in_request":1,
"vars":[
"REQUEST_METHOD=GET",
"REQUEST_URI=/",
"PATH_INFO=/",
"QUERY_STRING=",
"SERVER_PROTOCOL=HTTP/1.1",
"SCRIPT_NAME=",
"SERVER_NAME=myserver",
"SERVER_PORT=8000",
"UWSGI_ROUTER=http",
"REMOTE_ADDR=127.0.0.1",
"REMOTE_PORT=60051",
"HTTP_HOST=localhost:8000",
"HTTP_USER_AGENT=curl/7.58.0",
"HTTP_ACCEPT=*/*",
""
],
"req_info": {
"request_start":1557740273
}
}
]
}
]
}
listen_queue
が4で
"listen_queue":4,
in_request
が1なので
"in_request":1,
現在リクエストを処理していて、待っているのが4つということがわかります。
ちなみに、このin_request
の1は、数ではなく意味的にはboolean
っぽいです…。
今回は、スレッド数もなにもチューニングしていないですから、待ちが多いですね…。
ここで、プロセス数を2、スレッド数を2にしてみましょう。
$ uwsgi --http :8000 --stats :9000 --stats-http --master --processes 2 --threads 2 --wsgi-file app.py
同じように、リクエストを投げてみます。
$ curl localhost:8000 &
$ curl localhost:8000 &
$ curl localhost:8000 &
$ curl localhost:8000 &
$ curl localhost:8000 &
Statsを取得してみましょう。workers
と配下のcores
が増え、4つまで同時にリクエストを処理できるようになっています。
$ curl localhost:9000
{
"version":"2.0.18",
"listen_queue":1,
"listen_queue_errors":0,
"signal_queue":0,
"load":1,
"pid":93145,
"uid":1000,
"gid":1000,
"cwd":"/path",
"locks":[
{
"user 0":0
},
{
"signal":0
},
{
"filemon":0
},
{
"timer":0
},
{
"rbtimer":0
},
{
"cron":0
},
{
"rpc":0
},
{
"snmp":0
}
],
"sockets":[
{
"name":"127.0.0.1:43177",
"proto":"uwsgi",
"queue":1,
"max_queue":100,
"shared":0,
"can_offload":0
}
],
"workers":[
{
"id":1,
"pid":93146,
"accepting":1,
"requests":0,
"delta_requests":0,
"exceptions":0,
"harakiri_count":0,
"signals":0,
"signal_queue":0,
"status":"busy",
"rss":0,
"vsz":0,
"running_time":0,
"last_spawn":1557740578,
"respawn_count":1,
"tx":0,
"avg_rt":0,
"apps":[
{
"id":0,
"modifier1":0,
"mountpoint":"",
"startup_time":0,
"requests":2,
"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":1,
"vars":[
"REQUEST_METHOD=GET",
"REQUEST_URI=/",
"PATH_INFO=/",
"QUERY_STRING=",
"SERVER_PROTOCOL=HTTP/1.1",
"SCRIPT_NAME=",
"SERVER_NAME=myserver",
"SERVER_PORT=8000",
"UWSGI_ROUTER=http",
"REMOTE_ADDR=127.0.0.1",
"REMOTE_PORT=55956",
"HTTP_HOST=localhost:8000",
"HTTP_USER_AGENT=curl/7.58.0",
"HTTP_ACCEPT=*/*",
""
],
"req_info": {
"request_start":1557740581
}
},
{
"id":1,
"requests":0,
"static_requests":0,
"routed_requests":0,
"offloaded_requests":0,
"write_errors":0,
"read_errors":0,
"in_request":1,
"vars":[
"REQUEST_METHOD=GET",
"REQUEST_URI=/",
"PATH_INFO=/",
"QUERY_STRING=",
"SERVER_PROTOCOL=HTTP/1.1",
"SCRIPT_NAME=",
"SERVER_NAME=myserver",
"SERVER_PORT=8000",
"UWSGI_ROUTER=http",
"REMOTE_ADDR=127.0.0.1",
"REMOTE_PORT=56980",
"HTTP_HOST=localhost:8000",
"HTTP_USER_AGENT=curl/7.58.0",
"HTTP_ACCEPT=*/*",
""
],
"req_info": {
"request_start":1557740582
}
}
]
},
{
"id":2,
"pid":93147,
"accepting":1,
"requests":0,
"delta_requests":0,
"exceptions":0,
"harakiri_count":0,
"signals":0,
"signal_queue":0,
"status":"busy",
"rss":0,
"vsz":0,
"running_time":0,
"last_spawn":1557740578,
"respawn_count":1,
"tx":0,
"avg_rt":0,
"apps":[
{
"id":0,
"modifier1":0,
"mountpoint":"",
"startup_time":0,
"requests":2,
"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":1,
"vars":[
"REQUEST_METHOD=GET",
"REQUEST_URI=/",
"PATH_INFO=/",
"QUERY_STRING=",
"SERVER_PROTOCOL=HTTP/1.1",
"SCRIPT_NAME=",
"SERVER_NAME=myserver",
"SERVER_PORT=8000",
"UWSGI_ROUTER=http",
"REMOTE_ADDR=127.0.0.1",
"REMOTE_PORT=54932",
"HTTP_HOST=localhost:8000",
"HTTP_USER_AGENT=curl/7.58.0",
"HTTP_ACCEPT=*/*",
""
],
"req_info": {
"request_start":1557740581
}
},
{
"id":1,
"requests":0,
"static_requests":0,
"routed_requests":0,
"offloaded_requests":0,
"write_errors":0,
"read_errors":0,
"in_request":1,
"vars":[
"REQUEST_METHOD=GET",
"REQUEST_URI=/",
"PATH_INFO=/",
"QUERY_STRING=",
"SERVER_PROTOCOL=HTTP/1.1",
"SCRIPT_NAME=",
"SERVER_NAME=myserver",
"SERVER_PORT=8000",
"UWSGI_ROUTER=http",
"REMOTE_ADDR=127.0.0.1",
"REMOTE_PORT=58004",
"HTTP_HOST=localhost:8000",
"HTTP_USER_AGENT=curl/7.58.0",
"HTTP_ACCEPT=*/*",
""
],
"req_info": {
"request_start":1557740582
}
}
]
}
]
}
今度は、キューが1になります。
"listen_queue":1,
こんな感じで挙動を見つつ、ソースコードを見つつ、Statsを把握していくのだと思います。
大変。