透过源码分析:GraphQL在开源项目skywalking中的应用

前置说明

Skywalking应用GraphQL源码

GraphQL依赖

初始化GraphQL

初始化方式

初始化时间

应用GraphQL实例

暴露接口,提供服务


前置说明

skywalking是国人开源的一款分布式链路追踪系统(应用性能监控工具)。本文重点关注GraphQL在项目中如何应用,其它核心特性及实现,后续有时间再进行补充。

我在前面的文章:IDEA配置skywalking开发环境提到了如何在本地配置skywalking开发环境。如果感兴趣可以对着上面这篇博客把代码clone下来,配置好,对着源码来。

或者直接看下面内容,关键代码我会贴出来。

以下针对skywalking 8.5.0版本。

Skywalking应用GraphQL源码

GraphQL依赖

依赖如下,与我前面文章及示例中使用的依赖并不完全一样:

            <dependency>
                <groupId>com.graphql-java</groupId>
                <artifactId>graphql-java-tools</artifactId>
                <version>5.2.3</version>
            </dependency>
            <dependency>
                <groupId>com.graphql-java</groupId>
                <artifactId>graphql-java</artifactId>
                <version>8.0</version>
            </dependency>

初始化GraphQL

初始化方式

GraphQL实例的初始化在下面这个类里,在prepare方法里面

oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/GraphQLQueryProvider.java

关于它的初始化并没有特别多的说明,我在前面浅尝GraphQL再谈GraphQL之在spring boot项目中快速应用的示例中用的都是类似的这种方式(因为依赖不一样,所以实际使用的类是不同的,请注意区分)

        GraphQLSchema schema = SchemaParser.newParser()
                                           .file("query-protocol/common.graphqls")
                                           .resolvers(new Query(), new Mutation(), new HealthQuery(getManager()))
                                           .file("query-protocol/metadata.graphqls")
                                           .resolvers(new MetadataQuery(getManager()))
                                            // ...省略中间这一大堆重复代码
                                           .file("query-protocol/event.graphqls")
                                           .resolvers(new EventQuery(getManager()))
                                           .build()
                                           .makeExecutableSchema();
        this.graphQL = GraphQL.newGraphQL(schema).build();

初始化时间

通过debug调用栈,可以快速看到在哪里被谁调用了prepare方法初始化GraphQL:

OAPServerStartUp#main()方法和OAPServerBootstrap#start()方法都比较简单,重点看下MoudleManager#init()方法,如下:

在第一步加载所有module时,QueryMoulde初始化了GraphQL实例。

应用GraphQL实例

在前面已经初始好了GraphQL实例,需要再看下它是如何嵌套到一个Http Server里面。代码如下,还是在GraphQLProvider类里面:

start方法的调用时机就是在上面截图的第二步start那里,这个时候会调用加载的所有module的start()方法。

而GraphQLQueryHandler是一个HttpServlet的子类,GraphQL作为构造参数传递了进去:

看下GraphQLQueryHandler的代码实现,无关代码我都删除了,只留了核心代码:

@RequiredArgsConstructor
public class GraphQLQueryHandler extends JettyJsonHandler {

    @Override
    protected JsonElement doGet(HttpServletRequest req) {
        // 不支持GET请求
        throw new UnsupportedOperationException("GraphQL only supports POST method");
    }

    @Override
    protected 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();
            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) {
            // 异常处理的代码,删除了
            return jsonObject;
        }
    }
}

可以看到,Get请求不支持, Post请求来的时候,解析参数,进行调用,返回结果。

在调用截图中的service.addHandler(...)方法,便会注册这个servert,调用栈如下:

这个JettyServer是skywalking自己实现的:

暴露接口,提供服务

最后看下skywalking是在什么时间启动这个Http Server,再来看下debug栈:

正好是在我们前面截图中的第三步:notifyAfterCompleted(),这时会调用所有加载module的notifyAfterCompleted方法,而这个是属于CoreModuleProvider,如下:

至此,整个流程便已经串联起来了。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
SkyWalkingGraphQL实现是基于GraphQL Java实现的。GraphQL Java是一个用于Java的GraphQL实现,它提供了创建GraphQL服务所需的一切,包括解析器和执行器等。 在SkyWalkingGraphQL的实现包含以下步骤: 1. 创建GraphQL服务 首先,需要创建一个GraphQL服务。这可以通过创建一个GraphQLSchema对象来实现。GraphQLSchema代表了整个GraphQL服务的模式,它定义了可查询的数据类型以及它们之间的关系。在SkyWalkingGraphQLSchema由GraphQLSchemaBuilder创建,它使用GraphQLJavaTools从定义的GraphQL类型和解析器构建。 2. 定义GraphQL类型 GraphQL类型是GraphQL服务的基本构建块。在SkyWalkingGraphQL类型定义了可查询的数据类型,例如Span、Trace等。GraphQL类型由GraphQLTypeBuilder创建,它定义了类型的名称、字段和解析器。 3. 定义GraphQL解析器 GraphQL解析器用于解析GraphQL查询。在SkyWalkingGraphQL解析器由GraphQLResolverBuilder创建,它为每个GraphQL类型定义了一个解析器。解析器实际上是一组方法,用于执行查询并返回数据。 4. 注册GraphQL服务 最后,需要将GraphQL服务注册到SkyWalking,以便它可以被查询和调用。这可以通过在SkyWalking的模块注册GraphQL服务来实现。模块是SkyWalking的扩展点,它允许开发人员添加自己的代码和服务到SkyWalking。 总的来说,SkyWalkingGraphQL实现是通过定义GraphQL类型、解析器和创建GraphQL服务来实现的。它提供了一种简单且灵活的方式来查询和检索SkyWalking的跟踪数据。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不识君的荒漠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值