跳转到主要内容

StarWork 云控服务端技术架构

star-work-cloud 仓库全景解读:单进程双服务(TLS 长连接 + gin HTTP)、统一信封协议、配置定向下发、技能/版本包分片分发、设备连接生命周期与多副本就绪的部署架构,附四张交互式图表。

依据 star-work-cloud 仓库源码与工程内文档(README.md / PROTOCOL.md)编制。文中四张图表均为交互式:支持缩放、语义视图、关系追踪与 PNG/SVG 导出。

一、项目概览

star-work-cloud 是桌面端(StarWork / Milo 客户端)的云控服务端:单个 Go 进程同时对外提供两条服务,共用一份 PostgreSQL 与同一套业务层。

服务默认端口面向职责
TLS 长连接8443桌面客户端设备上报 / 配置下发 / 技能包与版本包分片下发,一行业务消息一行 JSON
HTTP API (gin)8090管理端登录、菜单、设备、配置、模型、技能、版本、日志、角色用户
组件选型
语言Go 1.26(go.mod 要求 ≥ 1.26.3)
HTTP 框架gin v1.12
ORMGORM v1.31 + pgx v5(纯 Go,CGO_ENABLED=0 静态编译)
数据库PostgreSQL(运维提供,无外键,关联在应用层维护)
权限casbin v3 + gorm-adapter(策略落库)
鉴权golang-jwt v5
加密AES-256-GCM(字段加密)+ RSA(登录密码传输)

代码采用经典分层:handler(解析请求/统一响应)→ service(业务逻辑)→ repository(数据访问)→ model(GORM 模型);云控长连接独立成 internal/cloud(信封、会话表、topic 注册表、分片下发),TLS 治理在 internal/tls,后台巡检在 internal/worker + internal/leader(PG advisory lock 选主)。

二、总体架构

单进程内三个协作者:TLS 长连接服务(握手 / AUTH / 信封协议 / 会话表)、HTTP API(JWT + casbin 鉴权的 10 组管理接口)、后台巡检 Worker(心跳、补推、归档、选主)。入口分两条:设备走 L4 直达(NodePort / LB 的 TCP 8443,HTTP Ingress 无法转发 4 层 TLS),管理端走 Ingress 的 /starwork/api 前缀;前端 SPA 与后端同源部署,刻意不配置 CORS。

全屏打开 ↗

说明

探针三件套(startup / readiness / liveness)全部打 8090 的 /healthz——8443 是裸 TLS 长连接,kubelet 探不了。/healthz 只回 {"status":"ok"} 不查库,库挂了不会因此被摘流量。

三、统一信封协议(Protocol v1)

一套信封覆盖上报 / 下发 / 配置同步 / 心跳,传输与业务分离:信封(怎么传)一次写死,业务能力以 topic 扩展——新增功能 = 注册表加一行 + 两端各一个处理函数,协议本身不动。

字段必填说明
ver / id / ts是协议版本 / 消息 ID(UUID,幂等去重 + 应答关联)/ 时间戳
type是固定 5 种:cmd / resp / report / ack / event
topic是业务主题(config.sync、skill.install、release.download…)
from / to条件to 为定向描述(§四),支持用户 / 平台 / 设备 / 版本过滤
payload / code / meta—业务数据 / 应答码 / timeout·retry·seq·total·compress

可靠性三件套:幂等(客户端记录已处理 id,重复消息直接 ack)、应答超时重试(按 meta.timeout/retry 重发,只补发未确认分片)、大 payload 分片(meta.seq/total 重组,可选 gzip 压缩)。

四、配置同步与定向下发

配置是云控的自举基座——客户端连接地址本身就在配置里,因此「地址可远程更新」是第一需求。服务端维护全局配置表(configs:每份配置独立 configId 与单调递增版本号,不按用户区分),客户端本地原子持久化(cloud-config.json),启动 / 重连必执行 config.sync 对账。

全屏打开 ↗

三个核心设计:

  1. 双通道兜底:config.push(服务端定向推送,在线即时生效)+ config.sync(客户端拉取,离线攒下的新配置上线即补)。配置类消息靠 sync 全量对账兜底,离线队列只保留给一次性指令(重启、升级等)。
  2. 全量下发 + 按字段合并:服务端始终下发完整配置对象,客户端按顶层字段合并(收到覆盖、未收到保留)。自带自愈——任何丢失或本地文件损坏,下次连接全量对账即恢复;配合原子写(临时文件 + rename)与版本守卫(旧版本拒绝覆盖新版本)。
  3. 定向匹配:to 描述支持 platforms / deviceIds / minVersion 的 AND 组合——同一账号的 mac 更新只命中 mac 设备。每次下发落 config_deliveries + 逐设备明细(msg_id 关联 ack,状态 pending / delivering / done / failed),删除下发记录即撤销。

五、技能包与版本包分发

技能(zip,内含 SKILL.md)与版本安装包共用同一套 TLS 分片分发套路:base64 分片(128KB 原始字节/片)、共用命令 id 幂等、收齐重组后与元信息 sha256 强比对。设备端不暴露公网下载地址,HTTP 下载接口仅限管理端(JWT)。

全屏打开 ↗

关键防线与落地策略:

  • 版本更新走配置携带:release 表(version / platform / arch / sha256)绑定到配置,随 config.push / config.sync 全量下发;客户端做平台/架构匹配(不符不弹窗)、版本比较(不高于当前忽略)、单会话去重后才弹确认窗。
  • 技能按 category 六类落盘:global(全部编辑器)/ docs / sheets / slides / pdf / markdown 各自目录,非白名单取值兜底为 global;同目录原子替换 + .cloud-skill.json 登记,装完即用无需重启;后台改分类后,已装设备「更新」时自动迁移目录并清旧副本。
  • 解压安全:防 zip-slip 路径穿越、校验 SKILL.md 必须存在,非法即回滚。

六、设备连接生命周期

连接治理三参数:握手读超时(10s)、空闲读超时(600s)、并发连接上限(默认 10000);可选 DEVICE_AUTH_SECRET 二次鉴权。心跳按连接时长四阶段退避(15s → 30s → 60s → 5min),在线判定随策略动态计算:now - lastSeen < onlineTimeoutFactor × 当前阶段期望间隔(默认 2 倍系数)——Dashboard 不写死超时窗口,直接消费服务端状态字段。

全屏打开 ↗

提示

断开时在线状态实时写库(不依赖心跳窗口);心跳过期的半开连接由 worker 每 60s 扫描兜底置离线。断线期间本地配置保持不变,重连后重新握手 → config.sync 立即补上离线期间的新配置。

七、数据模型

16 张业务表 + casbin 策略表,全部 GORM AutoMigrate 自动建表,无外键约束(运维规范),关联仅应用层维护:

域表说明
账号权限users / roles / menus / role_menus / casbin_rule菜单/按钮/接口三级权限;启动扫描路由自动同步「接口权限」条目
设备client_users / client_devices设备在线状态、last_seen_at 心跳窗口
配置下发configs / config_deliveries / config_delivery_items全局配置版本链 + 下发留痕(复合索引支撑按设备轮询待下发)
包分发skills / releasessha256 / platform / arch / 分类登记
模型配置model_protocols / model_endpoints / models / config_models三级结构:协议 L1 → 端点 L2 → 模型 L3;密钥等敏感字段 AES-256-GCM 加密入库、接口掩码返回
日志app_logs客户端错误日志上报;超 ARCHIVE_KEEP_DAYS(默认 7 天)自动归档为 .log 并删库(PG advisory lock 选主,多副本只跑一份)

八、安全设计

启动强校验(不满足直接退出):JWT_SECRET ≥ 32 字符且非默认值;DATA_ENC_KEY 必须为 base64 的 32 字节。登录密码经 RSA 公钥加密传输(/auth/public-key 分发公钥,私钥服务端持有,生产务必挂载固定私钥文件)。设备侧可选 AUTH 二次鉴权;上传体积(UPLOAD_MAX_MB)与连接治理参数防滥用。刻意不配置 CORS:前后端同源部署于同一 Ingress host(/starwork 前端 SPA、/starwork/api 后端),浏览器只见一个 origin。

九、部署架构

  • 镜像:CGO_ENABLED=0 静态二进制(amd64 / arm64 交叉编译),builder golang:1.26.3 + runtime alpine:3.12;vendor/ 入库离线构建(-mod=vendor),构建机无外网可编译。alpine 无 tzdata,二进制内嵌 time/tzdata 支撑 DB_TIMEZONE=Asia/Shanghai。
  • K8s(Helm):两端口同 Service(tls 8443 / http 8090,ClusterIP);/app/data(上传包、归档、RSA 私钥)挂 PVC(nfs-client 动态供给,20Gi RWO);8443 证书由 chart 建 Secret、流水线 --set-file 注入;JWT_SECRET / DATA_ENC_KEY 必须环境变量注入。
  • Ingress 三前缀(最长前缀优先):/starwork/api → 后端 8090;/starwork → 前端 SPA;/admin/static → 前端旧入口(301 到 /starwork/)。
  • CI/CD:Jenkins 三环境流水线(sit / uat / prd:拉码 → make release-image.amd64 构图推 Harbor → Helm 部署);敏感参数 sit 明文 / uat·prd 打码。

多副本就绪

设备长连接只在持有它的副本内存里,方案是不让副本共享会话(那要 Redis),每个副本自扫 + DB 兜底:

能力做法
心跳每副本每 10s 写自己在线设备的 last_seen_at(在线 = online + 35s 窗口)
离线清理每 60s 只把心跳过期的置离线(不做启动全表清零)
下发补推每副本每 2s 查自己设备的待下发项(msg_id 为空)并推送
权限casbin 策略逐条落库(不用全表重写);每 60s LoadPolicy 对齐
定时任务归档 / 下发巡检用 PG advisory lock 选主,只跑一份
警告

副本数目前仍建议 1:nfs-client 是 RWO(多副本需换 RWX)、podAntiAffinity 为 required + hostname(需 ≥2 可调度节点)、8443 的 L4 转发需确认非「单后端」。helm uninstall 会删除 chart 自建的 PVC。

十、已知约束与演进

  1. scripts/docker-deploy.sh 依赖的 docker-compose.yml 尚未入库。
  2. builder 镜像 golang:1.26.3 tag 需 DevOps 在 Harbor 确认或新建。
  3. vendor/ 约 270 MB(含已不被引用的 sqlite 依赖),清理需外网重跑 go mod vendor,或改私有 proxy。
  4. 8443 需运维单独放通(NodePort / LoadBalancer / TCP 转发其一),HTTP Ingress 转发不了 4 层 TLS。
  5. Ingress 的 serviceName 与前端仓库耦合(/admin/static 指向 star-work-cloud-front),改 release 名需两边同步。
  6. 路由前缀 /staroffice → /starwork 的旧库迁移已自动化:种子菜单就地更新、casbin 旧前缀策略清理、接口条目按新路由重建,旧库重启即自动跟上。