Files
2026-08-11 14:16:15 +08:00

6.0 KiB
Raw Permalink Blame History

collab-go

实时协作文档编辑的 WebSocket 网关。负责 JWT 鉴权并将协作流量代理到 collab-core。

概述

collab-go 是一个 Go 服务,作为 Yoresee Doc 协作编辑平台中浏览器 WebSocket 连接的入口。主要职责:

  • JWT 令牌校验(HS256/384/512)
  • 通过 gRPC 调用后端校验文档是否存在
  • 与 collab-core(Node.js Yjs 服务)之间的双向 WebSocket 代理
  • 健康检查与优雅停机

连接流程

浏览器 --ws://host/ws/doc/{docId}?token={jwt}--> collab-go --> collab-core
  1. 浏览器连接 /ws/doc/{docId},JWT 通过 query 参数传递
  2. collab-go 校验 JWT 令牌
  3. collab-go 调用后端 gRPC DocumentService.GetDocumentSettings() 验证文档存在
  4. 成功后将 HTTP 升级为 WebSocket
  5. 拨号连接 collab-core:{COLLAB_CORE_URL}/doc-{docId}
  6. 在浏览器与 collab-core 之间双向代理消息

前置依赖

Proto 桩代码

pkg/gen/ 包含生成的 protobuf 代码,已被 gitignore。构建前必须先生成:

# 推荐:使用父仓库脚本
bash ../deploy/script/gen_proto.sh

# 或手动 protoc
mkdir -p pkg/gen
protoc -I ../proto \
  --go_out=pkg/gen --go_opt=paths=source_relative \
  --go-grpc_out=pkg/gen --go-grpc_opt=paths=source_relative \
  ../proto/yoresee_doc/v1/yoresee_doc.proto

所需工具:

  • protoc-gen-go:go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
  • protoc-gen-go-grpc:go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

Go

Go 1.24 或更高版本。

构建与运行

# 构建
go build -o collab-go main.go

# 运行
./collab-go

或直接运行:

go run main.go

默认端口:1234

配置

环境变量:

变量 默认值 必填 说明
ADDR :1234 否 监听地址
JWT_SECRET (空) 是 JWT 签名密钥。为空 = 接受任何可解析的 JWT(开发模式)
COLLAB_CORE_URL ws://collab-core:1234 否 collab-core 的 WebSocket 地址
BACKEND_GRPC_ADDR backend:9090 否 后端 gRPC 地址
INTERNAL_RPC_KEY (空) 否 作为 x-internal-key gRPC 元数据发送,用于内部鉴权

JWT 模式

生产模式(设置了 JWT_SECRET):

  • 使用 HS256/384/512 校验 JWT 签名
  • 令牌必须有效且签名正确

开发模式(JWT_SECRET 为空):

  • 解析 JWT 但不校验签名
  • 接受任何结构有效的 JWT
  • 适用于本地开发时无共享密钥的场景

API 端点

WebSocket

GET /ws/doc/{docId}?token={jwt}
  • 成功后升级为 WebSocket
  • JWT 无效返回 401 Unauthorized
  • 文档不存在返回 404 Not Found
  • 后端 gRPC 不可达返回 503 Service Unavailable

健康探针

GET /health
GET /readyz
GET /livez

均返回 JSON:

{
  "status": "ok",
  "detail": "",
  "backend": "ok"
}
  • /health:始终返回 200 OK。包含后端状态(如可用)。
  • /readyz:停机 draining 阶段或后端不健康时返回 503 Service Unavailable。
  • /livez:始终返回 200 OK。用于存活检查。

架构

collab-go/
├── auth/          JWT 校验
├── config/        基于环境变量的配置
├── handler/       WebSocket 处理器:鉴权 + 文档校验 + 代理
├── health/        健康探针处理器
├── proxy/         双向 WebSocket 代理
├── pkg/gen/       生成的 protobuf 桩代码(gitignored)
└── main.go        入口文件

核心组件

auth/

  • Authenticator 校验 JWT 令牌
  • 支持 HS256、HS384、HS512 签名方式
  • 密钥为空时降级为不校验签名的解析

config/

  • 使用 caarlos0/env/v11 解析环境变量
  • 所有配置通过环境变量传入

handler/

  • WSHandler 实现 WebSocket 连接的 http.Handler
  • 从 ?token= query 参数获取 JWT
  • 调用后端 gRPC 校验文档是否存在
  • 升级连接后委托给代理

health/

  • 实现 Kubernetes 风格的健康探针
  • readiness 检查后端 gRPC 健康状态
  • 支持优雅停机 draining

proxy/

  • Proxy 管理与 collab-core 的 WebSocket 连接
  • ProxyWS 启动两个 goroutine 进行双向消息转发
  • 连接路径:collab-core 上的 /doc-{docId}

优雅停机

收到 SIGINT 或 SIGTERM 信号后:

  1. 健康检查器进入 draining 模式(readiness 返回 not_ready)
  2. 停止接受新连接
  3. 等待已有连接关闭,最长 15 秒
  4. 退出

负载均衡器应在 /readyz 返回非 200 时停止路由流量。

Docker

开发环境

docker build -t collab-go-dev -f Dockerfile .
docker run -p 1234:1234 --env-file .env collab-go-dev

运行 go run main.go(无构建缓存)。

生产环境

# 从父目录构建
docker build -t collab-go -f Dockerfile.prod ..

注意:Dockerfile.prod 期望构建上下文在父目录(..),因为它需要复制:

  • collab-go/ 源码
  • proto/ 用于 protobuf 生成
  • 在容器内部生成 proto 桩代码

多阶段构建:

  1. Builder 阶段:生成 proto 桩代码,编译二进制
  2. Runner 阶段:将二进制复制到最小化 Alpine 镜像

协作栈集成

collab-go 是双服务协作栈的一部分:

  1. collab-core(Node.js):在内存 + Redis 中维护 Yjs 文档状态
  2. collab-go(Go):WebSocket 网关,鉴权,代理

网关模式实现了职责分离:

  • 鉴权与授权(Go,快速)
  • 文档状态管理(Node.js,Yjs 运行时)
  • 水平扩展(多个网关实例,每个文档一个 core)

日志

所有日志使用 collab-gateway 前缀:

collab-gateway listening on :1234
collab-gateway unauthorized path=/ws/doc/abc remote=127.0.0.1:12345 err=...
collab-gateway doc check failed docID=abc err=...

测试

当前无测试。可通过以下命令验证:

go vet ./...
go build ./...

相关文档

  • 父项目:../AGENTS.md
  • 完整系统文档:../../docs/README.md
  • 中文版本:../../docs/README_zh.md