上一篇我们在说网关的时候,遗留了一个问题,就是通过 serviceList
配置子服务无法实现动态更新。同时,各个子服务的 Schema 在合成的时候可能会出错,比如类型重复定义等。
因此,我们需要一个独立的 Schema 合成管理系统来处理 Schema 的合成校验、不同环境的隔离、CI/CD 等工程问题。
首先要说明的是,Apollo 官方提供了一个 Apollo Studioopen in new window 解决方案,可以解决我们本文讨论的问题。缺点是没有提供自托管(self host)方案,需要将 schema 上传到 Apollo 的服务中。
本文来探讨实现一个 Schema 合成管理系统。
在我们这个系统中,存储各个子服务的 Schema,同时提供 Schema 的合并校验能力,将合并后的 Schema 提供给网关使用。
通常来说,服务部署都会依赖 CI/CD 来实现。因此,我们的 Schema 合成管理系统应该与 CI/CD 对接,而不是手动上传 Schema。
Schema 的合成校验
在子服务中,我们可以通过如何 GraphQL 查询获取子服务的 Schema 信息。
query SDL {
_service {
sdl
}
}
2
3
4
5
在上传到 Schema 合成管理系统中后,可以通过如下方法校验各个子服务的 Schema 是否合法。
import { parse } from 'graphql';
import { composeAndValidate } from '@apollo/federation';
// serviceList 为查到的所有子服务的 Schema 信息
const serviceDefinitions = [
{
typeDefs: parse(service1.sdl),
name: service1.name,
},
{
typeDefs: parse(service2.sdl),
name: service2.name,
}
];
// 获取合成结果
const validateResult = composeAndValidate(serviceDefinitions);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
数据字段设计
知道如何获取子服务的 Schema 以及如何进行校验之后,我们来定义系统的数据库表。
首先定义一张存储各个子服务信息的表。
tag
字段可以给每个服务打上不同的标签,方便后续管理。
再来定义每个服务的 Schema 信息表。
在 SDL 表中,除了记录 Schema 的 content
字段以外,我们使用 git_commit_hash
来作为每次发布的版本号,同时记录提交人,方便后续问题的追踪。
为了方便网关拉取合成后的数据,我们单独定义一张表记录合成后的各服务 Schema 信息。
不同的环境都会有网关,因此通过 env
字段记录环境。
与 CI/CD 系统的交互
前面说到,Schema 合成管理系统应该与 CI/CD 对接,杜绝个人上传 Schema 的操作。因此,我们需要定义与 CI/CD 的交过流程。
子服务构建时推送 Schema
如下是子服务构建时与 Schema 合成管理系统的交互时序图。
子服务启动时确定 Schema 是否合法
如下是子服务启动时与 Schema 合成管理系统的交互时序图。
之所以要在子服务启动时才校验 Schema 是否合法,是因为服务部署需要考虑环境,而 CI 在构建打包的时候应该与环境无关,因此,我们在服务启动时做校验。
网关拉取 Schema
网关是对外提供服务的,需要拉取最新可用的全部 Schema。前面的设计中,Schema 合成管理系统的 Running_Config
表记录了当前可用的所有子服务的 Schema。网关只需要从 Schema 合成管理系统中拉取这个信息就可以了。
在拉取到配置以后,可以通过如下方式实现自动更新网关的 Schema 信息。
const graphqlGatewayModule = GraphQLGatewayModule.forRootAsync({
useFactory: async () => ({
server: {
// 其他配置项
path: '/bff/graphql',
},
gateway: {
// 一分钟轮询时间
experimental_pollInterval: 60 * 1000,
experimental_updateServiceDefinitions: updateServicesDefinitions,
}
}),
});
2
3
4
5
6
7
8
9
10
11
12
13
我们将原来的 serviceList
换成 updateServicesDefinitions
,而 updateServicesDefinitions
负责从 Schema 合成管理系统拉取最新的 Schema 信息返回给网关。
async function updateServicesDefinitions() {
const data = await fetch('https://gateway-running-config-endpoint')
const serviceDefinitions = data.map(({ name, url, sdl }) => ({
typeDefs: parse(sdl),
name,
url,
}));
return {
serviceDefinitions,
isNewSchema: true,
};
}
2
3
4
5
6
7
8
9
10
11
12
13
通过这个方法就可以实现网关自动拉取最新的 Schema 信息了。
总结
我们先探索了获取子服务 Schema,并进行合成校验的方法,这是构建 Schema 合成管理系统的技术基石。然后我们设计了实现最简功能所需的数据库表。
完成了 Schema 合成管理系统的内部设计以后,我们对 CI/CD 以及网关如何与 Schema 合成管理系统交互做了定义和说明。
关注微信公众号,获取最新推送~
加微信,深入交流~