lolly/docs/comments.md
xfy 6ae7e32ef1 feat(proxy,loadbalance): 实现反向代理和负载均衡模块
实现 Phase 3 核心功能:
- loadbalance: 轮询、加权轮询、最少连接、IP哈希四种算法
- proxy: HTTP 反向代理、健康检查、故障转移
- 所有实现均为并发安全,使用 atomic 操作

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-02 17:06:29 +08:00

521 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Golang 注释规范
本文档定义了 Cadmus 项目中 Go 代码的注释标准,所有开发者应遵循这些规范以确保代码的可读性和可维护性。所有注释使用中文。
## 1. 文件头注释
每个 Go 源文件必须在开头添加文件头注释,说明文件的作用、主要功能和所属模块。
### 格式
```go
// Package xxx 提供了 xxx 功能的实现。
//
// 该文件包含 xxx 相关的核心逻辑,包括:
// - 功能 A 的实现
// - 功能 B 的处理
// - 与 xxx 服务的交互
//
// 主要用途:
// 用于处理 xxx 场景下的 xxx 业务逻辑。
//
// 注意事项:
// - 使用前需要确保 xxx 条件满足
// - 并发安全/非并发安全
//
// 作者xfy
package xxx
```
### 示例
```go
// Package services 提供了 Cadmus 核心业务服务的实现。
//
// 该文件包含媒体服务相关的核心逻辑,包括:
// - 媒体文件的存储和管理
// - 媒体元数据的解析和处理
// - 与存储后端的交互
//
// 主要用途:
// 用于处理用户上传媒体文件的完整生命周期管理。
//
// 注意事项:
// - 所有公开方法均为并发安全
// - 使用前需确保存储服务已正确初始化
//
// 作者xfy
package services
```
## 2. 函数注释
每个函数(包括公开函数和私有函数)都必须添加注释,说明其用途、参数、返回值和使用注意事项。
### 格式
```go
// FunctionName 执行 xxx 操作。
//
// 该函数用于处理 xxx 场景,实现 xxx 功能。
//
// 参数:
// - param1: 参数1的说明类型为 xxx用于 xxx
// - param2: 参数2的说明类型为 xxx用于 xxx
//
// 返回值:
// - result: 返回结果的说明,类型为 xxx
// - err: 错误信息,当 xxx 时返回非 nil 值
//
// 使用示例:
// result, err := FunctionName(param1, param2)
// if err != nil {
// // 处理错误
// }
//
// 注意事项:
// - 调用前需确保 xxx 条件满足
// - 该函数不处理 xxx 情况,需要调用方自行处理
func FunctionName(param1 Type1, param2 Type2) (Result, error) {
// 实现
}
```
### 简单函数格式
对于功能单一、逻辑简单的函数,可使用简化格式:
```go
// ValidateInput 验证用户输入是否有效。
//
// 检查输入字符串是否非空且符合长度要求1-100字符
// 返回 true 表示验证通过false 表示验证失败。
func ValidateInput(input string) bool {
return len(input) > 0 && len(input) <= 100
}
```
### 公开函数示例
```go
// SaveMedia 保存媒体文件到存储后端。
//
// 该方法将媒体文件及其元数据持久化到配置的存储系统中。
// 支持多种存储后端包括本地文件系统、S3 和 MinIO。
//
// 参数:
// - ctx: 上下文,用于控制超时和取消操作
// - media: 媒体对象,包含文件数据和元数据
//
// 返回值:
// - id: 保存成功后生成的唯一标识符
// - err: 可能的错误包括:
// - ErrStorageUnavailable: 存储服务不可用
// - ErrInvalidMedia: 媒体数据无效
// - ErrQuotaExceeded: 存储配额超限
//
// 使用示例:
// id, err := service.SaveMedia(ctx, media)
// if errors.Is(err, services.ErrStorageUnavailable) {
// // 重试或切换存储后端
// }
//
// 注意事项:
// - 该方法是并发安全的,可在多个 goroutine 中同时调用
// - 大文件上传建议使用 StreamMedia 方法
func (s *MediaService) SaveMedia(ctx context.Context, media *Media) (string, error) {
// 实现
}
```
### 私有函数示例
```go
// generateUniqueID 生成唯一的媒体标识符。
//
// 使用时间戳和随机数组合生成 UUID 格式的标识符。
// 保证在分布式环境下的唯一性。
//
// 返回值:
// - 返回格式为 "media-{timestamp}-{random}" 的唯一字符串
func generateUniqueID() string {
// 实现
}
```
## 3. 代码步骤注释
代码中的关键步骤、复杂逻辑、重要决策点都需要添加注释说明。
### 块注释格式
```go
func ProcessData(data []byte) error {
// 步骤1: 验证数据格式
// 检查数据是否符合预期的 JSON 格式,并包含必要字段
if err := validateFormat(data); err != nil {
return fmt.Errorf("数据格式验证失败: %w", err)
}
// 步骤2: 解析数据内容
// 将 JSON 字节数组转换为结构体,便于后续处理
var payload DataPayload
if err := json.Unmarshal(data, &payload); err != nil {
return fmt.Errorf("数据解析失败: %w", err)
}
// 步骤3: 数据转换
// 将原始数据转换为业务模型,应用必要的转换规则
model := transformToModel(payload)
// 步骤4: 持久化存储
// 将处理后的数据保存到数据库,使用事务确保数据一致性
if err := saveWithTransaction(model); err != nil {
return fmt.Errorf("数据保存失败: %w", err)
}
return nil
}
```
### 行内注释格式
对于单行代码的解释,使用行末注释:
```go
func CalculateScore(items []Item) int {
total := 0
for _, item := range items {
// 权重计算:基础分数乘以优先级系数
weightedScore := item.BaseScore * item.PriorityFactor
total += weightedScore
}
// 应用上限约束,防止分数溢出
if total > MaxScore {
total = MaxScore
}
return total
}
```
### 复杂逻辑注释示例
```go
func (s *Service) ProcessRequest(ctx context.Context, req *Request) (*Response, error) {
// === 预处理阶段 ===
// 验证请求参数完整性
// 必须字段包括ID、Type、Timestamp
if err := s.validateRequest(req); err != nil {
return nil, err
}
// 检查请求是否已处理过
// 通过缓存查询避免重复处理,提高系统效率
if cached, exists := s.cache.Get(req.ID); exists {
// 缓存命中,直接返回缓存结果
return cached.(*Response), nil
}
// === 核心处理阶段 ===
// 根据请求类型选择处理策略
// 不同类型有不同的处理逻辑和性能特征
handler := s.getHandler(req.Type)
if handler == nil {
// 未找到处理器,返回不支持错误
return nil, ErrUnsupportedType
}
// 执行处理逻辑
// 注意:此处可能耗时较长,已通过 ctx 控制超时
result, err := handler.Handle(ctx, req)
if err != nil {
// 处理失败,记录详细错误信息用于排查
s.logger.Error("处理失败", "request_id", req.ID, "error", err)
return nil, err
}
// === 后处理阶段 ===
// 构建响应对象
response := &Response{
ID: req.ID,
Result: result,
Status: StatusSuccess,
Created: time.Now(),
}
// 缓存响应结果,有效期 5 分钟
// 热点请求的缓存可显著降低系统负载
s.cache.Set(req.ID, response, 5*time.Minute)
return response, nil
}
```
### 特殊情况注释
```go
func LoadConfig(path string) (*Config, error) {
data, err := os.ReadFile(path)
if err != nil {
// 文件不存在时返回默认配置,而非错误
// 设计决策:允许用户不提供配置文件,使用系统默认值
if os.IsNotExist(err) {
return DefaultConfig(), nil
}
return nil, err
}
// 注意:此处故意忽略未知字段
// 原因:支持配置文件向后兼容,旧版本配置文件可在新版本中使用
var config Config
decoder := json.NewDecoder(bytes.NewReader(data))
decoder.DisallowUnknownFields = false // 允许未知字段
if err := decoder.Decode(&config); err != nil {
return nil, err
}
return &config, nil
}
```
## 4. 注释最佳实践
### 使用中文注释
所有注释必须使用中文,确保团队成员易于理解:
```go
// 正确 ✓
// 获取用户信息,根据用户 ID 查询数据库返回完整用户对象。
// 错误 ✗
// Get user info by ID from database.
```
### 注释内容要求
注释应说明"为什么"而非仅仅是"是什么"
```go
// 正确 ✓ - 解释原因
// 使用批处理而非逐条处理,原因是:
// 1. 减少 I/O 操作次数,从 N 次降为 1 次
// 2. 降低事务开销,提升整体性能约 10 倍
func batchInsert(items []Item) error {
// ...
}
// 错误 ✗ - 仅重复代码含义
// 循环遍历所有元素
for _, item := range items {
// ...
}
```
### 避免冗余注释
```go
// 正确 ✓ - 提供有价值的信息
// 超时时间设置为 30 秒,因为下游服务 P99 响应时间为 25 秒
const Timeout = 30 * time.Second
// 错误 ✗ - 无意义的注释
// 超时变量
const Timeout = 30 * time.Second
```
### TODO 和 FIXME 注释
使用标准格式标记待办事项:
```go
// TODO(author): 待实现功能的描述
// - 详细说明需要做什么
// - 预计完成时间或优先级
func unfinishedFunction() {
// TODO(xfy): 添加缓存支持,预计 2024-04-01 完成
}
// FIXME(author): 问题描述和修复建议
func problematicFunction() {
// FIXME(xfy): 当前实现在高并发下存在竞态条件
// 需要添加互斥锁保护共享资源
}
```
## 5. 结构体和接口注释
### 结构体注释
```go
// Media 表示系统中的媒体文件对象。
//
// 包含媒体文件的基本信息和元数据,用于媒体管理功能。
// 所有字段在创建后不可修改(不可变设计)。
//
// 注意事项:
// - ID 由系统自动生成,无需手动设置
// - CreatedAt 使用 UTC 时间
type Media struct {
// ID 媒体的唯一标识符,格式为 UUID
ID string `json:"id"`
// Name 原始文件名,保留用户上传时的文件名
Name string `json:"name"`
// Size 文件大小,单位为字节
Size int64 `json:"size"`
// Type 媒体类型,如 "image/jpeg"、"video/mp4"
Type string `json:"type"`
// CreatedAt 创建时间,使用 UTC 时间戳
CreatedAt time.Time `json:"created_at"`
// Metadata 扩展元数据,存储自定义属性
Metadata map[string]string `json:"metadata,omitempty"`
}
```
### 接口注释
```go
// Storage 定义存储后端的抽象接口。
//
// 该接口抽象了不同存储系统的共性操作,支持:
// - 本地文件系统
// - Amazon S3
// - MinIO
// - 其他兼容 S3 API 的存储服务
//
// 实现要求:
// - 所有方法必须是并发安全的
// - 上传方法应支持大文件流式传输
// - 错误返回应使用定义的错误类型
type Storage interface {
// Upload 上传文件到存储后端。
//
// 参数:
// - ctx: 上下文,用于超时控制
// - key: 存储键名,作为文件的唯一标识
// - data: 文件内容字节
//
// 返回值:
// - err: 上传失败时返回错误
Upload(ctx context.Context, key string, data []byte) error
// Download 从存储后端下载文件。
//
// 参数:
// - ctx: 上下文,用于超时控制
// - key: 存储键名
//
// 返回值:
// - data: 文件内容字节
// - err: 文件不存在或其他错误
Download(ctx context.Context, key string) ([]byte, error)
// Delete 从存储后端删除文件。
Delete(ctx context.Context, key string) error
}
```
## 6. 常量和变量注释
### 常量注释
```go
// 定义系统配置常量
const (
// DefaultPort 默认服务端口
// 当配置文件未指定端口时使用此值
DefaultPort = 8080
// MaxUploadSize 最大上传文件大小限制
// 设为 100MB平衡存储成本和用户需求
MaxUploadSize = 100 * 1024 * 1024
// CacheTTL 缓存默认过期时间
// 5 分钟可覆盖大多数热点请求场景
CacheTTL = 5 * time.Minute
)
```
### 变量注释
```go
var (
// ErrStorageUnavailable 存储服务不可用错误
// 当无法连接到存储后端时返回
ErrStorageUnavailable = errors.New("存储服务不可用")
// ErrInvalidMedia 无效媒体错误
// 当媒体文件格式或内容不符合要求时返回
ErrInvalidMedia = errors.New("无效的媒体文件")
// ErrQuotaExceeded 配额超限错误
// 当用户存储空间达到上限时返回
ErrQuotaExceeded = errors.New("存储配额已超限")
)
```
## 7. 包文档注释
每个包应有一个 doc.go 文件或在其主要文件中添加包级注释:
```go
// Package services 提供 Cadmus 的核心业务服务层实现。
//
// 该包包含以下主要服务:
//
// - MediaService: 媒体文件管理服务
// - AuthService: 用户认证和授权服务
// - UserService: 用户信息管理服务
// - PluginService: 插件系统管理服务
//
// 服务设计原则:
//
// 1. 所有服务通过接口定义,便于测试和替换实现
// 2. 服务间通过依赖注入解耦
// 3. 所有公开方法均为并发安全
// 4. 错误使用语义化错误类型,便于调用方处理
//
// 初始化示例:
//
// cfg := services.Config{Storage: storageBackend}
// mediaSvc := services.NewMediaService(cfg)
// authSvc := services.NewAuthService(cfg)
//
// 注意事项:
//
// - 使用前需确保依赖服务已正确初始化
// - 建议通过 services.NewServices() 创建服务集合
// - 所有服务支持通过配置启用/禁用
package services
```
## 8. 注释检查清单
在提交代码前,请检查以下事项:
- [ ] 文件头部是否有完整的文件注释
- [ ] 所有公开函数是否有详细注释(包括参数、返回值、示例)
- [ ] 所有私有函数是否有功能说明注释
- [ ] 关键逻辑步骤是否有注释说明
- [ ] 复杂算法是否有解释其原理的注释
- [ ] 特殊处理是否有解释其原因的注释
- [ ] 结构体及其字段是否有注释
- [ ] 接口及其方法是否有注释
- [ ] 常量和全局变量是否有注释
- [ ] 所有注释是否使用中文
- [ ] TODO/FIXME 是否标注了负责人和时间
---
_本规范适用于 Cadmus 项目所有 Go 源文件。_