You need to enable JavaScript to run this app.
文档中心
托管 Prometheus

托管 Prometheus

复制全文
下载 pdf
工作区数据写入和查询
使用 Java 语言实现工作区数据写入和查询
复制全文
下载 pdf
使用 Java 语言实现工作区数据写入和查询
本文面向首次接入 VMP 工作区的 Java 开发者,覆盖 BasicAuth 和 AK/SK 两种鉴权方式,并提供完整示例代码,为您详细介绍如何通过 Java 语言进行工作区指标数据查询或写入的方法。
前提条件
接入前,请确认以下条件均已满足:
  • 已创建托管 Prometheus 工作区,并获取查询地址 query_url写入地址 remote_write_url,详情请参见 获取工作区地址
  • 鉴权方式,请根据实际需要选择以下任一一种鉴权方式:
  • 鉴权方式
    适用场景
    说明
    BasicAuth
    适用于工作区已提供用户名/密码、需快速完成接入验证的场景,如联调测试与示例验证。
    用于使用 BasicAuth 鉴权访问工作区,为用户在工作区详情中配置的 BasicAuth 账号和密码。
    AK/SK
    适用于需统一使用火山引擎签名鉴权体系的正式集成场景,支持子账号及临时凭证(需额外传入 sessionToken)访问。
    用于使用 AK/SK 鉴权访问工作区,所使用的账号须具备 VMPRemoteWriteAccess 或 VMPQueryAccess 权限。可参考 API访问密钥管理 获取。
  • 开发环境需满足以下条件:
  • 依赖项
    用途
    是否必需
    JDK 21 及以上
    运行示例代码
    OkHttp 4.x
    HTTP 请求基础库
    Jackson 2.x
    JSON 解析
    snappy-java
    Remote Write 数据压缩
    仅写入场景必需
  • Maven 依赖配置:
  • <dependencies>
    <dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>4.12.0</version>
    </dependency>
    <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.17.0</version>
    </dependency>
    <dependency>
    <groupId>org.xerial.snappy</groupId>
    <artifactId>snappy-java</artifactId>
    <version>1.1.10.5</version>
    </dependency>
    </dependencies>
操作步骤
VMP 工作区访问接口封装
将以下代码保存为 VmpWorkspaceClient.java,该文件提供 VMP 工作区访问的最小可用封装,查询或写入示例均基于此文件进行调用,包括:
  • WorkspaceConfig:用于集中管理工作区地址、鉴权方式和超时时间等配置。
  • VmpWorkspaceClient:对 Prometheus Query API 和 Remote Write 能力进行了轻量封装。
package com.example.vmp;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.Credentials;
import okhttp3.HttpUrl;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.xerial.snappy.Snappy;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;
/**
* VMP Workspace Java SDK: supports Query API and Remote Write with BasicAuth / AK-SK authentication.
*/
public class VmpWorkspaceClient {
private static final MediaType PROTOBUF = MediaType.parse("application/x-protobuf");
private static final ObjectMapper MAPPER = new ObjectMapper();
private final WorkspaceConfig config;
private final OkHttpClient httpClient;
public VmpWorkspaceClient(WorkspaceConfig config) {
this.config = config;
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.callTimeout(Duration.ofSeconds(config.timeoutSeconds));
if ("basic".equalsIgnoreCase(config.authMode)) {
builder.addInterceptor(new BasicAuthInterceptor(config.username, config.password));
} else if ("aksk".equalsIgnoreCase(config.authMode)) {
builder.addInterceptor(new VolcSignatureInterceptor(
config.accessKey, config.secretKey, config.region, config.service, config.sessionToken));
} else {
throw new IllegalArgumentException("Unsupported auth mode: " + config.authMode);
}
this.httpClient = builder.build();
}
// ======================== Query API ========================
public JsonNode query(String promql) throws IOException {
return query(promql, null);
}
public JsonNode query(String promql, Instant ts) throws IOException {
requireQueryUrl();
HttpUrl.Builder urlBuilder = HttpUrl.parse(config.queryUrl + "/api/v1/query").newBuilder()
.addQueryParameter("query", promql);
if (ts != null) {
urlBuilder.addQueryParameter("time", toRfc3339(ts));
}
return executeGet(urlBuilder.build());
}
public JsonNode queryRange(String promql, Instant start, Instant end, String step) throws IOException {
requireQueryUrl();
HttpUrl url = HttpUrl.parse(config.queryUrl + "/api/v1/query_range").newBuilder()
.addQueryParameter("query", promql)
.addQueryParameter("start", toRfc3339(start))
.addQueryParameter("end", toRfc3339(end))
.addQueryParameter("step", step)
.build();
return executeGet(url);
}
public JsonNode series(List<String> matches, Instant start, Instant end) throws IOException {
requireQueryUrl();
if (matches == null || matches.isEmpty()) {
throw new IllegalArgumentException("series API requires at least one match selector");
}
HttpUrl.Builder builder = HttpUrl.parse(config.queryUrl + "/api/v1/series").newBuilder();
for (String match : matches) {
builder.addQueryParameter("match[]", match);
}
if (start != null) builder.addQueryParameter("start", toRfc3339(start));
if (end != null) builder.addQueryParameter("end", toRfc3339(end));
return executeGet(builder.build());
}
public JsonNode labels(Instant start, Instant end, List<String> matches) throws IOException {
requireQueryUrl();
HttpUrl.Builder builder = HttpUrl.parse(config.queryUrl + "/api/v1/labels").newBuilder();
if (matches != null) {
for (String match : matches) builder.addQueryParameter("match[]", match);
}
if (start != null) builder.addQueryParameter("start", toRfc3339(start));
if (end != null) builder.addQueryParameter("end", toRfc3339(end));
return executeGet(builder.build());
}
public JsonNode labelValues(String labelName, Instant start, Instant end, List<String> matches) throws IOException {
requireQueryUrl();
if (labelName == null || labelName.isBlank()) {
throw new IllegalArgumentException("labelValues API requires a non-empty labelName");
}
HttpUrl.Builder builder = HttpUrl.parse(config.queryUrl + "/api/v1/label/" + labelName + "/values").newBuilder();
if (matches != null) {
for (String match : matches) builder.addQueryParameter("match[]", match);
}
if (start != null) builder.addQueryParameter("start", toRfc3339(start));
if (end != null) builder.addQueryParameter("end", toRfc3339(end));
return executeGet(builder.build());
}
// ======================== Remote Write ========================
/**
* Write samples via Remote Write. Each entry is (labels, value, timestampMs).
*/
public String writeSamples(List<SampleEntry> series) throws IOException {
if (config.remoteWriteUrl == null) {
throw new IllegalStateException("remote_write_url is not configured");
}
byte[] payload = encodeWriteRequest(series);
byte[] compressed = Snappy.compress(payload);
Request request = new Request.Builder()
.url(config.remoteWriteUrl)
.post(RequestBody.create(compressed, PROTOBUF))
.header("Content-Type", "application/x-protobuf")
.header("Content-Encoding", "snappy")
.header("X-Prometheus-Remote-Write-Version", "0.1.0")
.build();
try (Response response = httpClient.newCall(request).execute()) {
String body = response.body() == null ? "" : response.body().string();
if (!response.isSuccessful()) {
throw new IOException("Remote Write failed: HTTP " + response.code() + ", response: " + body);
}
return body;
}
}
private void requireQueryUrl() {
if (config.queryUrl == null) {
throw new IllegalStateException("query_url is not configured");
}
}
// ======================== Internal Methods ========================
private JsonNode executeGet(HttpUrl url) throws IOException {
Request request = new Request.Builder().url(url).get().build();
try (Response response = httpClient.newCall(request).execute()) {
String body = response.body() == null ? "" : response.body().string();
if (!response.isSuccessful()) {
throw new IOException("GET " + url.encodedPath() + " failed: HTTP " + response.code() + ", response: " + body);
}
return MAPPER.readTree(body);
}
}
private static String toRfc3339(Instant instant) {
return RFC3339.format(instant);
}
// ======================== Inline Protobuf Encoding ========================
private static byte[] encodeWriteRequest(List<SampleEntry> series) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
for (SampleEntry entry : series) {
byte[] tsBytes = encodeTimeSeries(entry);
// field 1, wire type 2 (length-delimited)
writeTag(out, 1, 2);
writeVarint(out, tsBytes.length);
out.write(tsBytes);
}
return out.toByteArray();
}
private static byte[] encodeTimeSeries(SampleEntry entry) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
// labels (field 1) — sorted by key
List<Map.Entry<String, String>> sorted = new ArrayList<>(entry.labels.entrySet());
sorted.sort(Map.Entry.comparingByKey());
for (Map.Entry<String, String> label : sorted) {
byte[] labelBytes = encodeLabel(label.getKey(), label.getValue());
writeTag(out, 1, 2);
writeVarint(out, labelBytes.length);
out.write(labelBytes);
}
// samples (field 2)
byte[] sampleBytes = encodeSample(entry.value, entry.timestampMs);
writeTag(out, 2, 2);
writeVarint(out, sampleBytes.length);
out.write(sampleBytes);
return out.toByteArray();
}
private static byte[] encodeLabel(String name, String value) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
// name: field 1, wire type 2
byte[] nameBytes = name.getBytes(StandardCharsets.UTF_8);
writeTag(out, 1, 2);
writeVarint(out, nameBytes.length);
out.write(nameBytes);
// value: field 2, wire type 2
byte[] valueBytes = value.getBytes(StandardCharsets.UTF_8);
writeTag(out, 2, 2);
writeVarint(out, valueBytes.length);
out.write(valueBytes);
return out.toByteArray();
}
private static byte[] encodeSample(double value, long timestampMs) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
// value: field 1, wire type 1 (64-bit)
if (Double.doubleToRawLongBits(value) != 0) {
writeTag(out, 1, 1);
writeLittleEndian64(out, Double.doubleToRawLongBits(value));
}
// timestamp: field 2, wire type 0 (varint)
if (timestampMs != 0) {
writeTag(out, 2, 0);
writeVarint(out, timestampMs);
}
return out.toByteArray();
}
private static void writeTag(ByteArrayOutputStream out, int fieldNumber, int wireType) {
writeVarint(out, (fieldNumber << 3) | wireType);
}
private static void writeVarint(ByteArrayOutputStream out, long value) {
while ((value & ~0x7FL) != 0) {
out.write((int) ((value & 0x7F) | 0x80));
value >>>= 7;
}
out.write((int) value);
}
private static void writeLittleEndian64(ByteArrayOutputStream out, long value) {
for (int i = 0; i < 8; i++) {
out.write((int) (value & 0xFF));
value >>= 8;
}
}
// ======================== 数据结构 ========================
/** 写入样本条目:标签集合 + 值 + 毫秒时间戳。 */
public static class SampleEntry {
public final Map<String, String> labels;
public final double value;
public final long timestampMs;
public SampleEntry(Map<String, String> labels, double value, long timestampMs) {
this.labels = labels;
this.value = value;
this.timestampMs = timestampMs;
}
}
// ======================== Configuration ========================
/** VMP workspace access configuration. */
public static class WorkspaceConfig {
public final String queryUrl;
public final String remoteWriteUrl;
public final String authMode;
public final String region;
public final String service;
public final String accessKey;
public final String secretKey;
public final String sessionToken;
public final String username;
public final String password;
public final long timeoutSeconds;
public WorkspaceConfig(
String queryUrl,
String remoteWriteUrl,
String authMode,
String region,
String service,
String accessKey,
String secretKey,
String sessionToken,
String username,
String password,
long timeoutSeconds
) {
this.queryUrl = queryUrl == null || queryUrl.isBlank() ? null : trimTrailingSlash(queryUrl);
this.remoteWriteUrl = remoteWriteUrl == null || remoteWriteUrl.isBlank() ? null : remoteWriteUrl;
this.authMode = authMode == null ? "aksk" : authMode.toLowerCase(Locale.ROOT);
this.region = region;
this.service = service == null || service.isBlank() ? "vmp" : service;
this.accessKey = accessKey;
this.secretKey = secretKey;
this.sessionToken = sessionToken == null ? "" : sessionToken;
this.username = username;
this.password = password;
this.timeoutSeconds = timeoutSeconds <= 0 ? 30 : timeoutSeconds;
if ("basic".equals(this.authMode)) {
required(username, "username");
required(password, "password");
} else if ("aksk".equals(this.authMode)) {
required(region, "region");
required(accessKey, "access_key");
required(secretKey, "secret_key");
}
}
/** Load configuration from environment variables. */
public static WorkspaceConfig fromEnv() {
return new WorkspaceConfig(
env("VMP_QUERY_URL"),
env("VMP_REMOTE_WRITE_URL"),
envOr("VMP_AUTH_MODE", "aksk"),
firstNonBlank(env("VOLCENGINE_REGION"), env("VMP_REGION")),
envOr("VMP_SERVICE", "vmp"),
firstNonBlank(env("VOLCENGINE_ACCESS_KEY"), env("VMP_ACCESS_KEY")),
firstNonBlank(env("VOLCENGINE_SECRET_KEY"), env("VMP_SECRET_KEY")),
envOr("VOLCENGINE_SESSION_TOKEN", ""),
env("VMP_BASIC_AUTH_USERNAME"),
env("VMP_BASIC_AUTH_PASSWORD"),
Long.parseLong(envOr("VMP_TIMEOUT_SECONDS", "30"))
);
}
private static String env(String name) {
return System.getenv(name);
}
private static String envOr(String name, String defaultValue) {
String value = System.getenv(name);
return (value != null && !value.isBlank()) ? value : defaultValue;
}
private static String firstNonBlank(String... values) {
for (String v : values) {
if (v != null && !v.isBlank()) return v;
}
return null;
}
private static String required(String value, String name) {
if (value == null || value.isBlank()) {
throw new IllegalArgumentException("Missing required config: " + name);
}
return value;
}
private static String trimTrailingSlash(String value) {
while (value.endsWith("/")) value = value.substring(0, value.length() - 1);
return value;
}
}
// ======================== BasicAuth ========================
static class BasicAuthInterceptor implements Interceptor {
private final String authorization;
BasicAuthInterceptor(String username, String password) {
this.authorization = Credentials.basic(username, password, StandardCharsets.UTF_8);
}
@Override
public Response intercept(Chain chain) throws IOException {
return chain.proceed(chain.request().newBuilder()
.header("Authorization", authorization)
.build());
}
}
// ======================== AK/SK Signature Authentication ========================
static class VolcSignatureInterceptor implements Interceptor {
private final String accessKey;
private final String secretKey;
private final String region;
private final String service;
private final String sessionToken;
VolcSignatureInterceptor(String accessKey, String secretKey, String region, String service, String sessionToken) {
this.accessKey = accessKey;
this.secretKey = secretKey;
this.region = region;
this.service = service;
this.sessionToken = sessionToken == null ? "" : sessionToken;
}
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
byte[] body = readBody(request);
HttpUrl url = request.url();
String xDate = X_DATE.format(Instant.now());
String shortDate = xDate.substring(0, 8);
String payloadHash = sha256Hex(body);
// Collect headers to sign
Map<String, String> signedHeaders = collectHeaders(request, url, xDate, payloadHash, sessionToken);
String canonicalHeaders = canonicalHeaders(signedHeaders);
String signedHeaderNames = String.join(";", signedHeaders.keySet());
// Build Canonical Request
String canonicalRequest = request.method() + "\n"
+ canonicalUri(url.encodedPath()) + "\n"
+ canonicalQuery(url) + "\n"
+ canonicalHeaders + "\n"
+ signedHeaderNames + "\n"
+ payloadHash;
// Build String to Sign
String credentialScope = shortDate + "/" + region + "/" + service + "/request";
String stringToSign = "HMAC-SHA256\n" + xDate + "\n" + credentialScope + "\n"
+ sha256Hex(canonicalRequest.getBytes(StandardCharsets.UTF_8));
// Derive signing key and generate signature
byte[] signingKey = hmac(hmac(hmac(hmac(
secretKey.getBytes(StandardCharsets.UTF_8), shortDate), region), service), "request");
String signature = toHex(hmac(signingKey, stringToSign));
String authorization = "HMAC-SHA256 Credential=" + accessKey + "/" + credentialScope
+ ", SignedHeaders=" + signedHeaderNames
+ ", Signature=" + signature;
Request.Builder builder = request.newBuilder()
.header("Host", hostHeader(url))
.header("X-Date", xDate)
.header("X-Content-Sha256", payloadHash)
.header("Authorization", authorization);
if (!sessionToken.isBlank()) {
builder.header("X-Security-Token", sessionToken);
}
return chain.proceed(builder.build());
}
private static Map<String, String> collectHeaders(
Request request, HttpUrl url, String xDate, String payloadHash, String sessionToken) {
TreeMap<String, String> headers = new TreeMap<>();
headers.put("host", hostHeader(url));
headers.put("x-date", xDate);
headers.put("x-content-sha256", payloadHash);
if (sessionToken != null && !sessionToken.isBlank()) {
headers.put("x-security-token", sessionToken);
}
if (request.header("Content-Type") != null) {
headers.put("content-type", request.header("Content-Type"));
}
for (String name : request.headers().names()) {
String lower = name.toLowerCase(Locale.ROOT);
if (lower.startsWith("x-") || lower.equals("content-type") || lower.equals("host")) {
headers.put(lower, request.header(name));
}
}
return headers;
}
}
// ======================== Signing Utilities ========================
private static final DateTimeFormatter X_DATE = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'").withZone(ZoneOffset.UTC);
private static final DateTimeFormatter RFC3339 = DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(ZoneOffset.UTC);
private static String canonicalHeaders(Map<String, String> headers) {
StringBuilder sb = new StringBuilder();
headers.forEach((k, v) -> sb.append(k).append(':').append(v.trim()).append('\n'));
return sb.toString();
}
private static String canonicalUri(String path) {
if (path == null || path.isEmpty()) return "/";
return encode(path).replace("%2F", "/");
}
private static String canonicalQuery(HttpUrl url) {
List<String> entries = new ArrayList<>();
for (int i = 0; i < url.querySize(); i++) {
String name = url.queryParameterName(i);
String value = url.queryParameterValue(i);
entries.add(encode(name) + "=" + encode(value == null ? "" : value));
}
entries.sort(Comparator.naturalOrder());
return String.join("&", entries);
}
private static byte[] readBody(Request request) throws IOException {
if (request.body() == null) return new byte[0];
okio.Buffer buffer = new okio.Buffer();
request.body().writeTo(buffer);
return buffer.readByteArray();
}
private static String hostHeader(HttpUrl url) {
int port = url.port();
boolean std = (url.isHttps() && port == 443) || (!url.isHttps() && port == 80);
return std ? url.host() : url.host() + ":" + port;
}
private static String encode(String value) {
return URLEncoder.encode(value, StandardCharsets.UTF_8)
.replace("+", "%20").replace("*", "%2A").replace("%7E", "~");
}
private static String sha256Hex(byte[] data) {
try {
return toHex(MessageDigest.getInstance("SHA-256").digest(data));
} catch (Exception e) { throw new RuntimeException(e); }
}
private static byte[] hmac(byte[] key, String data) {
try {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(key, "HmacSHA256"));
return mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
} catch (Exception e) { throw new RuntimeException(e); }
}
private static String toHex(byte[] data) {
StringBuilder sb = new StringBuilder();
for (byte b : data) sb.append(String.format("%02x", b));
return sb.toString();
}
}
查询指标数据
调用的接口说明与示例
方法
接口路径
典型用途
说明
query(promql, ts)
/api/v1/query
即时查询
查询某个时间点的当前值。
queryRange(promql, start, end, step)
/api/v1/query_range
范围查询
查询一段时间内的趋势数据。
series(matches, start, end)
/api/v1/series
查询匹配的时间序列
适合确认某个指标或标签组合是否存在。
labels(start, end, matches)
/api/v1/labels
查询标签名
适合排查当前工作区有哪些可用标签。
labelValues(labelName, start, end, matches)
/api/v1/label/<name>/values
查询标签值
适合查看某个标签下已有的取值集合。
说明
调用查询接口后,返回结果中的关键字段说明如下:
  • status:表示请求是否成功。
  • data.resultType:表示结果类型。
  • data.result:包含实际的指标查询结果。
使用 BasicAuth 查询指标
package com.example.vmp;
import com.example.vmp.VmpWorkspaceClient.WorkspaceConfig;
import com.fasterxml.jackson.databind.JsonNode;
import java.time.Instant;
import java.util.List;
public class Main {
public static void main(String[] args) throws Exception {
// 初始化 Workspace 配置(Basic Auth 鉴权)
WorkspaceConfig config = new WorkspaceConfig(
"<query-url>", // 查询地址
"",
"basic", // 认证方式:basic
null, null, null, null, null,
"<your-username>", // Basic Auth 用户名
"<your-password>", // Basic Auth 密码
30 // 请求超时时间(秒)
);
VmpWorkspaceClient client = new VmpWorkspaceClient(config);
// 以下示例统一使用 demo_metric 作为查询对象。
// 实际使用时,请根据业务场景替换:
// 1. queryUrl / remoteWriteUrl
// 2. 用户名和密码
// 3. PromQL 表达式
// 4. 时间范围(start / end)
// 5. step、label 名称、series selector 等查询参数
// 1) 即时查询(query)
// 用于执行一次 PromQL 即时查询,返回当前时刻或指定时刻的查询结果。
// 此处示例查询指标 demo_metric 的当前值。
JsonNode result = client.query("demo_metric");
System.out.println("query result:");
System.out.println(result);
// 构造时间范围:结束时间为当前时间,开始时间为 5 分钟前。
Instant end = Instant.now();
Instant start = end.minusSeconds(300);
// 2) 范围查询(queryRange)
// 用于查询一段时间范围内的时序数据。
// 参数说明:
// - "demo_metric":PromQL 表达式,可根据实际场景替换
// - start:开始时间
// - end:结束时间
// - "30s":查询步长(step),表示每 30 秒返回一个数据点
JsonNode rangeResult = client.queryRange("demo_metric", start, end, "30s");
System.out.println("queryRange result:");
System.out.println(rangeResult);
// 3) 查询时序集合(series)
// 用于根据 match[] 条件查询时间范围内存在的时序集合。
// 这里通过 {__name__="demo_metric"} 查询该指标对应的所有时序。
// 如果需要按标签过滤,也可以写成:
// {__name__="demo_metric", job="example", env="test"}
JsonNode series = client.series(List.of("{__name__=\"demo_metric\"}"), start, end);
System.out.println("series result:");
System.out.println(series);
// 4) 查询 label 名称(labels)
// 用于查询指定时间范围内出现过的 label 名称。
// 第三个参数用于传入 match[] 条件;
// 此处传 null,表示不按特定时序过滤,直接查询该时间范围内的 label 名称。
// 如需过滤,可传入:
// List.of("{__name__=\"demo_metric\"}")
JsonNode labels = client.labels(start, end, null);
System.out.println("labels result:");
System.out.println(labels);
// 5) 查询某个 label 的所有取值(labelValues)
// 用于查询指定 label 在时间范围内的所有可能取值。
// 此处以 label "job" 为例。
// 第四个参数同样用于传入 match[] 条件;
// 传 null 表示不按特定时序过滤。
// 如需仅查询 demo_metric 相关的 job 取值,可传入:
// List.of("{__name__=\"demo_metric\"}")
JsonNode values = client.labelValues("job", start, end, null);
System.out.println("labelValues result:");
System.out.println(values);
}
}
使用 AK/SK 查询指标
package com.example.vmp;
import com.example.vmp.VmpWorkspaceClient.WorkspaceConfig;
import com.fasterxml.jackson.databind.JsonNode;
import java.time.Instant;
import java.util.List;
public class Main {
public static void main(String[] args) throws Exception {
// 初始化 Workspace 配置(AK/SK 鉴权)
WorkspaceConfig config = new WorkspaceConfig(
"<query-url>", // 查询地址
"",
"aksk", // 认证方式:AK/SK
"cn-beijing", // Region
"vmp", // Service
"<your-access-key>", // Access Key
"<your-secret-key>", // Secret Key
"", // Session Token;如无可传空字符串
null, null,
30 // 请求超时时间(秒)
);
VmpWorkspaceClient client = new VmpWorkspaceClient(config);
// 以下示例统一使用 demo_metric 作为查询对象。
// 实际使用时,请根据业务场景替换:
// 1. queryUrl / remoteWriteUrl
// 2. Region、Service
// 3. Access Key、Secret Key、Session Token
// 4. PromQL 表达式
// 5. 时间范围(start / end)
// 6. step、label 名称、series selector 等查询参数
// 1) 即时查询(query)
// 用于执行一次 PromQL 即时查询,返回当前时刻或指定时刻的查询结果。
// 此处示例查询指标 demo_metric 的当前值。
JsonNode result = client.query("demo_metric");
System.out.println("query result:");
System.out.println(result);
// 构造时间范围:结束时间为当前时间,开始时间为 5 分钟前。
Instant end = Instant.now();
Instant start = end.minusSeconds(300);
// 2) 范围查询(queryRange)
// 用于查询一段时间范围内的时序数据。
// 参数说明:
// - "demo_metric":PromQL 表达式,可根据实际场景替换
// - start:开始时间
// - end:结束时间
// - "30s":查询步长(step),表示每 30 秒返回一个数据点
JsonNode rangeResult = client.queryRange("demo_metric", start, end, "30s");
System.out.println("queryRange result:");
System.out.println(rangeResult);
// 3) 查询时序集合(series)
// 用于根据 match[] 条件查询时间范围内存在的时序集合。
// 这里通过 {__name__="demo_metric"} 查询该指标对应的所有时序。
// 如果需要按标签过滤,也可以写成:
// {__name__="demo_metric", job="example", env="test"}
JsonNode series = client.series(List.of("{__name__=\"demo_metric\"}"), start, end);
System.out.println("series result:");
System.out.println(series);
// 4) 查询 label 名称(labels)
// 用于查询指定时间范围内出现过的 label 名称。
// 第三个参数用于传入 match[] 条件;
// 此处传 null,表示不按特定时序过滤,直接查询该时间范围内的 label 名称。
// 如需仅查询 demo_metric 相关的 label 名称,可传入:
// List.of("{__name__=\"demo_metric\"}")
JsonNode labels = client.labels(start, end, null);
System.out.println("labels result:");
System.out.println(labels);
// 5) 查询某个 label 的所有取值(labelValues)
// 用于查询指定 label 在时间范围内的所有可能取值。
// 此处以 label "job" 为例。
// 第四个参数同样用于传入 match[] 条件;
// 传 null 表示不按特定时序过滤。
// 如需仅查询 demo_metric 相关的 job 取值,可传入:
// List.of("{__name__=\"demo_metric\"}")
JsonNode values = client.labelValues("job", start, end, null);
System.out.println("labelValues result:");
System.out.println(values);
}
}
写入指标数据
使用 BasicAuth 写入指标
package com.example.vmp;
import com.example.vmp.VmpWorkspaceClient.WorkspaceConfig;
import com.example.vmp.VmpWorkspaceClient.SampleEntry;
import java.time.Instant;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class Main {
public static void main(String[] args) throws Exception {
// 初始化 Workspace 配置
WorkspaceConfig config = new WorkspaceConfig(
"",
"<remotewrite-url>", // 写入地址
"basic", // 认证方式:basic
null, null, null, null, null,
"<your-username>", // Basic Auth 用户名
"<your-password>", // Basic Auth 密码
30 // 请求超时时间(秒)
);
VmpWorkspaceClient client = new VmpWorkspaceClient(config);
// 构造一个时序样本:
// 指标名:demo_metric
// 标签:job=example, env=test
// 样本值:42.0
// 时间戳:当前时间(毫秒)
//
// 实际使用时,请根据业务场景替换:
// 1. Workspace 的查询/写入地址
// 2. 认证信息
// 3. 指标名(__name__)
// 4. 标签内容
// 5. 样本值和时间戳
Map<String, String> labels = new LinkedHashMap<>();
labels.put("__name__", "demo_metric"); // 指标名
labels.put("job", "example"); // 业务标签
labels.put("env", "test"); // 环境标签
String resp = client.writeSamples(List.of(
new SampleEntry(
labels,
42.0, // 样本值
Instant.now().toEpochMilli() // 时间戳(毫秒)
)
));
System.out.println(resp);
}
}
使用 AK/SK 写入指标
package com.example.vmp;
import com.example.vmp.VmpWorkspaceClient.WorkspaceConfig;
import com.example.vmp.VmpWorkspaceClient.SampleEntry;
import java.time.Instant;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class Main {
public static void main(String[] args) throws Exception {
// 初始化 Workspace 配置
WorkspaceConfig config = new WorkspaceConfig(
"",
"<remotewrite-url>", // 写入地址
"basic", // 认证方式:basic
null, null, null, null, null,
"<your-username>", // Basic Auth 用户名
"<your-password>", // Basic Auth 密码
30 // 请求超时时间(秒)
);
VmpWorkspaceClient client = new VmpWorkspaceClient(config);
// 构造一个时序样本:
// 指标名:demo_metric
// 标签:job=example, env=test
// 样本值:42.0
// 时间戳:当前时间(毫秒)
//
// 实际使用时,请根据业务场景替换:
// 1. Workspace 的查询/写入地址
// 2. 认证信息
// 3. 指标名(__name__)
// 4. 标签内容
// 5. 样本值和时间戳
Map<String, String> labels = new LinkedHashMap<>();
labels.put("__name__", "demo_metric"); // 指标名
labels.put("job", "example"); // 业务标签
labels.put("env", "test"); // 环境标签
String resp = client.writeSamples(List.of(
new SampleEntry(
labels,
42.0, // 样本值
Instant.now().toEpochMilli() // 时间戳(毫秒)
)
));
System.out.println(resp);
}
}
常见问题与排查建议
Q1:鉴权失败,返回 401 / 403
可能原因:
  • BasicAuth 用户名或密码错误。
  • AK / SK / Region 配置错误。
  • 当前账号或凭证缺少目标工作区的访问权限。
排查建议:
  • 检查鉴权方式是否与实际配置一致。
  • 确认复制的接入地址是否与工作区匹配。
Q2:查询成功但结果为空
可能原因:
  • PromQL 查询未匹配到相应数据。
  • 查询时间范围过短或时间点不合适。
  • 写入的指标数据暂未同步至查询侧。
排查建议:
  • 使用更简单的指标名。
  • 将时间范围由最近 5 分钟扩大到最近 30 分钟。
  • 等待 5~10 分钟再查询。
Q3:Remote Write 写入失败
可能原因:
  • remote_write_url 填写错误。
  • 未引入 snappy-java 依赖。
  • 网络策略或权限限制拦截。
排查建议:
  • 确认地址是否为完整写入地址,通常以 /api/v1/write 结尾。
  • 确认依赖是否引入成功。
  • 检查网络策略或权限限制。
Q4:AK/SK 授权失败
可能原因:
  • Region 与工作区实际地域不一致。
  • 使用了临时凭证但遗漏 SessionToken。
  • 服务名被误改,导致签名计算不匹配。
排查建议:
  • service 保持默认值 vmp。
  • 若使用临时凭证,请同时提供 AccessKey、SecretKey 和 SessionToken。
最近更新时间:2026.06.26 16:12:57
这个页面对您有帮助吗?
有用
有用
无用
无用