天天看點

透過源碼分析:GraphQL在開源項目skywalking中的應用前置說明Skywalking應用GraphQL源碼

前置說明

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:

透過源碼分析:GraphQL在開源項目skywalking中的應用前置說明Skywalking應用GraphQL源碼

OAPServerStartUp#main()方法和OAPServerBootstrap#start()方法都比較簡單,重點看下MoudleManager#init()方法,如下:

透過源碼分析:GraphQL在開源項目skywalking中的應用前置說明Skywalking應用GraphQL源碼

在第一步加載所有module時,QueryMoulde初始化了GraphQL執行個體。

應用GraphQL執行個體

在前面已經初始好了GraphQL執行個體,需要再看下它是如何嵌套到一個Http Server裡面。代碼如下,還是在GraphQLProvider類裡面:

透過源碼分析:GraphQL在開源項目skywalking中的應用前置說明Skywalking應用GraphQL源碼

start方法的調用時機就是在上面截圖的第二步start那裡,這個時候會調用加載的所有module的start()方法。

而GraphQLQueryHandler是一個HttpServlet的子類,GraphQL作為構造參數傳遞了進去:

透過源碼分析:GraphQL在開源項目skywalking中的應用前置說明Skywalking應用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,調用棧如下:

透過源碼分析:GraphQL在開源項目skywalking中的應用前置說明Skywalking應用GraphQL源碼

這個JettyServer是skywalking自己實作的:

透過源碼分析:GraphQL在開源項目skywalking中的應用前置說明Skywalking應用GraphQL源碼

暴露接口,提供服務

最後看下skywalking是在什麼時間啟動這個Http Server,再來看下debug棧:

透過源碼分析:GraphQL在開源項目skywalking中的應用前置說明Skywalking應用GraphQL源碼

正好是在我們前面截圖中的第三步:notifyAfterCompleted(),這時會調用所有加載module的notifyAfterCompleted方法,而這個是屬于CoreModuleProvider,如下:

透過源碼分析:GraphQL在開源項目skywalking中的應用前置說明Skywalking應用GraphQL源碼

至此,整個流程便已經串聯起來了。

繼續閱讀