likes
comments
collection
share

tornado 并发编程系列(6)——Prometheus、StatsD Exporter 和 Grafana监控和可视化应用程序指标数据

作者站长头像
站长
· 阅读数 50

在现代软件开发中,了解应用程序的性能和健康状况非常重要。通过监控指标,我们可以了解应用程序的运行状态,并能够及时识别和解决潜在的问题。

Prometheus

Prometheus 是一个开源的系统监控和告警工具。它使用 Pull 模型定期从目标系统获取指标数据,并将其存储在时间序列数据库中。Prometheus 提供了丰富的查询语言,可以用于分析和可视化指标数据。

在本教程中,我们将使用 Prometheus 来收集和存储应用程序的指标数据。

tornado 并发编程系列(6)——Prometheus、StatsD Exporter 和 Grafana监控和可视化应用程序指标数据

tornado 并发编程系列(6)——Prometheus、StatsD Exporter 和 Grafana监控和可视化应用程序指标数据

StatsD Exporter

StatsD Exporter 是 Prometheus 的一个扩展,它提供了与 StatsD 协议兼容的接口,使得我们可以使用现有的 StatsD 客户端来发送指标数据到 Prometheus。

在本教程中,我们将配置 StatsD Exporter 来接收来自 StatsD 客户端的指标数据,并将其转发给 Prometheus。

tornado 并发编程系列(6)——Prometheus、StatsD Exporter 和 Grafana监控和可视化应用程序指标数据

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的状态 tornado 并发编程系列(6)——Prometheus、StatsD Exporter 和 Grafana监控和可视化应用程序指标数据

查看grafana的状态 tornado 并发编程系列(6)——Prometheus、StatsD Exporter 和 Grafana监控和可视化应用程序指标数据 我们访问http://localhost:9090/targets?search= 查看Endpoint是不是up的状态

tornado 并发编程系列(6)——Prometheus、StatsD Exporter 和 Grafana监控和可视化应用程序指标数据

访问 http://192.168.105.1:9123/metrics 你应该看到

tornado 并发编程系列(6)——Prometheus、StatsD Exporter 和 Grafana监控和可视化应用程序指标数据

Tornado StatsD 客户端示例

最简单方法是将它们发送到 DogStatsD,这是与 Datadog Agent 捆绑在一起的指标聚合服务。DogStatsD 实现了StatsD协议并添加了一些 Datadog 特定的扩展:

DogStatsD 通过 UDP 接受自定义指标事件服务检查,并定期聚合并将它们转发给 Datadog。

自定义指标非常重要,目前也就只是发现datadog能够实现为statsd标记tag

由于它使用 UDP,因此您的应用程序可以将指标发送到 DogStatsD 并恢复其工作,而无需等待响应。如果 DogStatsD 变得不可用,您的应用程序不会遇到中断。

tornado 并发编程系列(6)——Prometheus、StatsD Exporter 和 Grafana监控和可视化应用程序指标数据

安装 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>)

  • 由于提交了多个指标,因此存储的指标类型 ( GAUGERATE) 取决于指标。请参阅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}"])

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ee052a81d75243caa239020790652ca4~tplv-k3u1fbpfcp-watermark.image?)
        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 摘要。

tornado 并发编程系列(6)——Prometheus、StatsD Exporter 和 Grafana监控和可视化应用程序指标数据

在 Grafana 中可视化 API 调用指标

最后一部分介绍在端口 3000 上启动 Grafana、添加 Prometheus 数据源以及创建图表以可视化 API 调用指标。

这里填ip

tornado 并发编程系列(6)——Prometheus、StatsD Exporter 和 Grafana监控和可视化应用程序指标数据

tornado 并发编程系列(6)——Prometheus、StatsD Exporter 和 Grafana监控和可视化应用程序指标数据

配置查询规则并且展示

tornado 并发编程系列(6)——Prometheus、StatsD Exporter 和 Grafana监控和可视化应用程序指标数据

tornado 并发编程系列(6)——Prometheus、StatsD Exporter 和 Grafana监控和可视化应用程序指标数据

转载自:https://juejin.cn/post/7251878440327069756
评论
请登录