Java獲取Prometheus監(jiān)控?cái)?shù)據(jù)的方法實(shí)現(xiàn)
前言
這里只是簡(jiǎn)單的使用Java獲取到Prometheus監(jiān)控?cái)?shù)據(jù)的資源監(jiān)控,并不做深入解析,實(shí)際上是我也不會(huì),只是記錄一下怎么使用。本篇文章局限性很高?。?!
一、使用步驟
1. 封裝參數(shù)類【DockerResourceMonitorVo】
/**
* 容器資源監(jiān)控
*/
@Data
@ApiModel("容器資源監(jiān)控")
public class DockerResourceMonitorVo {
/**
* 實(shí)例編碼
*/
@NotNull(message = "容器實(shí)例編碼不能為空")
@ApiModelProperty("容器實(shí)例編碼")
private String code;
/**
* 開始時(shí)間
*/
@ApiModelProperty("開始時(shí)間")
private String startDate;
/**
* 結(jié)束時(shí)間
*/
@ApiModelProperty("結(jié)束時(shí)間")
private String endDate;
/**
* 手工輸入時(shí)間
*/
@ApiModelProperty("手工輸入時(shí)間")
private Integer handWriteTime;
}
2. 處理參數(shù),對(duì)CPU、內(nèi)存、GPU、GPU顯存發(fā)送監(jiān)控請(qǐng)求
public Result<JSONObject> getAllResourceMonitor(DockerResourceMonitorVo body) {
try {
//獲取開始/結(jié)束時(shí)間的毫秒數(shù)
String startDate = "";
String endDate = "";
//時(shí)間為空,則使用默認(rèn)時(shí)間
if (StringUtils.isEmpty(body.getStartDate()) || StringUtils.isEmpty(body.getEndDate())) {
//則默認(rèn)將當(dāng)前時(shí)間往前推15分鐘作為開始時(shí)間
Integer timeCycle = 15;
if (null != body.getHandWriteTime()) { //用戶手動(dòng)輸入時(shí)間,由前端轉(zhuǎn)成分鐘傳入
timeCycle = body.getHandWriteTime();
}
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
calendar.add(Calendar.MINUTE, -timeCycle);
//設(shè)置開始時(shí)間毫秒數(shù)
startDate = String.valueOf(calendar.getTimeInMillis());
//結(jié)束時(shí)間為空,則使用當(dāng)前時(shí)間
endDate = String.valueOf(new Date().getTime());
} else {
startDate = String.valueOf(DateUtils.yyyyMMddHHmmssWithWhiffletreeToDate(body.getStartDate()).getTime());
endDate = String.valueOf(DateUtils.yyyyMMddHHmmssWithWhiffletreeToDate(body.getEndDate()).getTime());
}
//計(jì)算結(jié)束時(shí)間-開始時(shí)間的差值
long minute = (Long.parseLong(endDate) - Long.parseLong(startDate)) / 1000 / 60;
//獲取步長(zhǎng)/采集周期
String step = getStep(minute);
//設(shè)置請(qǐng)求參數(shù)開始時(shí)間/結(jié)束時(shí)間的格式
// 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);
//內(nèi)存使用量
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)控失?。?);
}
}
/**
* 根據(jù)時(shí)間差獲取步長(zhǎng)/采集周期
*
* @param minute
* @return
*/
private String getStep(Long minute) {
if (minute <= 5) {//5分鐘以內(nèi)
return "1";
} else if (minute > 5 && minute <= 60) {//大于5分鐘小于1小時(shí)
return "1";
} else if (minute > 60 && minute <= (60 * 24)) {//1440-->大于1小時(shí)小于24小時(shí)
return "30";
} else if (minute > (60 * 24) && minute <= (60 * 24 * 7)) {//10080-->大于24小時(shí)小于一周
return "300";
} else if (minute > (60 * 24 * 7) && minute <= (60 * 24 * 7 * 4)) {//40320-->大于1周小于4周
return "1800";
} else {//大于4周
return "43200";
}
}
3. 設(shè)置請(qǐng)求參數(shù),發(fā)送請(qǐng)求
/**
* 獲取cpu利用率 單位:%
*
* @param name 容器實(shí)例名
* @param startTime 開始時(shí)間字符串
* @param endTime 結(jié)束時(shí)間字符串
* @param step 步長(zhǎng)/采集周期
*/
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}";
//設(shè)置參數(shù)
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ā)起請(qǐng)求
JSONObject resultJson = sendResourceMonitor(url, param);
//解析結(jié)果集
return analyticResult("CPU利用率", resultJson, step, startTime);
}
/**
* 獲取內(nèi)存使用量 單位:GB
*
* @param name 容器實(shí)例名
* @param startTime 開始時(shí)間字符串
* @param endTime 結(jié)束時(shí)間字符串
* @param step 步長(zhǎng)/采集周期
*/
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}";
//設(shè)置參數(shù)
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ā)起請(qǐng)求
JSONObject resultJson = sendResourceMonitor(url, param);
//解析結(jié)果集
return analyticResult("內(nèi)存使用量", resultJson, step, startTime);
}
/**
* 獲取GPU利用率 單位:%
*
* @param name 容器實(shí)例名
* @param startTime 開始時(shí)間字符串
* @param endTime 結(jié)束時(shí)間字符串
* @param step 步長(zhǎng)/采集周期
*/
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}";
//設(shè)置參數(shù)
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ā)起請(qǐng)求
JSONObject resultJson = sendResourceMonitor(url, param);
//解析結(jié)果集
return analyticResult("GPU利用率", resultJson, step, startTime);
}
/**
* 獲取GPU顯存使用量 單位:GB
*
* @param name 容器實(shí)例名
* @param startTime 開始時(shí)間字符串
* @param endTime 結(jié)束時(shí)間字符串
* @param step 步長(zhǎng)/采集周期
*/
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}";
//設(shè)置參數(shù)
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ā)起請(qǐng)求
JSONObject resultJson = sendResourceMonitor(url, param);
//解析結(jié)果集
return analyticResult("GPU顯存使用量", resultJson, step, startTime);
}
/**
* 發(fā)送HTTP請(qǐng)求Prometheus
*
* @param url
* @param param
* @return
*/
public JSONObject sendResourceMonitor(String url, Map<String, String> param) {
try {
//發(fā)起請(qǐng)求
Map<String, Object> resultMap = restTemplate.getForObject(url, Map.class, param);
//將map轉(zhuǎn)為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)控請(qǐng)求失敗! url:{}, param:{},", url, param.toString());
return null;
}
return resultJson;
} catch (Exception e) {
e.printStackTrace();
log.error("資源監(jiān)控請(qǐng)求失敗! url:{}, param:{},", url, param.toString());
return null;
}
}
4. 解析Prometheus返回結(jié)果集
/**
* 解析Prometheus返回結(jié)果
*
* @param title 標(biāo)題
* @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<>();//獲取時(shí)間軸
if (null == result) {
return null;
}
try {
//獲取result結(jié)果集
JSONArray resultArray = result.getJSONObject("data").getJSONArray("result");
if (null == resultArray || resultArray.isEmpty()) {
return null;
}
//獲取請(qǐng)求的開始時(shí)間毫秒數(shù)
long startMillisecond = Long.parseLong(startTime.replace(".", ""));
for (int i = 0; i < resultArray.size(); i++) {
JSONObject resultJson = resultArray.getJSONObject(i);
//獲取數(shù)據(jù)集合
List<String> valueList = new ArrayList<>();
JSONObject jsonObject = new JSONObject();
//獲取實(shí)例名
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天,而容器卻在昨天購(gòu)買,前3天的日期軸沒有,通過對(duì)比查詢時(shí)間與數(shù)據(jù)返回時(shí)間,補(bǔ)齊時(shí)間軸
verifyCompleteTimeline(startMillisecond, currentMillisecond, step, (i == 0), true, dateList, valueList);
}
//flag:false-->如果查詢范圍為3天,機(jī)器第1,3天開啟,在第2天關(guān)機(jī),則Prometheus不會(huì)返回第二天的數(shù)據(jù),時(shí)間軸只有第一天和第三天的數(shù)據(jù),直接跳過第二天
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利用率 則保留兩位小數(shù)
if (title.contains("CPU利用率") || title.contains("GPU利用率")) {
valueList.add(String.format("%.2f", value));
} else {//保留一位小數(shù)
valueList.add(String.format("%.1f", value));
}
} catch (Exception e) {
//轉(zhuǎn)換失敗,將上一次的結(jié)果賦值本次或跳過
e.printStackTrace();
if (dateList.size() == 0) {
continue;
}
if (i == 0 && dateList.size() == valueList.size()) {
//根據(jù)上一個(gè)日期毫秒,和步長(zhǎng),設(shè)置當(dāng)前時(shí)間軸,確保時(shí)間軸的連續(xù)性
dateList.add(DateUtils.yyyyMMddHHmmssWithWhiffletree(new Date(upMillisecond + (Long.parseLong(step) * 1000))));
}
valueList.add("0");//轉(zhuǎn)換失敗,值設(shè)置為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 + ": 解析結(jié)果集錯(cuò)誤:{}", e.getMessage());
return null;
}
return returnJson;
}
/**
* 校驗(yàn)時(shí)間軸及數(shù)據(jù)
* 問題1:如果查詢范圍為3天,機(jī)器第1,3天開啟,在第2天關(guān)機(jī),則Prometheus不會(huì)返回第二天的數(shù)據(jù),時(shí)間軸只有第一天和第三天的數(shù)據(jù),直接跳過第二天
* 解決1:根據(jù)上一次時(shí)間加步長(zhǎng)賦值時(shí)間軸,確保時(shí)間軸連續(xù)性,值為"";
* 問題2: 如果查詢近七天的數(shù)據(jù),容器是在昨天購(gòu)買,時(shí)間軸只有昨天到現(xiàn)在的,前五天的沒有
* 解決2: 根據(jù)查詢開始時(shí)間和數(shù)據(jù)返回的第一條作對(duì)比,補(bǔ)齊時(shí)間軸
* @param upMillisecond 上一次時(shí)間毫秒值
* @param currentMillisecond 本次時(shí)間毫秒值
* @param step 步長(zhǎng)/采集周期
* @param isDate 是否對(duì)日期軸進(jìn)行添加 true:是 false:否
* @param flag true:補(bǔ)齊購(gòu)買容器之前的日期 false:補(bǔ)齊關(guān)機(jī)期間的日期
* @param dateList 日期集合
* @param valueList 數(shù)據(jù)集合
* @return true: 有時(shí)間差距,需補(bǔ)充時(shí)間軸 false:無時(shí)間差距
*/
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)控-->日期時(shí)間缺失,開始補(bǔ)充:{}", dateList.get((dateList.size() - 1)));
for (int i = 0; i < count; i++) {
if (isDate) {
//補(bǔ)充時(shí)間差距,確保日期軸的完整性
long dateMillisecond = upMillisecond + Long.parseLong(step) * 1000 * (i + 1);
dateList.add(DateUtils.yyyyMMddHHmmssWithWhiffletree(new Date(dateMillisecond)));
}
valueList.add("");
}
// log.error("資源監(jiān)控-->日期時(shí)間缺失,結(jié)束補(bǔ)充:{}", 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);
}
}到此這篇關(guān)于Java獲取Prometheus監(jiān)控?cái)?shù)據(jù)的方法實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Java獲取Prometheus監(jiān)控?cái)?shù)據(jù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java基礎(chǔ)之toString的序列化 匿名對(duì)象 復(fù)雜度精解
序列化即為把內(nèi)存中的對(duì)象轉(zhuǎn)換為字節(jié)寫入文件或通過網(wǎng)絡(luò)傳輸?shù)竭h(yuǎn)端服務(wù)器,本章節(jié)將帶你了解Java toString的序列化 匿名對(duì)象 復(fù)雜度,需要的朋友可以參考下2021-09-09
java文件操作練習(xí)代碼 讀取某個(gè)盤符下的文件
這篇文章主要介紹了java讀取某個(gè)盤符下的文件示例,代碼中要求的是絕對(duì)路徑,編譯過程中要注意絕對(duì)路徑問題和異常的抓取2014-01-01
MyBatis中使用分頁(yè)插件PageHelper實(shí)現(xiàn)分頁(yè)功能
分頁(yè)是經(jīng)常使用的功能,本文主要介紹了Mybatis中處理特殊SQL處理邏輯,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06
詳談Spring是否支持對(duì)靜態(tài)方法進(jìn)行Aop增強(qiáng)
這篇文章主要介紹了Spring是否支持對(duì)靜態(tài)方法進(jìn)行Aop增強(qiáng),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
Java多態(tài)實(shí)現(xiàn)原理詳細(xì)梳理總結(jié)
這篇文章主要介紹了Java多態(tài)實(shí)現(xiàn)原理詳細(xì)梳理總結(jié),多態(tài)是繼封裝、繼承之后,面向?qū)ο蟮牡谌筇匦?,本文只總結(jié)了多態(tài)的實(shí)現(xiàn)原理,需要的朋友可以參考一下2022-06-06
Spring boot + mybatis + Vue.js 
這篇文章主要介紹了Spring boot + mybatis + Vue.js + ElementUI 實(shí)現(xiàn)數(shù)據(jù)的增刪改查實(shí)例代碼(二),非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-05-05
基于logback 實(shí)現(xiàn)springboot超級(jí)詳細(xì)的日志配置
java web 下有好幾種日志框架,比如:logback,log4j,log4j2(slj4f 并不是一種日志框架,它相當(dāng)于定義了規(guī)范,實(shí)現(xiàn)了這個(gè)規(guī)范的日志框架就能夠用 slj4f 調(diào)用)。這篇文章主要介紹了基于logback springboot超級(jí)詳細(xì)的日志配置,需要的朋友可以參考下2019-06-06

