Browse Source

chore: 添加 Redis 服务到 Docker Compose 配置

- 新增 redis 服务(redis:7-alpine,AOF 持久化)
- 配置健康检查和数据持久化 volume
- 更新 app 服务环境变量(REDIS_URL、ENABLE_RATE_LIMIT、SESSION_TTL)
- 优化 .env.example 的 Redis 配置注释说明
- 完善 README.md,新增 5 个 Redis 相关 FAQ
- 支持 Fail Open 策略,Redis 不可用时自动降级

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
ding113 5 months ago
parent
commit
72cde07736
3 changed files with 245 additions and 5 deletions
  1. 8 4
      .env.example
  2. 216 1
      README.md
  3. 21 0
      docker-compose.yaml

+ 8 - 4
.env.example

@@ -5,9 +5,13 @@ AUTO_MIGRATE=true
 
 DSN="postgres://user:password@host:port/db_name"
 
-# Redis 配置(限流功能)
-ENABLE_RATE_LIMIT=true
-REDIS_URL=redis://localhost:6379
+# Redis 配置(用于限流和 Session 追踪)
+# 功能说明:
+# - 限流功能:金额限制(5小时/周/月)+ Session 并发限制
+# - Session 追踪:5 分钟上下文缓存优化(避免频繁切换供应商)
+# - Fail Open 策略:Redis 不可用时自动降级,不影响服务可用性
+ENABLE_RATE_LIMIT=true                  # 是否启用限流功能(默认:true)
+REDIS_URL=redis://localhost:6379        # Redis 连接地址(Docker 部署使用 redis://redis:6379)
 
 # Session 配置
-SESSION_TTL=300
+SESSION_TTL=300                         # Session 过期时间(秒,默认 300 = 5 分钟)

+ 216 - 1
README.md

@@ -84,18 +84,37 @@ services:
       retries: 10
       start_period: 10s
 
+  redis:
+    image: redis:7-alpine
+    container_name: claude-code-hub-redis
+    restart: unless-stopped
+    volumes:
+      - redis_data:/data
+    command: redis-server --appendonly yes
+    healthcheck:
+      test: ["CMD", "redis-cli", "ping"]
+      interval: 5s
+      timeout: 3s
+      retries: 5
+      start_period: 5s
+
   app:
     image: ghcr.io/ding113/claude-code-hub:latest
     container_name: claude-code-hub-app
     depends_on:
       postgres:
         condition: service_healthy
+      redis:
+        condition: service_started
     env_file:
       - ./.env
     environment:
       NODE_ENV: production
       PORT: ${APP_PORT:-23000}
       DSN: postgresql://${DB_USER:-postgres}:${DB_PASSWORD:-postgres}@postgres:5432/${DB_NAME:-claude_code_hub}
+      REDIS_URL: redis://redis:6379
+      ENABLE_RATE_LIMIT: ${ENABLE_RATE_LIMIT:-true}
+      SESSION_TTL: ${SESSION_TTL:-300}
     ports:
       - "${APP_PORT:-23000}:${APP_PORT:-23000}"
     restart: unless-stopped
@@ -103,6 +122,8 @@ services:
 volumes:
   postgres_data:
     driver: local
+  redis_data:
+    driver: local
 ```
 
 </details>
@@ -123,7 +144,10 @@ docker compose logs -f
    ```bash
    docker compose ps
    ```
-   确保两个容器都是 `healthy` 或 `running` 状态
+   确保三个容器都是 `healthy` 或 `running` 状态:
+   - `claude-code-hub-db` (PostgreSQL)
+   - `claude-code-hub-redis` (Redis)
+   - `claude-code-hub-app` (应用服务)
 
 
 ### 环境变量配置
@@ -150,6 +174,9 @@ DB_NAME=claude_code_hub
 | `DB_PASSWORD` | ❌ | `postgres` | 数据库密码(生产环境建议修改) |
 | `DB_NAME` | ❌ | `claude_code_hub` | 数据库名称 |
 | `AUTO_MIGRATE` | ❌ | `true` | 启动时自动执行数据库迁移 |
+| `ENABLE_RATE_LIMIT` | ❌ | `true` | 是否启用限流功能(需要 Redis) |
+| `REDIS_URL` | ❌ | `redis://redis:6379` | Redis 连接地址(容器内网络) |
+| `SESSION_TTL` | ❌ | `300` | Session 过期时间(秒,5 分钟) |
 
 </details>
 
@@ -160,10 +187,12 @@ DB_NAME=claude_code_hub
 docker compose logs -f          # 所有服务
 docker compose logs -f app      # 仅应用
 docker compose logs -f postgres # 仅数据库
+docker compose logs -f redis    # 仅 Redis
 
 # 重启服务
 docker compose restart          # 重启所有
 docker compose restart app      # 仅重启应用
+docker compose restart redis    # 仅重启 Redis
 
 # 停止服务
 docker compose stop             # 停止但保留容器
@@ -179,6 +208,12 @@ docker exec claude-code-hub-db pg_dump -U postgres claude_code_hub > backup_$(da
 # 恢复数据
 docker exec -i claude-code-hub-db psql -U postgres claude_code_hub < backup.sql
 
+# Redis 操作
+docker compose exec redis redis-cli ping           # 检查 Redis 连接
+docker compose exec redis redis-cli info stats     # 查看 Redis 统计信息
+docker compose exec redis redis-cli --scan         # 查看所有 key
+docker compose exec redis redis-cli FLUSHALL       # ⚠️ 清空所有 Redis 数据
+
 # 完全清理(⚠️ 会删除所有数据)
 docker compose down -v
 ```
@@ -437,6 +472,186 @@ docker stats claude-code-hub-app claude-code-hub-db
 
 </details>
 
+<details>
+<summary><b>❓ Redis 连接失败怎么办?</b></summary>
+
+本服务采用 **Fail Open 策略**,Redis 连接失败不会影响服务可用性:
+
+1. **检查 Redis 状态**:
+   ```bash
+   docker compose ps redis
+   docker compose logs redis
+   ```
+
+2. **验证 Redis 连接**:
+   ```bash
+   docker compose exec redis redis-cli ping
+   # 应返回 PONG
+   ```
+
+3. **检查应用日志**:
+   ```bash
+   docker compose logs app | grep -i redis
+   # 查看是否有 Redis 连接错误
+   ```
+
+4. **降级模式**:
+   - Redis 不可用时,限流功能会自动降级
+   - 所有请求仍然正常通过
+   - 日志会记录警告信息:"Redis connection failed, rate limiting disabled"
+
+5. **重启 Redis 服务**:
+   ```bash
+   docker compose restart redis
+   ```
+
+</details>
+
+<details>
+<summary><b>❓ 如何查看 Redis 数据?</b></summary>
+
+**查看存储的 Key**:
+```bash
+# 查看所有 key
+docker compose exec redis redis-cli --scan
+
+# 查看特定模式的 key
+docker compose exec redis redis-cli --scan --pattern "key:*"
+docker compose exec redis redis-cli --scan --pattern "provider:*"
+docker compose exec redis redis-cli --scan --pattern "session:*"
+```
+
+**查看 Key 的值**:
+```bash
+# 查看字符串类型的值(成本数据)
+docker compose exec redis redis-cli GET "key:123:cost_5h"
+
+# 查看集合类型的值(活跃 Session)
+docker compose exec redis redis-cli SMEMBERS "provider:1:active_sessions"
+
+# 查看 Key 的 TTL
+docker compose exec redis redis-cli TTL "session:abc123:last_seen"
+```
+
+**实时监控 Redis 命令**:
+```bash
+docker compose exec redis redis-cli MONITOR
+```
+
+**查看 Redis 统计信息**:
+```bash
+docker compose exec redis redis-cli info stats
+docker compose exec redis redis-cli info memory
+```
+
+</details>
+
+<details>
+<summary><b>❓ 如何清空 Redis 缓存?</b></summary>
+
+**清空所有数据**(⚠️ 谨慎操作):
+```bash
+docker compose exec redis redis-cli FLUSHALL
+```
+
+**清空特定 Key**:
+```bash
+# 删除特定用户的限流数据
+docker compose exec redis redis-cli DEL "key:123:cost_5h"
+docker compose exec redis redis-cli DEL "key:123:cost_weekly"
+
+# 删除所有 Session 数据
+docker compose exec redis redis-cli EVAL "
+  local keys = redis.call('keys', 'session:*')
+  for i=1,#keys do
+    redis.call('del', keys[i])
+  end
+  return #keys
+" 0
+```
+
+**重启 Redis 但保留数据**:
+```bash
+docker compose restart redis
+```
+
+**完全清空并重建**(⚠️ 会丢失所有 Redis 数据):
+```bash
+docker compose stop redis
+docker volume rm claude-code-hub_redis_data
+docker compose up -d redis
+```
+
+</details>
+
+<details>
+<summary><b>❓ Redis 数据会持久化吗?</b></summary>
+
+✅ **会持久化**,配置了双重保障:
+
+1. **AOF(Append Only File)持久化**:
+   - 每次写操作都会追加到日志文件
+   - 配置:`redis-server --appendonly yes`
+   - 重启后自动恢复数据
+
+2. **Docker Volume 持久化**:
+   - 数据存储在 `redis_data` volume
+   - 即使删除容器,数据仍然保留
+   - 查看 volume:`docker volume ls | grep redis`
+
+**数据恢复**:
+- 正常重启:数据自动恢复
+- 迁移到新机器:复制 `/var/lib/docker/volumes/claude-code-hub_redis_data` 目录
+
+**备份 Redis 数据**:
+```bash
+# 手动触发保存
+docker compose exec redis redis-cli BGSAVE
+
+# 导出 AOF 文件
+docker cp claude-code-hub-redis:/data/appendonly.aof ./redis_backup_$(date +%Y%m%d).aof
+```
+
+**注意事项**:
+- ⚠️ `docker compose down -v` 会删除 volume,包括 Redis 数据
+- ✅ `docker compose down` 或 `docker compose stop` 不会删除数据
+
+</details>
+
+<details>
+<summary><b>❓ 限流功能如何工作?</b></summary>
+
+**限流机制**:
+
+1. **金额限流**(三个时间窗口):
+   - 5 小时限制:`key:{keyId}:cost_5h`
+   - 周限制:`key:{keyId}:cost_weekly`
+   - 月限制:`key:{keyId}:cost_monthly`
+
+2. **Session 并发限流**:
+   - 追踪活跃 Session 数量(5 分钟 TTL)
+   - 防止恶意并发请求
+   - Key: `key:{keyId}:active_sessions`
+
+3. **供应商限流**:
+   - 保护上游供应商
+   - 类似机制:`provider:{id}:cost_*` 和 `active_sessions`
+
+**响应头示例**(触发限流时):
+```http
+HTTP/1.1 429 Too Many Requests
+X-RateLimit-Limit: 100
+X-RateLimit-Remaining: 0
+X-RateLimit-Reset: 3600
+Retry-After: 3600
+```
+
+**禁用限流**:
+- 设置环境变量 `ENABLE_RATE_LIMIT=false`
+- 重启服务:`docker compose restart app`
+
+</details>
+
 ## 🤝 贡献
 
 欢迎提交 Issue 和 Pull Request!

+ 21 - 0
docker-compose.yaml

@@ -21,12 +21,28 @@ services:
       retries: 10
       start_period: 10s
 
+  redis:
+    image: redis:7-alpine
+    container_name: claude-code-hub-redis
+    restart: unless-stopped
+    volumes:
+      - redis_data:/data
+    command: redis-server --appendonly yes
+    healthcheck:
+      test: ["CMD", "redis-cli", "ping"]
+      interval: 5s
+      timeout: 3s
+      retries: 5
+      start_period: 5s
+
   app:
     image: ghcr.io/ding113/claude-code-hub:latest
     container_name: claude-code-hub-app
     depends_on:
       postgres:
         condition: service_healthy
+      redis:
+        condition: service_started
     env_file:
       - ./.env
       - ./.env.local
@@ -34,6 +50,9 @@ services:
       NODE_ENV: production
       PORT: ${APP_PORT:-23000}
       DSN: postgresql://${DB_USER:-postgres}:${DB_PASSWORD:-postgres}@postgres:5432/${DB_NAME:-claude_code_hub}
+      REDIS_URL: redis://redis:6379
+      ENABLE_RATE_LIMIT: ${ENABLE_RATE_LIMIT:-true}
+      SESSION_TTL: ${SESSION_TTL:-300}
     ports:
       - "${APP_PORT:-23000}:${APP_PORT:-23000}"
     restart: unless-stopped
@@ -41,3 +60,5 @@ services:
 volumes:
   postgres_data:
     driver: local
+  redis_data:
+    driver: local