467 lines
20 KiB
Markdown
467 lines
20 KiB
Markdown
# Janus - AI 中转站 完整开发计划
|
||
|
||
## Context
|
||
|
||
构建一个 AI API 中转/网关服务(Janus),使用 Go + Gin + PostgreSQL(GORM),前端采用前后端分离架构(Vue 3)。
|
||
|
||
用户背景:Java 转 Go,项目兼顾学习目的。计划按阶段递增复杂度,每个阶段引入新的 Go 语言特性:
|
||
- 早期:struct、interface、error handling、package 组织
|
||
- 中期:goroutine、channel、sync 包(并发安全)
|
||
- 后期:middleware 模式、context、observability
|
||
|
||
## 架构总览
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────┐
|
||
│ Clients │
|
||
│ (OpenAI SDK / Anthropic SDK / curl) │
|
||
└─────────────────┬───────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────┐
|
||
│ Janus Gateway │
|
||
│ │
|
||
│ ┌──────────┐ ┌──────────┐ ┌───────────────┐ │
|
||
│ │ Gin │ │ Proxy │ │ Management │ │
|
||
│ │ Router │ │ Engine │ │ API (CRUD) │ │
|
||
│ └────┬─────┘ └────┬─────┘ └───────┬───────┘ │
|
||
│ │ │ │ │
|
||
│ ┌────┴─────────────┴───────────────┴───────┐ │
|
||
│ │ Service Layer │ │
|
||
│ │ ┌──────────┐ ┌────────┐ ┌────────────┐ │ │
|
||
│ │ │ Balancer │ │ Health │ │ Rate Limiter│ │ │
|
||
│ │ │ Service │ │Checker │ │ Service │ │ │
|
||
│ │ └──────────┘ └────────┘ └────────────┘ │ │
|
||
│ └────────────────┬──────────────────────────┘ │
|
||
│ │ │
|
||
│ ┌────────────────┴──────────────────────────┐ │
|
||
│ │ Repository Layer (GORM) │ │
|
||
│ └────────────────┬──────────────────────────┘ │
|
||
│ │ │
|
||
│ ┌────────────────┴──────────────────────────┐ │
|
||
│ │ PostgreSQL │ │
|
||
│ └────────────────────────────────────────────┘ │
|
||
│ │
|
||
│ ┌────────────────────────────────────────────┐ │
|
||
│ │ Vue 3 Dashboard (SPA) │ │
|
||
│ └────────────────────────────────────────────┘ │
|
||
└─────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────┐
|
||
│ Upstream AI Providers │
|
||
│ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
|
||
│ │ OpenAI │ │Anthropic │ │ Compatible │ │
|
||
│ │ API │ │ API │ │ (DS, QWen) │ │
|
||
│ └──────────┘ └──────────┘ └──────────────┘ │
|
||
└─────────────────────────────────────────────────┘
|
||
```
|
||
|
||
## 项目目录结构
|
||
|
||
```
|
||
janus/
|
||
├── cmd/
|
||
│ └── server/
|
||
│ └── main.go # 应用入口,依赖组装
|
||
├── internal/
|
||
│ ├── config/
|
||
│ │ └── config.go # YAML 配置加载(viper)
|
||
│ ├── model/
|
||
│ │ ├── provider.go # Provider 模型
|
||
│ │ ├── api_key.go # ApiKey 模型
|
||
│ │ ├── model.go # AIModel 模型
|
||
│ │ └── request_log.go # RequestLog 模型
|
||
│ ├── repository/
|
||
│ │ ├── provider_repo.go # Provider CRUD
|
||
│ │ ├── api_key_repo.go # ApiKey CRUD
|
||
│ │ ├── model_repo.go # AIModel CRUD
|
||
│ │ └── request_log_repo.go # Log 查询
|
||
│ ├── service/
|
||
│ │ ├── provider_svc.go # Provider 业务逻辑
|
||
│ │ ├── api_key_svc.go # ApiKey 管理
|
||
│ │ ├── balancer_svc.go # 负载均衡核心
|
||
│ │ ├── proxy_svc.go # 请求代理引擎
|
||
│ │ ├── health_svc.go # 健康检查 & 故障转移
|
||
│ │ └── rate_limit_svc.go # 速率限制
|
||
│ ├── handler/
|
||
│ │ ├── proxy_handler.go # /v1/* 代理端点
|
||
│ │ ├── provider_handler.go # Provider 管理 API
|
||
│ │ ├── api_key_handler.go # ApiKey 管理 API
|
||
│ │ ├── model_handler.go # Model 管理 API
|
||
│ │ ├── stats_handler.go # 统计 API
|
||
│ │ └── health_handler.go # 健康检查
|
||
│ ├── middleware/
|
||
│ │ ├── auth.go # API Key 认证
|
||
│ │ ├── ratelimit.go # 速率限制中间件
|
||
│ │ ├── logging.go # 请求日志中间件
|
||
│ │ ├── cors.go # CORS
|
||
│ │ └── recovery.go # Panic 恢复
|
||
│ ├── proxy/
|
||
│ │ ├── openai.go # OpenAI 协议适配器
|
||
│ │ ├── anthropic.go # Anthropic 协议适配器
|
||
│ │ └── adapter.go # 适配器接口定义
|
||
│ ├── balancer/
|
||
│ │ ├── strategy.go # 策略接口
|
||
│ │ ├── round_robin.go # 轮询实现
|
||
│ │ └── weighted.go # 加权实现
|
||
│ ├── health/
|
||
│ │ ├── checker.go # 健康检查 Worker
|
||
│ │ └── circuit_breaker.go # 熔断器
|
||
│ └── router/
|
||
│ └── router.go # 路由注册
|
||
├── pkg/
|
||
│ ├── response/
|
||
│ │ └── response.go # 统一响应格式
|
||
│ ├── errors/
|
||
│ │ └── errors.go # 自定义错误类型
|
||
│ └── utils/
|
||
│ └── utils.go # 工具函数
|
||
├── web/ # Vue 3 前端项目
|
||
│ ├── src/
|
||
│ │ ├── views/
|
||
│ │ ├── components/
|
||
│ │ ├── api/
|
||
│ │ └── router/
|
||
│ ├── package.json
|
||
│ └── vite.config.ts
|
||
├── migrations/ # 数据库迁移 SQL
|
||
│ ├── 001_init.up.sql
|
||
│ └── 001_init.down.sql
|
||
├── config.yaml # 默认配置
|
||
└── go.mod
|
||
```
|
||
|
||
## 数据库设计
|
||
|
||
```sql
|
||
-- providers: AI 服务提供商
|
||
CREATE TABLE providers (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
name VARCHAR(50) NOT NULL UNIQUE, -- openai / anthropic / deepseek
|
||
base_url VARCHAR(255) NOT NULL, -- https://api.openai.com
|
||
protocol VARCHAR(20) NOT NULL DEFAULT 'openai', -- openai / anthropic
|
||
status VARCHAR(20) NOT NULL DEFAULT 'active', -- active / disabled
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||
);
|
||
|
||
-- api_keys: API 密钥池
|
||
CREATE TABLE api_keys (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
provider_id BIGINT NOT NULL REFERENCES providers(id),
|
||
key_value TEXT NOT NULL, -- 加密存储
|
||
name VARCHAR(100), -- 别名/备注
|
||
weight INT NOT NULL DEFAULT 1, -- 负载权重
|
||
status VARCHAR(20) NOT NULL DEFAULT 'active', -- active/disabled/depleted
|
||
fail_count INT NOT NULL DEFAULT 0, -- 连续失败计数
|
||
last_used_at TIMESTAMPTZ,
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||
);
|
||
|
||
-- models: 可用模型列表
|
||
CREATE TABLE models (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
provider_id BIGINT NOT NULL REFERENCES providers(id),
|
||
name VARCHAR(100) NOT NULL, -- gpt-4o / claude-sonnet-4-5
|
||
input_price DECIMAL(10,8) DEFAULT 0, -- 每 1K token 价格
|
||
output_price DECIMAL(10,8) DEFAULT 0,
|
||
status VARCHAR(20) NOT NULL DEFAULT 'active',
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
UNIQUE(provider_id, name)
|
||
);
|
||
|
||
-- request_logs: 请求日志
|
||
CREATE TABLE request_logs (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
api_key_id BIGINT,
|
||
model_id BIGINT,
|
||
provider_id BIGINT,
|
||
request_method VARCHAR(10),
|
||
request_path VARCHAR(500),
|
||
request_body TEXT,
|
||
response_body TEXT,
|
||
status_code INT,
|
||
latency_ms INT,
|
||
tokens_in INT DEFAULT 0,
|
||
tokens_out INT DEFAULT 0,
|
||
cost DECIMAL(10,6) DEFAULT 0,
|
||
error_message TEXT,
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||
);
|
||
|
||
-- 索引
|
||
CREATE INDEX idx_api_keys_provider_id ON api_keys(provider_id);
|
||
CREATE INDEX idx_api_keys_status ON api_keys(status);
|
||
CREATE INDEX idx_models_provider_id ON models(provider_id);
|
||
CREATE INDEX idx_request_logs_created_at ON request_logs(created_at DESC);
|
||
CREATE INDEX idx_request_logs_provider_id ON request_logs(provider_id);
|
||
```
|
||
|
||
## API 设计
|
||
|
||
### 代理端点(OpenAI 兼容 & Anthropic 兼容)
|
||
```
|
||
POST /v1/chat/completions # OpenAI 聊天补全
|
||
POST /v1/embeddings # OpenAI 嵌入
|
||
POST /v1/messages # Anthropic Messages API
|
||
GET /v1/models # 列出可用模型(聚合所有 provider)
|
||
```
|
||
|
||
### 管理 API(Dashboard 用)
|
||
```
|
||
# Providers
|
||
GET /api/admin/providers
|
||
POST /api/admin/providers
|
||
GET /api/admin/providers/:id
|
||
PUT /api/admin/providers/:id
|
||
DELETE /api/admin/providers/:id
|
||
|
||
# API Keys
|
||
GET /api/admin/api-keys?provider_id=
|
||
POST /api/admin/api-keys
|
||
PUT /api/admin/api-keys/:id
|
||
DELETE /api/admin/api-keys/:id
|
||
POST /api/admin/api-keys/:id/toggle # 启用/禁用
|
||
|
||
# Models
|
||
GET /api/admin/models?provider_id=
|
||
POST /api/admin/models
|
||
PUT /api/admin/models/:id
|
||
DELETE /api/admin/models/:id
|
||
|
||
# Stats
|
||
GET /api/admin/stats/overview # 总览统计
|
||
GET /api/admin/stats/requests # 请求明细
|
||
```
|
||
|
||
## 负载均衡 & 故障转移设计
|
||
|
||
### 负载策略接口
|
||
```go
|
||
type Strategy interface {
|
||
Next(keys []*model.ApiKey) *model.ApiKey
|
||
Name() string
|
||
}
|
||
```
|
||
|
||
### 策略实现
|
||
1. **RoundRobin** - 无状态轮询,适合均匀分布
|
||
2. **WeightedRoundRobin** - 按 weight 字段加权分配
|
||
|
||
### 健康检查
|
||
- 每个 ApiKey 维护 `fail_count` 连续失败计数
|
||
- 失败超过阈值(默认 3 次)自动标记为 `disabled`
|
||
- 后台 goroutine 定期重试 disabled 的 key(30s 间隔)
|
||
- 正常请求也作为隐式健康检查
|
||
|
||
### 故障转移流程
|
||
1. Balancer 选取一个 ApiKey
|
||
2. 代理请求到上游
|
||
3. 如果失败(5xx / 超时 / 网络错误):
|
||
- 递增 `fail_count`
|
||
- 达到阈值标记 `disabled`
|
||
- 立即从剩余 key 中选择重试(最多 3 次)
|
||
4. 如果成功:重置 `fail_count` 为 0
|
||
|
||
### 熔断器(单例 per ApiKey)
|
||
```
|
||
状态机: CLOSED → (连续失败) → OPEN → (冷却时间) → HALF_OPEN → (成功) → CLOSED
|
||
→ (失败) → OPEN
|
||
```
|
||
|
||
## 分阶段实施计划
|
||
|
||
### Phase 1: 项目骨架 & 基础设施(第 1-2 天)
|
||
**学习目标**: Go 项目组织、package 管理、Gin 基础
|
||
|
||
具体任务:
|
||
- [-] 初始化 `go.mod`(go mod init github.com/synoth/janus)
|
||
- [ ] 安装核心依赖:gin, gorm, viper, pgx 驱动
|
||
- [ ] 实现 `internal/config/config.go`:Viper 加载 YAML 配置
|
||
- [ ] 编写 `config.yaml`:数据库连接、服务器端口等
|
||
- [ ] 实现 `cmd/server/main.go`:组装依赖,启动 Gin
|
||
- [ ] 实现 `internal/router/router.go`:基础路由
|
||
- [ ] 实现 `internal/handler/health_handler.go`:GET /health
|
||
- [ ] 实现 GORM 数据库连接 & AutoMigrate
|
||
- [ ] 添加 `.env` 支持敏感配置
|
||
|
||
**交付物**: 可运行的服务,响应 `/health`,连接数据库
|
||
|
||
### Phase 2: 数据模型 & Repository 层(第 2-3 天)
|
||
**学习目标**: Go struct、GORM 关系映射、interface 定义
|
||
|
||
具体任务:
|
||
- [ ] 定义 `internal/model/provider.go`(struct + GORM 标签)
|
||
- [ ] 定义 `internal/model/api_key.go`
|
||
- [ ] 定义 `internal/model/model.go`(AIModel)
|
||
- [ ] 定义 `internal/model/request_log.go`
|
||
- [ ] 编写 SQL 迁移脚本 `migrations/001_init.up.sql`
|
||
- [ ] 实现 `internal/repository/provider_repo.go`(CRUD)
|
||
- [ ] 实现 `internal/repository/api_key_repo.go`
|
||
- [ ] 实现 `internal/repository/model_repo.go`
|
||
- [ ] 编写 Repository 层的单元测试
|
||
|
||
**交付物**: 完整的数据访问层,可通过测试验证
|
||
|
||
### Phase 3: Provider & API Key 管理 API(第 3-4 天)
|
||
**学习目标**: Gin handler、请求验证、JSON 序列化、统一错误处理
|
||
|
||
具体任务:
|
||
- [ ] 实现 `pkg/response/response.go`:统一 JSON 响应格式
|
||
- [ ] 实现 `pkg/errors/errors.go`:业务错误码定义
|
||
- [ ] 实现 `internal/service/provider_svc.go`
|
||
- [ ] 实现 `internal/service/api_key_svc.go`
|
||
- [ ] 实现 `internal/handler/provider_handler.go`:CRUD API
|
||
- [ ] 实现 `internal/handler/api_key_handler.go`:CRUD API
|
||
- [ ] 实现 `internal/handler/model_handler.go`:模型管理 API
|
||
- [ ] 添加请求验证(binding tags + validator)
|
||
- [ ] Postman/curl 可测试的完整管理 API
|
||
|
||
**交付物**: 可通过 REST API 管理 Provider、ApiKey、Model
|
||
|
||
### Phase 4: 代理引擎(第 4-6 天)
|
||
**学习目标**: HTTP 反向代理、streaming、context、Go http.Client
|
||
|
||
具体任务:
|
||
- [ ] 实现 `internal/proxy/adapter.go`:协议适配器接口
|
||
```go
|
||
type Adapter interface {
|
||
Forward(ctx context.Context, key *model.ApiKey, body []byte) (*ProxyResult, error)
|
||
Protocol() string
|
||
}
|
||
```
|
||
- [ ] 实现 `internal/proxy/openai.go`:OpenAI 请求转发
|
||
- 支持 `/v1/chat/completions`(含 streaming)
|
||
- 支持 `/v1/embeddings`
|
||
- [ ] 实现 `internal/proxy/anthropic.go`:Anthropic 请求转发
|
||
- 支持 `/v1/messages`(含 streaming)
|
||
- [ ] 实现 `internal/service/proxy_svc.go`:编排代理流程
|
||
- [ ] 实现 `internal/handler/proxy_handler.go`:
|
||
- 解析请求路径确定目标 protocol
|
||
- 从 header 获取 Janus API Key → 查找 upstream key 池
|
||
- 调用代理服务
|
||
- [ ] 实现 `internal/middleware/auth.go`:Janus 自身的 API Key 认证
|
||
- [ ] 添加请求日志记录(写入 request_logs)
|
||
- [ ] 处理 streaming 响应(SSE)
|
||
|
||
**交付物**: 可以用 curl 通过 Janus 调用 OpenAI/Anthropic
|
||
|
||
### Phase 5: 负载均衡(第 6-7 天)
|
||
**学习目标**: Go interface 多态、sync.Mutex、原子操作
|
||
|
||
具体任务:
|
||
- [ ] 实现 `internal/balancer/strategy.go`:Strategy 接口
|
||
- [ ] 实现 `internal/balancer/round_robin.go`:
|
||
- 线程安全的轮询计数器(sync.Mutex 或 atomic)
|
||
- 跳过 disabled key
|
||
- [ ] 实现 `internal/balancer/weighted.go`:
|
||
- 加权轮询算法
|
||
- 平滑加权轮询(Nginx 算法)
|
||
- [ ] 实现 `internal/service/balancer_svc.go`:
|
||
- 集成策略选择(可通过 API 或配置切换)
|
||
- 从 DB 加载可用 key 列表(带缓存)
|
||
- [ ] 修改 ProxyService 集成 BalancerService
|
||
- [ ] 编写负载均衡的单元测试(验证分布均匀性)
|
||
|
||
**交付物**: 请求自动在多个 ApiKey 间负载均衡
|
||
|
||
### Phase 6: 健康检查 & 故障转移(第 7-9 天)
|
||
**学习目标**: Goroutine、Channel、context 取消、并发模式
|
||
|
||
具体任务:
|
||
- [ ] 实现 `internal/health/circuit_breaker.go`:
|
||
- 三态状态机(Closed/Open/HalfOpen)
|
||
- 可配置阈值和冷却时间
|
||
- [ ] 实现 `internal/health/checker.go`:
|
||
- 后台 goroutine 定期检查 disabled key
|
||
- 向每个 provider 发送轻量健康检查请求
|
||
- 通过 channel 接收检查结果
|
||
- [ ] 实现 `internal/service/health_svc.go`:
|
||
- 管理所有 key 的健康状态
|
||
- 优雅关闭(context 取消 → 停止所有 goroutine)
|
||
- [ ] 修改 ProxyService 添加故障转移逻辑:
|
||
- 请求失败 → 标记 key → 从剩余池中重试
|
||
- 最大重试次数 3
|
||
- 熔断器介入
|
||
- [ ] 添加健康状态 API:GET /api/admin/keys/:id/health
|
||
|
||
**交付物**: 单个 key 故障时自动切换,故障 key 自动恢复
|
||
|
||
### Phase 7: 速率限制 & 可观测性(第 9-10 天)
|
||
**学习目标**: Middleware 模式、token bucket 算法、结构化日志
|
||
|
||
具体任务:
|
||
- [ ] 实现 `internal/service/rate_limit_svc.go`:
|
||
- Token bucket 算法(golang.org/x/time/rate)
|
||
- 支持按 Janus API Key 限流
|
||
- 支持按上游模型限流
|
||
- [ ] 实现 `internal/middleware/ratelimit.go`:
|
||
- 从 Gin context 提取 key
|
||
- 返回 429 Too Many Requests
|
||
- 添加 Retry-After header
|
||
- [ ] 实现 `internal/middleware/logging.go`:
|
||
- 结构化日志(使用 slog 或 zerolog)
|
||
- 记录 latency、status、path、tokens
|
||
- [ ] 实现 `internal/middleware/recovery.go`:Panic 恢复
|
||
- [ ] 实现 `internal/handler/stats_handler.go`:
|
||
- 总请求数、成功率、平均延迟
|
||
- 按 Provider/Model 分组统计
|
||
- 费用汇总
|
||
- [ ] 添加 `internal/middleware/cors.go`
|
||
|
||
**交付物**: 完整的中间件链,可观测的统计数据
|
||
|
||
### Phase 8: Dashboard 前端 & 容器化(第 10-14 天)
|
||
**学习目标**: 前后端集成、Docker、项目收尾
|
||
|
||
具体任务:
|
||
- [ ] 初始化 Vue 3 + Vite 项目(`web/`)
|
||
- [ ] 安装 UI 组件库(Element Plus 或 Naive UI)
|
||
- [ ] 实现 Providers 管理页面
|
||
- [ ] 实现 API Keys 管理页面
|
||
- [ ] 实现 Models 管理页面
|
||
- [ ] 实现统计仪表盘页面(图表)
|
||
- [ ] 前端构建产物由 Gin 静态文件服务托管
|
||
- [ ] 编写 Dockerfile(多阶段构建)
|
||
- [ ] 编写 docker-compose.yml(Janus + PostgreSQL)
|
||
- [ ] 编写 README.md(中英文)
|
||
|
||
**交付物**: 完整的可部署项目
|
||
|
||
## Go 学习路径映射
|
||
|
||
| Phase | Go 特性 | Java 对比 |
|
||
|-------|---------|-----------|
|
||
| 1 | package, import, func, defer | package ≈ package, defer ≈ finally |
|
||
| 2 | struct, interface, 指针 | struct ≈ class(无继承), interface 隐式满足 |
|
||
| 3 | error handling, type assertion | error 返回值 ≠ exception |
|
||
| 4 | context, http.Client, io.Reader | context ≈ 请求上下文传递 |
|
||
| 5 | sync.Mutex, atomic, slice | 无内置锁语法,需显式使用 |
|
||
| 6 | goroutine, channel, select | ≈ ThreadPool + BlockingQueue |
|
||
| 7 | middleware 闭包, functional options | ≈ Filter chain / Builder pattern |
|
||
| 8 | 前后端集成, Docker | 通用技能 |
|
||
|
||
## 关键技术决策
|
||
|
||
1. **配置管理**: Viper(支持 YAML + 环境变量覆盖)
|
||
2. **ORM**: GORM v2(AutoMigrate 用于开发,生产用迁移脚本)
|
||
3. **API Key 加密**: 使用 AES-256-GCM 加密存储 key_value
|
||
4. **Streaming**: 使用 `http.Flusher` + SSE 协议
|
||
5. **日志**: slog(Go 1.21+ 内置结构化日志)
|
||
6. **前端**: Vue 3 + Vite + Element Plus(轻量、国内文档丰富)
|
||
7. **容器化**: 多阶段 Docker 构建,docker-compose 一键部署
|
||
|
||
## 验证方式
|
||
|
||
每个 Phase 完成后的验证方法:
|
||
1. `go build ./...` 编译通过
|
||
2. `go vet ./...` 静态分析通过
|
||
3. `go test ./...` 单元测试通过
|
||
4. curl / Postman 手动测试关键 API
|
||
5. Phase 4+ 可用 OpenAI SDK 指向 Janus 实际调用
|
||
6. Phase 6 手动停用一个 key 验证故障转移
|
||
7. Phase 8 `docker-compose up` 一键启动
|