尝试使用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: 新たに登録された本を通知する。
组成项目

创建应用程序
构建.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.");
}
}

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

度量衡也被准确地收集了起来。我们使用了Metricat这个工具来将其以图表的形式呈现,该工具可以在以下网站上找到。
- PrometheusとMetricatによるアプリケーション監視
如果将spring-actuator的prometheus端点(http://localhost:8080/actuator/prometheus)设置为Metricat的Prometheus导出URL,然后在GraphiQL中执行查询,将显示如下图所示的图形。

综上所述
我已经描述了如何在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