tornado 并发编程系列(6)——Prometheus、StatsD Exporter 和 Grafana监控和可视化应用程序指标数据
在现代软件开发中,了解应用程序的性能和健康状况非常重要。通过监控指标,我们可以了解应用程序的运行状态,并能够及时识别和解决潜在的问题。
Prometheus
Prometheus 是一个开源的系统监控和告警工具。它使用 Pull 模型定期从目标系统获取指标数据,并将其存储在时间序列数据库中。Prometheus 提供了丰富的查询语言,可以用于分析和可视化指标数据。
在本教程中,我们将使用 Prometheus 来收集和存储应用程序的指标数据。
StatsD Exporter
StatsD Exporter 是 Prometheus 的一个扩展,它提供了与 StatsD 协议兼容的接口,使得我们可以使用现有的 StatsD 客户端来发送指标数据到 Prometheus。
在本教程中,我们将配置 StatsD Exporter 来接收来自 StatsD 客户端的指标数据,并将其转发给 Prometheus。
Grafana
Grafana 是一个开源的数据可视化和监控工具。它可以从多个数据源获取指标数据,并将其以直观和丰富的方式展示出来。
在本教程中,我们将使用 Grafana 来创建仪表盘,并将 Prometheus 作为数据源,以便我们可以实时监控和可视化应用程序的指标数据。
docker-compose
prom-statsd-exporter prometheus grafana
version: '2'
services:
prom-statsd-exporter:
image: prom/statsd-exporter
ports:
- 8125:8125/udp
- 9123:9123
command:
- --statsd.mapping-config=/tmp/test-mapping.yml
- --statsd.listen-udp=:8125
- --web.listen-address=:9123
volumes:
- ./statsd_mapping.yml:/tmp/test-mapping.yml
prometheus:
image: prom/prometheus
ports:
- 9090:9090
links:
- prom-statsd-exporter
volumes:
- ./prometheus.yml:/prometheus.yml
command:
- --config.file=/prometheus.yml
- --log.level=debug
- --web.listen-address=:9090
grafana:
image: grafana/grafana
links:
- prometheus
ports:
- 3000:3000
prometheus.yml
这里注意,需要填写ip 192.168.105.1
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
# optional: this makes the metrics available to us about Promethus itself.
- job_name: "prometheus"
static_configs:
- targets: ["localhost:9090"]
# tells Prometheus to scrape metrics an address over port 9123
- job_name: "test_metrics"
static_configs:
- targets: ["192.168.105.1:9123"] # see statsd-exporter further down
statsd_mapping.yml
启动docker-compose up -d
查看docker-compose的状态
查看grafana的状态
我们访问http://localhost:9090/targets?search= 查看Endpoint是不是up的状态
访问 http://192.168.105.1:9123/metrics 你应该看到
Tornado StatsD 客户端示例
最简单方法是将它们发送到 DogStatsD,这是与 Datadog Agent 捆绑在一起的指标聚合服务。DogStatsD 实现了StatsD协议并添加了一些 Datadog 特定的扩展:
DogStatsD 通过 UDP 接受自定义指标、事件和服务检查,并定期聚合并将它们转发给 Datadog。
自定义指标非常重要,目前也就只是发现datadog能够实现为statsd标记tag
由于它使用 UDP,因此您的应用程序可以将指标发送到 DogStatsD 并恢复其工作,而无需等待响应。如果 DogStatsD 变得不可用,您的应用程序不会遇到中断。
安装 DogStatsD 客户端
pip install datadog
安装 DogStatsD 客户端后,在代码中实例化它:
from datadog import initialize, statsd
options = {
'statsd_host':'127.0.0.1',
'statsd_port':8125
}
initialize(**options)
指标提交:DogStatsD
increment(<METRIC_NAME>, <SAMPLE_RATE>, <TAGS>)
- 用于增加 COUNT 个指标。作为类型存储
RATE
在 Datadog 中。存储的时间序列中的每个值都是 StatsD 刷新周期内指标值的时间归一化增量。
decrement(<METRIC_NAME>, <SAMPLE_RATE>, <TAGS>)
- 用于减少 COUNT 个指标。作为类型存储
RATE
在 Datadog 中。存储的时间序列中的每个值都是 StatsD 刷新周期内指标值的时间归一化增量。
from datadog import initialize, statsd
import time
options = {
'statsd_host':'127.0.0.1',
'statsd_port':8125
}
initialize(**options)
while(1):
statsd.increment('example_metric.increment', tags=["environment:dev"])
statsd.decrement('example_metric.decrement', tags=["environment:dev"])
time.sleep(10)
gauge(<METRIC_NAME>, <METRIC_VALUE>, <SAMPLE_RATE>, <TAGS>)
- 作为类型存储
GAUGE
在 Datadog 中。存储的时间序列中的每个值都是 StatsD 刷新期间为指标提交的最后一个仪表值。
from datadog import initialize, statsd
import time
options = {
'statsd_host':'127.0.0.1',
'statsd_port':8125
}
initialize(**options)
i = 0
while(1):
i += 1
statsd.gauge('example_metric.gauge', i, tags=["environment:dev"])
time.sleep(10)
timed(<METRIC_NAME>, <METRIC_VALUE>, <SAMPLE_RATE>, <TAGS>)
- 由于提交了多个指标,因此存储的指标类型 (
GAUGE
,RATE
) 取决于指标。请参阅HISTOGRAM 度量类型文档以了解更多信息。
from datadog import initialize, statsd
import time
import random
options = {
'statsd_host':'127.0.0.1',
'statsd_port':8125
}
initialize(**options)
@statsd.timed('example_metric.timer', tags=["environment:dev,function:my_function"])
def my_function():
time.sleep(random.randint(0, 10))
while(1):
my_function()
或使用上下文管理器:
from datadog import statsd
import time
import random
def my_function():
# First some stuff you don't want to time
sleep(1)
# Now start the timer
with statsd.timed('example_metric.timer', tags=["environment:dev"]):
# do something to be measured
sleep(random.randint(0, 10))
while(1):
my_function()
集成Tornado
class BaseHandler(RequestHandler, ABC):
def on_finish(self):
# 处理完请求后输出日志,将请求头信息和响应结果json化
end_time = datetime.datetime.now()
response_time_ms = (end_time - self.start_time).total_seconds() * 1000
request_data = {
"tid": self.request.headers.get("tid", None),
"url": self.request.uri,
"method": self.request.method,
"remote_ip": self.request.remote_ip,
"user_agent": self.request.headers["User-Agent"],
"request_body": json.loads(self.request.body) if self.request.body else "",
"start_time": self.start_time,
"end_time": end_time
}
response_data = {
"status": self.get_status(),
"response_body": json.loads(self.response_dict) if not isinstance(self.response_dict,
dict) else self.response_dict,
"response_time": round(response_time_ms, 2)
}
logger = get_logger("access")
logger.bind(**request_data).bind(**response_data).info("request success")
code = self.get_status()
uri = self.request.uri
# 对接ops网关会自动带上?需要去除
uri = uri.split("?")[0]
statsd.increment(f'http_responses_total_by_status_view_method_total',
tags=[f"namespace:dev", f"view:{uri}", f"status:{code}"])

statsd.timing('http_responses_response_time_by_view', response_time_ms,
tags=[f"namespace:dev", f"view:{uri}"])
statsd.timing('http_responses_response_time', response_time_ms, tags=[f"status:{code}"])
BaseHandler作为中间件,每个路由类必须继承BaseHandler,通过on_finish
函数结束整个请求。这时候便可以做url请求的埋点
statsd.increment(f'http_responses_total_by_status_view_method_total', tags=[f"namespace:dev", f"view:{uri}", f"status:{code}"])
statsd.timing('http_responses_response_time_by_view', response_time_ms, tags=[f"namespace:dev", f"view:{uri}"])
statsd.timing('http_responses_response_time', response_time_ms, tags=[f"status:{code}"])
搜索http_responses_response_time
,您应该会看到类似于以下内容的 Prometheus 摘要。
在 Grafana 中可视化 API 调用指标
最后一部分介绍在端口 3000 上启动 Grafana、添加 Prometheus 数据源以及创建图表以可视化 API 调用指标。
这里填ip
配置查询规则并且展示
转载自:https://juejin.cn/post/7251878440327069756