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 集成困境

在现代微服务架构中,每个服务通常使用 gRPC 和 Protocol Buffers(Protobuf)进行通信。然而,当需要为前端应用提供统一的 API 接口时,开发者面临以下挑战:

  • 协议不一致:gRPC 服务无法直接被浏览器调用
  • 数据聚合困难:需要从多个服务获取数据时,客户端需要发起多次请求
  • Schema 管理复杂:每个服务都有独立的 Protobuf 定义,难以维护统一的 API 契约
  • 开发效率低下:需要手动编写大量的数据转换和聚合代码

Rejoiner 正是为了解决这些问题而生,它能够自动从 gRPC 微服务和 Protobuf 源生成统一的 GraphQL Schema,让开发者能够:

  • ✅ 自动生成 GraphQL 类型定义
  • ✅ 支持查询和变更操作
  • ✅ 实现跨服务的数据关联
  • ✅ 提供灵活的 Schema 修改能力
  • ✅ 支持 gRPC 流式传输

Rejoiner 核心架构解析

mermaid

核心组件说明

组件功能描述使用场景
SchemaModule核心模块,用于生成 GraphQL Schema 部分定义查询、变更和 Schema 修改
@Query注解,标记 GraphQL 查询方法定义数据查询操作
@Mutation注解,标记 GraphQL 变更方法定义数据修改操作
@SchemaModification注解,标记 Schema 修改添加、删除或修改字段
SchemaProviderModuleGuice 模块,组合所有 Schema 部分生成最终的 GraphQL Schema

快速开始:构建你的第一个 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'

基础示例:待办事项应用

1. 定义 Protobuf 消息
syntax = "proto3";

package todo;

message Todo {
  string id = 1;
  string title = 2;
  string description = 3;
  bool completed = 4;
  string creator_email = 5;
}

message ListTodoRequest {
  int32 page_size = 1;
  string page_token = 2;
}

message ListTodoResponse {
  repeated Todo todos = 1;
  string next_page_token = 2;
}

message CreateTodoRequest {
  string title = 1;
  string description = 2;
}
2. 创建查询 Schema 模块
import com.google.api.graphql.rejoiner.Query;
import com.google.api.graphql.rejoiner.SchemaModule;
import com.google.common.util.concurrent.ListenableFuture;

public final class TodoQuerySchemaModule extends SchemaModule {
  
  @Query("listTodos")
  ListenableFuture<ListTodoResponse> listTodos(
      ListTodoRequest request, 
      TodoService todoService) {
    return todoService.listTodos(request);
  }
  
  @Query("getTodo")
  ListenableFuture<Todo> getTodo(
      GetTodoRequest request, 
      TodoService todoService) {
    return todoService.getTodo(request);
  }
}
3. 创建变更 Schema 模块
import com.google.api.graphql.rejoiner.Mutation;
import com.google.api.graphql.rejoiner.SchemaModule;
import com.google.common.util.concurrent.ListenableFuture;

public final class TodoMutationSchemaModule extends SchemaModule {
  
  @Mutation("createTodo")
  ListenableFuture<Todo> createTodo(
      CreateTodoRequest request, 
      TodoService todoService,
      @AuthenticatedUser String email) {
    return todoService.createTodo(request, email);
  }
  
  @Mutation("updateTodo")
  ListenableFuture<Todo> updateTodo(
      UpdateTodoRequest request, 
      TodoService todoService) {
    return todoService.updateTodo(request);
  }
  
  @Mutation("deleteTodo")
  ListenableFuture<Empty> deleteTodo(
      DeleteTodoRequest request, 
      TodoService todoService) {
    return todoService.deleteTodo(request);
  }
}
4. 实现数据关联
import com.google.api.graphql.rejoiner.SchemaModification;
import com.google.api.graphql.rejoiner.SchemaModule;
import com.google.common.util.concurrent.ListenableFuture;

public final class TodoToUserSchemaModule extends SchemaModule {
  
  @SchemaModification(addField = "creator", onType = Todo.class)
  ListenableFuture<User> getTodoCreator(
      UserService userService, 
      Todo todo) {
    return userService.getUserByEmail(todo.getCreatorEmail());
  }
}
5. 配置主模块
import com.google.api.graphql.rejoiner.SchemaProviderModule;
import com.google.inject.AbstractModule;

public final class TodoAppModule extends AbstractModule {
  
  @Override
  protected void configure() {
    // 安装 Schema 提供者模块
    install(new SchemaProviderModule());
    
    // 安装所有 Schema 模块
    install(new TodoQuerySchemaModule());
    install(new TodoMutationSchemaModule());
    install(new TodoToUserSchemaModule());
    
    // 安装服务绑定
    bind(TodoService.class).to(TodoServiceImpl.class);
    bind(UserService.class).to(UserServiceImpl.class);
  }
}

高级特性详解

1. 命名空间管理

对于大型应用,可以使用 @Namespace 注解来组织查询和变更:

@Namespace("library")
public final class LibrarySchemaModule extends SchemaModule {
  
  @Query("books")
  ListenableFuture<ListBooksResponse> listBooks(ListBooksRequest request) {
    // 实现逻辑
  }
  
  @Mutation("createBook") 
  ListenableFuture<Book> createBook(CreateBookRequest request) {
    // 实现逻辑
  }
}

这样生成的 GraphQL Schema 会将操作组织在 library 命名空间下:

query {
  library {
    books(pageSize: 10) {
      title
      author
    }
  }
}

2. Schema 修改 DSL

Rejoiner 提供了强大的 DSL 来修改生成的 Schema:

public final class SchemaModificationsModule extends SchemaModule {
  
  @SchemaModification
  TypeModification removeSensitiveData = 
      Type.find(Todo.getDescriptor())
          .removeField("internal_id")
          .removeField("audit_log");
  
  @SchemaModification  
  TypeModification addCustomField =
      Type.find(Todo.getDescriptor())
          .addField("formatted_date", Scalars.GraphQLString)
          .withDataFetcher(env -> {
            Todo todo = env.getSource();
            return formatDate(todo.getCreatedTime());
          });
}

3. 支持的数据类型

Rejoiner 支持丰富的返回类型:

返回类型描述示例
Message任何 Protobuf 消息Todo
ImmutableList<Message>消息列表ImmutableList<Todo>
ListenableFuture<Message>异步消息ListenableFuture<Todo>
ListenableFuture<ImmutableList<Message>>异步消息列表ListenableFuture<ImmutableList<Todo>>
基本类型Java 基本类型String, Integer, Boolean

4. 字段掩码(Field Masks)支持

Rejoiner 自动生成字段掩码,优化数据传输:

@Query("getUserWithMask")
ListenableFuture<User> getUserWithMask(
    GetUserRequest request,
    @FieldMask List<String> fieldMask) {
  // Rejoiner 会自动处理字段掩码
  return userService.getUser(request, fieldMask);
}

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

项目结构

mermaid

核心实现代码

Protobuf 定义
// book.proto
message Book {
  string id = 1;
  string title = 2;
  string author = 3;
  string shelf_id = 4;
}

message Shelf {
  string id = 1;
  string name = 2;
  string location = 3;
}
数据关联实现
public final class LibrarySchemaModule extends SchemaModule {
  
  // 查询所有书架
  @Query("shelves")
  ListenableFuture<ListShelvesResponse> listShelves(ListShelvesRequest request) {
    return shelfService.listShelves(request);
  }
  
  // 查询特定书架的书籍
  @Query("booksByShelf")
  ListenableFuture<ListBooksResponse> booksByShelf(
      BooksByShelfRequest request) {
    return bookService.listBooksByShelf(request);
  }
  
  // 为书架添加书籍列表字段
  @SchemaModification(addField = "books", onType = Shelf.class)
  ListenableFuture<List<Book>> getShelfBooks(
      BookService bookService, 
      Shelf shelf) {
    BooksByShelfRequest request = BooksByShelfRequest.newBuilder()
        .setShelfId(shelf.getId())
        .build();
    return bookService.listBooksByShelf(request);
  }
  
  // 为书籍添加所属书架字段
  @SchemaModification(addField = "shelf", onType = Book.class)  
  ListenableFuture<Shelf> getBookShelf(
      ShelfService shelfService,
      Book book) {
    GetShelfRequest request = GetShelfRequest.newBuilder()
        .setShelfId(book.getShelfId())
        .build();
    return shelfService.getShelf(request);
  }
}

生成的 GraphQL Schema

type Query {
  shelves(pageSize: Int, pageToken: String): ShelfList
  booksByShelf(shelfId: ID!, pageSize: Int): BookList
}

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

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

type ShelfList {
  shelves: [Shelf!]!
  nextPageToken: String
}

type BookList {
  books: [Book!]!
  nextPageToken: String
}

性能优化与最佳实践

1. 使用 DataLoader 批量处理

public final class LibraryDataLoaderModule extends SchemaModule {
  
  @Provides
  @Singleton
  public DataLoader<String, Shelf> shelfDataLoader(ShelfService shelfService) {
    return DataLoader.newDataLoader(keys -> 
        shelfService.batchGetShelves(keys));
  }
  
  @SchemaModification(addField = "shelf", onType = Book.class)
  public DataLoader<String, Shelf> getBookShelfLoader(
      DataLoader<String, Shelf> shelfLoader,
      Book book) {
    return shelfLoader.load(book.getShelfId());
  }
}

2. 查询优化策略

策略实施方法收益
字段掩码使用 @FieldMask 参数减少网络传输数据量
批量加载实现 DataLoader减少 N+1 查询问题
缓存策略添加缓存层提高重复查询性能
分页查询使用 pageSize/pageToken控制数据量,提高响应速度

3. 错误处理与监控

public final class MonitoringSchemaModule extends SchemaModule {
  
  @Query("listBooks")
  ListenableFuture<ListBooksResponse> listBooksWithMonitoring(
      ListBooksRequest request,
      BookService bookService,
      MetricsService metrics) {
    
    Timer.Context timer = metrics.startTimer("list_books");
    return Futures.transform(
        bookService.listBooks(request),
        response -> {
          timer.stop();
          metrics.recordSuccess("list_books");
          return response;
        },
        MoreExecutors.directExecutor());
  }
}

常见问题与解决方案

Q1: 如何处理循环依赖?

问题:当两个类型相互引用时,GraphQL Schema 会出现循环依赖错误。

解决方案:使用懒加载或中间类型:

@SchemaModification(addField = "shelfInfo", onType = Book.class)
ShelfInfo getShelfInfo(Book book) {
  return ShelfInfo.newBuilder()
      .setShelfId(book.getShelfId())
      .build();
}

// 定义中间类型
message ShelfInfo {
  string shelf_id = 1;
}

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

解决方案:实现自定义标量转换器:

public final class CustomScalarsModule extends SchemaModule {
  
  @Provides
  @Singleton
  public GraphQLScalarType customDateScalar() {
    return GraphQLScalarType.newScalar()
        .name("CustomDate")
        .description("Custom date format")
        .coercing(new Coercing() {
          // 实现序列化和反序列化逻辑
        })
        .build();
  }
}

Q3: 如何实现权限控制?

解决方案:使用注解和拦截器:

@Query("sensitiveData")
@RequiresPermission("admin")
ListenableFuture<SensitiveData> getSensitiveData(
    GetDataRequest request,
    @AuthenticatedUser User user) {
  // 权限检查已在拦截器中完成
  return dataService.getSensitiveData(request);
}

总结与展望

Rejoiner 为微服务架构下的 API 集成提供了优雅的解决方案。通过本教程,你已经掌握了:

  1. 基础概念:理解 Rejoiner 的核心组件和工作原理
  2. 实践技能:能够创建查询、变更和 Schema 修改模块
  3. 高级特性:使用命名空间、DataLoader、字段掩码等优化技术
  4. 实战经验:通过图书馆案例掌握完整开发流程

Rejoiner 的优势在于:

  • 开发效率:自动生成 GraphQL Schema,减少手动编码
  • 维护性:基于 Protobuf 定义,保证 API 契约一致性
  • 灵活性:支持丰富的自定义和扩展能力
  • 性能:内置优化机制,支持大规模应用

随着 GraphQL 在微服务架构中的广泛应用,Rejoiner 这样的工具将成为连接后端服务和前端应用的重要桥梁。建议进一步探索:

  • 与 Apollo Client/Relay 的集成
  • 实时订阅功能的使用
  • 分布式追踪和监控
  • 自动化测试策略

开始使用 Rejoiner,构建更加统一、高效和可维护的微服务 API 体系吧!

【免费下载链接】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、付费专栏及课程。

余额充值