Schema Stitching 与 Federation 差异
解读
国内 PHP 面试里问 GraphQL 的“Schema Stitching 与 Federation 差异”,并不是要你背概念,而是考察:
- 你是否真的在生产环境落地过微服务/多端 BFF(Backend For Frontend);
- 面对多团队并行开发,你如何保证 API 版本零侵入、字段零冲突;
- 当 QPS 涨到万级,你的网关层是继续堆 Stitching 还是迁移到 Federation,如何做灰度、回滚、监控。
一句话:面试官想听你“踩过哪些坑、为什么换方案、PHP 里怎么落地”。
知识点
-
Schema Stitching(模式缝合)
- 核心思想:在网关层把多个子服务的 SDL 字符串手动 merge,resolver 用远程 HTTP 调用(graphql-php + Guzzle)拼接结果。
- 典型 PHP 库:rebing/graphql-laravel + stitcher 插件,或自写 SchemaBuilder。
- 优点:上手快,老项目改造只需加一层网关;缺点:冲突字段需人工 rename,类型重复需手动 transform,上线后“谁改谁翻车”。
-
Federation(联邦)
- 核心思想:各子服务按 Apollo Federation 规范暴露
_entities与_service,网关用 Router(rust 版或 Node.js 版)自动拼 Supergraph,PHP 端只需实现 resolver 级联。 - 官方 PHP 实现:apollo-federation-php(GitHub 2.3k star),与 Laravel/Symfony 无缝集成。
- 优点:字段归属声明式,冲突自动检测,支持分布式追踪;缺点:PHP 进程常驻(RoadRunner/Swoole)才配玩,团队需统一 CI 流程。
- 核心思想:各子服务按 Apollo Federation 规范暴露
-
国内落地差异
- Stitching 在 2018-2020 年电商大促中用得最多,因为 PHP-FPM 存量大、改造小;但 2021 年后阿里、京东、美团逐步切 Federation,解决“商品域涨价、订单域优惠券”字段互踩问题。
- 面试常追问:如何做“热加载”?PHP 没有原生动态 schema,Federation 需把
.graphql文件编译成 OPcache 可缓存的 PHP 数组,再配 Envoy 做 schema 版本灰度。
答案
“我在上一家公司用 PHP 7.4 + Laravel 搭建营销中台,最早采用 Schema Stitching:
- 用 rebing/graphql-laravel 把优惠券、商品、库存三个子服务的 schema 通过
buildSchemaFromSDL()合并,网关层写TypeMerger解决字段冲突; - 上线后发现大促期间商品域加字段
promotionTags,优惠券域也加了同名不同类型,导致网关 500,回滚一次损失 300 万 GMV; - 随后迁移到 Federation:商品服务用 apollo-federation-php 暴露
@key(fields: "skuId"),优惠券服务通过@extends扩展,网关换成 Apollo Router,PHP 端只写 resolver; - 灰度方案:在 Nginx + Consul 做流量 5% 实验,对比 p99 延迟从 120 ms 降到 65 ms,字段冲突降为 0;
- 代码层面,把
.graphqlsdl 预编译成schema.cache.php,OPcache 命中 99%,CPU 降 18%。
结论:Stitching 适合‘一人一把梭’的快速原型;Federation 适合‘多团队、高并发、长期演进’的国内电商场景。”
拓展思考
-
如果公司仍跑 PHP-FPM,无法常驻,Federation 的
__resolveReference每次请求都要重新建连接,怎么优化? → 用 PHP-PM 或 RoadRunner 做进程池,把 DataLoader 池化,连接复用率可提升 6 倍。 -
国内部分云厂商(阿里云 MSE、腾讯云 TSE)已推出“托管 Federation 网关”,但只支持 Node.js/Rust,PHP 子服务如何无缝接入? → 在 CI 阶段把
rover supergraph compose做成 Composer Script,自动上传 schema,PHP 侧零改造即可被聚合。 -
当业务继续拆分出“用户画像”服务,字段重名但含义不同(如
User.name在登录域是昵称,在风控域是实名),Federation 的@tag与@policy如何配合 ACL? → 结合 Laravel Gate 定义字段级策略,网关层通过@tag(name: "internal")统一鉴权,实现“同样字段、不同权限”的细粒度隔离。