0、寫在前面
1> 使用RestTemplate作爲遠程調用工具調用prometheus原生api獲取數據
2> prometheus原生api文檔地址如下:https://prometheus.io/docs/prometheus/latest/querying/api/
3> 通過訪問prometheus原生api,查看原生api返回的數據格式,定義對應的實體類格式
4> 下面所列功能代碼,僅爲部分調用api結果,僅供參考,如若需要調用其他api,可自行編寫對應方法
1、遠程調用類
1.1、pom依賴
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.2.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.16</version>
</dependency>
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Joiner;
import com.google.common.base.Throwables;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.http.Header;
import org.apache.http.client.config.RequestConfig;
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.client.support.BasicAuthenticationInterceptor;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.stereotype.Component;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import java.net.URI;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
/**
* RestTemplate工具類
*
* @author 星空流年
* @date 2023/7/10
*/
@Slf4j
@Component
@SuppressWarnings("all")
public class RestTemplateUtils {
/**
* http 請求 GET
*
* @param url 地址
* @param params 參數
* @return Http連接
*/
public String getHttp(String url, JSONObject params) {
return getRestConnection(url, params, "http");
}
/**
* https 請求 GET
*
* @param url 地址
* @param params 參數
* @return Https連接
*/
public String getHttps(String url, JSONObject params) {
return getRestConnection(url, params, "https");
}
/**
* 獲取遠程連接
*
* @param url 請求地址
* @param params JSON對象
* @param connectionFlag 請求標誌
* @return 遠程連接
*/
private String getRestConnection(String url, JSONObject params, String connectionFlag) {
String restConnection = null;
if (StringUtils.equals("http", connectionFlag)) {
restConnection = getRestHttpConnection(url, params, 10000, 60000, 3);
}
if (StringUtils.equals("https", connectionFlag)) {
restConnection = getRestHttpsConnection(url, params, 10000, 60000, 3);
}
return restConnection;
}
/**
* http 請求 GET
*
* @param url 地址
* @param params 參數
* @param connectTimeout 連接時間
* @param readTimeout 讀取時間
* @param retryCount 重試機制
* @return 請求字符串
*/
public String getRestHttpConnection(String url, JSONObject params, int connectTimeout, int readTimeout, int retryCount) {
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setConnectTimeout(connectTimeout);
requestFactory.setReadTimeout(readTimeout);
RestTemplate restTemplate = new RestTemplate(requestFactory);
// 設置編碼集
restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
// 異常處理
restTemplate.setErrorHandler(new DefaultResponseErrorHandler());
// 獲取URI
URI uri = getUriByUrl(url, params);
// 重試機制
for (int i = 1; i <= retryCount; i++) {
try {
// 此處設置值爲認證的用戶名和密碼信息, 請注意修改
restTemplate.getInterceptors().add(new BasicAuthenticationInterceptor("username", "password");
return restTemplate.getForEntity(uri, String.class).getBody();
} catch (Exception e) {
log.error("[GET/HTTP請求信息]異常, 重試次數:{}, 請求地址:{}, 請求參數:{}, 異常信息:{}", i, url, params, Throwables.getStackTraceAsString(e));
}
}
return null;
}
/**
* https 請求 GET
*
* @param url 地址
* @param params 參數
* @param connectTimeout 連接時間
* @param readTimeout 讀取時間
* @param retryCount 重試機制
* @return 請求字符串
*/
public String getRestHttpsConnection(String url, JSONObject params, int connectTimeout, int readTimeout, int retryCount) {
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setConnectTimeout(connectTimeout);
requestFactory.setReadTimeout(readTimeout);
RestTemplate restTemplate = restTemplate();
clientHttpRequestFactory();
// 設置編碼集
restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
// 異常處理
restTemplate.setErrorHandler(new DefaultResponseErrorHandler());
// 繞過https
restTemplate.setRequestFactory(clientHttpRequestFactory());
// 獲取URI
URI uri = getUriByUrl(url, params);
for (int i = 1; i <= retryCount; i++) {
try {
// 此處設置值爲認證的用戶名和密碼信息, 請注意修改
restTemplate.getInterceptors().add(new BasicAuthenticationInterceptor("username", "password");
return restTemplate.getForEntity(uri, String.class).getBody();
} catch (Exception e) {
log.error("[GET/HTTPS請求信息]異常, 重試次數:{}, 請求地址:{}, 請求參數:{}, 異常信息:{}", i, url, params, Throwables.getStackTraceAsString(e));
}
}
return null;
}
/**
* 獲取RestTemplate實例對象,可自由調用其方法
*
* @return RestTemplate實例對象
*/
public 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(RestConnectionConstants.HTTP_CONNECTION_FLAG, PlainConnectionSocketFactory.getSocketFactory())
.register(RestConnectionConstants.HTTPS_CONNECTION_FLAG, sslConnectionSocketFactory).build();
//使用Httpclient連接池的方式配置
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);
// 設置請求連接超時時間
RequestConfig requestConfig = RequestConfig.custom()
.setConnectionRequestTimeout(10000)
.setConnectTimeout(10000)
.setSocketTimeout(60000).build();
httpClientBuilder.setDefaultRequestConfig(requestConfig);
return (HttpClient) httpClientBuilder.build();
} catch (Exception e) {
throw new RestException(RestStatus.SYSTEM_ERROR, Throwables.getStackTraceAsString(e));
}
}
/**
* 創建RestTemplate
*
* @return RestTemplate
*/
public RestTemplate restTemplate() {
return new RestTemplate(clientHttpRequestFactory());
}
/**
* 創建ClientHttpRequestFactory
*
* @return ClientHttpRequestFactory
*/
private ClientHttpRequestFactory clientHttpRequestFactory() {
return new HttpComponentsClientHttpRequestFactory(httpClient());
}
/**
* 通過URL獲取URI
*
* @param url url
* @param params 請求參數
* @return {@code URI}
*/
private URI getUriByUrl(String url, JSONObject params) {
String query = "query";
if (!params.isEmpty()) {
// 網關針對URL中特殊字符進行加密訪問, 這裏針對網關未處理特殊字符參數進行轉義處理
if (params.containsKey(query)) {
String replaceQuery = params.getString(query)
.replace("=", "%3D").replace(" ", "%20")
.replace("{", "%7B").replace("}", "%7D")
.replace("\"", "%22").replace("/", "%2F")
.replace("|", "%7C").replace("+", "%2B")
.replace("[", "%5B").replace("]", "%5D")
.replace("<", "%3C").replace(">", "%3E")
.replace("\n", "%20");
params.put(query, replaceQuery);
} else {
params.keySet().forEach(key -> {
String decode = URLDecoder.decode(params.getString(key), StandardCharsets.UTF_8);
params.put(key, decode);
});
}
url = expandUrl(url, params);
}
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
if (params.containsKey(query)) {
return builder.build(true).toUri();
} else {
return builder.build().encode().toUri();
}
}
/**
* URL拼接
*
* @param url 請求URL
* @param jsonObject JSON對象
* @return 拼接之後的URL
*/
private String expandUrl(String url, JSONObject jsonObject) {
HashMap<String, Object> paramMap = new HashMap<>(16);
StringBuilder stringBuilder = new StringBuilder(url);
stringBuilder.append("?");
Set<String> keys = jsonObject.keySet();
keys.forEach(key -> paramMap.put(key, jsonObject.getString(key)));
String joinStr = Joiner.on("&").withKeyValueSeparator("=").join(paramMap);
return stringBuilder.append(joinStr).toString();
}
}
2、即時查詢獲取數據
Prometheus查詢結果實體類:PromQueryResult
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* Prometheus查詢結果對象信息
*
* @author 星空流年
* @date 2023/7/10
*/
@Data
public class PromQueryResult {
/**
* prometheus指標屬性
*/
private Map<String, Object> metric;
/**
* prometheus即時查詢指標值
*/
private List<String> value;
}
Prometheus查詢數據結果實體類:PromQueryData
import lombok.Data;
import java.util.List;
/**
* Prometheus查詢數據結果對象
*
* @author 星空流年
* @date 2023/7/10
*/
@Data
public class PromQueryData {
/**
* prometheus結果類型
* vector--瞬時向量
* matrix--區間向量
* scalar--標量
* string--字符串
*/
private String resultType;
/**
* prometheus指標屬性和值
*/
private List<PromQueryResult> result;
}
Prometheus查詢響應實體類:PromQueryResponse
import lombok.Data;
/**
* Prometheus查詢響應對象
*
* @author 星空流年
* @date 2023/7/10
*/
@Data
public class PromQueryResponse {
/**
* 狀態
* 成功-- success
*/
private String status;
/**
* prometheus指標屬性和值
*/
private PromQueryData data;
}
2.2、即時查詢接口實現
即時查詢接口類:PromQueryService
import xxx.entity.pojo.PromQueryData;
/**
* 指標查詢接口類
*
* @author 星空流年
* @date 2023/7/10
*/
public interface PromQueryService {
/**
* Prometheus即時查詢
*
* @param query 查詢
* @param time 時間戳, 單位: 秒
* @return {@code PromQueryData}
*/
PromQueryData getQueryDataInfo(String query, String time);
}
即時查詢接口實現類:PromQueryServiceImpl
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Throwables;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import xxx.entity.pojo.PromQueryData;
import xxx.entity.pojo.PromQueryResponse;
import xxx.service.PromQueryService;
import xxx.util.RestTemplateUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.util.Objects;
/**
* 指標查詢接口實現類
*
* @author 星空流年
* @date 2023/7/10
*/
@Slf4j
@Service("promQueryService")
public class PromQueryServiceImpl implements PromQueryService {
@Resource
private RestTemplateUtils restTemplateUtils;
@Override
public PromQueryData getQueryDataInfo(String query, String time) {
if (StringUtils.isBlank(time)) {
time = String.valueOf(DateUtil.currentSeconds());
}
JSONObject param = new JSONObject();
param.put("query", query);
param.put("time", time);
// prometheus的URL連接地址, 根據需要修改
String url = "http://localhost:9090" + "/api/v1/query";
return (PromQueryData) getDataInfo(url, param);
}
/**
* 獲取查詢結果數據
*
* @param promUrl 調用的prometheus的URL
* @param param 請求參數
* @return 查詢結果對象
*/
private Object getDataInfo(String promUrl, JSONObject param) {
String http = getHttp(promUrl, param);
PromQueryResponse responseInfo = JSON.parseObject(http, PromQueryResponse.class);
log.info("即時查詢請求地址: {}, 請求參數: {}", promUrl, param);
if (Objects.isNull(responseInfo)) {
return null;
}
String status = responseInfo.getStatus();
if (StringUtils.isBlank(status) || !StringUtils.equals("success", status)) {
return null;
}
return responseInfo.getData();
}
/**
* 獲取http連接
*
* @param promUrl 連接URL
* @param param 請求參數
* @return http連接
*/
private String getHttp(String promUrl, JSONObject param) {
String http = null;
try {
http = restTemplateUtils.getHttp(promUrl, param);
} catch (Exception e) {
log.error("請求地址: {}, 請求參數: {}, 異常信息: {}", promUrl, param, Throwables.getStackTraceAsString(e));
}
return http;
}
}
3、範圍查詢獲取數據
3.1、範圍查詢實體類
範圍查詢實體類:PromQueryRange
import lombok.Data;
/**
* Prometheus範圍查詢實體類
*
* @author 星空流年
* @date 2023/7/10
*/
@Data
public class PromQueryRange {
/**
* 查詢指標
*/
private String query;
/**
* 區間範圍查詢開始時間
* 格式爲:時分秒時間戳
*/
private String start;
/**
* 區間範圍查詢結束時間
* 格式爲:時分秒時間戳
*/
private String end;
/**
* 時間區間步長, 即:時間間隔
*/
private Integer step;
}
Prometheus範圍區間查詢結果實體類:PromQueryRangeResult
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* Prometheus範圍區間查詢結果對象信息
*
* @author 星空流年
* @date 2023/7/10
*/
@Data
public class PromQueryRangeResult {
/**
* prometheus指標屬性
*/
private Map<String, Object> metric;
/**
* prometheus範圍查詢指標值
*/
private List<List<String>> values;
}
Prometheus範圍區間查詢數據結果對象實體類:PromQueryRangeData
import lombok.Data;
import java.util.List;
/**
* Prometheus範圍區間查詢數據結果對象
*
* @author 星空流年
* @date 2023/7/10
*/
@Data
public class PromQueryRangeData {
/**
* prometheus結果類型
* vector--瞬時向量
* matrix--區間向量
* scalar--標量
* string--字符串
*/
private String resultType;
/**
* prometheus指標屬性和值
*/
private List<PromQueryRangeResult> result;
}
Prometheus範圍區間查詢響應對象實體類:PromQueryRangeResponse
import lombok.Data;
/**
* Prometheus範圍區間查詢響應對象
*
* @author 星空流年
* @date 2023/7/10
*/
@Data
public class PromQueryRangeResponse {
/**
* 狀態
* 成功-- success
*/
private String status;
/**
* prometheus範圍查詢指標屬性和值
*/
private PromQueryRangeData data;
}
3.2、範圍查詢接口實現
範圍查詢接口類:PromQueryService
import xxx.entity.pojo.PromQueryRangeData;
import xxx.entity.pojo.PromQueryRange;
/**
* 指標查詢接口類
*
* @author 星空流年
* @date 2023/7/10
*/
public interface PromQueryService {
/**
* Prometheus範圍區間查詢
*
* @param queryRangeDto 查詢範圍類
* @return {@code PromQueryRangeData}
*/
PromQueryRangeData getQueryRangeDataInfo(PromQueryRange queryRange);
}
範圍查詢接口實現類:PromQueryServiceImpl
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Throwables;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import xxx.entity.pojo.PromQueryRange;
import xxx.entity.pojo.PromQueryRangeData;
import xxx.entity.pojo.PromQueryRangeResponse;
import xxx.service.PromQueryService;
import xxx.util.RestTemplateUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.util.Objects;
/**
* 指標查詢接口實現類
*
* @author 星空流年
* @date 2023/7/10
*/
@Slf4j
@Service("promQueryService")
public class PromQueryServiceImpl implements PromQueryService {
@Resource
private RestTemplateUtils restTemplateUtils;
@Override
public PromQueryRangeData getQueryRangeDataInfo(PromQueryRange queryRange) {
JSONObject param = new JSONObject();
handleQueryRangeParams(param, queryRange);
// prometheus的URL連接地址, 根據需要修改
String url = "http://localhost:9090" + "/api/v1/query_range";
return (PromQueryRangeData) getDataInfo(url, param);
}
/**
* 處理範圍查詢參數
*
* @param param 參數
* @param queryRange PromQueryRange對象
*/
private void handleQueryRangeParams(JSONObject param, PromQueryRange queryRange) {
String start = queryRange.getStart();
if (StringUtils.isBlank(start)) {
// 開始時間爲空, 則設置默認值爲當前時間
start = String.valueOf(DateUtil.currentSeconds());
}
String end = queryRange.getEnd();
if (StringUtils.isBlank(end)) {
// 結束時間爲空, 則設置默認值爲當前時間向後偏移1小時
end = String.valueOf(DateUtil.offsetHour(DateUtil.parse(start), 1).getTime());
}
param.put("query", queryRange.getQuery());
param.put("start", start);
param.put("end", end);
param.put("step", queryRange.getStep());
}
/**
* 獲取查詢結果數據
*
* @param promUrl 調用的prometheus的URL
* @param param 請求參數
* @return 查詢結果對象
*/
private Object getDataInfo(String promUrl, JSONObject param) {
String http = getHttp(promUrl, param);
PromQueryRangeResponse rangeResponse = JSON.parseObject(http, PromQueryRangeResponse.class);
log.info("範圍區間查詢請求地址: {}, 請求參數: {}", promUrl, param);
if (Objects.isNull(rangeResponse)) {
return null;
}
String status = rangeResponse.getStatus();
if (StringUtils.isBlank(status) || !StringUtils.equals("success", status)) {
return null;
}
return rangeResponse.getData();
}
/**
* 獲取http連接
*
* @param promUrl 連接URL
* @param param 請求參數
* @return http連接
*/
private String getHttp(String promUrl, JSONObject param) {
String http = null;
try {
http = restTemplateUtils.getHttp(promUrl, param);
} catch (Exception e) {
log.error("請求地址: {}, 請求參數: {}, 異常信息: {}", promUrl, param, Throwables.getStackTraceAsString(e));
}
return http;
}
}
4、根據標籤匹配器獲取時序數據
4.1、時序數據實體類
import lombok.Data;
import java.util.LinkedHashMap;
import java.util.List;
/**
* Prometheus時序對象
*
* @author 星空流年
* @date 2023/7/10
*/
@Data
public class PromSeries {
/**
* 狀態
* 成功-- success
*/
private String status;
/**
* 時序數據列表
*/
private List<LinkedHashMap<String, Object>> data;
}
4.2、時序數據接口實現
/**
* 指標查詢接口類
*
* @author 星空流年
* @date 2023/7/10
*/
public interface PromQueryService {
/**
* 獲取時序數據
*
* @param start 開始時間戳, 單位:秒
* @param end 結束時間戳, 單位:秒
* @param match 查詢指標
* @return {@code List<LinkedHashMap<String, Object>>}
*/
List<LinkedHashMap<String, Object>> getSeriesList(String start, String end, String match);
}
根據標籤匹配器獲取時序數據接口實現類:PromQueryServiceImpl
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Throwables;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import xxx.entity.pojo.PromQueryData;
import xxx.entity.pojo.PromQueryResponse;
import xxx.service.PromQueryService;
import xxx.util.RestTemplateUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.util.Objects;
/**
* 指標查詢接口實現類
*
* @author 星空流年
* @date 2023/7/10
*/
@Slf4j
@Service("promQueryService")
public class PromQueryServiceImpl implements PromQueryService {
@Resource
private RestTemplateUtils restTemplateUtils;
@Override
public List<LinkedHashMap<String, Object>> getSeriesList(String start, String end, String match, Integer datasource) {
JSONObject param = new JSONObject();
param.put("start", start);
param.put("end", end);
param.put("match[]", match);
// prometheus的URL連接地址, 根據需要修改
String url = "http://localhost:9090" + "/api/v1/series";
return getSeriesDataList(url, param);
}
/**
* 獲取時序數據列表
*
* @param promUrl 時序URL
* @param param 請求參數
* @return 時序數據列表
*/
private List<LinkedHashMap<String, Object>> getSeriesDataList(String promUrl, JSONObject param) {
String http = getHttp(promUrl, param);
PromSeries promSeries = JSON.parseObject(http, PromSeries.class);
if (Objects.nonNull(promSeries)) {
String status = promSeries.getStatus();
if (StringUtils.isBlank(status) || !StringUtils.equals(PromConstants.SUCCESS, status)) {
return Collections.emptyList();
}
} else {
return Collections.emptyList();
}
return promSeries.getData();
}
/**
* 獲取http連接
*
* @param promUrl 連接URL
* @param param 請求參數
* @return http連接
*/
private String getHttp(String promUrl, JSONObject param) {
String http = null;
try {
http = restTemplateUtils.getHttp(promUrl, param);
} catch (Exception e) {
log.error("請求地址: {}, 請求參數: {}, 異常信息: {}", promUrl, param, Throwables.getStackTraceAsString(e));
}
return http;
}
}