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监控数据内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
Spring Boot之搞定mongoTemplate的知识小结
这篇文章主要介绍了Spring Boot之搞定mongoTemplate的知识小结,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下2020-12-12详解Java并发编程中的优先级队列PriorityBlockingQueue
PriorityBlockingQueue是Java中实现了堆数据结构的线程安全的有界阻塞队列。本文将会深入解读PriorityBlockingQueue的源码实现,感兴趣的可以了解一下2023-05-05Java安全 ysoserial CommonsCollections2示例分析
这篇文章主要为大家介绍了Java安全 ysoserial CommonsCollections2示例分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪2022-11-11Java多线程之JUC(java.util.concurrent)的常见类(多线程编程常用类)
这篇文章主要给大家介绍了关于Java多线程之JUC(java.util.concurrent)的常见类(多线程编程常用类)的相关资料,Java中的JUC(java.util.concurrent)包提供了一些并发编程中常用的类,这些类可以帮助我们更方便地实现多线程编程,需要的朋友可以参考下2024-02-02
最新评论