FlutterPatch:把 Shorebird 热更新的控制面搬回自己服务器
之前写过几篇 Shorebird 相关的东西,大概就是国内下载补丁、自己接 Appwrite 存补丁这些。后来又在项目里搞过 Petrel(雨燕)那套 Web 热更。
这篇文章说的是另一条线:Dart 代码还是走 Shorebird 那套补丁能力,但是检查补丁、存制品、发布回滚,全部放到自己服务器上。 我把这套东西叫 FlutterPatch。要用的话去官网下安装脚本,拿授权再装。网站在文末。
起因
Shorebird 本身能用。引擎和补丁格式不用我重新发明,冷启动之后 Dart 补丁就能生效,线上救急修 BUG 够用。
但是我们实际用下来,卡点往往不在「能不能打补丁」,而在:
- 补丁检查和制品默认走官方云,内网 / 数据出域说不清楚
- 有时候就想换张图、换点资源,不一定非要跟代码补丁绑死
- 发补丁之前想先知道这次改动能不能热更,别打完才发现必须整包
所以 FlutterPatch 干的事情很窄:设备端继续用开源的 Shorebird updater,把 base_url 指到你自己部署的控制面。控制面负责 check、制品、后台、回滚这些。
不是 Fair / MXFlutter 那种动态化,也不是我之前研究过的 AST + DSL 无侵入那套。引擎和补丁格式我没去跟官方比快慢,那一层就是 Shorebird 的。真正比官方强的是控制面这一截,尤其是国内落地。
比官方强在哪
官方 Shorebird 的 Dart 补丁能力够用,这个我认。引擎和补丁格式没去比快慢。下面是实际用官方云觉得不够、后来自己补上的。
| Shorebird 官方云 | FlutterPatch | |
|---|---|---|
| 补丁检查 / 制品 | 走 api.shorebird.dev,补丁在官方侧 |
全部打你自己的 base_url,文件在本地盘或 MinIO |
| 国内 / 内网 | 以前补丁在谷歌存储,国内最后一步经常下不下来;现在换了官方 CDN,数据和 check 还是他们的云 | 手机不访问官方 API,内网、等保、数据不出域能过 |
| 资源热更 | 主要修 Dart,换图基本还是发版 | 资源包单独下发,图片不用跟代码补丁绑死,往往不用等冷启动 |
| 能不能热更 | 打完补丁、真机不生效再查 | check-ota 发版前对比基线,字体这类 unsupported 直接拦住,必须整包 |
| 国内落地方式 | 我 2023 年是向官方要地址,再把 dlc.vmcode 拷进沙盒改 patches_state.json,能验证,上不了生产 |
updater 认 base_url,协议还是 check / events,不用改沙盒 |
| 安装量 | 走官方账号和计费,安装量在他们那边 | 自搭建默认不按月安装量、月流量掐新补丁,授权看应用数和节点 |
| Dart 补丁生效 | 冷启动 | 一样,冷启动,这点没吹 |
| 灰度 | 百分比放量 | 现在 check 不看百分比,用设备白名单先打测试机。适合内测,不是百分比的替代 |
| 引擎构建包 CDN | Shorebird 官方源 | 现阶段还有可能用到官方源,补丁本身不走他们 |
整体是怎么串起来的
可以大概分成几块:
- 手机上的 updater:还是 Shorebird 开源那套(
shorebird_code_push) - 控制面:检查该下哪个补丁、存文件、Dashboard、回滚(安装脚本部署)
- CLI:
flutterpatch,用来打 release / patch 并上传到控制面 - 授权:装控制面的时候要带 license
设备侧配置差不多就这样:
yaml
app_id: <uuid>
base_url: https://ota.你自己的域名
auto_update: true
# 生产建议再配 patch_public_key
App 起来之后大致流程是:
POST {base_url}/api/v1/patches/check- 控制面按 app、版本、平台、架构、channel 找最新且没回滚的补丁
- 返回带 HMAC 的下载地址和 hash
- 设备下载,下次冷启动生效
POST /api/v1/patches/events上报成功或失败
匹配规则没啥新鲜的,补丁号要比当前大才会下。回滚就是撤销发布,响应里会带需要卸掉的补丁号。下载地址有过期时间,不是永久裸链。
Dart 补丁是 Shorebird 的二进制 diff。native、插件、引擎换不了,这个之前写 Shorebird 的时候也说过,商店政策和技术边界都在这。
资源热更单独走
业务里经常出现「就换张活动图」。所以资源和代码补丁拆开了。
发版的时候先传一份资源快照(按应用、版本、平台)。后面热更发增减改清单,文件按 hash 存。设备另打:
text
POST /api/v1/resources/check
再按 hash 拉内容。图片这类往往能比较快用上;Dart 补丁还是冷启动。
另外有个 check-ota。发版上传基线,打补丁前对比这次到底改了什么。碰到字体之类 unsupported 的变更,会直接告诉你必须整包发。这步挺实用,少打几次无效包。
灰度这块我说一下现状
以前内部留过百分比放量一类字段,现在设备 check 不看百分比。别被文档里的字段名带偏。
线上目前靠白名单:
- 白名单关着:全量
- 白名单开着,名单空的:谁都拿不到(适合先挂闸)
- 白名单开着,名单里有设备 ID:只给这些人下
bash
flutterpatch patch --whitelist --unique-ids=测试机1,测试机2
我们自己一般是先白名单几台真机,看着没问题再关。
部署也可以慢慢长。一开始 SQLite + 本地盘就能跑通;制品多了再上 MinIO;再往后 Postgres、Redis。没必要一上来按大厂拓扑抄。
怎么装、怎么打第一版补丁
从官网下载页拿安装脚本。先点接入,拿授权 ID,确认版本。然后:
bash
# 本机装 CLI
chmod +x install_cli.sh
./install_cli.sh
# Windows
# .\install_cli.ps1
# 服务器装控制面
chmod +x install_remote.sh
./install_remote.sh --version <版本号> --license <授权ID>
装完会有 API 地址、Dashboard、Token。App 里加:
yaml
dependencies:
shorebird_code_push: ^2.0.3
shorebird.yaml 的 base_url 指到控制面。Android 模拟器访问电脑可以用 10.0.2.2,真机就局域网 IP 或者正式域名。
发版和打补丁大概这样:
bash
export FLUTTERPATCH_TOKEN=<token>
flutterpatch init --base-url https://ota.example.com
flutterpatch release android --upload-baselines
flutterpatch check-ota --flutter . --android ./android --ios ./ios --version 1.0.0+1
flutterpatch patch --platforms=android --release-version=1.0.0+1 \
--assets ./flutterpatch_assets.json \
--baseline-assets ./flutterpatch_assets.prev.json
验证我们一般用 Build Marker:发版写一个标记,打补丁换另一个,真机杀进程再开,变了就算成。
iOS 必须真机。 模拟器跑 Shorebird patch 没用,这个坑以前踩过。
上线前 HTTPS、Token、补丁签名这些该开开。热更别拿去改 App 主用途,审核那关自己清楚。
不适合什么
想完全自研解释器、或者继续走我以前那套 AST/DSL 无侵入路线的,这不是同一条。
主做 Web 动态化的,可以看 Petrel 那套,跟 FlutterPatch 不是一个问题。
必须 OTA 改 native 插件的,做不到。
适合的大概是:Shorebird 的 Dart 补丁你认,但官方云过不了------数据要留在自己机器、国内要下得下来、资源要单独热更、发补丁前要先判断能不能 OTA。这几条是官方现在给不了、我们做这套就是为了补上的。
关于现状
前面 Shorebird 中国区、Appwrite 存补丁那些文章,可以当成这条路上更早的尝试;现在控制面是完整做下来的一版,安装脚本从官网走。
还有不少边边角角会继续改,比如百分比灰度目前就没用上,白名单先顶着。
想试用或者自搭建,去官网看一下,点「立即接入」联系我就行。备注掘金也行。