TH1/MD/GameMDFramework/17-服务端迁移交接文档.md

20 KiB
Raw Blame History

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 服务。公开入口当前硬编码在 Unityhttps://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.jsonNode 依赖和运行时要求。
  • 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.csUnity 编辑器内 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:调用 STS AssumeRole
    • tablestore:读写 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_IDSSTEAM_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"
}

服务端流程:

  1. 查询 Tablestore 是否已有 {steamId}#steamauth#{steamAppId} 身份缓存。
  2. 缓存命中则直接返回剩余秒数。
  3. 缓存未命中则调用 Steam Web API ISteamUserAuth/AuthenticateUserTicket/v1/
  4. 校验通过后写入 Tablestore缓存 10 分钟。

这个缓存只代表玩家身份近期通过验证,不直接授权 OSS 写入。

6.2 上传凭证签发

客户端请求 type=ossdata|collectdata|bugreport|multilingualreport|questionnaire

服务端流程:

  1. 对可缓存类型先查 STS 上传凭证缓存。
  2. 如果未命中,检查 Steam 身份缓存。
  3. 身份缓存也未命中时,用 authTicket 调 Steam Web API。
  4. 调阿里云 STS AssumeRole 获取临时凭证。
  5. 按上传类型和版本生成一个精确 OSS objectKey
  6. 用 STS 临时密钥生成 Post Policy 和 HMAC-SHA1 签名。
  7. 可缓存类型写回 Tablestore。
  8. 返回凭证给 Unity。
  9. Unity 使用 multipart/form-data 直接 POST 到 https://{bucket}.{endpoint}

7. 上传类型和 OSS 路径

type 文件 是否缓存 STS 大小限制 OSS 路径
ossdata .dat 3 MB {version}/{steamId}/{timestamp}.datcommon/{steamId}/{timestamp}.dat
collectdata .dat 3 MB collect/{version}/{steamId}/{timestamp}.datcollect/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}

字段:

  • accessKeyId
  • accessKeySecret
  • securityToken
  • endpoint
  • bucket
  • objectKey
  • policy
  • signature
  • issuedAt
  • stsExpireAt
  • steamAppId
  • version

命中条件:

  • 请求版本和缓存版本一致。
  • 签发时间在 5 分钟内。
  • STS 剩余有效期大于 2 分钟。

bugreportmultilingualreportquestionnaire 不使用 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}"]
    }
  ]
}

迁移时至少需要两层权限:

  1. 函数自身使用的 RAM 身份:
    • 可以 sts:AssumeRoleROLE_ARN
    • 可以读写目标 Tablestore Players 表。
  2. 被扮演的 RAM Role
    • 基础权限允许对目标 Bucket 执行 oss:PutObject
    • 实际下发给客户端的权限由 inline policy 缩小到单个 objectKey。

不要为了省事把客户端 STS 权限放宽到整个 Bucket。当前安全边界依赖“STS exact objectKey + Post Policy exact key + 文件大小限制”共同生效。


10. OSS 读写和本地工具

客户端写入:

  • Unity 先向函数计算拿 StsCredentials
  • 再用 OssUploadService 直传到 https://{bucket}.{endpoint}
  • multipart 字段包括 keyOSSAccessKeyIdpolicySignaturex-oss-security-token,最后是 file

开发者读取:

  • Unity Tools/Oss 编辑器 可以从 collect/ 拉取统计数据到 Tools/OSS/Data/
  • Tools/PlayerBugViewerbugreport/ 拉取 zip。
  • Tools/PlayerMultilingualReportViewermultilingualreport/ 拉取 zip。
  • Tools/PlayerQuestionnaireViewerquestionnaire/ 拉取 JSON。

这些本地 Data/ 目录是开发者缓存,不是迁移源。迁移应以 OSS Bucket 为准。


11. 客户端硬编码入口

当前函数 URL 在以下代码里硬编码:

  • Unity/Assets/Scripts/TH1_Logic/Oss/OssManager.cs
  • Unity/Assets/Scripts/TH1_Logic/Editor/OssEditorWindow.cs
  • Unity/Assets/Scripts/TH1_Logic/Editor/SteamEditorWindow.cs

当前 URL

https://get-sts-token-qltjykaafr.cn-shanghai.fcapp.run

迁移风险:

  • 已发布老客户端不会自动知道新函数 URL。
  • 如果直接换新的 fcapp.run 地址,老版本上传会失败。

推荐策略:

  1. 如果可以,给函数计算接入项目自有域名,并让客户端以后只写自有域名。
  2. 迁移期间保留旧函数或让旧 URL 代理到新环境,直到不再需要兼容老客户端。
  3. 新版本客户端再修改 FunctionUrl,并通过 Steam 上传流程测试器验证。

12. 完整迁移步骤

12.1 迁移前盘点

从旧环境记录以下信息,记录时不要把密钥值写入仓库:

  • Function Compute
    • 地域、函数/应用名、运行时、启动命令、端口、HTTP 触发器、公开 URL。
    • 环境变量名称和值来源。
    • 日志查看位置和错误报警方式。
  • OSS
    • Bucket 名称、地域、Endpoint。
    • 对象总量、总大小、关键前缀。
    • 是否配置生命周期、跨域、版本控制、服务端加密。
  • RAM / STS
    • 函数使用的 RAM 身份。
    • ROLE_ARN 对应角色。
    • 角色信任策略和权限策略。
  • Tablestore
    • 实例名、Endpoint、表 Players 的主键结构。
  • Steam
    • Steam Web API Key 管理账号。
    • 允许的 AppID当前默认兼容 37744403887950
  • Unity 客户端:
    • 当前发布版本是否还能接受修改函数 URL。
    • 是否需要保留旧 URL 兼容历史版本。

12.2 新环境资源创建

建议优先保持同地域 cn-shanghai,减少 Endpoint、延迟和配置差异。

  1. 创建 OSS Bucket。
    • 名称可以沿用或换新,但如果换新,要同步 BUCKET_NAME 和工具默认值。
    • Endpoint 目前按 oss-cn-shanghai.aliyuncs.com 返回给客户端。
  2. 创建 Tablestore 实例。
    • 创建 Players 表。
    • 主键:PlayerId,字符串。
  3. 创建 RAM 身份和 Role。
    • 函数 RAM 身份:允许 sts:AssumeRole,允许访问 Tablestore Players
    • STS Role允许 OSS PutObject 到目标 Bucket最终权限由 inline policy 缩小。
  4. 创建 Function Compute 应用/函数。
    • 上传 Tools/OSS/game-upload-function/ 代码包。
    • 安装依赖或随包部署 node_modules,以目标平台部署规范为准。
    • 启动 node index.js
    • 监听端口 9000。
    • 配置 HTTP 触发器。
  5. 配置函数环境变量。
    • 填入第 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 切流

优先选择可回滚方案:

  1. 新函数先用独立测试 URL 验证完整链路。
  2. 新 OSS Bucket 先用测试客户端验证上传和拉取。
  3. 如果使用自有域名,切 CNAME/路由到新函数。
  4. 如果必须改 Unity 硬编码 URL先发测试包Tools/Steam 上传流程测试器 验证。
  5. 保留旧函数和旧 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.js
  • node 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 请求 403Steam 验证失败 STEAM_API_KEYsteamAppId、允许 AppID、客户端生成的 Auth Ticket 是否属于该 App。
Ticket for other app 当前客户端 AppID 与服务端允许列表不一致,或测试包/正式包 Steam AppID 混用。
OSS PostObject 403 objectKey 是否被客户端改动Post Policy 是否过期Bucket/Endpoint 是否匹配STS Role 是否有 PutObject。
上传大文件失败 对应类型大小限制:普通/collect 3MBbugreport 10MBmultilingualreport 1MBquestionnaire 512KB。
新环境首次上传变慢 Tablestore 缓存为空是正常现象;第一次会重新调用 Steam 和 STS。
老版本客户端上传失败 检查旧 FunctionUrl 是否仍可用,或是否已发布包含新 URL 的客户端。

15. 交接结论

服务端同事需要优先理解以下事实:

  1. 当前后端是“上传授权服务”,不是游戏权威服务器。
  2. Function Compute 只签发凭证,不保存玩家文件。
  3. OSS 是永久文件数据源,迁移时要整桶/全前缀保留 objectKey。
  4. Tablestore 是短时缓存,表结构必须有,但缓存行通常不用迁移。
  5. RAM / STS 是安全核心,不能放宽到全桶长期写权限。
  6. 函数 URL 已硬编码到客户端,迁移切流必须考虑老版本兼容。
  7. 任何迁移验证都必须覆盖 Steam 预校验、STS 签发、OSS 直传、开发者查看器拉取四段链路。