Skip to content

Commit 479b454

Browse files
[improve] http protocol prometheus parsing optimization (apache#1996)
Co-authored-by: zhangshenghang <[email protected]>
1 parent cc4e328 commit 479b454

File tree

5 files changed

+110
-111
lines changed

5 files changed

+110
-111
lines changed

collector/src/main/java/org/apache/hertzbeat/collector/collect/http/HttpCollectImpl.java

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ public class HttpCollectImpl extends AbstractCollect {
9797
public static final String OpenAIUsageAPI = "/dashboard/billing/usage";
9898
public static final String startDate = "start_date";
9999
public static final String endDate = "end_date";
100-
100+
private static final Map<Long, ExporterParser> EXPORTER_PARSER_TABLE = new ConcurrentHashMap<>();
101101
private final Set<Integer> defaultSuccessStatusCodes = Stream.of(HttpStatus.SC_OK, HttpStatus.SC_CREATED,
102102
HttpStatus.SC_ACCEPTED, HttpStatus.SC_MULTIPLE_CHOICES, HttpStatus.SC_MOVED_PERMANENTLY,
103103
HttpStatus.SC_MOVED_TEMPORARILY).collect(Collectors.toSet());
@@ -367,8 +367,6 @@ private void parseResponseByPromQl(String resp, List<String> aliasFields, HttpPr
367367
prometheusParser.handle(resp, aliasFields, http, builder);
368368
}
369369

370-
private static final Map<Long, ExporterParser> EXPORTER_PARSER_TABLE = new ConcurrentHashMap<>();
371-
372370
private void parseResponseByPrometheusExporter(String resp, List<String> aliasFields,
373371
CollectRep.MetricsData.Builder builder) {
374372
if (!EXPORTER_PARSER_TABLE.containsKey(builder.getId())) {
@@ -381,8 +379,8 @@ private void parseResponseByPrometheusExporter(String resp, List<String> aliasFi
381379
MetricFamily metricFamily = metricFamilyMap.get(metrics);
382380
for (MetricFamily.Metric metric : metricFamily.getMetricList()) {
383381
Map<String, String> labelMap = metric.getLabelPair()
384-
.stream()
385-
.collect(Collectors.toMap(MetricFamily.Label::getName, MetricFamily.Label::getValue));
382+
.stream()
383+
.collect(Collectors.toMap(MetricFamily.Label::getName, MetricFamily.Label::getValue));
386384
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
387385
for (String aliasField : aliasFields) {
388386
if ("value".equals(aliasField)) {
@@ -394,9 +392,14 @@ private void parseResponseByPrometheusExporter(String resp, List<String> aliasFi
394392
valueRowBuilder.addColumns(String.valueOf(metric.getUntyped().getValue()));
395393
} else if (metric.getInfo() != null) {
396394
valueRowBuilder.addColumns(String.valueOf(metric.getInfo().getValue()));
395+
} else if (metric.getSummary() != null) {
396+
valueRowBuilder.addColumns(String.valueOf(metric.getSummary().getValue()));
397+
} else if (metric.getHistogram() != null) {
398+
valueRowBuilder.addColumns(String.valueOf(metric.getHistogram().getValue()));
397399
}
398400
} else {
399-
valueRowBuilder.addColumns(labelMap.get(aliasField));
401+
String columnValue = labelMap.get(aliasField);
402+
valueRowBuilder.addColumns(columnValue == null ? CommonConstants.NULL_VALUE : columnValue);
400403
}
401404
}
402405
builder.addValues(valueRowBuilder.build());
@@ -443,6 +446,7 @@ private void getValueFromJson(List<String> aliasFields, CollectRep.MetricsData.B
443446

444447
/**
445448
* create httpContext
449+
*
446450
* @param httpProtocol http protocol
447451
* @return context
448452
*/
@@ -451,7 +455,7 @@ public HttpContext createHttpContext(HttpProtocol httpProtocol) {
451455
if (auth != null && DispatchConstants.DIGEST_AUTH.equals(auth.getType())) {
452456
HttpClientContext clientContext = new HttpClientContext();
453457
if (StringUtils.hasText(auth.getDigestAuthUsername())
454-
&& StringUtils.hasText(auth.getDigestAuthPassword())) {
458+
&& StringUtils.hasText(auth.getDigestAuthPassword())) {
455459
CredentialsProvider provider = new BasicCredentialsProvider();
456460
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(auth.getDigestAuthUsername(),
457461
auth.getDigestAuthPassword());
@@ -468,6 +472,7 @@ public HttpContext createHttpContext(HttpProtocol httpProtocol) {
468472

469473
/**
470474
* create http request
475+
*
471476
* @param httpProtocol http params
472477
* @return http uri request
473478
*/
@@ -522,7 +527,7 @@ public HttpUriRequest createHttpRequest(HttpProtocol httpProtocol) {
522527
}
523528
// add accept
524529
if (DispatchConstants.PARSE_DEFAULT.equals(httpProtocol.getParseType())
525-
|| DispatchConstants.PARSE_JSON_PATH.equals(httpProtocol.getParseType())) {
530+
|| DispatchConstants.PARSE_JSON_PATH.equals(httpProtocol.getParseType())) {
526531
requestBuilder.addHeader(HttpHeaders.ACCEPT, "application/json");
527532
} else if (DispatchConstants.PARSE_XML_PATH.equals(httpProtocol.getParseType())) {
528533
requestBuilder.addHeader(HttpHeaders.ACCEPT, "text/xml,application/xml");
@@ -537,7 +542,7 @@ public HttpUriRequest createHttpRequest(HttpProtocol httpProtocol) {
537542
requestBuilder.addHeader(HttpHeaders.AUTHORIZATION, value);
538543
} else if (DispatchConstants.BASIC_AUTH.equals(authorization.getType())) {
539544
if (StringUtils.hasText(authorization.getBasicAuthUsername())
540-
&& StringUtils.hasText(authorization.getBasicAuthPassword())) {
545+
&& StringUtils.hasText(authorization.getBasicAuthPassword())) {
541546
String authStr = authorization.getBasicAuthUsername() + ":" + authorization.getBasicAuthPassword();
542547
String encodedAuth = new String(Base64.encodeBase64(authStr.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
543548
requestBuilder.addHeader(HttpHeaders.AUTHORIZATION, DispatchConstants.BASIC + " " + encodedAuth);
@@ -558,8 +563,8 @@ public HttpUriRequest createHttpRequest(HttpProtocol httpProtocol) {
558563
} else {
559564
String ipAddressType = IpDomainUtil.checkIpAddressType(httpProtocol.getHost());
560565
String baseUri = CollectorConstants.IPV6.equals(ipAddressType)
561-
? String.format("[%s]:%s%s", httpProtocol.getHost(), httpProtocol.getPort(), uri)
562-
: String.format("%s:%s%s", httpProtocol.getHost(), httpProtocol.getPort(), uri);
566+
? String.format("[%s]:%s%s", httpProtocol.getHost(), httpProtocol.getPort(), uri)
567+
: String.format("%s:%s%s", httpProtocol.getHost(), httpProtocol.getPort(), uri);
563568
boolean ssl = Boolean.parseBoolean(httpProtocol.getSsl());
564569
if (ssl) {
565570
requestBuilder.setUri(CollectorConstants.HTTPS_HEADER + baseUri);
@@ -572,10 +577,10 @@ public HttpUriRequest createHttpRequest(HttpProtocol httpProtocol) {
572577
int timeout = CollectUtil.getTimeout(httpProtocol.getTimeout(), 0);
573578
if (timeout > 0) {
574579
RequestConfig requestConfig = RequestConfig.custom()
575-
.setConnectTimeout(timeout)
576-
.setSocketTimeout(timeout)
577-
.setRedirectsEnabled(true)
578-
.build();
580+
.setConnectTimeout(timeout)
581+
.setSocketTimeout(timeout)
582+
.setRedirectsEnabled(true)
583+
.build();
579584
requestBuilder.setConfig(requestConfig);
580585
}
581586
return requestBuilder.build();

collector/src/main/java/org/apache/hertzbeat/collector/collect/http/promethus/exporter/ExporterParser.java

Lines changed: 30 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
import lombok.extern.slf4j.Slf4j;
2828
import org.apache.hertzbeat.collector.collect.http.promethus.ParseException;
2929
import org.apache.hertzbeat.common.util.StrBuffer;
30-
import org.springframework.util.StringUtils;
3130

3231
/**
3332
* Resolves the data passed by prometheus's exporter interface http:xxx/metrics
@@ -38,7 +37,7 @@ public class ExporterParser {
3837
private static final String HELP = "HELP";
3938
private static final String TYPE = "TYPE";
4039
private static final String EOF = "EOF";
41-
40+
private static final String METRIC_NAME_LABEL = ".name";
4241
private static final String QUANTILE_LABEL = "quantile";
4342
private static final String BUCKET_LABEL = "le";
4443
private static final String NAME_LABEL = "__name__";
@@ -52,13 +51,11 @@ public class ExporterParser {
5251
private static final char ENTER = '\n';
5352
private static final char SPACE = ' ';
5453
private static final char COMMA = ',';
55-
54+
private final Lock lock = new ReentrantLock();
5655
private MetricFamily currentMetricFamily;
5756
private String currentQuantile;
5857
private String currentBucket;
5958

60-
private final Lock lock = new ReentrantLock();
61-
6259
public Map<String, MetricFamily> textToMetric(String resp) {
6360
// key: metric name, value: metric family
6461
Map<String, MetricFamily> metricMap = new ConcurrentHashMap<>(10);
@@ -86,7 +83,8 @@ private void parseLine(Map<String, MetricFamily> metricMap, StrBuffer buffer) {
8683
this.currentMetricFamily = null;
8784
this.parseComment(metricMap, buffer);
8885
}
89-
case ENTER -> {}
86+
case ENTER -> {
87+
}
9088
default -> {
9189
this.currentBucket = null;
9290
this.currentQuantile = null;
@@ -114,7 +112,8 @@ private void parseComment(Map<String, MetricFamily> metricMap, StrBuffer buffer)
114112
switch (token) {
115113
case HELP -> this.parseHelp(buffer);
116114
case TYPE -> this.parseType(buffer);
117-
default -> {}
115+
default -> {
116+
}
118117
}
119118
}
120119

@@ -135,28 +134,27 @@ private void parseType(StrBuffer line) {
135134

136135
private void parseMetric(StrBuffer buffer) {
137136
String metricName = this.readTokenAsMetricName(buffer);
137+
MetricFamily.Label label = new MetricFamily.Label();
138+
label.setName(METRIC_NAME_LABEL);
139+
label.setValue(metricName);
140+
138141
if (metricName.isEmpty()) {
139142
log.error("error parse metric, metric name is null, line: {}", buffer.toStr());
140143
return;
141144
}
145+
142146
List<MetricFamily.Metric> metricList = this.currentMetricFamily.getMetricList();
143147
if (metricList == null) {
144148
metricList = new ArrayList<>();
145149
this.currentMetricFamily.setMetricList(metricList);
146150
}
147-
// TODO: This part may have issues. The current logic creates only one metric for both HISTOGRAM and SUMMARY
148-
// compared to the source code, there is a slight modification: the source code stores parsing results in a property
149-
// here, the results are passed through parameters.
150-
MetricFamily.Metric metric;
151-
if (!metricList.isEmpty()
152-
&& (this.currentMetricFamily.getMetricType().equals(MetricType.HISTOGRAM)
153-
|| this.currentMetricFamily.getMetricType().equals(MetricType.SUMMARY))) {
154-
metric = metricList.get(0);
155-
} else {
156-
metric = new MetricFamily.Metric();
157-
metricList.add(metric);
158-
}
159151

152+
// TODO For the time being, the data is displayed in the form of labels. If there is a better chart display method in the future, we will optimize it.
153+
MetricFamily.Metric metric = new MetricFamily.Metric();
154+
metricList.add(metric);
155+
156+
metric.setLabelPair(new ArrayList<>());
157+
metric.getLabelPair().add(label);
160158
this.readLabels(metric, buffer);
161159
}
162160

@@ -165,7 +163,6 @@ private void readLabels(MetricFamily.Metric metric, StrBuffer buffer) {
165163
if (buffer.isEmpty()) {
166164
return;
167165
}
168-
metric.setLabelPair(new ArrayList<>());
169166
if (buffer.charAt(0) == LEFT_CURLY_BRACKET) {
170167
buffer.read();
171168
this.startReadLabelName(metric, buffer);
@@ -218,9 +215,9 @@ private void startReadLabelValue(MetricFamily.Metric metric, MetricFamily.Label
218215
this.currentQuantile = labelValue;
219216
} else if (this.currentMetricFamily.getMetricType().equals(MetricType.HISTOGRAM) && label.getName().equals(BUCKET_LABEL)) {
220217
this.currentBucket = labelValue;
221-
} else {
222-
metric.getLabelPair().add(label);
223218
}
219+
metric.getLabelPair().add(label);
220+
224221
if (buffer.isEmpty()) {
225222
return;
226223
}
@@ -257,47 +254,16 @@ private void readLabelValue(MetricFamily.Metric metric, MetricFamily.Label label
257254
metric.setUntyped(untyped);
258255
}
259256
case SUMMARY -> {
260-
MetricFamily.Summary summary = metric.getSummary();
261-
if (summary == null) {
262-
summary = new MetricFamily.Summary();
263-
metric.setSummary(summary);
264-
}
265-
// Process data for xxx_sum
266-
if (label != null && this.isSum(label.getName())) {
267-
summary.setSum(buffer.toDouble());
268-
}
269-
// Process data for xxx_count
270-
else if (label != null && this.isCount(label.getName())) {
271-
summary.setCount(buffer.toLong());
272-
}
273-
// Handle format for "xxx{quantile=\"0\"} 0"
274-
else if (StringUtils.hasText(this.currentQuantile)) {
275-
List<MetricFamily.Quantile> quantileList = summary.getQuantileList();
276-
MetricFamily.Quantile quantile = new MetricFamily.Quantile();
277-
quantile.setXLabel(StrBuffer.parseDouble(this.currentQuantile));
278-
quantile.setValue(buffer.toDouble());
279-
quantileList.add(quantile);
280-
}
257+
// For the time being, the data is displayed in the form of labels. If there is a better chart display method in the future, we will optimize it.
258+
MetricFamily.Summary summary = new MetricFamily.Summary();
259+
summary.setValue(buffer.toDouble());
260+
metric.setSummary(summary);
281261
}
282262
case HISTOGRAM -> {
283-
MetricFamily.Histogram histogram = metric.getHistogram();
284-
if (histogram == null) {
285-
histogram = new MetricFamily.Histogram();
286-
metric.setHistogram(histogram);
287-
}
288-
if (label != null && this.isSum(label.getName())) {
289-
histogram.setSum(buffer.toDouble());
290-
} else if (label != null && this.isCount(label.getName())) {
291-
histogram.setCount(buffer.toLong());
292-
}
293-
// Process the format "xxx{quantile=\"0\"} 0"
294-
else if (StringUtils.hasText(this.currentBucket)) {
295-
List<MetricFamily.Bucket> bucketList = histogram.getBucketList();
296-
MetricFamily.Bucket bucket = new MetricFamily.Bucket();
297-
bucket.setUpperBound(StrBuffer.parseDouble(this.currentBucket));
298-
bucket.setCumulativeCount(buffer.toLong());
299-
bucketList.add(bucket);
300-
}
263+
// For the time being, the data is displayed in the form of labels. If there is a better chart display method in the future, we will optimize it.
264+
MetricFamily.Histogram histogram = new MetricFamily.Histogram();
265+
histogram.setValue(buffer.toDouble());
266+
metric.setHistogram(histogram);
301267
}
302268
default -> throw new ParseException("no such type in metricFamily");
303269
}
@@ -390,7 +356,9 @@ private String readTokenAsLabelValue(StrBuffer buffer) {
390356
escaped = false;
391357
} else {
392358
switch (c) {
393-
case QUOTES -> { return builder.toString(); }
359+
case QUOTES -> {
360+
return builder.toString();
361+
}
394362
case ENTER -> throw new ParseException("parse label value error, next line");
395363
case '\\' -> escaped = true;
396364
default -> builder.append(c);

collector/src/main/java/org/apache/hertzbeat/collector/collect/http/promethus/exporter/MetricFamily.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,11 @@ public static class Untyped {
169169
@Data
170170
public static class Summary {
171171

172+
/**
173+
* value
174+
*/
175+
private double value;
176+
172177
/**
173178
* count
174179
*/
@@ -207,6 +212,11 @@ public static class Quantile {
207212
@Data
208213
public static class Histogram {
209214

215+
/**
216+
* value
217+
*/
218+
private double value;
219+
210220
/**
211221
* count
212222
*/

collector/src/main/java/org/apache/hertzbeat/collector/dispatch/MetricsCollect.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ private void calculateFields(Metrics metrics, CollectRep.MetricsData.Builder col
264264
break;
265265
}
266266
}
267-
// valueList为空时也执行,涵盖纯字符串赋值表达式
267+
// Also executed when valueList is empty, covering pure string assignment expressions
268268
Object objValue = JexlExpressionRunner.evaluate(expression, fieldValueMap);
269269
if (objValue != null) {
270270
value = String.valueOf(objValue);

0 commit comments

Comments
 (0)