Java获取Prometheus监控数据的方法实现

 更新时间:2023年12月22日 15:24:29   作者:要加油!  
本文主要介绍了Java获取Prometheus监控数据的方法实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

前言

这里只是简单的使用Java获取到Prometheus监控数据的资源监控,并不做深入解析,实际上是我也不会,只是记录一下怎么使用。本篇文章局限性很高!!!

普罗米修斯学习地址

一、使用步骤

1. 封装参数类【DockerResourceMonitorVo】

/**
 * 容器资源监控
 */
@Data
@ApiModel("容器资源监控")
public class DockerResourceMonitorVo {
    /**
     * 实例编码
     */
    @NotNull(message = "容器实例编码不能为空")
    @ApiModelProperty("容器实例编码")
    private String code;

    /**
     * 开始时间
     */
    @ApiModelProperty("开始时间")
    private String startDate;

    /**
     * 结束时间
     */
    @ApiModelProperty("结束时间")
    private String endDate;

    /**
     * 手工输入时间
     */
    @ApiModelProperty("手工输入时间")
    private Integer handWriteTime;

}

2. 处理参数,对CPU、内存、GPU、GPU显存发送监控请求

public Result<JSONObject> getAllResourceMonitor(DockerResourceMonitorVo body) {
        try {
            //获取开始/结束时间的毫秒数
            String startDate = "";
            String endDate = "";
            //时间为空,则使用默认时间
            if (StringUtils.isEmpty(body.getStartDate()) || StringUtils.isEmpty(body.getEndDate())) {
                //则默认将当前时间往前推15分钟作为开始时间
                Integer timeCycle = 15;
                if (null != body.getHandWriteTime()) { //用户手动输入时间,由前端转成分钟传入
                    timeCycle = body.getHandWriteTime();
                } 
                Calendar calendar = Calendar.getInstance();
                calendar.setTime(new Date());
                calendar.add(Calendar.MINUTE, -timeCycle);
                //设置开始时间毫秒数
                startDate = String.valueOf(calendar.getTimeInMillis());
                //结束时间为空,则使用当前时间
                endDate = String.valueOf(new Date().getTime());
            } else {
                startDate = String.valueOf(DateUtils.yyyyMMddHHmmssWithWhiffletreeToDate(body.getStartDate()).getTime());
                endDate = String.valueOf(DateUtils.yyyyMMddHHmmssWithWhiffletreeToDate(body.getEndDate()).getTime());
            }
            //计算结束时间-开始时间的差值
            long minute = (Long.parseLong(endDate) - Long.parseLong(startDate)) / 1000 / 60;
            //获取步长/采集周期
            String step = getStep(minute);

            //设置请求参数开始时间/结束时间的格式
           // String startTime = StringUtils.substring(startDate, 0, startDate.length() - 3) + ".006";
           // String endTime = StringUtils.substring(endDate, 0, endDate.length() - 3) + ".006";
			String startTime = startDate.substring(0, startDate.length() - 3) + ".006";
			String endTime = endDate.substring(0, endDate.length() - 3) + ".006";
            JSONObject resourceMonitorData = new JSONObject(true);
            //GPU利用率
            JSONObject gpuRate = getGpuRate(body.getCode(), startTime, endTime, step);
            resourceMonitorData.put("gpuRate", gpuRate);
            //GPU显存使用量
            JSONObject gpuMemoryRate = getGpuMemoryRate(body.getCode(), startTime, endTime, step);
            resourceMonitorData.put("gpuMemoryRate", gpuMemoryRate);
            //获取CPU利用率
            JSONObject cpuRate = getCpuRate(body.getCode(), startTime, endTime, step);
            resourceMonitorData.put("cpuRate", cpuRate);
            //内存使用量
            JSONObject memoryRate = getMemoryRate(body.getCode(), startTime, endTime, step);
            resourceMonitorData.put("memoryRate", memoryRate);
            return Result.ok(resourceMonitorData);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException("获取资源监控失败!");
        }
    }

	 /**
     * 根据时间差获取步长/采集周期
     *
     * @param minute
     * @return
     */
    private String getStep(Long minute) {
        if (minute <= 5) {//5分钟以内
            return "1";
        } else if (minute > 5 && minute <= 60) {//大于5分钟小于1小时
            return "1";
        } else if (minute > 60 && minute <= (60 * 24)) {//1440-->大于1小时小于24小时
            return "30";
        } else if (minute > (60 * 24) && minute <= (60 * 24 * 7)) {//10080-->大于24小时小于一周
            return "300";
        } else if (minute > (60 * 24 * 7) && minute <= (60 * 24 * 7 * 4)) {//40320-->大于1周小于4周
            return "1800";
        } else {//大于4周
            return "43200";
        }
    }

3. 设置请求参数,发送请求

/**
     * 获取cpu利用率 单位:%
     *
     * @param name      容器实例名
     * @param startTime 开始时间字符串
     * @param endTime   结束时间字符串
     * @param step      步长/采集周期
     */
    public JSONObject getCpuRate(String name, String startTime, String endTime, String step) {
        //拼接url   monitorUrl 是我在配置文件中定义的==>http://ip:9090/api/v1/query_range
        String url = monitorUrl + "?query={query}&start={startTime}&end={endTime}&step={step}";

        //设置参数
        Map<String, String> param = new HashMap();
        String query = "sum(rate(container_cpu_user_seconds_total{name='" + name + "'}[5m])) by (name) * 100";
        param.put("query", query);
        param.put("startTime", startTime);
        param.put("endTime", endTime);
        param.put("step", step);

        //发起请求
        JSONObject resultJson = sendResourceMonitor(url, param);
        //解析结果集
        return analyticResult("CPU利用率", resultJson, step, startTime);
    }

    /**
     * 获取内存使用量 单位:GB
     *
     * @param name      容器实例名
     * @param startTime 开始时间字符串
     * @param endTime   结束时间字符串
     * @param step      步长/采集周期
     */
    public JSONObject getMemoryRate(String name, String startTime, String endTime, String step) {
        //拼接url monitorUrl 是我在配置文件中定义的==>http://ip:9090/api/v1/query_range
        String url = monitorUrl + "?query={query}&start={startTime}&end={endTime}&step={step}";

        //设置参数
        Map<String, String> param = new HashMap();
        String query = "sum(container_memory_rss{name='" + name + "'}) by (name) /1024/1024/1024";
        param.put("query", query);
        param.put("startTime", startTime);
        param.put("endTime", endTime);
        param.put("step", step + "");

        //发起请求
        JSONObject resultJson = sendResourceMonitor(url, param);
        //解析结果集
        return analyticResult("内存使用量", resultJson, step, startTime);
    }

    /**
     * 获取GPU利用率 单位:%
     *
     * @param name      容器实例名
     * @param startTime 开始时间字符串
     * @param endTime   结束时间字符串
     * @param step      步长/采集周期
     */
    public JSONObject getGpuRate(String name, String startTime, String endTime, String step) {
        //拼接url monitorUrl 是我在配置文件中定义的==>http://ip:9090/api/v1/query_range
        String url = monitorUrl + "?query={query}&start={startTime}&end={endTime}&step={step}";

        //设置参数
        Map<String, String> param = new HashMap();
        String query = "sum(container_accelerator_duty_cycle{name='" + name + "'}) by(name, acc_id)";
        param.put("query", query);
        param.put("startTime", startTime);
        param.put("endTime", endTime);
        param.put("step", step);

        //发起请求
        JSONObject resultJson = sendResourceMonitor(url, param);
        //解析结果集
        return analyticResult("GPU利用率", resultJson, step, startTime);
    }

    /**
     * 获取GPU显存使用量 单位:GB
     *
     * @param name      容器实例名
     * @param startTime 开始时间字符串
     * @param endTime   结束时间字符串
     * @param step      步长/采集周期
     */
    public JSONObject getGpuMemoryRate(String name, String startTime, String endTime, String step) {
        //拼接url monitorUrl 是我在配置文件中定义的==>http://ip:9090/api/v1/query_range
        String url = monitorUrl + "?query={query}&start={startTime}&end={endTime}&step={step}";

        //设置参数
        Map<String, String> param = new HashMap();
        String query = "sum(container_accelerator_memory_used_bytes{name= '" + name + "'}) by (name, acc_id) /1024/1024/1024";
        param.put("query", query);
        param.put("startTime", startTime);
        param.put("endTime", endTime);
        param.put("step", step);

        //发起请求
        JSONObject resultJson = sendResourceMonitor(url, param);
        //解析结果集
        return analyticResult("GPU显存使用量", resultJson, step, startTime);
    }

    /**
     * 发送HTTP请求Prometheus
     *
     * @param url
     * @param param
     * @return
     */
    public JSONObject sendResourceMonitor(String url, Map<String, String> param) {
        try {
            //发起请求
            Map<String, Object> resultMap = restTemplate.getForObject(url, Map.class, param);
            //将map转为json
            //JSONObject resultJson = JsonUtils.parseObject(JsonUtils.toJSONString(resultMap), JSONObject.class);
            JSONObject resultJson = JSON.parseObject(JSONObject.toJSONString(resultMap), JSONObject.class);
            String status = resultJson.getString("status");
            if (!"success".equals(status)) {
                log.error("资源监控请求失败! url:{}, param:{},", url, param.toString());
                return null;
            }
            return resultJson;
        } catch (Exception e) {
            e.printStackTrace();
            log.error("资源监控请求失败! url:{}, param:{},", url, param.toString());
            return null;
        }

    }

4. 解析Prometheus返回结果集

      /**
     * 解析Prometheus返回结果
     *
     * @param title  标题
     * @param result
     * @return
     */
    private JSONObject analyticResult(String title, JSONObject result, String step, String startTime) {
        JSONObject returnJson = new JSONObject();
        JSONArray returnArray = new JSONArray();
        List<String> dateList = new ArrayList<>();//获取时间轴
        if (null == result) {
            return null;
        }
        try {
            //获取result结果集
            JSONArray resultArray = result.getJSONObject("data").getJSONArray("result");
            if (null == resultArray || resultArray.isEmpty()) {
                return null;
            }
            //获取请求的开始时间毫秒数
            long startMillisecond = Long.parseLong(startTime.replace(".", ""));
            for (int i = 0; i < resultArray.size(); i++) {
                JSONObject resultJson = resultArray.getJSONObject(i);
                //获取数据集合
                List<String> valueList = new ArrayList<>();
                JSONObject jsonObject = new JSONObject();
                //获取实例名
                String name = title.contains("GPU") ? "卡" + (i + 1) : title;
                jsonObject.put("name", name);
                //获取values
                JSONArray valuesArray = resultJson.getJSONArray("values");
                long upMillisecond = 0l;
                for (int j = 0; j < valuesArray.size(); j++) {
                    try {
                        JSONArray json = valuesArray.getJSONArray(j);
                        long currentMillisecond = Long.parseLong(json.getString(0).replace(".", ""));
                        if (j == 0) {//flag:true-->如果查询范围为5天,而容器却在昨天购买,前3天的日期轴没有,通过对比查询时间与数据返回时间,补齐时间轴
                            verifyCompleteTimeline(startMillisecond, currentMillisecond, step, (i == 0), true, dateList, valueList);
                        }
                        //flag:false-->如果查询范围为3天,机器第1,3天开启,在第2天关机,则Prometheus不会返回第二天的数据,时间轴只有第一天和第三天的数据,直接跳过第二天
                        if (verifyCompleteTimeline(upMillisecond, currentMillisecond, step, (i == 0), false, dateList, valueList)) {
                            upMillisecond = DateUtils.yyyyMMddHHmmssWithWhiffletreeToDate(dateList.get(dateList.size() - 1)).getTime();
                            continue;
                        }
                        if (i == 0) {
                            dateList.add(DateUtils.yyyyMMddHHmmssWithWhiffletree(new Date(currentMillisecond)));
                        }
                        upMillisecond = currentMillisecond;
                        double value = Double.valueOf(json.getString(1));
                        // 如果是CPU或GPU利用率 则保留两位小数
                        if (title.contains("CPU利用率") || title.contains("GPU利用率")) {
                            valueList.add(String.format("%.2f", value));
                        } else {//保留一位小数
                            valueList.add(String.format("%.1f", value));
                        }
                    } catch (Exception e) {
                        //转换失败,将上一次的结果赋值本次或跳过
                        e.printStackTrace();
                        if (dateList.size() == 0) {
                            continue;
                        }
                        if (i == 0 && dateList.size() == valueList.size()) {
                            //根据上一个日期毫秒,和步长,设置当前时间轴,确保时间轴的连续性
                            dateList.add(DateUtils.yyyyMMddHHmmssWithWhiffletree(new Date(upMillisecond + (Long.parseLong(step) * 1000))));
                        }
                        valueList.add("0");//转换失败,值设置为0。
                        continue;
                    }
                }
                jsonObject.put("value", valueList);
                returnArray.add(jsonObject);
            }
            returnJson.put("title", title);
            returnJson.put("date", dateList);
            returnJson.put("values", returnArray);
        } catch (Exception e) {
            e.printStackTrace();
            log.error(title + ": 解析结果集错误:{}", e.getMessage());
            return null;
        }
        return returnJson;
    }


    /**
     * 校验时间轴及数据
     * 问题1:如果查询范围为3天,机器第1,3天开启,在第2天关机,则Prometheus不会返回第二天的数据,时间轴只有第一天和第三天的数据,直接跳过第二天
     * 解决1:根据上一次时间加步长赋值时间轴,确保时间轴连续性,值为"";
     * 问题2: 如果查询近七天的数据,容器是在昨天购买,时间轴只有昨天到现在的,前五天的没有
     * 解决2: 根据查询开始时间和数据返回的第一条作对比,补齐时间轴
     * @param upMillisecond      上一次时间毫秒值
     * @param currentMillisecond 本次时间毫秒值
     * @param step               步长/采集周期
     * @param isDate             是否对日期轴进行添加  true:是  false:否
     * @param flag               true:补齐购买容器之前的日期  false:补齐关机期间的日期
     * @param dateList           日期集合
     * @param valueList          数据集合
     * @return true: 有时间差距,需补充时间轴   false:无时间差距
     */
    private boolean verifyCompleteTimeline(Long upMillisecond, Long currentMillisecond, String step, boolean isDate, boolean flag, List<String> dateList, List<String> valueList) {
        if (upMillisecond == 0 && !flag) {
            return false;
        }
        long count = (currentMillisecond - upMillisecond) / (Long.parseLong(step) * 1000);
        if (count == 1) {
            return false;
        }
//        log.error("资源监控-->日期时间缺失,开始补充:{}", dateList.get((dateList.size() - 1)));
        for (int i = 0; i < count; i++) {
            if (isDate) {
                //补充时间差距,确保日期轴的完整性
                long dateMillisecond = upMillisecond + Long.parseLong(step) * 1000 * (i + 1);
                dateList.add(DateUtils.yyyyMMddHHmmssWithWhiffletree(new Date(dateMillisecond)));
            }
            valueList.add("");
        }
//        log.error("资源监控-->日期时间缺失,结束补充:{}", dateList.get((dateList.size() - 1)));
        return true;
    }

5. 工具类

DateUtils

import org.springframework.util.StringUtils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateUtils {

    public static String yyyyMMddHHmmssWithWhiffletree(Date date) {
        if(date == null) {
            return null;
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return sdf.format(date);
    }

    public static Date yyyyMMddHHmmssWithWhiffletreeToDate(String dateStr) throws ParseException {
        if(!StringUtils.hasText(dateStr)) {
            return null;
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return sdf.parse(dateStr);
    }

}

到此这篇关于Java获取Prometheus监控数据的方法实现的文章就介绍到这了,更多相关Java获取Prometheus监控数据内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

相关文章

  • SpringBoot中的yml文件中读取自定义配置信息及遇到问题小结

    SpringBoot中的yml文件中读取自定义配置信息及遇到问题小结

    这篇文章主要介绍了SpringBoot中的yml文件中读取自定义配置信息,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-06-06
  • JVM中判定对象需要回收的方法

    JVM中判定对象需要回收的方法

    这篇文章主要介绍了jvm中如何判定对象需要回收,jvm在确定是否回收的对象的时候采用的是root搜索算法来实现,需要的朋友可以参考下
    2022-04-04
  • java实现自定义时钟并实现走时功能

    java实现自定义时钟并实现走时功能

    这篇文章主要为大家详细介绍了java实现自定义时钟并实现走时功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06
  • mybatis中使用not in与 in的写法说明

    mybatis中使用not in与 in的写法说明

    这篇文章主要介绍了mybatis中使用not in与 in的写法说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-01-01
  • Java实现读取Jar文件属性的方法详解

    Java实现读取Jar文件属性的方法详解

    这篇文章主要为大家详细介绍了如何利用Java语言实现读取Jar文件属性的功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2022-08-08
  • JAVA之读取properties时路径的注意问题

    JAVA之读取properties时路径的注意问题

    这篇文章主要介绍了JAVA之读取properties时路径的注意问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-08-08
  • Java8使用Stream流实现List列表查询、统计、排序以及分组

    Java8使用Stream流实现List列表查询、统计、排序以及分组

    List的Stream流操作可以简化我们的代码,减少程序运行的压力,应对上面的问题,下面这篇文章主要给大家介绍了关于Java8使用Stream流实现List列表查询、统计、排序以及分组的相关资料,需要的朋友可以参考下
    2023-06-06
  • Mybatis查询返回两个或多个参数问题

    Mybatis查询返回两个或多个参数问题

    这篇文章主要介绍了Mybatis查询返回两个或多个参数问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-06-06
  • JavaWeb中请求转发和请求重定向的区别以及使用

    JavaWeb中请求转发和请求重定向的区别以及使用

    今天带大家学习JavaWeb的相关知识,文章围绕着JavaWeb中请求转发和请求重定向的区别以及使用展开,文中有非常详细的介绍,需要的朋友可以参考下
    2021-06-06
  • springboot注解之@Conditional使用解析

    springboot注解之@Conditional使用解析

    这篇文章主要介绍了springboot注解之@Conditional使用解析,conditional 这个英文单词翻译过来是有条件的,所以 @Conditional 注解是作为条件存在的,如果满足配置的条件则执行,如果没有满足的话就不执行,需要的朋友可以参考下
    2023-11-11

最新评论