尝试使用Java实现GraphQL服务器

你好。我个人有机会在Java中实现GraphQL服务器,今天我想写一些关于这方面的见解。
这篇文章是GraphQL Advent Calendar 2020第18天的文章。

首先

这篇文章没有描述GraphQL的概述以及在实现中所使用的Spring Boot及其周边库。它适用于那些在一定程度上了解这些内容的人,但由于没有涉及太过困难的内容,我认为您可能可以通过上下文来理解。

寻找图书馆

首先,我们需要寻找实现GraphQL服务器所必需的库。

    • GraphQL Java Kickstart

 

    GraphQL Java

找到了两个选项。看起来都可以与Spring Boot结合使用。
查看GraphQL Resolver的实现示例后,发现GraphQL Java Kickstart似乎更简洁,所以我决定使用它。

当看到GraphQL Java Kickstart的README时,

这个项目封装了由GraphQL Java提供的Java实现的GraphQL。

写着它将GraphQL Java进行了封装。噢,原来是这样。

确定要实施的功能

接下来我们将决定使用GraphQL来实现的功能。这次我们决定实现基本的Query、Mutation和Subscription功能。功能的用例大致如下。

    • Query: 本の一覧を取得する。

 

    • Query: 本のIDを指定して、特定の本を取得する。

 

    • Mutation: 新しい本を登録する。

 

    Subscription: 新たに登録された本を通知する。

组成项目

spring_initializer.png

创建应用程序

构建.gradle

首先,我们需要修改 build.gradle 文件。

(Hajime ni, build.gradle wo shu xiugai.)

将com.graphql-java-kickstart:graphql-spring-boot-starter添加为依赖。同时,还添加了com.graphql-java-kickstart:graphiql-spring-boot-starter,用于在GraphiQL上测试API。
此外,还添加了io.projectreactor:reactor-core、spring-actuator和micrometer-registry-prometheus的依赖。前者用于实现Subscription,后者用于获取指标数据。

plugins {
    id 'org.springframework.boot' version '2.4.1'
    id 'io.spring.dependency-management' version '1.0.10.RELEASE'
    id 'java'
}

sourceCompatibility = '11'

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'com.graphql-java-kickstart:graphql-spring-boot-starter:8.0.0'
    runtimeOnly 'com.graphql-java-kickstart:graphiql-spring-boot-starter:8.0.0'
    // To embed GraphiQL tool
    implementation 'com.graphql-java-kickstart:graphiql-spring-boot-starter:8.0.0'
    // For subscripion
    implementation 'io.projectreactor:reactor-core:3.4.1'
    // For metrics
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    implementation 'io.micrometer:micrometer-registry-prometheus:1.6.0'

    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
}

应用程序配置文件 yml

接下来,我们要创建 application.yml 文件。

1. To enable graphiql以下の設定で、GraphiQLのURLエイリアスとGraphQLのアクエスポイントURLを指定します。

2. To enable graphql metricsは、GraphQLに関するMetricsを取得するための設定です。

3. To enable to get metrics..は、spring-actuatorの設定です。/actuator/prometheusにアクセスすると、各MetricsがPrometheusのフォーマットで取得できます。

# 1. To enable graphiql
graphiql:
  mapping: /graphiql
  endpoint:
    graphql: /graphql
# 2. To enable graphql metrics
graphql:
  servlet:
    actuator-metrics: true
# 3. To enable to get metrics of spring-actuator from /actuator/prometheus
management:
  endpoints:
    web:
      exposure:
        include: health,metrics,prometheus

schema.graphqls的含义是什么?

创建一个schema.graphqls文件,并定义GraphQL模式。
将schema.graphqls文件注册在src/main/resources/graphsql目录下。

type Query {
    bookById(id: ID): Book
    books: [Book]!
}

type Book {
    id: ID
    name: String
    pageCount: Int
}

type Mutation {
    registerBook (
        id: ID
        name: String
        pageCount: Int
    ): Book
}

type Subscription {
    subscribeBooks: Book!
}

Java模型

在GraphQL Java中,需要定义与模式相匹配的Java类。首先,创建一个名为Book Type的Java模型。

@AllArgsConstructor
@Data
public class Book {
    private String id;
    private String name;
    private int pageCount;
}

解决者

接下来,我们将创建一个实现了Query、Mutation和Subscription的Resolver。需要分别创建以下实现类,并且查询名称与实现的方法名称必须相同。另外,实现类需要作为Spring的Bean进行注册。

    • Query : GraphQLQueryResolverを実装したクラスを作成し、スキーマに定義したクエリー名と引数を持つメソッドを追加します。

 

    • Mutation : GraphQLMutationResolverを実装したクラスを作成し、スキーマに定義したMutation名と引数を持つメソッドを追加します。

 

    Subscription : GraphQLSubscriptionResolverを実装したクラスを作成し、スキーマに定義したSubscription名と引数を持つメソッドを実装します。Return値はreactive-streamsのPublisherである必要があります。

DataProvider和IBookProcessor是我們獨自創建的類別。它們執行以下處理步驟。

    • DataProvider : データ提供クラス。すべてのBookのList、または指定されたbookIdのBookを返す。

 

    IBookProcessor : 本の登録をイベントとして登録(emit)し、イベントのPublisherを発行(publish)する。
@Slf4j
@AllArgsConstructor
@Component
public class BookResolver implements GraphQLQueryResolver,
                                     GraphQLMutationResolver,
                                     GraphQLSubscriptionResolver {
    private final DataProvider dataProvider;
    private final IBookProcessor bookProcessor;

    /**
     * Query: Get all books.
     */
    public List<Book> books() {
        return dataProvider.books();
    }

    /**
     * Query: Retrieve a book by id.
     */
    public Book bookById(String bookId) {
        return dataProvider.bookById(bookId);
    }

    /**
     * Mutation: Register a book.
     */
    public Book registerBook(String id, String name, int pageCount) {
        final Book book = new Book(id, name, pageCount);
        dataProvider.books().add(book);

        // Emit an event for subscription.
        bookProcessor.emit(book);
        return book;
    }

    /**
     * Subscription: Publish an event that a book is registered.
     * Need to return Publisher on reactive-streams.
     */
    public Publisher<Book> subscribeBooks() {
        return bookProcessor.publish();
    }

    /**
     * Error handler. can handle an throwable that occurs in resolver execution.
     */
    @ExceptionHandler(Throwable.class)
    GraphQLError handle(Throwable e) {
        log.error("Failed to execute resolver.", e);
        return new ThrowableGraphQLError(e, "Failed to execute resolver.");
    }
}
service_image.png

运行应用程序

运行创建的应用程序。启动SpringBoot应用程序,然后使用浏览器访问GraphiQL的端点(http://localhost:8080/graphiql),并执行查询。

subscription.png

度量衡也被准确地收集了起来。我们使用了Metricat这个工具来将其以图表的形式呈现,该工具可以在以下网站上找到。

    PrometheusとMetricatによるアプリケーション監視

如果将spring-actuator的prometheus端点(http://localhost:8080/actuator/prometheus)设置为Metricat的Prometheus导出URL,然后在GraphiQL中执行查询,将显示如下图所示的图形。

metrics.png

综上所述

我已经描述了如何在Java中实现GraphQL服务器的方法。尽管实现的代码只是示例代码,并且很容易开发解析器等,但使用起来方便,并且给人的印象也不错。

虽然目前还没有在实际工作中使用的计划,但我想象了一些在实际工作中可能会遇到的问题。

    • Resolverのasyncで実装したい。

 

    • → CompletionStage(実装クラス:CompletableFuture)がreturnできるようです。Async Resolvers #1

分散Tracingの導入
→ 一応、ここTracing and Metricsに記述がありますが、Apollo style tracing ? 謎なので後で調べる。
MetricにResolverのレイテンシを追加したい。
→ Micrometerを使って、実装する必要がある感じ?Resolverの中にハードコードしたくないので、Spring AOPなどで実装したい。後で調べる。
Spring WebFlux対応
→ なにかあるので後で調べる。graphql-kickstart-spring-webflux

我已经把使用过的源代码注册在GitHub上,请参考。非常希望能对您有所帮助。

    GitHub:Simple application of GraphQL server
bannerAds