Files
tamabox/internal/shared/redis.go
T
tamakyi 11d6e701ee fix(ops): 访问日志脱敏、优雅关闭、Redis 可降级并启动即探活
三处都属于「故障时才发现」的运维缺口,一起改。

1) 访问日志泄露凭证
   flamego.Classic() 内部 Use(Logger()),而 Logger 记的是 RequestURI —— 含 query。
   实测日志出现 path=/_/tblocal/4?t=SECRETTOKEN123:?t= 是私密提问钥匙,
   ?code= 是一次性改密凭证,?q= 是搜索词,全都会被明文落盘,凡是能看日志的人
   都能直接读私密提问或重置密码。internal/tracing 早就为同一原因只取 URL.Path
   并留了注释,漏的是框架自带那层。
   → 改 flamego.New() + accessLogMiddleware,只记 Method/Path/Status/Duration/IP;
     静态资源成功请求不记(噪音),4xx/5xx 照记(排查裂图靠它)。

2) 没有优雅关闭
   直接 ListenAndServe,Go 对 SIGTERM 是立即退出,每次部署都会把正在写库的 POST
   就地处死,留下「提问已 INSERT、图片没传完」这类半成品。
   → signal.NotifyContext(SIGTERM/SIGINT) + srv.Shutdown(15s),超时才 Close;
     退出前关连接池。实测 8 个并发分享图请求中发 SIGTERM,8/8 全部成功。

3) Redis 单点
   session 建在 Redis 上,load 失败 flamego/session 会 panic → 每请求 500(含匿名访客);
   而 cache.Cacher 没判空 Addr,go-redis 会回退 localhost:6379,于是「不配 Redis」
   这种被允许的部署里找回密码整体不可用、私信邮件节流失效,且启动零提示。
   → cache 跟随开关(未配则 MemoryIniter);启动 PingRedis fail-fast,显式 2s 超时。
     实测停掉 Redis 后启动立即失败并明确指出是 Redis。

验证:单测 2 项(query 永不入日志 / 静态资源过滤口径);重启日志出现
「收到退出信号」→「HTTP 服务已关闭」;启动打「Redis 连接正常」。
2026-10-29 15:53:32 +08:00

46 lines
1.7 KiB
Go
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.
// Copyright 2026 tamakyi. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package shared
import (
"context"
"time"
"github.com/redis/go-redis/v9"
)
// RedisPingTimeout 是启动期探活的总超时。
// 刻意短:启动阶段卡住比启动失败更难排查,3 秒足够区分「连不上」与「慢」。
const RedisPingTimeout = 3 * time.Second
// PingRedis 在启动时探一次 Redis 连通性,用于 fail fast。
//
// 为什么必须做这件事:session 建在 Redis 上(route.go 的 sessioner),而
// flamego/session 对 load 错误是直接 panic,被 Recovery 兜成 500 ——
// 也就是 **Redis 一宕,每个请求都 500,包括完全未登录的访客**。
// 等到第一个真实请求才暴露时,运维看到的现场是「站点全 500」,
// 很难第一时间联想到 Redis;启动期直接报错退出,信号干净得多。
//
// 同时显式设置读写超时:go-redis 默认 Dial 5s / Read 3s,Redis 半死不活
// (进程在但不响应)时会让每个请求白等这几秒,表现为整站变慢而无报错。
func PingRedis(addr, password string) error {
ctx, cancel := context.WithTimeout(context.Background(), RedisPingTimeout)
defer cancel()
rdb := redis.NewClient(&redis.Options{
Addr: addr,
Password: password,
// DB 1 与 route.go 里 session 用的库保持一致:探活要探真正会用到的那一个,
// 否则「DB 0 能用、DB 1 被 ACL 禁掉」这种配置错误探不出来。
DB: 1,
DialTimeout: 2 * time.Second,
ReadTimeout: 2 * time.Second,
WriteTimeout: 2 * time.Second,
})
defer func() { _ = rdb.Close() }()
return rdb.Ping(ctx).Err()
}