一個簡單的程序
這里使用Python3語言,創建虛擬環境,並安裝prometheus_client
mkvirtualenv --python "/usr/local/python36/bin/python3" prom pip install prometheus_client
編寫一個簡單的HTTP服務器(運行在192.168.88.50上)
import http.server from prometheus_client import start_http_server class MyHandler(http.server.BaseHTTPRequestHandler): def do_GET(self): self.send_response(200) self.end_headers() self.wfile.write(b"Hello world") if __name__ == "__main__": start_http_server(8000) # 在8000端口上啟動一個http服務器給promethes metrics 提供服務 server = http.server.HTTPServer(('localhost', 8001), MyHandler) server.serve_forever()
訪問http://192.168.88.50:8000/metrics查看

可以把它配置到prometheus服務中, 編輯prometheus.yml,添加如下配置, 並重啟prometheus
- job_name: "demo" static_configs: - targets: - "192.168.88.50:8000"
現在我們可以從prometheus中獲取指標數據

Counter
counter是一種會經常使用到指標類型,只增不減(除非發生重置),例如我們可以在應用程序中記錄某些事件發生的次數,通過以時序的形式存儲這些數據,我們可以輕松的了解該事件產生速率的變化。
擴展前面的代碼,增加一個新的mertic: 請求Hello World的次數
from prometheus_client import Counter # 第1個參數是mertic name必須是唯一的, 第二個參數是對mertic的描述 REQUESTS = Counter('hello_worlds_total', 'Hello worlds requested.') class MyHandler(http.server.BaseHTTPRequestHandler): def do_GET(self): REQUESTS.inc() # 遞增 self.send_response(200) self.end_headers() self.wfile.write(b"Hello world")
當我們在192.168.88.50上每訪問一次(curl http://127.0.0.1:8001), 則 'hello_worlds_total'的值就是增加1

使用PromQL表達式查看 請求的速率

Counting Exceptions
客戶端庫不僅提供核心功能,還提供了實用的方法。在Python中計算異常我們可以直接利用count_exceptions上下文管理器和裝飾器。
import random from prometheus_client import Counter REQUESTS = Counter('hello_worlds_total', 'Hello worlds requested.') EXCEPTIONS = Counter('hello_word_excpetions_total', 'Exceptions serving Hello World.') class MyHandler(http.server.BaseHTTPRequestHandler): def do_GET(self): REQUESTS.inc() # 這里我們使用隨機數才產生異常,通過count_exceptions能夠記錄發生異常的次數,並且不干擾程序邏輯 with EXCEPTIONS.count_exceptions(): if random.random() < 0.2: raise Exception self.send_response(200) self.end_headers() self.wfile.write(b"Hello world")
查看異常率 : rate(hello_word_excpetions_total[1m])
查看異常請求比例: rate(hello_word_excpetions_total[1m]) / rate(hello_worlds_total[1m])
我們也可以使用count_exceptions裝飾器
import random from prometheus_client import Counter REQUESTS = Counter('hello_worlds_total', 'Hello worlds requested.') EXCEPTIONS = Counter('hello_word_excpetions_total', 'Exceptions serving Hello World.') class MyHandler(http.server.BaseHTTPRequestHandler): #使用裝飾器 @EXCEPTIONS.count_exceptions() def do_GET(self): REQUESTS.inc() if random.random() < 0.2: raise Exception self.send_response(200) self.end_headers() self.wfile.write(b"Hello world")
因為Prometheus 使用 64位浮點數作為值,所以計數器不是只能加1,我們可以將計數器加到任何非負數。例如
REQUESTS = Counter('hello_worlds_total', 'Hello worlds requested.') REQUESTS.inc(2.5)
Gauge
Gauge指標類型反應了當前的狀態,它是一個可以上下浮動的實際值。而Counter我們往往關心它的增長速度。
Gauge一些常見的例子如:
- 隊列中消息的數量
- 內存使用情況
- 活動線程數
- 最后一次處理記錄的時間
- 最后一分鍾每秒平均請求數
我們可以使用Gauge提供的3個主要方法: inc、dec、和set。
inc 默認將值追加1, dec默認將值遞減1,set可以通過參數將值設置成我們想要的
下面通過案例展示如何使用Gauge來跟蹤 正在調用的數量和最后一次調用完成的時間
import time
from prometheus_client import Gauge
INPROGRESS = Gauge('hello_worlds_inprogress', 'Number of Hello Worlds in progress.')
LAST = Gauge('hello_world_last_time_seconds', 'The last time a Hello World was served.')
class MyHandler(http.server.BaseHTTPRequestHandler):
def do_GET(self):
# 開始調用,值+1
INPROGRESS.inc()
self.send_response(200)
self.end_headers()
self.wfile.write(b"Hello World")
# 記錄當前時間戳
LAST.set(time.time())
# 調用完成,值-1
INPROGRESS.dec()
這種Gauge類型的指標,我們是直接在expression browser中使用, 比如: hello_world_last_time_seconds, 確定訪問的最后時間

我們也可以通過 time() - hello_world_last_time_seconds來計算出距離最后一次訪問過了多少秒

上面的案例都是比較常見的,因此也對他們提供了使用的裝飾器函數 : track_inprogress
from prometheus_client import Gauge INPROGRESS = Gauge('hello_worlds_inprogress', 'Number of Hello Worlds in progress.') LAST = Gauge('hello_world_last_time_seconds', 'The last time a Hello World was served.') class MyHandler(http.server.BaseHTTPRequestHandler): @INPROGRESS.track_inprogress() def do_GET(self): self.send_response(200) self.end_headers() self.wfile.write(b"Hello World") # 記錄當前時間戳, 可以直接使用set_to_current_time方法 LAST.set_to_current_time()
通常counter類型的mertic都會加上后綴,如: _total、_count、sum和_bucket等,並且強烈建議加上單位名:如,(counter類型指標)處理的字節數可能會命名為:myapp_requests_processed_bytes_total. 然而guage類型的mertic並沒有這些后綴,為了避免混淆,我們也不應該使用后綴
Summary
當我們想要知道程序響應請求花費的時間或延遲時,就可以用到Summary類型的指標了。下面這個案例我們來跟蹤程序延遲。
import time from prometheus_client import Summary LATENCY = Summary('hello_world_latency_seconds', 'Time for a request Hello World') class MyHandler(http.server.BaseHTTPRequestHandler): def do_GET(self): # 記錄當前程序開始時間 start = time.time() self.send_response(200) self.end_headers() self.wfile.write(b"Hello World") # 程序運行完成時間-開始時間=花費的時間 LATENCY.observe(time.time() - start)
當我們查看 /mertics或發現有兩個指標

hello_world_latency_seconds_count
記錄的是observe調用的次數,因此可以使用表達式: rate(hello_world_latency_seconds_count[1m])返回請求的每秒速率
hello_world_latency_seconds_sum
記錄的是傳遞個observe值得總和,因此可以使用表達式: rate(hello_world_latency_seconds_sum[1m])返回每秒響應請求花費的時間
計算最后一分鍾的平均延遲:
rate(hello_world_latency_seconds_sum[1m]) / rate(hello_world_latency_seconds_count[1m])
對於延時監控,我們可以使用裝飾器來簡化代碼
from prometheus_client import Summary LATENCY = Summary('hello_world_latency_seconds', 'Time for a request Hello World') class MyHandler(http.server.BaseHTTPRequestHandler): #使用裝飾器 @LATENCY.time() def do_GET(self): self.send_response(200) self.end_headers() self.wfile.write(b"Hello World")
Histogram
Summary可以提供平均延遲。但如果是分位數(分位數告訴我們某個事件的大小比例低於給定的值),例如:0.95分位數300ms意味着95%的請求花費的時間小於300ms.
分位數在考慮實際的最終用戶體驗時很有用。如果用戶的瀏覽器向應用程序發出20個並發請求,那么決定用戶可見延遲的是請求最慢的一個
案例 :第95百分位數(0.95分位數)捕獲延遲
from prometheus_client import Histogram LATENCY = Histogram('histogram_hello_world_latency_seconds', 'Time for a request Hello World') class MyHandler(http.server.BaseHTTPRequestHandler): @LATENCY.time() def do_GET(self): self.send_response(200) self.end_headers() self.wfile.write(b"Hello World")
他將生成一組名為 histogram_hello_word_latency_seconds_bucket的時間序列

這是一組計數器, 柱狀圖有一組桶,例如1毫秒、10毫秒和25毫秒,它們跟蹤每個桶中的事件數量
現在我們要計算: 95%的訪問請求延遲分布在 哪個范圍
histogram_quantile(0.95, rate(histogram_hello_world_latency_seconds_bucket[1m]))
Buckets
從圖中可以發現默認的buckets延遲范圍是0.005秒-10秒, 這是捕獲web應用程序的典型延遲范圍。如果我們任務默認值不適合自己的用例,我們可以提供自己的buckets, 如下
# buckets里面的值要從小到大, 一般建議配置10個 LATENCY = Histogram('histogram_hello_world_latency_seconds', 'Time for a request Hello World', buckets=[0.0001, 0.0002, 0.0005, 0.001, 0.01, 0.1])

觀察到每個bucket並不是只屬於它自己的事件計數,它包括了比它小的 bukect計數,一直到+Inf(表示事件總數), 這也就是為什么標簽是 le(小於或等於)
在分析上圖:
+Inf 285 表示一共有285個請求
延遲<=0.0002秒的請求有144個
延遲<=0.001秒的請求有284個
我們也可以准確的計算出請求超過5毫秒(0.005)的請求比例

