项目目录结构

Go 官网并没有给出一个目录结构的标准模板,但是 golang-standards 倒是给出了一个。

中文readme

在此基础上,我的项目目录结构具体使用如下:【持续更新!!!】

https://github.com/gw-gong/template_project

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
.
├── api
│   ├── router
│   │   ├── service01.go
│   │   └── service02.go
│   └── rpc
│   ├── <service_name>.pb.go
│   ├── <service_name>.proto
│   └── makefile
├── cmd
│   ├── service01
│   │   └── main.go
│   └── service02
│   └── main.go
├── config
│   ├── protocol
│   │   └── ...
│   ├── service01
│   │   ├── common
│   │   │   ├── config.go
│   │   │   └── config.yaml
│   │   └── net
│   │   ├── config.go
│   │   └── config.yaml
│   └── service02
│   ├── common
│   │   ├── config.go
│   │   └── config.yaml
│   └── net
│   ├── config.go
│   └── config.yaml
├── docs
│   └── <diy>
├── go.mod
├── internal
│   ├── app
│   │   ├── service01
│   │   │   ├── handler
│   │   │   │   ├── handler.go
│   │   │   │   └── protocol.go
│   │   │   └── rpc
│   │   │   └── <service_name>.go
│   │   └── service02
│   │   ├── handler
│   │   │   ├── handler.go
│   │   │   └── protocol.go
│   │   └── rpc
│   │   └── <service_name>.go
│   └── pkg
│   ├── protocol
│   │   └── <structs_define>
│   ├── db
│   │   ├── mysql_client
│   │   │   ├── interface_and_impl.go
│   │   │   └── mysql.go
│   │   └── redis_client
│   │   ├── interface_and_impl.go
│   │   └── redis.go
│   ├── middleware
│   │   └── <middleware_name>.go
│   └── util
│   └── some_tools.go
└── web
├── <page01>.html
├── <page02>.html
├── error_pages
│   └── ...
└── static
├── css
│   └── xxx.css
└── js
└── xxx.js




Request ID

1. Request ID 是什么?

  • 定义:唯一标识一次请求的字符串,用于日志追踪、全链路监控。
  • 作用
    • 关联同一请求的所有日志(前端→网关→服务→数据库)。
    • 快速定位故障点(如错误日志中查找特定 Request ID)。
    • 支持分布式系统中的请求链路分析。

2. 生成方式

方案 长度 唯一性 有序性 适用场景
UUIDv4 36 字符 全球唯一 无序 高并发、跨系统追踪
ULID 26 字符 全球唯一 有序 需要时间排序的场景
自定义短 ID 16-20 字符 业务内唯一 可选 存储 / 传输成本敏感场景
哈希截取 8-12 字符 有极低冲突概率 无序 内部系统、临时追踪

代码示例(Go)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import (
"github.com/google/uuid"
"github.com/oklog/ulid/v2"
"crypto/rand"
"encoding/base64"
)

// UUIDv4
uuid.New().String() // "550e8400-e29b-41d4-a716-446655440000"

// ULID(有序且唯一)
ulid.MustNew(ulid.Timestamp(time.Now()), rand.Reader).String() // "01ARZ3NDEKTSV4RRFFQ69G5FAV"

// 自定义短ID(8字节随机数 → Base64编码后约11字符)
func ShortID() string {
b := make([]byte, 8)
rand.Read(b)
return base64.RawURLEncoding.EncodeToString(b) // 如 "abc123xyz789"
}

3. 存储位置选择

位置 优点 缺点 适用场景
HTTP Header 无侵入、性能高、标准实践 前端需特殊处理(跨域) 分布式系统、API 网关
JSON Body 用户可见、兼容性强 侵入业务结构、需解析 JSON 客服系统、单体应用
同时使用 兼顾追踪效率和用户体验 增加传输开销 对可见性和自动化都有要求的场景

最佳实践:优先使用 Header(如 X-Request-ID),必要时补充到 Body。


4. 命名规范

  • X-Request-ID:
    • X- 前缀:标识自定义 HTTP Header(避免与标准字段冲突)。
    • Request-ID:明确语义为 “请求唯一标识”。
  • 其他常见命名:
    • X-Trace-ID:侧重分布式追踪链路。
    • X-Correlation-ID:关联多个相关请求(如批量操作)。

5. 代码实现(Go)

中间件生成并设置 Request ID

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func RequestIDMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 从请求中获取或生成新的 Request ID
reqID := r.Header.Get("X-Request-ID")
if reqID == "" {
reqID = uuid.New().String()
}

// 设置到响应 Header
w.Header().Set("X-Request-ID", reqID)

// 存入上下文,用于后续日志
ctx := context.WithValue(r.Context(), "requestID", reqID)
next.ServeHTTP(w, r.WithContext(ctx))
})
}

日志中记录 Request ID

1
2
3
4
5
func MyHandler(w http.ResponseWriter, r *http.Request) {
reqID := r.Context().Value("requestID").(string)
log.Printf("[%s] 处理请求", reqID) // 日志中包含 Request ID
// ...
}

可选:同时返回 Request ID 到 Body

1
2
3
4
5
response := map[string]interface{}{
"data": "业务数据",
"request_id": reqID, // 放入响应体
}
json.NewEncoder(w).Encode(response)

6. 注意事项

  1. 跨域问题
    若需前端获取 Header,服务器需配置:

    1
    w.Header().Set("Access-Control-Expose-Headers", "X-Request-ID")
  2. 性能优化

    • 使用 ULID 替代 UUID(有序性减少数据库索引碎片)。
    • 短 ID 减少存储 / 传输开销(如 12 字符 Base62 编码)。
  3. 兼容性

    • 与 OpenTracing、W3C Trace Context 等标准结合时,考虑字段映射(如 traceparent)。

7. 常见问题

问题 解决方案
日志分散无法关联 在所有服务的日志中强制添加 Request ID
前端获取不到 Header 配置 CORS Access-Control-Expose-Headers
Request ID 生成性能瓶颈 使用预生成池或原子操作生成 ID
长 ID 导致存储成本高 使用短 ID 或压缩算法(如 Base62)

8. 工具推荐

  • Go 库:
    • github.com/google/uuid:生成 UUIDv4。
    • github.com/oklog/ulid:生成有序 ULID。
    • go.uber.org/zap:日志库支持上下文传递 Request ID。
  • 分布式追踪:
    • Jaeger、Zipkin、Skywalking 等自动注入 Trace ID。