20 KiB
17 - 服务端迁移交接文档
面向对象:新加入的服务端/运维同事。
目标:说明 TH1 当前服务器架构、使用的阿里云服务、各服务怎么协作,以及完整迁移时需要迁移和验证什么。
更新时间:2026-06-30。
事实来源:Tools/OSS/game-upload-function/、Unity/Assets/Scripts/TH1_Logic/Oss/、MD/GameMDFramework/15-服务端-GameUploadFunction.md。
1. 当前服务端定位
TH1 目前没有传统意义上的常驻游戏逻辑服务器。回合逻辑、联机同步和战斗结算主要在 Unity 客户端侧完成,联机链路依赖 Steam 相关能力。当前阿里云后端的核心职责是:
- 验证 Steam 玩家身份。
- 为客户端签发有限权限的 OSS 上传凭证。
- 让客户端把对局数据、统计数据、玩家问题汇报、问卷答卷等文件直接上传到 OSS。
- 使用 Tablestore 做短时缓存,减少 Steam Web API 和 STS 调用次数。
- 提供开发者本地工具从 OSS 拉取数据并查看。
因此,迁移重点不是迁移一套业务 API 集群,而是迁移“函数计算 + OSS + STS/RAM + Tablestore + 硬编码客户端入口”的上传基础设施。
2. 一页架构图
flowchart TD
Unity["Unity 客户端<br/>OssManager / StsTokenService"] -->|POST JSON<br/>steamId, steamAppId, authTicket, type, version| FC["阿里云函数计算<br/>game-upload-function<br/>Node.js, port 9000"]
FC -->|AuthenticateUserTicket| Steam["Steam Web API"]
FC -->|getRow / putRow| OTS["阿里云 Tablestore<br/>Players 表"]
FC -->|AssumeRole| STS["阿里云 STS / RAM Role"]
STS --> FC
FC -->|STS + Post Policy<br/>exact objectKey| Unity
Unity -->|PostObject multipart/form-data| OSS["阿里云 OSS<br/>Bucket: th1-oss<br/>endpoint: oss-cn-shanghai.aliyuncs.com"]
Tools["Unity 编辑器工具 / Python 查看器"] -->|ListObjects / GetObject| OSS
关键点:
- Function Compute 不接收游戏数据文件,只返回上传凭证。
- 文件上传是
Unity -> OSS直传。 - STS 和 Post Policy 都限制到一个精确
objectKey,不是给客户端开放整个 Bucket 写权限。 - Tablestore 的数据主要是短时缓存,不是永久业务数据源。
3. 使用的阿里云服务
| 服务 | 当前用途 | 迁移时要做什么 |
|---|---|---|
| 函数计算 Function Compute | 承载 game-upload-function HTTP 服务。公开入口当前硬编码在 Unity:https://get-sts-token-qltjykaafr.cn-shanghai.fcapp.run。 |
新建或迁移函数应用,Node.js 运行时需支持 >=18,启动 index.js,监听端口 9000。若 URL 变化,要同步客户端代码或通过域名/代理保持老 URL 可用。 |
| OSS 对象存储 | 存放玩家上传文件。当前默认 Endpoint 是 oss-cn-shanghai.aliyuncs.com,工具默认 Bucket 是 th1-oss,服务端实际 Bucket 来自 BUCKET_NAME。 |
创建目标 Bucket,复制全部对象,保持 objectKey 不变。重点前缀包括版本根目录、common/、collect/、bugreport/、multilingualreport/、questionnaire/。 |
| STS | 函数调用 AssumeRole 获取临时凭证。临时凭证有效期 900 秒。 |
创建/迁移 RAM Role,允许被函数使用的 RAM 身份扮演;Role 本身需要允许对目标 Bucket 执行 oss:PutObject,函数再用 inline policy 缩到单个 objectKey。 |
| RAM | 保存函数调用 STS、Tablestore 的 AccessKey 身份,以及被 STS 扮演的 Role。 | 重建最小权限 RAM 用户/角色策略。不要把 AccessKey、Secret、Steam API Key 写入仓库或文档。 |
| Tablestore | Players 表,主键 PlayerId。缓存 Steam 身份和 STS 上传凭证。 |
创建实例和 Players 表。缓存都是分钟级,通常可以不迁移历史行,空表上线只会让首次请求重新验证/签发。 |
当前未看到 ECS、RDS、Redis、CDN、独立 API Gateway 作为这条链路的必需依赖。
4. 代码和文档入口
服务端源码:
Tools/OSS/game-upload-function/index.js:后端事实来源,包含接口、缓存、Steam 验证、STS 签发、OSS Post Policy。Tools/OSS/game-upload-function/package.json:Node 依赖和运行时要求。Tools/OSS/game-upload-function/README.md:函数服务说明。
架构文档:
MD/GameMDFramework/15-服务端-GameUploadFunction.md:接口和流程的详细说明。- 本文档:迁移和交接总览。
Unity 运行时客户端:
Unity/Assets/Scripts/TH1_Logic/Oss/OssManager.cs:上传入口、Steam 预校验调度、各上传类型调用。Unity/Assets/Scripts/TH1_Logic/Oss/StsTokenService.cs:向函数计算请求 STS 凭证。Unity/Assets/Scripts/TH1_Logic/Oss/OssUploadService.cs:用 PostObject 直传 OSS。Unity/Assets/Scripts/TH1_Logic/Oss/PlayerBugReportService.cs:玩家 Bug 汇报 zip 打包。
开发者工具:
Unity/Assets/Scripts/TH1_Logic/Editor/OssEditorWindow.cs:Unity 编辑器内 OSS 拉取、上传测试、统计导出入口。Tools/PlayerBugViewer/:拉取和查看bugreport/。Tools/PlayerMultilingualReportViewer/:拉取和查看multilingualreport/。Tools/PlayerQuestionnaireViewer/:拉取和查看questionnaire/。
5. Function Compute 服务
当前服务是一个 Node.js CommonJS HTTP 服务:
- 入口文件:
Tools/OSS/game-upload-function/index.js - 监听端口:
9000 - Node 版本:
>=18.0.0 - 依赖:
@alicloud/pop-core:调用 STSAssumeRoletablestore:读写 Tablestore
函数接收 POST / JSON 请求,最大请求体 1024 字节。OPTIONS 用于 CORS 预检,其他非 POST 请求返回 405。
5.1 必需环境变量
这些值必须从阿里云/Steam 控制台配置到函数计算环境变量,不得提交到仓库:
| 变量 | 用途 |
|---|---|
ACCESS_KEY_ID |
函数使用的 RAM AccessKey ID。 |
ACCESS_KEY_SECRET |
函数使用的 RAM AccessKey Secret。 |
ROLE_ARN |
STS AssumeRole 的目标 RAM Role ARN。 |
BUCKET_NAME |
目标 OSS Bucket。 |
STEAM_API_KEY |
Steam Web API Key。 |
OTS_ENDPOINT |
Tablestore Endpoint。 |
OTS_INSTANCE |
Tablestore 实例名。 |
兼容/可选变量:
| 变量 | 用途 |
|---|---|
STEAM_APP_ID |
老客户端未传 steamAppId 时的默认 AppID。生产环境建议显式配置。 |
STEAM_ALLOWED_APP_IDS 或 STEAM_APP_IDS |
允许的 Steam AppID 列表。未配置时默认包含 3774440,3887950。 |
5.2 接口请求字段
| 字段 | 必填 | 说明 |
|---|---|---|
steamId |
是 | 玩家 Steam ID。 |
steamAppId |
否 | 客户端当前 Steam AppID。新客户端会传,老客户端可能不传。 |
authTicket |
条件必填 | Steam Auth Ticket。身份缓存未命中时必需。 |
version |
否 | 客户端版本。不传时使用 common 路径段。 |
action |
否 | steamauth 表示只做 Steam 身份预校验。 |
type |
否 | 上传类型,默认 ossdata。 |
上传凭证响应直接映射 Unity StsCredentials:
{
"accessKeyId": "STS.xxx",
"accessKeySecret": "xxx",
"securityToken": "xxx",
"steamAppId": "3774440",
"endpoint": "oss-cn-shanghai.aliyuncs.com",
"bucket": "th1-oss",
"objectKey": "0.7.5/76561198xxx/1780000000000.dat",
"policy": "base64...",
"signature": "hmac-sha1...",
"expiresIn": 900
}
6. 运行时请求流程
6.1 Steam 预校验
客户端 Steam 就绪后会周期性调用:
{
"action": "steamauth",
"steamId": "...",
"steamAppId": "...",
"authTicket": "...",
"version": "0.7.5"
}
服务端流程:
- 查询 Tablestore 是否已有
{steamId}#steamauth#{steamAppId}身份缓存。 - 缓存命中则直接返回剩余秒数。
- 缓存未命中则调用 Steam Web API
ISteamUserAuth/AuthenticateUserTicket/v1/。 - 校验通过后写入 Tablestore,缓存 10 分钟。
这个缓存只代表玩家身份近期通过验证,不直接授权 OSS 写入。
6.2 上传凭证签发
客户端请求 type=ossdata|collectdata|bugreport|multilingualreport|questionnaire。
服务端流程:
- 对可缓存类型先查 STS 上传凭证缓存。
- 如果未命中,检查 Steam 身份缓存。
- 身份缓存也未命中时,用
authTicket调 Steam Web API。 - 调阿里云 STS
AssumeRole获取临时凭证。 - 按上传类型和版本生成一个精确 OSS
objectKey。 - 用 STS 临时密钥生成 Post Policy 和 HMAC-SHA1 签名。
- 可缓存类型写回 Tablestore。
- 返回凭证给 Unity。
- Unity 使用 multipart/form-data 直接 POST 到
https://{bucket}.{endpoint}。
7. 上传类型和 OSS 路径
| type | 文件 | 是否缓存 STS | 大小限制 | OSS 路径 |
|---|---|---|---|---|
ossdata |
.dat |
是 | 3 MB | {version}/{steamId}/{timestamp}.dat 或 common/{steamId}/{timestamp}.dat |
collectdata |
.dat |
是 | 3 MB | collect/{version}/{steamId}/{timestamp}.dat 或 collect/common/{steamId}/{timestamp}.dat |
bugreport |
.zip |
否 | 10 MB | bugreport/{version}/{steamId}/{timestamp}-{random}.zip |
multilingualreport |
.zip |
否 | 1 MB | multilingualreport/{version}/{steamId}/{timestamp}-{random}.zip |
questionnaire |
.json |
否 | 512 KB | questionnaire/{version}/{steamId}/{timestamp}-{random}.json |
迁移 OSS 时不要只复制显式前缀。ossdata 直接放在 Bucket 根部的版本目录下,例如 0.7.5/{steamId}/{timestamp}.dat,因此最稳妥策略是整桶复制,保持 objectKey 完全一致。
8. Tablestore 数据模型
Tablestore 表:
- 表名:
Players - 主键:
PlayerId,字符串类型
8.1 Steam 身份缓存
主键:
{steamId}#steamauth#{steamAppId}
字段:
verifiedAt:验证通过时间,毫秒时间戳。authExpireAt:身份缓存过期时间,毫秒时间戳。steamAppId:验证使用的 Steam AppID。version:客户端版本。
有效期:10 分钟。
8.2 STS 上传凭证缓存
主键:
{steamId}#{type}#{steamAppId}
字段:
accessKeyIdaccessKeySecretsecurityTokenendpointbucketobjectKeypolicysignatureissuedAtstsExpireAtsteamAppIdversion
命中条件:
- 请求版本和缓存版本一致。
- 签发时间在 5 分钟内。
- STS 剩余有效期大于 2 分钟。
bugreport、multilingualreport、questionnaire 不使用 STS 缓存,因为每次提交必须生成唯一 objectKey,避免覆盖。
迁移建议:Tablestore 只存短时缓存,可以在新环境创建空表。迁移历史缓存意义不大。
9. RAM / STS 权限模型
当前函数使用环境变量里的 ACCESS_KEY_ID / ACCESS_KEY_SECRET:
- 调用 Tablestore
getRow/putRow。 - 调用 STS
AssumeRole。
STS 临时凭证来源于 ROLE_ARN,签发时会附带 inline policy:
{
"Version": "1",
"Statement": [
{
"Effect": "Allow",
"Action": ["oss:PutObject"],
"Resource": ["acs:oss:*:*:{BUCKET_NAME}/{objectKey}"]
}
]
}
迁移时至少需要两层权限:
- 函数自身使用的 RAM 身份:
- 可以
sts:AssumeRole到ROLE_ARN。 - 可以读写目标 Tablestore
Players表。
- 可以
- 被扮演的 RAM Role:
- 基础权限允许对目标 Bucket 执行
oss:PutObject。 - 实际下发给客户端的权限由 inline policy 缩小到单个 objectKey。
- 基础权限允许对目标 Bucket 执行
不要为了省事把客户端 STS 权限放宽到整个 Bucket。当前安全边界依赖“STS exact objectKey + Post Policy exact key + 文件大小限制”共同生效。
10. OSS 读写和本地工具
客户端写入:
- Unity 先向函数计算拿
StsCredentials。 - 再用
OssUploadService直传到https://{bucket}.{endpoint}。 - multipart 字段包括
key、OSSAccessKeyId、policy、Signature、x-oss-security-token,最后是file。
开发者读取:
- Unity
Tools/Oss 编辑器可以从collect/拉取统计数据到Tools/OSS/Data/。 Tools/PlayerBugViewer从bugreport/拉取 zip。Tools/PlayerMultilingualReportViewer从multilingualreport/拉取 zip。Tools/PlayerQuestionnaireViewer从questionnaire/拉取 JSON。
这些本地 Data/ 目录是开发者缓存,不是迁移源。迁移应以 OSS Bucket 为准。
11. 客户端硬编码入口
当前函数 URL 在以下代码里硬编码:
Unity/Assets/Scripts/TH1_Logic/Oss/OssManager.csUnity/Assets/Scripts/TH1_Logic/Editor/OssEditorWindow.csUnity/Assets/Scripts/TH1_Logic/Editor/SteamEditorWindow.cs
当前 URL:
https://get-sts-token-qltjykaafr.cn-shanghai.fcapp.run
迁移风险:
- 已发布老客户端不会自动知道新函数 URL。
- 如果直接换新的 fcapp.run 地址,老版本上传会失败。
推荐策略:
- 如果可以,给函数计算接入项目自有域名,并让客户端以后只写自有域名。
- 迁移期间保留旧函数或让旧 URL 代理到新环境,直到不再需要兼容老客户端。
- 新版本客户端再修改
FunctionUrl,并通过 Steam 上传流程测试器验证。
12. 完整迁移步骤
12.1 迁移前盘点
从旧环境记录以下信息,记录时不要把密钥值写入仓库:
- Function Compute:
- 地域、函数/应用名、运行时、启动命令、端口、HTTP 触发器、公开 URL。
- 环境变量名称和值来源。
- 日志查看位置和错误报警方式。
- OSS:
- Bucket 名称、地域、Endpoint。
- 对象总量、总大小、关键前缀。
- 是否配置生命周期、跨域、版本控制、服务端加密。
- RAM / STS:
- 函数使用的 RAM 身份。
ROLE_ARN对应角色。- 角色信任策略和权限策略。
- Tablestore:
- 实例名、Endpoint、表
Players的主键结构。
- 实例名、Endpoint、表
- Steam:
- Steam Web API Key 管理账号。
- 允许的 AppID:当前默认兼容
3774440、3887950。
- Unity 客户端:
- 当前发布版本是否还能接受修改函数 URL。
- 是否需要保留旧 URL 兼容历史版本。
12.2 新环境资源创建
建议优先保持同地域 cn-shanghai,减少 Endpoint、延迟和配置差异。
- 创建 OSS Bucket。
- 名称可以沿用或换新,但如果换新,要同步
BUCKET_NAME和工具默认值。 - Endpoint 目前按
oss-cn-shanghai.aliyuncs.com返回给客户端。
- 名称可以沿用或换新,但如果换新,要同步
- 创建 Tablestore 实例。
- 创建
Players表。 - 主键:
PlayerId,字符串。
- 创建
- 创建 RAM 身份和 Role。
- 函数 RAM 身份:允许
sts:AssumeRole,允许访问 TablestorePlayers。 - STS Role:允许 OSS
PutObject到目标 Bucket,最终权限由 inline policy 缩小。
- 函数 RAM 身份:允许
- 创建 Function Compute 应用/函数。
- 上传
Tools/OSS/game-upload-function/代码包。 - 安装依赖或随包部署
node_modules,以目标平台部署规范为准。 - 启动
node index.js。 - 监听端口 9000。
- 配置 HTTP 触发器。
- 上传
- 配置函数环境变量。
- 填入第 5.1 节列出的必需变量。
- 显式配置
STEAM_APP_ID和允许 AppID 列表,避免老客户端行为不确定。
12.3 OSS 数据迁移
必须保持 objectKey 不变。迁移对象包括:
0.x.x/{steamId}/{timestamp}.dat等版本根目录。common/collect/bugreport/multilingualreport/questionnaire/
推荐做法:
- 使用阿里云官方迁移工具或
ossutil整桶复制。 - 复制后按前缀抽样比对对象数量和文件大小。
- 不要通过 Git 或本地
Tools/OSS/Data反向恢复 OSS;本地目录只是缓存。
12.4 Tablestore 迁移
通常不迁移旧缓存:
- Steam 身份缓存只有 10 分钟。
- STS 上传凭证缓存最多复用 5 分钟,STS 自身 15 分钟过期。
- 空表上线后,第一次请求会重新走 Steam 验证和 STS 签发。
只有在做无感切换且非常在意首次请求延迟时,才考虑导出导入缓存。即便不迁移,功能正确性不受影响。
12.5 切流
优先选择可回滚方案:
- 新函数先用独立测试 URL 验证完整链路。
- 新 OSS Bucket 先用测试客户端验证上传和拉取。
- 如果使用自有域名,切 CNAME/路由到新函数。
- 如果必须改 Unity 硬编码 URL,先发测试包,用
Tools/Steam 上传流程测试器验证。 - 保留旧函数和旧 Bucket 一段时间,直到确认没有老客户端继续写入。
12.6 回滚
回滚条件:
- 大量
STS request failed。 Steam verification failed异常升高。- OSS PostObject 403/Policy 失败。
- 查看器无法拉取新对象。
回滚方式:
- 域名切回旧函数。
- 或客户端测试包退回旧
FunctionUrl。 - OSS 写入如果已经分散到新旧 Bucket,需要按时间窗口补复制新 Bucket 新增对象回旧 Bucket。
13. 上线验证清单
13.1 本地代码检查
在函数目录运行:
cd Tools/OSS/game-upload-function
npm run check
该命令会执行:
node -c index.jsnode check-upload-contract.js
13.2 云端接口验证
需要 Steam 登录态和真实 Auth Ticket 的测试,建议用 Unity 的 Tools/Steam 上传流程测试器:
action=steamauth预校验成功。type=ossdata上传成功。type=collectdata上传成功。type=bugreport返回bugreport/...zip且每次 key 不同。type=multilingualreport返回multilingualreport/...zip且每次 key 不同。type=questionnaire返回questionnaire/...json且每次 key 不同。- 错误 objectKey 上传被 OSS 拒绝。
- 超大文件被客户端或 OSS Policy 拒绝。
13.3 OSS 验证
- OSS 控制台能看到新对象。
collect/可以被 Unity OSS 编辑器拉取。bugreport/可以被 PlayerBugViewer 拉取和解析。multilingualreport/可以被对应查看器拉取和解析。questionnaire/可以被对应查看器拉取和解析。
13.4 日志验证
Function Compute 日志里应能看到:
[服务启动][请求开始][Steam验证][STS签发][PostPolicy][请求完成]
异常日志要重点看:
- 缺少环境变量。
- Steam API 请求超时或
Ticket for other app。 - Tablestore 读写失败。
- STS
AssumeRole失败。 - OSS PostObject 403。
14. 常见问题定位
| 现象 | 优先检查 |
|---|---|
| STS 请求 500 | 函数环境变量、RAM 权限、ROLE_ARN、Tablestore Endpoint/实例名。 |
| STS 请求 403,Steam 验证失败 | STEAM_API_KEY、steamAppId、允许 AppID、客户端生成的 Auth Ticket 是否属于该 App。 |
Ticket for other app |
当前客户端 AppID 与服务端允许列表不一致,或测试包/正式包 Steam AppID 混用。 |
| OSS PostObject 403 | objectKey 是否被客户端改动,Post Policy 是否过期,Bucket/Endpoint 是否匹配,STS Role 是否有 PutObject。 |
| 上传大文件失败 | 对应类型大小限制:普通/collect 3MB,bugreport 10MB,multilingualreport 1MB,questionnaire 512KB。 |
| 新环境首次上传变慢 | Tablestore 缓存为空是正常现象;第一次会重新调用 Steam 和 STS。 |
| 老版本客户端上传失败 | 检查旧 FunctionUrl 是否仍可用,或是否已发布包含新 URL 的客户端。 |
15. 交接结论
服务端同事需要优先理解以下事实:
- 当前后端是“上传授权服务”,不是游戏权威服务器。
- Function Compute 只签发凭证,不保存玩家文件。
- OSS 是永久文件数据源,迁移时要整桶/全前缀保留 objectKey。
- Tablestore 是短时缓存,表结构必须有,但缓存行通常不用迁移。
- RAM / STS 是安全核心,不能放宽到全桶长期写权限。
- 函数 URL 已硬编码到客户端,迁移切流必须考虑老版本兼容。
- 任何迁移验证都必须覆盖 Steam 预校验、STS 签发、OSS 直传、开发者查看器拉取四段链路。