Rust GraphQL联邦库apollo-federation的使用,实现高效微服务API聚合与查询

Rust GraphQL联邦库apollo-federation的使用,实现高效微服务API聚合与查询

Apollo Federation

Apollo Federation是一种架构,用于声明性地将API组合成一个统一的图。每个团队可以独立拥有他们的图的一部分,使他们能够自主和增量地交付。

Federation 2是原始Apollo Federation的演进,具有改进的共享所有权模型、增强的类型合并和更简洁的语法,以提供更流畅的开发体验。它向后兼容,不需要对您的子图进行重大更改。

使用

这个crate是Apollo Router内部的,不打算直接使用。

完整示例demo

以下是一个使用apollo-federation构建GraphQL联邦服务的完整示例:

use async_graphql::*;
use apollo_federation::{Schema, FederatedObject, FederatedSchema};

// 定义用户实体
#[derive(SimpleObject)]
struct User {
    id: ID,
    name: String,
    email: String,
}

// 定义产品实体
#[derive(SimpleObject)]
struct Product {
    upc: String,
    name: String,
    price: i32,
}

// 定义用户服务
struct UserQuery;

#[Object]
impl UserQuery {
    #[graphql(entity)]
    async fn find_user_by_id(&self, id: ID) -> Option<User> {
        // 这里应该是数据库查询
        Some(User {
            id,
            name: "John Doe".to_string(),
            email: "john@example.com".to_string(),
        })
    }
}

// 定义产品服务
struct ProductQuery;

#[Object]
impl ProductQuery {
    #[graphql(entity)]
    async fn find_product_by_upc(&self, upc: String) -> Option<Product> {
        // 这里应该是数据库查询
        Some(Product {
            upc,
            name: "Awesome Product".to_string(),
            price: 999,
        })
    }
}

// 组合服务
#[derive(FederatedObject)]
struct Query {
    user: UserQuery,
    product: ProductQuery,
}

#[async_std::main]
async fn main() -> std::io::Result<()> {
    // 创建联邦模式
    let schema = Schema::build(Query {
        user: UserQuery,
        product: ProductQuery,
    })
    .finish();

    // 启动服务器
    println!("Federated service running at http://localhost:8000");
    async_graphql::http::playground_source("/graphql")
        .run()
        .await
}

完整示例代码解析

use async_graphql::*;
use apollo_federation::{Schema, FederatedObject, FederatedSchema};

// 定义用户实体 - 使用SimpleObject宏自动实现GraphQL对象
#[derive(SimpleObject)]
struct User {
    id: ID,           // GraphQL ID类型
    name: String,     // 用户名
    email: String,    // 用户邮箱
}

// 定义产品实体
#[derive(SimpleObject)]
struct Product {
    upc: String,      // 产品统一编码
    name: String,     // 产品名称
    price: i32,       // 产品价格
}

// 用户服务查询结构体
struct UserQuery;

// 为用户服务实现GraphQL查询
#[Object]
impl UserQuery {
    // entity标记表示这是联邦实体解析器
    #[graphql(entity)]
    async fn find_user_by_id(&self, id: ID) -> Option<User> {
        // 实际应用中这里应该查询数据库
        Some(User {
            id,
            name: "John Doe".to_string(),
            email: "john@example.com".to_string(),
        })
    }
}

// 产品服务查询结构体
struct ProductQuery;

// 为产品服务实现GraphQL查询
#[Object]
impl ProductQuery {
    // entity标记表示这是联邦实体解析器
    #[graphql(entity)]
    async fn find_product_by_upc(&self, upc: String) -> Option<Product> {
        // 实际应用中这里应该查询数据库
        Some(Product {
            upc,
            name: "Awesome Product".to_string(),
            price: 999,
        })
    }
}

// 组合服务 - 使用FederatedObject宏标记为联邦查询根
#[derive(FederatedObject)]
struct Query {
    user: UserQuery,       // 用户服务
    product: ProductQuery, // 产品服务
}

// 主函数
#[async_std::main]
async fn main() -> std::io::Result<()> {
    // 创建联邦模式
    let schema = Schema::build(Query {
        user: UserQuery,
        product: ProductQuery,
    })
    .finish();

    // 启动服务器并提供GraphQL Playground
    println!("Federated service running at http://localhost:8000");
    async_graphql::http::playground_source("/graphql")
        .run()
        .await
}

Crate版本控制

apollo-federation crate不遵循Semantic Versioning。任何版本都可能有破坏性的API更改,因为此API预计仅由apollo-router使用。相反,版本号与使用它的apollo-router crate版本完全匹配。

许可证

源代码在本仓库中由Elastic License 2.0覆盖。除非文件头或子目录中的许可证文件指定了其他许可证,否则仓库中的默认许可证是Elastic License 2.0。


1 回复

基于您提供的完整内容,我将给出一个完整的Rust GraphQL联邦实现示例,包含用户服务和产品服务的完整demo代码。

完整联邦服务示例

用户服务 (user_service.rs)

use async_graphql::*;
use async_graphql_apollo_federation::{entity, FederationService, Object};

// 定义User实体
#[derive(SimpleObject)]
struct User {
    id: ID,
    name: String,
    email: String,
}

// 定义查询
struct Query;

#[Object]
impl Query {
    // 获取用户信息
    async fn user(&self, id: ID) -> Option<User> {
        // 模拟数据库查询
        Some(User {
            id,
            name: "John Doe".to_string(),
            email: "john@example.com".to_string(),
        })
    }
}

// 实现联邦服务
#[FederationService]
struct UserService;

impl FederationService for UserService {
    type Query = Query;
    
    fn query(&self) -> Query {
        Query
    }
}

// 实现实体解析器
#[entity]
impl User {
    async fn reference(&self, id: ID) -> Option<User> {
        Some(User {
            id,
            name: "John Doe".to_string(),
            email: "john@example.com".to_string(),
        })
    }
}

// 启动服务
#[tokio::main]
async fn main() {
    let schema = Schema::build(UserService.query(), EmptyMutation, EmptySubscription)
        .extension(async_graphql_apollo_federation::Extension)
        .finish();
    
    println!("用户服务运行在: http://localhost:4001");
    FederationServer::start("0.0.0.0:4001", schema).await.unwrap();
}

产品服务 (product_service.rs)

use async_graphql::*;
use async_graphql_apollo_federation::{entity, FederationService, Object};

// 定义Product实体
#[derive(SimpleObject)]
struct Product {
    id: ID,
    name: String,
    price: f64,
    user_id: ID,  // 关联用户ID
}

// 扩展User实体
#[derive(SimpleObject)]
#[graphql(extends)]
struct User {
    id: ID,
}

// 定义查询
struct Query;

#[Object]
impl Query {
    // 获取产品信息
    async fn product(&self, id: ID) -> Option<Product> {
        // 模拟数据库查询
        Some(Product {
            id,
            name: "Rust Programming Book".to_string(),
            price: 39.99,
            user_id: ID::from("1"),
        })
    }
    
    // 自定义实体解析器
    #[graphql(entity)]
    async fn find_user_by_id(&self, id: ID) -> User {
        User { id }
    }
}

// 实现联邦服务
#[FederationService]
struct ProductService;

impl FederationService for ProductService {
    type Query = Query;
    
    fn query(&self) -> Query {
        Query
    }
}

// 实现实体解析器
#[entity]
impl Product {
    async fn reference(&self, id: ID) -> Option<Product> {
        Some(Product {
            id,
            name: "Rust Programming Book".to_string(),
            price: 39.99,
            user_id: ID::from("1"),
        })
    }
}

// 扩展User实体功能
#[Object]
impl User {
    // 获取用户的所有产品
    async fn products(&self) -> Vec<Product> {
        vec![
            Product {
                id: ID::from("1"),
                name: "Rust Programming Book".to_string(),
                price: 39.99,
                user_id: self.id.clone(),
            }
        ]
    }
}

// 启动服务
#[tokio::main]
async fn main() {
    let schema = Schema::build(ProductService.query(), EmptyMutation, EmptySubscription)
        .extension(async_graphql_apollo_federation::Extension)
        .finish();
    
    println!("产品服务运行在: http://localhost:4002");
    FederationServer::start("0.0.0.0:4002", schema).await.unwrap();
}

网关配置 (gateway.js)

const { ApolloGateway } = require('@apollo/gateway');
const { ApolloServer } = require('apollo-server');

const gateway = new ApolloGateway({
  serviceList: [
    { name: 'users', url: 'http://localhost:4001/graphql' },
    { name: 'products', url: 'http://localhost:4002/graphql' },
  ],
});

const server = new ApolloServer({
  gateway,
  subscriptions: false,
});

server.listen().then(({ url }) => {
  console.log(`联邦网关运行在: ${url}`);
});

运行步骤

  1. 创建两个Rust项目分别实现用户服务和产品服务

  2. 在每个服务的Cargo.toml中添加依赖:

    [dependencies]
    async-graphql = "3.0"
    apollo-federation = "0.3"
    async-graphql-apollo-federation = "3.0"
    tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
    
  3. 分别运行两个服务:

    cargo run --bin user_service
    cargo run --bin product_service
    
  4. 运行网关服务:

    node gateway.js
    

测试查询

通过网关发送GraphQL查询:

query {
  user(id: "1") {
    id
    name
    email
    products {
      id
      name
      price
    }
  }
}

这个联邦查询会:

  1. 通过网关路由到用户服务获取用户基本信息
  2. 通过用户服务中扩展的products字段获取产品信息
  3. 产品服务会处理products字段的查询并返回结果

关键点说明

  1. 每个服务需要实现FederationService trait
  2. 使用#[entity]宏标记实体解析器
  3. 使用#[graphql(extends)]扩展其他服务的实体
  4. 网关负责组合所有服务并对外提供统一API
  5. 服务间通过ID引用关联数据

这个完整示例展示了如何使用Rust的apollo-federation构建一个功能完整的GraphQL联邦系统。

回到顶部