以查询 Metrics 信息案例来分析 Skywalking 查询协议

基本概述

Skywalking 查询协议默认基于 GraphQL ,如果有需要也可以自定义扩展,提供一个实现了 org.apache.skywalking.oap.server.core.query.QueryModule 的查询模块即可。

截取 Skywalking UI 发送的请求

  • 请求路径
POST http://127.0.0.1:8080/graphql
  • 请求体
{"query": "query queryData($condition: MetricsCondition!, $duration: Duration!) {\n  readMetricsValues: readMetricsValues(condition: $condition, duration: $duration) {\n    label\n    values {\n      values {value}\n    }\n  }}","variables": {"duration": {"start": "2021-07-03 1320","end": "2021-07-03 1321","step": "MINUTE"},"condition": {"name": "instance_jvm_thread_runnable_thread_count","entity": {"scope": "ServiceInstance","serviceName": "business-zone::projectA","serviceInstanceName": "e8cf34a1d54a4058a8c98505877770e2@192.168.50.113","normal": true}}}
}
  • 响应
{"data": {"readMetricsValues": {"values": {"values": [{"value": 22},{"value": 22}]}}}
}

Skywalking 源码中找到对应 GraphQL 定义

打开 oap-server/server-query-plugin/query-graphql-plugin/src/main/resources/query-protocol 目录,使用请求体中的模板关键字 readMetricsValues 搜索
oap-server/server-query-plugin/query-graphql-plugin/src/main/resources/query-protocol/metrics-v2.graphqls 中找到对应的定义

extend type Query {# etc...# Read time-series values in the duration of required metricsreadMetricsValues(condition: MetricsCondition!, duration: Duration!): MetricsValues!# etc...
}

输入参数定义

input MetricsCondition {# Metrics name, which should be defined in OAL script# Such as:# Endpoint_avg = from(Endpoint.latency).avg()# Then, `Endpoint_avg`name: String!# Follow entity definition description.entity: Entity!
}input Entity {# 1. scope=All, no name is required.# 2. scope=Service, ServiceInstance and Endpoint, set neccessary serviceName/serviceInstanceName/endpointName# 3. Scope=ServiceRelation, ServiceInstanceRelation and EndpointRelation#    serviceName/serviceInstanceName/endpointName is/are the source(s)#    destServiceName/destServiceInstanceName/destEndpointName is/are destination(s)#    set necessary names of sources and destinations.scope: Scope!serviceName: String# Normal service is the service having installed agent or metrics reported directly.# Unnormal service is conjectural service, usually detected by the agent.normal: BooleanserviceInstanceName: StringendpointName: StringdestServiceName: String# Normal service is the service having installed agent or metrics reported directly.# Unnormal service is conjectural service, usually detected by the agent.destNormal: BooleandestServiceInstanceName: StringdestEndpointName: String
}# The Duration defines the start and end time for each query operation.
# Fields: `start` and `end`
#   represents the time span. And each of them matches the step.
#   ref https://www.ietf.org/rfc/rfc3339.txt
#   The time formats are
#       `SECOND` step: yyyy-MM-dd HHmmss
#       `MINUTE` step: yyyy-MM-dd HHmm
#       `HOUR` step: yyyy-MM-dd HH
#       `DAY` step: yyyy-MM-dd
#       `MONTH` step: yyyy-MM
# Field: `step`
#   represents the accurate time point.
# e.g.
#   if step==HOUR , start=2017-11-08 09, end=2017-11-08 19
#   then
#       metrics from the following time points expected
#       2017-11-08 9:00 -> 2017-11-08 19:00
#       there are 11 time points (hours) in the time span.
input Duration {start: String!end: String!step: Step!
}enum Step {DAYHOURMINUTESECOND
}

返回结果定义

type MetricsValues {# Could be null if no label assigned in the query conditionlabel: String# Values of this label value.values: IntValues
}type IntValues {values: [KVInt!]!
}type KVInt {id: ID!# This is the value, the caller must understand the Unit.# Such as:# 1. If ask for cpm metric, the unit and result should be count.# 2. If ask for response time (p99 or avg), the unit should be millisecond.value: Long!
}

使用 GraphQL IDEA 插件验证 Skywalking UI 的请求

使用“ GraphQLSkywalking 中的应用”一节中的方式,模仿“截取 Skywalking UI 发送的请求”一节中前端发送的请求

  • 请求模板
query queryData($condition: MetricsCondition!, $duration: Duration!) {readMetricsValues: readMetricsValues(duration: $duration, condition: $condition) {label values { values { id value }}}
}
  • 请求参数
{"duration": {"start": "2021-07-03 1400","end": "2021-07-03 1401", "step": "MINUTE"},"condition": {"name": "instance_jvm_thread_runnable_thread_count","entity": {"scope": "ServiceInstance","serviceName": "business-zone::projectA","serviceInstanceName": "e8cf34a1d54a4058a8c98505877770e2@192.168.50.113","normal": true}}
}
  • 响应结果
{"data": {"readMetricsValues": {"values": {"values": [{"id": "202107031400_YnVzaW5lc3Mtem9uZTo6cHJvamVjdEE=.1_ZThjZjM0YTFkNTRhNDA1OGE4Yzk4NTA1ODc3NzcwZTJAMTkyLjE2OC41MC4xMTM=","value": 22},{"id": "202107031401_YnVzaW5lc3Mtem9uZTo6cHJvamVjdEE=.1_ZThjZjM0YTFkNTRhNDA1OGE4Yzk4NTA1ODc3NzcwZTJAMTkyLjE2OC41MC4xMTM=","value": 22}]}}}
}

PS:如果不使用模板的方式,写查询语句是会有代码提示的

query queryData {readMetricsValues(duration: {start: "2021-07-03 1400",end: "2021-07-03 1401", step: MINUTE},condition: {name: "instance_jvm_thread_runnable_thread_count",entity: {scope: ServiceInstance,serviceName: "business-zone::projectA",serviceInstanceName: "e8cf34a1d54a4058a8c98505877770e2@192.168.50.113",normal: true}}) {label values{ values{ id value }}}
}

如何将 GraphQL Schema 文件加载到程序中

搜索 metrics-v2.graphqls ,在 oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/GraphQLQueryProvider.java 找到加载代码

    // 初始化GraphQL引擎@Overridepublic void prepare() throws ServiceNotProvidedException, ModuleStartException {GraphQLSchema schema = SchemaParser.newParser()// etc....file("query-protocol/metrics-v2.graphqls").resolvers(new MetricsQuery(getManager())) // MetricsQuery 是 com.coxautodev.graphql.tools.GraphQLQueryResolver 接口实现类// etc....build().makeExecutableSchema();this.graphQL = GraphQL.newGraphQL(schema).build();}

org.apache.skywalking.oap.query.graphql.resolver.MetricsQuery 类中,找到 readMetricsValues 方法

    /*** Read time-series values in the duration of required metrics*/public MetricsValues readMetricsValues(MetricsCondition condition, Duration duration) throws IOException {if (MetricsType.UNKNOWN.equals(typeOfMetrics(condition.getName())) || !condition.getEntity().isValid()) {final List<PointOfTime> pointOfTimes = duration.assembleDurationPoints();MetricsValues values = new MetricsValues();pointOfTimes.forEach(pointOfTime -> {String id = pointOfTime.id(condition.getEntity().isValid() ? condition.getEntity().buildId() : "ILLEGAL_ENTITY");final KVInt kvInt = new KVInt();kvInt.setId(id);kvInt.setValue(0);values.getValues().addKVInt(kvInt);});return values;}return getMetricsQueryService().readMetricsValues(condition, duration);}private MetricsQueryService getMetricsQueryService() {if (metricsQueryService == null) {this.metricsQueryService = moduleManager.find(CoreModule.NAME).provider().getService(MetricsQueryService.class);}return metricsQueryService;}

org.apache.skywalking.oap.server.core.query.MetricsQueryService#readMetricsValues

    /*** Read time-series values in the duration of required metrics*/public MetricsValues readMetricsValues(MetricsCondition condition, Duration duration) throws IOException {return getMetricQueryDAO().readMetricsValues(condition, ValueColumnMetadata.INSTANCE.getValueCName(condition.getName()), duration);}private IMetricsQueryDAO getMetricQueryDAO() {if (metricQueryDAO == null) {metricQueryDAO = moduleManager.find(StorageModule.NAME).provider().getService(IMetricsQueryDAO.class);}return metricQueryDAO;}

查看Extend storage文档, IMetricsQueryDAO 为指标查询数据访问对象

# Implement all DAOs
# Here is the list of all DAO interfaces in storage
IServiceInventoryCacheDAO
IServiceInstanceInventoryCacheDAO
IEndpointInventoryCacheDAO
INetworkAddressInventoryCacheDAO
IBatchDAO
StorageDAO
IRegisterLockDAO
ITopologyQueryDAO
IMetricsQueryDAO
ITraceQueryDAO
IMetadataQueryDAO
IAggregationQueryDAO
IAlarmQueryDAO
IHistoryDeleteDAO
IMetricsDAO
IRecordDAO
IRegisterDAO
ILogQueryDAO
ITopNRecordsQueryDAO
IBrowserLogQueryDAO

通过类图,可以看出 IMetricsQueryDAO 实现类有 ESES7InfluxDBSQL 四种

如何将 GraphQL 引擎注册到 Jetty 服务

    // 注册GraphQL查询处理器至Jetty服务@Overridepublic void start() throws ServiceNotProvidedException, ModuleStartException {JettyHandlerRegister service = getManager().find(CoreModule.NAME).provider().getService(JettyHandlerRegister.class);service.addHandler(new GraphQLQueryHandler(config.getPath(), graphQL));}

通过分析 GraphQLQueryProvider 该类,发现就是 QueryModule (查询模块)的 Provider (提供)类

由此,也验证了在“基本概述”一节的说法:

Skywalking 查询协议默认基于 GraphQL ,如果有需要也可以自定义扩展,提供一个实现了 org.apache.skywalking.oap.server.core.query.QueryModule 的查询模块即可。

    @Overridepublic String name() {return "graphql";}@Overridepublic Class<? extends ModuleDefine> module() {return QueryModule.class;}
package org.apache.skywalking.oap.query.graphql;import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import graphql.ExecutionInput;
import graphql.ExecutionResult;
import graphql.GraphQL;
import graphql.GraphQLError;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.apache.skywalking.oap.server.library.server.jetty.JettyJsonHandler;
import org.apache.skywalking.oap.server.library.util.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;@RequiredArgsConstructor
public class GraphQLQueryHandler extends JettyJsonHandler {private static final Logger LOGGER = LoggerFactory.getLogger(GraphQLQueryHandler.class);private static final String QUERY = "query";private static final String VARIABLES = "variables";private static final String DATA = "data";private static final String ERRORS = "errors";private static final String MESSAGE = "message";private final Gson gson = new Gson();private final Type mapOfStringObjectType = new TypeToken<Map<String, Object>>() {}.getType();private final String path;private final GraphQL graphQL;@Overridepublic String pathSpec() {return path;}@Overrideprotected JsonElement doGet(HttpServletRequest req) {throw new UnsupportedOperationException("GraphQL only supports POST method");}@Overrideprotected JsonElement doPost(HttpServletRequest req) throws IOException {BufferedReader reader = new BufferedReader(new InputStreamReader(req.getInputStream()));String line;StringBuilder request = new StringBuilder();while ((line = reader.readLine()) != null) {request.append(line);}JsonObject requestJson = gson.fromJson(request.toString(), JsonObject.class);return execute(requestJson.get(QUERY).getAsString(), gson.fromJson(requestJson.get(VARIABLES), mapOfStringObjectType));}private JsonObject execute(String request, Map<String, Object> variables) {try {ExecutionInput executionInput = ExecutionInput.newExecutionInput().query(request).variables(variables).build();// 使用GraphQL引擎获取查询结果ExecutionResult executionResult = graphQL.execute(executionInput);LOGGER.debug("Execution result is {}", executionResult);// 封装返回结果Object data = executionResult.getData();List<GraphQLError> errors = executionResult.getErrors();JsonObject jsonObject = new JsonObject();if (data != null) {jsonObject.add(DATA, gson.fromJson(gson.toJson(data), JsonObject.class));}if (CollectionUtils.isNotEmpty(errors)) {JsonArray errorArray = new JsonArray();errors.forEach(error -> {JsonObject errorJson = new JsonObject();errorJson.addProperty(MESSAGE, error.getMessage());errorArray.add(errorJson);});jsonObject.add(ERRORS, errorArray);}return jsonObject;} catch (final Throwable e) {LOGGER.error(e.getMessage(), e);JsonObject jsonObject = new JsonObject();JsonArray errorArray = new JsonArray();JsonObject errorJson = new JsonObject();errorJson.addProperty(MESSAGE, e.getMessage());errorArray.add(errorJson);jsonObject.add(ERRORS, errorArray);return jsonObject;}}
}

Webapp 网关转发 GraphQL 请求至 OAP

v8.6.0 及之前,网关都是 zuulv8.7.0 及之后替换成了 Spring Cloud Gateway 。因为这块不是这篇文章的重点,这里不再赘述

总结

Skywalking 的查询协议默认使用通用性很强的 GraphQL 实现,客户端可以通过 GraphQL 协议很方便的选取自己需要的数据。
对应 Skywalking 这种模式相对固定、变更不频繁的查询需求来说,还是挺适合的。

参考文档

  • Extend storage

Skywalking-11:Skywalking查询协议——案例分析相关推荐

  1. mysql数据库多表查询(内连接,外连接,自连接,子查询及案例分析)

    mysql数据库多表查询 之前接触的项目或者自己涉及的部分对于多表查询的使用都比较的少,常常是自己更具案例进行学习,最近见到的比较的多,所以今天就好好的总结一下,为下一步学习做准备! 1.多表查询关系 ...

  2. 视频教程-2020年软考网络规划设计师案例分析软考视频教程-软考

    2020年软考网络规划设计师案例分析软考视频教程 10年以上软考培训经验,线下培训学员过万人.培训过的课程有:网络规划设计师.网络工程师.信 息系统项目管理师.系统集成项目管理师.信息安全技术.网络技 ...

  3. SQL优化实战经典案例分析

    前言 大家好,我是黎杜,今天和大家聊聊SQL优化的场景. SQL调优这块呢,大厂面试必问的.最近金九银十嘛,所以整理了SQL的调优思路,并且附几个经典案例分析. 1.慢SQL优化思路. 慢查询日志记录 ...

  4. php拼音模糊查询,PHP模糊查询技术实例分析【附源码下载】

    本文实例讲述了PHP模糊查询技术.分享给大家供大家参考,具体如下: 简介 从本质上揭密PHP模糊查询技术 功能 根据输入的关键字查找相关用户 PHP用户查询器案例分析 课程目标 掌握PHP模糊技术的应 ...

  5. 视频教程-2020年软考网络规划设计师案例分析历年真题详解软考视频教程-软考

    2020年软考网络规划设计师案例分析历年真题详解软考视频教程 10年以上软考培训经验,线下培训学员过万人.培训过的课程有:网络规划设计师.网络工程师.信 息系统项目管理师.系统集成项目管理师.信息安全 ...

  6. 《深度学习导论及案例分析》一2.11概率图模型的推理

    本节书摘来自华章出版社<深度学习导论及案例分析>一书中的第2章,第2.11节,作者李玉鑑 张婷,更多章节内容可以访问云栖社区"华章计算机"公众号查看. 2.11概率图模 ...

  7. SQL语句查询电影评分案例分析

    SQL语句查询电影评分案例分析 部分数据:(全数据有100万条) {"movie":"1193","rate":"5", ...

  8. 2013下半年(11月)信息系统项目管理师考试题型分析(综合知识、案例分析、论文)...

    2013下半年(11月)信息系统项目管理师考试真题题型分析PDF下载:http://www.cnitpm.com/down/ShowForum-409202-1.htm 2013下半年(11月)信息系 ...

  9. 《移动App测试的22条军规》—App测试综合案例分析23.11节测试微信App对多语言和地区的支持...

    本节书摘来自异步社区<移动App测试的22条军规>一书中的App测试综合案例分析,第23.11节测试微信App对多语言和地区的支持,作者黄勇,更多章节内容可以访问云栖社区"异步社 ...

最新文章

  1. redis学习之——CAP原理CAP+BASE
  2. docker 创建启用systemd服务的容器
  3. k8s查看pod的yaml文件_K8S系列学习,Pod实战那些事儿,有必要知道知道
  4. 双十一!教你用Python感知女朋友的情绪变化?
  5. 【LeetCode】【HOT】543. 二叉树的直径(递归)
  6. 关于Executors.newFixedThreadPool何时创建新线程
  7. TeeChart.NET 2022.4.8 专业版-Crack
  8. c语言创意作业蜂鸣器,蜂鸣器c语言程序_c语言编写蜂鸣器发声
  9. Biztalk AS2开发经验总结
  10. 学了计算机很迷茫怎么办?计算机大三学生怎么找实习工作?
  11. 利用外观模式Java投资理财_Java设计模式之外观模式和装饰器模式的设计(精选)...
  12. Winedit 下载第三方库
  13. 设计模式之禅【中介者模式】
  14. 微服务分布式构架开发实战PDF,阿里架构师推荐,快快收藏吧
  15. Action Unit到底是什么?
  16. Python学习周记(序列)
  17. python技术文档
  18. 妇女节手抄报Word电子小报
  19. STM32CubeMX基于HAL库实现简单串口通信
  20. ESP32A1S开发之智能家居 语音唤醒 语音命令控制(持续更新)

热门文章

  1. DOCKER容器与宿主机同网段互相通信
  2. 【安卓开发】AS神奇的报错:Cannot find AVD system path. Please define ANDROID_SDK_ROOT
  3. sqlmap源码阅读系列检查是否满足依赖
  4. 事务处理与事务的隔离级别
  5. golang web服务器_使用Go制作自己的Web服务器:快速指南
  6. php wula,PHP老师没教过你的那些知识点
  7. C++ 用遗传算法解决TSP问题,旅行商问题
  8. 一篇好的技术博文,快速让你通俗理解Python闭包!
  9. selenium 处理cookie及switch的使用
  10. 漫谈CMS:ZOOMLA、NETCMS、风讯、动易异同