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。
基于您提供的完整内容,我将给出一个完整的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}`);
});
运行步骤
-
创建两个Rust项目分别实现用户服务和产品服务
-
在每个服务的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"] }
-
分别运行两个服务:
cargo run --bin user_service cargo run --bin product_service
-
运行网关服务:
node gateway.js
测试查询
通过网关发送GraphQL查询:
query {
user(id: "1") {
id
name
email
products {
id
name
price
}
}
}
这个联邦查询会:
- 通过网关路由到用户服务获取用户基本信息
- 通过用户服务中扩展的products字段获取产品信息
- 产品服务会处理products字段的查询并返回结果
关键点说明
- 每个服务需要实现
FederationService
trait - 使用
#[entity]
宏标记实体解析器 - 使用
#[graphql(extends)]
扩展其他服务的实体 - 网关负责组合所有服务并对外提供统一API
- 服务间通过ID引用关联数据
这个完整示例展示了如何使用Rust的apollo-federation构建一个功能完整的GraphQL联邦系统。