Java獲取Prometheus監(jiān)控數據的方法實現
更新時間:2023年12月22日 15:24:29 作者:要加油!
本文主要介紹了Java獲取Prometheus監(jiān)控數據的方法實現,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
前言
這里只是簡單的使用Java獲取到Prometheus監(jiān)控數據的資源監(jiān)控,并不做深入解析,實際上是我也不會,只是記錄一下怎么使用。本篇文章局限性很高?。?!
一、使用步驟
1. 封裝參數類【DockerResourceMonitorVo】
/** * 容器資源監(jiān)控 */ @Data @ApiModel("容器資源監(jiān)控") 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顯存發(fā)送監(jiān)控請求
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("獲取資源監(jiān)控失?。?); } } /** * 根據時間差獲取步長/采集周期 * * @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. 設置請求參數,發(fā)送請求
/** * 獲取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); //發(fā)起請求 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 + ""); //發(fā)起請求 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); //發(fā)起請求 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); //發(fā)起請求 JSONObject resultJson = sendResourceMonitor(url, param); //解析結果集 return analyticResult("GPU顯存使用量", resultJson, step, startTime); } /** * 發(fā)送HTTP請求Prometheus * * @param url * @param param * @return */ public JSONObject sendResourceMonitor(String url, Map<String, String> param) { try { //發(fā)起請求 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("資源監(jiān)控請求失敗! url:{}, param:{},", url, param.toString()); return null; } return resultJson; } catch (Exception e) { e.printStackTrace(); log.error("資源監(jiān)控請求失敗! 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()) { //根據上一個日期毫秒,和步長,設置當前時間軸,確保時間軸的連續(xù)性 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:根據上一次時間加步長賦值時間軸,確保時間軸連續(xù)性,值為""; * 問題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("資源監(jiān)控-->日期時間缺失,開始補充:{}", 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("資源監(jiān)控-->日期時間缺失,結束補充:{}", 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監(jiān)控數據的方法實現的文章就介紹到這了,更多相關Java獲取Prometheus監(jiān)控數據內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java基礎之toString的序列化 匿名對象 復雜度精解
序列化即為把內存中的對象轉換為字節(jié)寫入文件或通過網絡傳輸到遠端服務器,本章節(jié)將帶你了解Java toString的序列化 匿名對象 復雜度,需要的朋友可以參考下2021-09-09MyBatis中使用分頁插件PageHelper實現分頁功能
分頁是經常使用的功能,本文主要介紹了Mybatis中處理特殊SQL處理邏輯,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-06-06Spring boot + mybatis + Vue.js 
這篇文章主要介紹了Spring boot + mybatis + Vue.js + ElementUI 實現數據的增刪改查實例代碼(二),非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-05-05基于logback 實現springboot超級詳細的日志配置
java web 下有好幾種日志框架,比如:logback,log4j,log4j2(slj4f 并不是一種日志框架,它相當于定義了規(guī)范,實現了這個規(guī)范的日志框架就能夠用 slj4f 調用)。這篇文章主要介紹了基于logback springboot超級詳細的日志配置,需要的朋友可以參考下2019-06-06