Java获取Prometheus监控指标数据

Posted 浪浪山的猿

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java获取Prometheus监控指标数据相关的知识,希望对你有一定的参考价值。

Java获取Prometheus监控指标数据

一. 准备工作

1. 有可以被Prometheus监控的服务

没有的话可以参考以下链接本地搭建:SpringBoot应用接入Prometheus+Grafana

2. 选择我们调用远程服务的方式

可以选择RestTemplate 作为远程调用工具,RestTemplate 内部默认用的是 jdk 自带的
HttpURLConnection 发送请求的,性能上面并不是太突出。可以将其替换为 httpclient 或者 okhttp。

二. 实战

1. 引入依赖

   <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
   </dependency>

2. 编写http请求工具

package com.alibaba.bizworks.om.account.prometheus;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.Header;
import org.apache.http.client.HttpClient;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContextBuilder;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;

/**
 * RestTemplate 远程调用工具类
 * @author gf
 * @date 2022/10/24
 */
@Slf4j
public class RestTemplateUtils 

    /**
     * 读取时间,自定义默认8s,0表示没有超时时间
     */
    public static final int READ_TIMEOUT = 1000*8;

    /**
     * 连接时间,自定义默认8s,0表示没有超时时间
     */
    public static final int CONNEC_TIMEOUT = 1000*8;

    /**
     * 重试次数,自定义默认1
     */
    public static final int RETRY_COUNT = 1;


    /**
     * http 请求 GET
     *
     * @param url           地址
     * @param params        参数
     * @return String 类型
     */
    public static String getHttp(String url, JSONObject params) 
        String result = getHttp(url, params, READ_TIMEOUT, CONNEC_TIMEOUT, RETRY_COUNT);
        return result;
    
    /**
     * http 请求 GET
     *
     * @param url           地址
     * @param params        参数
     * @param connecTimeout 连接时间
     * @param readTimeout   读取时间
     * @param retryCount    重试机制
     * @return String 类型
     */
    public static String getHttp(String url, JSONObject params, int connecTimeout, int readTimeout, int retryCount) 
        SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
        requestFactory.setConnectTimeout(connecTimeout);
        requestFactory.setReadTimeout(readTimeout);
        RestTemplate restTemplate = new RestTemplate(requestFactory);
        restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8)); // 设置编码集
        restTemplate.setErrorHandler(new DefaultResponseErrorHandler()); // 异常处理
        url = expandURL(url, params);
        String result = null; // 返回值类型;
        for (int i = 1; i <= retryCount; i++) 
            try 
                log.info("【GET/HTTP请求信息】,请求地址:,请求参数:", url, params);
                result = restTemplate.getForObject(url, String.class, params);
                log.info("【GET/HTTP请求信息】,请求地址:,请求参数:,返回结果:", url, params,result);
                return result;
             catch (Exception e) 
                log.error("【GET/HTTP请求信息】异常,重试count:,请求地址:,请求参数:,异常信息:", i, url, params,e);
                e.printStackTrace();
            
        
        return result;
    

    /**
     * https 请求 GET
     *
     * @param url           地址
     * @param params        参数
     * @return String 类型
     */
    public static String getHttps(String url, JSONObject params) 
        String result = getHttps(url, params, READ_TIMEOUT, CONNEC_TIMEOUT, RETRY_COUNT);
        return result;
    

    /**
     * https 请求 GET
     *
     * @param url           地址
     * @param params        参数
     * @param connecTimeout 连接时间
     * @param readTimeout   读取时间
     * @param retryCount    重试机制
     * @return String 类型
     */
    public static String getHttps(String url, JSONObject params, int connecTimeout, int readTimeout, int retryCount) 
        SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
        requestFactory.setConnectTimeout(connecTimeout);
        requestFactory.setReadTimeout(readTimeout);
        RestTemplate restTemplate = restTemplate();
        RestTemplateUtils.clientHttpRequestFactory();
        restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8)); // 设置编码集
        restTemplate.setErrorHandler(new DefaultResponseErrorHandler()); //error处理
        restTemplate.setRequestFactory(clientHttpRequestFactory()); // 绕过https
        url = expandURL(url, params);
        String result = null; // 返回值类型;
        for (int i = 1; i <= retryCount; i++) 

            try 
                log.info("【GET/HTTPS请求信息】,请求地址:,请求参数:", url, params);
                result = restTemplate.getForObject(url, String.class, params);
                log.info("【GET/HTTPS请求信息】,请求地址:,请求参数:,返回结果:", url, params,result);
                return result;
             catch (Exception e) 
                log.error("【GET/HTTPS请求信息】异常,重试count:,请求地址:,请求参数:,异常信息:", i, url, params,e);
                e.printStackTrace();
            
        
        return result;
    
    /**
     * @Title: URL拼接
     * @MethodName:  expandURL
     * @param url
     * @param jsonObject
     * @Return java.lang.String
     * @Exception
     * @Description:
     */
    private static String expandURL(String url,JSONObject jsonObject) 

        StringBuilder sb = new StringBuilder(url);
        sb.append("?");
        Set<String> keys = jsonObject.keySet();
        for (String key : keys) 
            sb.append(key).append("=").append(jsonObject.getString(key)).append("&");
        
        return sb.deleteCharAt(sb.length() - 1).toString();
    




    /**
     * 获取RestTemplate实例对象,可自由调用其方法
     *
     * @return RestTemplate实例对象
     */
    public static HttpClient httpClient() 
        HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
        try 
            //设置信任ssl访问
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, (arg0, arg1) -> true).build();
            httpClientBuilder.setSSLContext(sslContext);
            HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
            SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
            Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                    // 注册http和https请求
                    .register("http", PlainConnectionSocketFactory.getSocketFactory())
                    .register("https", sslConnectionSocketFactory).build();

            //使用Httpclient连接池的方式配置(推荐),同时支持netty,okHttp以及其他http框架
            PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
            // 最大连接数
            poolingHttpClientConnectionManager.setMaxTotal(1000);
            // 同路由并发数
            poolingHttpClientConnectionManager.setDefaultMaxPerRoute(100);
            //配置连接池
            httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);
            // 重试次数
            httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(1, true));
            //设置默认请求头
            List<Header> headers = new ArrayList<>();
            httpClientBuilder.setDefaultHeaders(headers);
            return httpClientBuilder.build();
         catch (Exception e) 
            throw new RuntimeException(e);
        
    

    public static ClientHttpRequestFactory clientHttpRequestFactory() 
        HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient());
        // 连接超时(毫秒),这里设置10秒
        clientHttpRequestFactory.setConnectTimeout(10 * 1000);
        // 数据读取超时时间(毫秒),这里设置60秒
        clientHttpRequestFactory.setReadTimeout(60 * 1000);
        // 从连接池获取请求连接的超时时间(毫秒),不宜过长,必须设置,比如连接不够用时,时间过长将是灾难性的
        clientHttpRequestFactory.setConnectionRequestTimeout(10 * 1000);
        return clientHttpRequestFactory;
    

    public static RestTemplate restTemplate()
        //创建RestTemplate的时候,指定ClientHttpRequestFactory
        return new RestTemplate(clientHttpRequestFactory());
    



3. 编写响应体

从浏览器中找到peomrtheus它获取服务指标的请求地址

然后根据这个地址在postman发get请求拿到它的响应体结构

接下来我们就可以根据这个结果来封装相应体了

@Data
public class PromResponceInfo 
    /**
     * 状态
     * 成功-- success
     */
    private String status;

    /**
     * prometheus指标属性和值
     */
    private PromDataInfo data;



这里只关注后面的resultType 和result参数

@Data
public class PromDataInfo 

    /**
     * prometheus监控服务指表参数
     */
    private List droppedTargets;
    private List<PromResultInfo> activeTargets;

    /**
     * prometheus监控样本指标参数
     */
    private String resultType;
    private List<PromMetric> result;


@Data
public class PromMetric 

    /**
     * metric name和描述当前样本特征的labelsets
     */
    private PromMetricInfo metric;

    /**
     * 一个float64的浮点型数据表示当前样本的值。
     */
    private String[] value;


@Data
public class PromMetricInfo 

    /**
     * prometheus指标名称
     */
    private String __name__;

    /**
     * prometheus实例名称
     */
    private String instance;

    /**
     * prometheus任务名称
     */
    private String job;


    private String application;
    private String exception;

    private String method;
    private String outcome;
    private String status;
    private String url;

到这里我们响应体的结构就写好了,还有一个指标参数,我这里写了一个类,后面需要查询指标或者一些聚合函数(PromQL)

package com.alibaba.bizworks.om.account.prometheus;

import lombok.Data;

/**
 * @Title: prometheus常量信息
 * @author gf
 * @date 2022/10/19
 */
@Data
public class PromConstants 
    /**
     * prometheus-查询SUCCESS
     */
    public static final String SUCCESS = "success";

    /**prometheus-查询参数*/
    public static final String QUERY = "query";

    /**系统CPU使用率*/
    public static final String SYSTEM_CPU_USAGE= "system_cpu_usage";
    /**Java虚拟机可用的处理器数量*/
    public static final String SYSTEM_CPU_COUNT= "system_cpu_count";
    /**JVM的CPU利用率*/
    public static final String PROCESS_CPU_COUNT= "process_cpu_usage";

    /**
     *  tomcat相关参数
     */
    /**tomcat_当前活跃会话数*/
    public static final String TOMCAT_SESSIONS_ACTIVE_CURRENT_SESSIONS = "tomcat_sessions_active_current_sessions";

    /**
     *  jvm 相关参数
     */

    /**Java虚拟机的正常运行时间*/
    public static final String PROCESS_UPTIME_SECONDS = "process_uptime_seconds";

    /**可供Java虚拟机使用的已提交的内存量*/
    public static final String JVM_MEMORT_COMMITTED_BYTES = "jvm_memory_committed_bytes";

    /**自Java虚拟机启动或重置峰值以来的活动线程峰值*/
    public static final String JVM_THREADS_PEAK_THREADS= "jvm_threads_peak_threads";

    /**在一个GC之后到下一个GC之前增加年轻代内存池的大小*/
    public static final String JVM_GC_MEMORT_ALLOCATED_BYTES_TOTAL= "jvm_gc_memory_allocated_bytes_total";

    /**进程的开始时间*/
    public static final String PROCESS_START_TIME_SECONDS= "process_start_time_seconds";

    /**最大内存*/
    public static final String JVM_MEMORT_MAX_BYTES= "jvm_memory_max_bytes";

    /**已使用内存*/
    public static final String JVM_MEMORT_USED_BYTES= "jvm_memory_used_bytes";

    /**请求次数*/
    public static final String HTTP_SERVER_REQUEST_SECONDS_COUNT= "http_server_requests_seconds_count";

    /**请求n次花费的时间*/
    public static final String HTTP_SERVER_REQUEST_SECONDS_SUM= "http_server_requests_seconds_sum";

    /**最长一次花了多长时间*/
    public static final String HTTP_SERVER_REQUEST_SECONDS_MAX= "http_server_requests_seconds_max";

    /**日志总数*/
    public static final String LOGBACK_EVENTS_TOTAL = "logback_events_total";



4. 编写接口测试

参数说明:

  • promURL:请求url
  • promQL:指标参数
@Slf4j
public class TestController 

    public static PromDataInfo getDateInfo(String promURL, String promQL) 

        log.info("请求地址:,请求QL:", promURL, promQL);
        JSONObject param = new JSONObject();
        param.put(PromConstants.QUERY, promQL);
        
    参考技术A
            

Prometheus 最开始是由 SoundCloud 开发的开源监控告警系统,是 Google BorgMon 监控系统的开源版本。在 2016 年,Prometheus 加入 CNCF,成为继 Kubernetes 之后第二个被 CNCF 托管的项目。随着 Kubernetes 在容器编排领头羊地位的确立,Prometheus 也成为 Kubernetes 容器监控的标配。


监控系统的总体架构大多是类似的,都有数据采集、数据处理存储、告警动作触发和告警,以及对监控数据的展示。下面是 Prometheus 的架构:

Prometheus Server 负责定时从 Prometheus 采集端 Pull(拉) 监控数据。Prometheus 采集端可以是实现了 /metrics 接口的服务,可以是从第三方服务导出监控数据的 exporter,也可以是存放短生命周期服务监控数据的 Pushgateway。相比大多数采用 Push(推) 监控数据的方式,Pull 使得 Promethues Server 与被采集端的耦合度更低,Prometheus Server 更容易实现水平拓展。对于采集的监控数据,Prometheus Server 使用内置时序数据库 TSDB 进行存储。同时也会使用这些监控数据进行告警规则的计算,产生的告警将会通过 Prometheus 另一个独立的组件 Alertmanager 进行发送。Alertmanager 提供了十分灵活的告警方式,并且支持高可用部署。对于采集到的监控数据,可以通过 Prometheus 自身提供的 Web UI 进行查询,也可以使用 Grafana 进行展示。

以上是关于Java获取Prometheus监控指标数据的主要内容,如果未能解决你的问题,请参考以下文章

Prometheus监控运维实战七: 主机监控指标

prometheus以监控Pod TCP连接数为例删除一个或多个metrics指标

Prometheus 监控K8S Node监控

Prometheus监控指标的label注入方法

Prometheus 使用 PushGateway 进行数据上报采集

容器云平台监控告警体系—— Golang应用接入Prometheus