生产环境部署前,r.run() 必须显式配置端口、tls、超时、日志路径及配置加载顺序:端口需从环境变量读取并设fallback;.env加载须在main开头且路径正确;viper需显式设路径/类型,yaml禁用tab缩进;应使用gin.new()自定义中间件与日志,http server须显式设置read/write超时。

直接上生产环境前,r.Run() 不能裸奔——端口、TLS、超时、日志输出路径、配置加载顺序,任何一个没对齐,服务就可能启动失败、监听错端口、或暴露调试信息。
端口和地址绑定必须显式控制
默认 r.Run() 绑定 :8080,但生产环境往往要求固定 IP + 非 root 端口(如 0.0.0.0:8080)或反向代理后只监听本地(127.0.0.1:8080)。硬编码或依赖环境变量但未校验,会导致启动时 panic 或监听失败。
-
r.Run()不传参会用:8080,但实际应从配置或环境变量读取,且需 fallback 判断:port := os.Getenv("PORT"); if port == "" { port = "8080" } - 不要写
r.Run("localhost:8080")—— 这会让服务只响应本机请求,外部 Nginx 反代会超时 - 若需 HTTPS,别用
r.RunTLS()硬塞证书路径;应提前读入内存并校验,避免启动时因文件权限/路径错误直接退出
os.Getenv 读不到环境变量?时机错了
os.Getenv("PORT") 返回空字符串,不是 Gin 的锅,是调用位置不对。Gin 启动前,所有配置必须已就绪;如果 godotenv.Load() 放在 r := gin.Default() 之后,或放在 handler 里,变量就永远读不到。
-
godotenv.Load(".env")必须放在main()函数最开头,且确保当前工作目录正确(go run默认从执行命令的路径找 .env) - Linux/macOS 下变量名严格区分大小写:
PORT=8080和port=8080是两个变量 - 容器部署时,环境变量由
docker run -e PORT=8080注入,但必须确保镜像启动命令执行顺序:先设环境,再跑go run或二进制
Viper 配置加载失败?路径和类型必须配对
viper.ReadInConfig() 报 “config file not found” 或 “unsupported config type”,基本是路径没加、后缀没认、缩进写错三类问题。Viper 不会自动猜路径,也不会忽略 YAML 的 tab 缩进。
- 必须显式调用
viper.AddConfigPath("configs/")和viper.AddConfigPath("."),否则即使文件存在也找不到 - 写了
viper.SetConfigFile("./config.yaml")就不能再调viper.SetConfigName(),否则冲突;统一用SetConfigName+SetConfigType+AddConfigPath更稳 - YAML 文件必须用空格缩进,tab 会导致解析失败(错误不明显,常表现为 key 为空或结构体字段零值)
- 多环境覆盖要用
MergeInConfig(),不是反复ReadInConfig()—— 后者会清空已有配置
日志和 panic 恢复不能依赖 gin.Default()
gin.Default() 自带 Logger 和 Recovery 中间件,但生产环境要改写日志输出目标(如写文件)、控制日志级别、捕获 panic 后上报 Sentry,而不是只打屏。
- 用
gin.New()替代gin.Default(),手动注册中间件:r.Use(gin.RecoveryWithWriter(customWriter)) - 自定义
LoggerWithFormatter时,注意param.ErrorMessage在无 panic 时为空,别直接拼接导致 panic - HTTP 超时必须显式设置:
srv := &http.Server{Addr: addr, Handler: r, ReadTimeout: 5 * time.Second, WriteTimeout: 10 * time.Second},否则连接空闲太久会卡住连接池
最常被跳过的其实是配置加载顺序和 HTTP Server 超时控制——前者让服务启不来,后者让服务看起来正常却悄悄积压连接。这两点不验证,上线后第一波流量就可能触发雪崩。











