Files
cache-proxy/memory-bank/systemPatterns.md
guochao 2a0bd28958
Some checks failed
build container / build-container (push) Has been cancelled
use cline and prepare to test the project for further development
2025-06-10 14:14:32 +08:00

47 lines
2.6 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.

# 系统架构与设计模式
`cache-proxy` 的系统设计遵循了几个关键的模式和原则,以实现其高性能和高可用性的目标。
## 核心架构
系统可以分为三个主要部分:
1. **HTTP 服务器层**: 负责接收客户端请求,并使用中间件进行日志记录、错误恢复等通用处理。
2. **缓存处理层**: 检查请求的文件是否存在于本地缓存中,并根据缓存策略决定是直接提供缓存文件还是向上游请求。
3. **上游选择与下载层**: 这是系统的核心,负责并发地从多个上游服务器获取数据,并管理下载过程。
## 关键设计模式
### 1. 竞争式请求 (Racing Requests)
这是实现“选择最快”功能的核心模式。
- `fastesUpstream` 函数为每个上游服务器创建一个 goroutine。
- 所有 goroutine 并发地向上游服务器发送请求。
- 使用 `sync.Once` 来确保只有一个 goroutine 能够“胜出”并成为最终的数据源。
- 一旦有 goroutine 胜出,它会调用 `context.CancelFunc` 来通知所有其他 goroutine 停止工作,从而避免不必要的资源消耗。
### 2. 生产者-消费者模式 (Producer-Consumer)
在文件下载过程中,使用了生产者-消费者模式。
- **生产者**: `tryUpstream` 函数中的 goroutine 负责从上游服务器读取数据块chunk并将其放入一个 `chan Chunk` 中。
- **消费者**: `streamOnline` 函数中的代码从 `chan Chunk` 中读取数据,并执行两个操作:
1. 将数据写入 `bytes.Buffer`,供后续的请求者使用。
2. 将数据写入本地临时文件,用于持久化缓存。
### 3. 并发访问控制 (Mutex for Concurrent Access)
为了处理多个客户端同时请求同一个文件的情况,系统使用了 `sync.Mutex` 和一个 `map[string]*StreamObject`
- 当第一个请求到达时,它会获得一个锁,并创建一个 `StreamObject` 来代表这个正在进行的下载任务。
- 后续对同一文件的请求会发现 `StreamObject` 已存在,它们不会再次向上游发起请求,而是会等待并从这个共享的 `StreamObject` 中读取数据。
- 下载完成后,`StreamObject` 会从 map 中移除。
### 4. 中间件 (Middleware)
项目使用了 Go 的标准 `http.Handler` 接口和中间件模式来构建请求处理链。
- `pkgs/middleware` 目录中定义了可重用的中间件,如 `httplog``recover`
- 这种模式使得在不修改核心业务逻辑的情况下,可以轻松地添加或删除日志、认证、错误处理等功能。