Rejoiner 开源项目教程:从 gRPC 微服务构建统一 GraphQL Schema

Rejoiner 开源项目教程:从 gRPC 微服务构建统一 GraphQL Schema

【免费下载链接】rejoiner Generates a unified GraphQL schema from gRPC microservices and other Protobuf sources 【免费下载链接】rejoiner 项目地址: https://gitcode.com/gh_mirrors/re/rejoiner

引言:微服务架构下的数据查询挑战

在现代微服务架构中,每个服务通常使用不同的协议和数据格式,这给前端开发带来了巨大的挑战。你是否遇到过以下痛点:

  • 需要向多个后端服务发起请求才能获取完整页面数据
  • 不同服务返回的数据格式不一致,需要在前端进行复杂的数据转换
  • 后端API变更导致前端需要大量重写代码
  • 过度获取数据造成网络带宽浪费

Rejoiner 正是为解决这些问题而生!它是一个强大的开源工具,能够从 gRPC 微服务和其他 Protobuf 源生成统一的 GraphQL schema,让你像查询单个数据库一样查询整个微服务生态系统。

Rejoiner 核心概念解析

什么是 Rejoiner?

Rejoiner 是一个基于 Java 的库,主要功能包括:

  • 🎯 统一 Schema 生成:从多个 gRPC 服务自动创建统一的 GraphQL schema
  • 🔄 协议转换:将 GraphQL 查询转换为 gRPC 调用
  • 🧩 灵活组合:支持 schema 的模块化定义和组合
  • 🛠️ DSL 支持:提供丰富的 DSL 来修改生成的 schema

核心架构概览

mermaid

快速开始:构建你的第一个 Rejoiner 应用

环境准备

首先添加 Maven 依赖:

<dependency>
    <groupId>com.google.api.graphql</groupId>
    <artifactId>rejoiner</artifactId>
    <version>0.0.4</version>
</dependency>

或者使用 Gradle:

implementation 'com.google.api.graphql:rejoiner:0.0.4'

基础示例:Hello World

让我们从一个简单的示例开始,了解 Rejoiner 的基本工作原理。

1. 定义 Protobuf 服务

syntax = "proto3";

package helloworld;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

2. 创建 SchemaModule

import com.google.api.graphql.rejoiner.Query;
import com.google.api.graphql.rejoiner.SchemaModule;
import io.grpc.examples.helloworld.GreeterGrpc;
import io.grpc.examples.helloworld.HelloReply;
import io.grpc.examples.helloworld.HelloRequest;

final class HelloWorldSchemaModule extends SchemaModule {
  @Query("sayHello")
  HelloReply sayHello(HelloRequest request, GreeterGrpc.GreeterBlockingStub client) {
    return client.sayHello(request);
  }
}

3. 配置 Guice 模块

import com.google.api.graphql.rejoiner.SchemaProviderModule;
import com.google.inject.AbstractModule;

public final class AppModule extends AbstractModule {
  @Override
  protected void configure() {
    // 提供生成的 GraphQLSchema 实例
    install(new SchemaProviderModule());
    
    // 安装 schema 模块
    install(new HelloWorldSchemaModule());
  }
}

生成的 GraphQL Schema

执行上述配置后,Rejoiner 会自动生成以下 GraphQL schema:

type Query {
  sayHello(name: String!): HelloReply
}

type HelloReply {
  message: String
}

现在你可以使用标准的 GraphQL 查询:

query {
  sayHello(name: "World") {
    message
  }
}

高级功能详解

1. 数据关联与连接

Rejoiner 最强大的功能之一是能够在不同类型之间建立关联。假设我们有两个服务:用户服务和待办事项服务。

final class TodoToUserSchemaModule extends SchemaModule {
  @SchemaModification(addField = "creator", onType = Todo.class)
  ListenableFuture<User> todoCreatorToUser(UserService userService, Todo todo) {
    return userService.getUserByEmail(todo.getCreatorEmail());
  }
}

这样就在 Todo 类型上添加了 creator 字段,实现了跨服务的关联查询。

2. Schema 修改与定制

Rejoiner 提供了灵活的 DSL 来修改生成的 schema:

final class TodoModificationsSchemaModule extends SchemaModule {
  @SchemaModification
  TypeModification removePrivateTodoData =
      Type.find(Todo.getDescriptor()).removeField("privateTodoData");
}

3. 支持的类型系统

Rejoiner 支持多种返回类型:

返回类型描述示例
Message任何 Protobuf 消息HelloReply
ImmutableList<? extends Message>消息列表ImmutableList<Todo>
ListenableFuture<? extends Message>异步消息ListenableFuture<User>
ListenableFuture<ImmutableList<? extends Message>>异步消息列表ListenableFuture<ImmutableList<Todo>>

4. 字段掩码(FieldMask)支持

Rejoiner 能够基于 GraphQL 选择器自动生成 Proto FieldMask,优化数据传输:

mermaid

实战案例:图书馆管理系统

让我们通过一个完整的示例来展示 Rejoiner 在实际项目中的应用。

系统架构

mermaid

核心代码实现

1. 图书查询模块

final class BookQuerySchemaModule extends SchemaModule {
  @Query("listBooks")
  ListenableFuture<ListBooksResponse> listBooks(
      ListBooksRequest request, BookService bookService) {
    return bookService.listBooks(request);
  }
  
  @Query("getBook")
  ListenableFuture<Book> getBook(GetBookRequest request, BookService bookService) {
    return bookService.getBook(request);
  }
}

2. 书架管理模块

final class ShelfMutationSchemaModule extends SchemaModule {
  @Mutation("createShelf")
  ListenableFuture<Shelf> createShelf(
      CreateShelfRequest request, ShelfService shelfService, 
      @AuthenticatedUser String userId) {
    request = request.toBuilder().setCreatorId(userId).build();
    return shelfService.createShelf(request);
  }
}

3. 数据关联模块

final class BookToShelfSchemaModule extends SchemaModule {
  @SchemaModification(addField = "shelf", onType = Book.class)
  ListenableFuture<Shelf> bookToShelf(ShelfService shelfService, Book book) {
    return shelfService.getShelf(GetShelfRequest.newBuilder()
        .setShelfId(book.getShelfId()).build());
  }
}

生成的 GraphQL Schema

type Query {
  listBooks(author: String, genre: String): ListBooksResponse
  getBook(id: ID!): Book
}

type Mutation {
  createShelf(name: String!, description: String): Shelf
}

type Book {
  id: ID!
  title: String!
  author: String!
  shelf: Shelf
}

type Shelf {
  id: ID!
  name: String!
  description: String
  books: [Book]
}

type ListBooksResponse {
  books: [Book]
  totalCount: Int
}

性能优化与最佳实践

1. 使用 DataLoader 进行批处理

Rejoiner 与 DataLoader 完美集成,可以自动批处理请求:

final class DataLoaderModule extends AbstractModule {
  @Override
  protected void configure() {
    // 配置 DataLoader 注册表
    bind(DataLoaderRegistryFactory.class).toInstance(
        new DataLoaderRegistryFactory() {
          @Override
          public DataLoaderRegistry create(DataLoaderRegistry base) {
            base.register("user", 
                new DataLoader<String, User>(userIds -> 
                    userService.batchGetUsers(userIds)));
            return base;
          }
        });
  }
}

2. 字段级权限控制

final class AuthorizationSchemaModule extends SchemaModule {
  @SchemaModification
  TypeModification addAuthorization = Type.find(User.getDescriptor())
      .addField(GraphQLFieldDefinition.newFieldDefinition()
          .name("canEdit")
          .type(Scalars.GraphQLBoolean)
          .dataFetcher(environment -> {
            User user = environment.getSource();
            String currentUser = environment.getContext();
            return currentUser.equals(user.getId());
          })
          .build());
}

3. 监控与日志

final class MonitoringSchemaModule extends SchemaModule {
  @Query("listBooks")
  ListenableFuture<ListBooksResponse> listBooks(
      ListBooksRequest request, BookService bookService, MeterRegistry registry) {
    
    Timer.Sample sample = Timer.start(registry);
    return Futures.transform(
        bookService.listBooks(request),
        response -> {
          sample.stop(registry.timer("graphql.query.listBooks"));
          return response;
        },
        MoreExecutors.directExecutor());
  }
}

常见问题与解决方案

Q1: 如何处理循环依赖?

解决方案: 使用 @SchemaModification 的延迟解析特性

final class CircularDependencySchemaModule extends SchemaModule {
  @SchemaModification(addField = "relatedBooks", onType = Book.class)
  ListenableFuture<List<Book>> getRelatedBooks(BookService service, Book book) {
    // 这里的 Book 类型会在 schema 生成完成后才解析
    return service.getRelatedBooks(book.getId());
  }
}

Q2: 如何自定义标量类型?

解决方案: 扩展 ProtoScalars

public class CustomScalars extends ProtoScalars {
  public static final GraphQLScalarType CUSTOM_DATE = GraphQLScalarType.newScalar()
      .name("CustomDate")
      .coercing(new Coercing() {
        // 实现序列化和反序列化逻辑
      })
      .build();
}

Q3: 如何优化大量数据的查询性能?

解决方案: 结合 FieldMask 和分页

final class OptimizedQuerySchemaModule extends SchemaModule {
  @Query("searchBooks")
  ListenableFuture<SearchBooksResponse> searchBooks(
      SearchBooksRequest request, BookService bookService) {
    // 自动应用 FieldMask 优化
    return bookService.searchBooks(request);
  }
}

扩展与生态系统

1. Google Cloud 集成

Rejoiner 提供了与 Google Cloud 服务的预配置集成:

// 集成 Firestore
install(new FirestoreSchemaModule());

// 集成 Google Cloud Container
install(new ContainerSchemaModule());

2. Relay 支持

Rejoiner 支持 Relay 规范,包括全局 ID 和连接模式:

final class RelaySupportSchemaModule extends SchemaModule {
  @Query("node")
  ListenableFuture<RelayNode> node(@Arg("id") String id, NodeResolver resolver) {
    return resolver.resolveNode(id);
  }
}

3. 流式响应

支持基于 gRPC 流式的 GraphQL 响应:

final class StreamingSchemaModule extends SchemaModule {
  @Query("streamUpdates")
  Publisher<Update> streamUpdates(UpdateService updateService) {
    return updateService.getUpdates();
  }
}

总结与展望

Rejoiner 为微服务架构下的数据查询提供了优雅的解决方案。通过将多个 gRPC 服务统一为单个 GraphQL schema,它极大地简化了前端开发复杂度,同时保持了后端的灵活性和独立性。

核心优势总结

特性优势适用场景
统一 Schema简化前端数据获取多微服务环境
自动类型转换减少胶水代码Protobuf 基础架构
灵活的组合性模块化开发大型项目
性能优化FieldMask 支持大数据量场景
生态集成Google Cloud 支持云原生应用

未来发展方向

  • 增强的流式处理能力
  • 更丰富的监控和可观测性集成
  • 对更多协议和数据源的支持
  • 改进的开发工具和调试体验

无论你是正在构建新的微服务系统,还是希望优化现有的分布式架构,Rejoiner 都值得你深入探索。它不仅能提升开发效率,还能为你的系统带来更好的可维护性和扩展性。

开始你的 Rejoiner 之旅,体验统一数据查询的强大魅力!

【免费下载链接】rejoiner Generates a unified GraphQL schema from gRPC microservices and other Protobuf sources 【免费下载链接】rejoiner 项目地址: https://gitcode.com/gh_mirrors/re/rejoiner

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

抵扣说明:

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

余额充值