From 6323b4951710b4d41948783887651b5ca87fd6d3 Mon Sep 17 00:00:00 2001 From: Jimmy Xue Date: Sun, 30 Nov 2025 14:53:54 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0Redis=E7=9A=84=E8=B0=83?= =?UTF-8?q?=E6=95=B4=EF=BC=8C=E7=9B=B4=E6=8E=A5=E8=BF=94=E5=9B=9Eredis?= =?UTF-8?q?=E5=AF=B9=E8=B1=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 13 +-- TROUBLESHOOTING.md | 208 ++++++++++++++++++++++++++++++++++++ docs/factory.md | 48 ++++++--- examples/factory_example.go | 28 +++-- factory/factory.go | 61 ++++++++++- go.mod | 5 +- go.sum | 15 ++- 7 files changed, 342 insertions(+), 36 deletions(-) create mode 100644 TROUBLESHOOTING.md diff --git a/README.md b/README.md index ed646aa..0d30876 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,8 @@ go env GOPRIVATE **详细配置说明请参考 [SETUP.md](./SETUP.md)** +**遇到问题?请查看 [故障排除指南](./TROUBLESHOOTING.md)** + ### 2. 安装模块 ```bash @@ -200,14 +202,9 @@ fac, _ := factory.NewFactoryFromFile("./config.json") db, _ := fac.GetDatabase() db.Find(&users) // 直接使用,无需再创建连接 -// 获取Redis配置(用于创建Redis客户端) -redisConfig := fac.GetRedisConfig() -// 使用go-redis创建客户端: -// rdb := redis.NewClient(&redis.Options{ -// Addr: fmt.Sprintf("%s:%d", redisConfig.Host, redisConfig.Port), -// Password: redisConfig.Password, -// DB: redisConfig.Database, -// }) +// 直接获取Redis客户端(已初始化,可直接使用) +redisClient, _ := fac.GetRedisClient() +val, _ := redisClient.Get(ctx, "key").Result() // 直接获取已初始化的客户端(无需重复实现创建逻辑) emailClient, _ := fac.GetEmailClient() diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md new file mode 100644 index 0000000..9b186ae --- /dev/null +++ b/TROUBLESHOOTING.md @@ -0,0 +1,208 @@ +# 故障排除指南 + +## 常见问题 + +### 1. 伪版本错误 (Pseudo-version error) + +**错误信息:** +``` +go: github.com/pmezard/go-difflib@v1.1.1-0.20181226105442-5d4384ee4fb2: invalid pseudo-version: preceding tag (v1.1.0) not found +``` + +**原因:** +- Go 模块缓存损坏 +- 依赖版本冲突 +- 间接依赖使用了无效的伪版本 + +**解决方案:** + +#### 方案1:清理模块缓存(推荐) + +```bash +# 清理 Go 模块缓存 +go clean -modcache + +# 重新下载依赖 +go mod download + +# 整理依赖 +go mod tidy +``` + +#### 方案2:在调用方项目中解决 + +如果是在调用方项目中遇到此问题: + +```bash +# 进入调用方项目目录 +cd /path/to/your/project + +# 清理模块缓存 +go clean -modcache + +# 删除 go.sum 文件(可选,会自动重新生成) +rm go.sum + +# 重新获取依赖 +go get git.toowon.com/jimmy/go-common@v0.0.1 + +# 整理依赖 +go mod tidy +``` + +#### 方案3:使用代理(如果网络问题) + +```bash +# 设置 Go 代理(国内用户推荐) +go env -w GOPROXY=https://goproxy.cn,direct + +# 或者使用官方代理 +go env -w GOPROXY=https://proxy.golang.org,direct +``` + +#### 方案4:强制更新依赖 + +```bash +# 强制更新所有依赖 +go get -u ./... + +# 或者更新特定依赖 +go get -u git.toowon.com/jimmy/go-common@latest +``` + +### 2. 私有仓库访问问题 + +**错误信息:** +``` +go: git.toowon.com/jimmy/go-common@v0.0.1: unrecognized import path +``` + +**解决方案:** + +```bash +# 配置 GOPRIVATE +go env -w GOPRIVATE=git.toowon.com + +# 配置 Git 认证(如果需要) +git config --global url."git@git.toowon.com:".insteadOf "https://git.toowon.com/" +``` + +详细说明请参考 [SETUP.md](./SETUP.md) + +### 3. 版本标签不存在 + +**错误信息:** +``` +go: git.toowon.com/jimmy/go-common@v0.0.1: invalid version: unknown revision +``` + +**解决方案:** + +1. 确认版本标签已创建并推送: + ```bash + # 在库项目中查看标签 + git tag -l + + # 如果标签不存在,创建并推送 + git tag -a v0.0.1 -m "Release v0.0.1" + git push origin v0.0.1 + ``` + +2. 在调用方项目中清理缓存后重试: + ```bash + go clean -modcache + go get git.toowon.com/jimmy/go-common@v0.0.1 + ``` + +### 4. 依赖版本冲突 + +**错误信息:** +``` +go: conflicting versions for module +``` + +**解决方案:** + +```bash +# 查看依赖树 +go mod graph | grep conflicting-module + +# 更新冲突的依赖 +go get -u conflicting-module@latest + +# 整理依赖 +go mod tidy +``` + +### 5. 网络连接问题 + +**错误信息:** +``` +dial tcp: lookup proxy.golang.org: no such host +``` + +**解决方案:** + +```bash +# 使用国内代理 +go env -w GOPROXY=https://goproxy.cn,direct + +# 或者使用七牛云代理 +go env -w GOPROXY=https://goproxy.io,direct + +# 禁用代理(直接访问) +go env -w GOPROXY=direct +``` + +## 通用排查步骤 + +如果遇到其他问题,按以下步骤排查: + +1. **清理缓存** + ```bash + go clean -modcache + ``` + +2. **验证模块** + ```bash + go mod verify + ``` + +3. **整理依赖** + ```bash + go mod tidy + ``` + +4. **查看依赖图** + ```bash + go mod graph + ``` + +5. **查看模块信息** + ```bash + go list -m all + ``` + +6. **检查 Go 环境** + ```bash + go env + ``` + +## 获取帮助 + +如果以上方法都无法解决问题,请: + +1. 检查 Go 版本(建议使用 Go 1.21 或更高版本) + ```bash + go version + ``` + +2. 查看详细的错误信息 + ```bash + go get -v git.toowon.com/jimmy/go-common@v0.0.1 + ``` + +3. 检查项目仓库是否有对应的版本标签 + +4. 联系项目维护者 + diff --git a/docs/factory.md b/docs/factory.md index f1b5180..510e0c2 100644 --- a/docs/factory.md +++ b/docs/factory.md @@ -62,19 +62,29 @@ db.Find(&users) db.Create(&user) ``` -### 4. 获取Redis配置 +### 4. 获取Redis客户端(已初始化,推荐) ```go -// 获取Redis配置(用于创建Redis客户端) -// 注意:Go标准库没有Redis客户端,需要调用方使用第三方库创建 -redisConfig := fac.GetRedisConfig() -if redisConfig != nil { - // 使用go-redis创建客户端示例: - // rdb := redis.NewClient(&redis.Options{ - // Addr: fmt.Sprintf("%s:%d", redisConfig.Host, redisConfig.Port), - // Password: redisConfig.Password, - // DB: redisConfig.Database, - // }) +import ( + "context" + "github.com/redis/go-redis/v9" +) + +// 直接获取已初始化的Redis客户端对象 +redisClient, err := fac.GetRedisClient() +if err != nil { + log.Fatal(err) +} + +// 直接使用,无需再创建连接 +ctx := context.Background() +val, err := redisClient.Get(ctx, "key").Result() +if err != nil && err != redis.Nil { + log.Printf("Redis error: %v", err) +} else if err == redis.Nil { + fmt.Println("Key not found") +} else { + fmt.Printf("Value: %s\n", val) } ``` @@ -244,6 +254,18 @@ func main() { - 自动配置连接池参数 - 数据库时间统一使用UTC时区 +### (f *Factory) GetRedisClient() (*redis.Client, error) + +获取Redis客户端对象(已初始化)。 + +**返回:** 已初始化的Redis客户端对象和错误信息 + +**说明:** +- 自动处理所有配置检查和连接测试 +- 自动设置默认值(连接池大小、超时时间等) +- 连接失败时会自动关闭客户端并返回错误 +- 返回的客户端已通过Ping测试,可直接使用 + ### (f *Factory) GetRedisConfig() *config.RedisConfig 获取Redis配置(用于创建Redis客户端)。 @@ -251,8 +273,8 @@ func main() { **返回:** Redis配置对象(可能为nil) **说明:** -- Go标准库没有Redis客户端,需要调用方使用第三方库(如go-redis/redis)创建 -- 返回配置对象,调用方可以根据配置创建Redis客户端 +- 推荐使用 `GetRedisClient()` 方法直接获取已初始化的客户端 +- 如果需要自定义创建Redis客户端,可以使用此方法获取配置 ### (f *Factory) GetConfig() *config.Config diff --git a/examples/factory_example.go b/examples/factory_example.go index 8b3a710..58792be 100644 --- a/examples/factory_example.go +++ b/examples/factory_example.go @@ -1,10 +1,12 @@ package main import ( + "context" "fmt" "log" "git.toowon.com/jimmy/go-common/factory" + "github.com/redis/go-redis/v9" ) func main() { @@ -28,17 +30,21 @@ func main() { } } - // 获取Redis配置(用于创建Redis客户端) - redisConfig := fac.GetRedisConfig() - if redisConfig != nil { - fmt.Printf("Redis config: %s:%d\n", redisConfig.Host, redisConfig.Port) - // 使用go-redis创建客户端示例: - // import "github.com/redis/go-redis/v9" - // rdb := redis.NewClient(&redis.Options{ - // Addr: fmt.Sprintf("%s:%d", redisConfig.Host, redisConfig.Port), - // Password: redisConfig.Password, - // DB: redisConfig.Database, - // }) + // 直接获取Redis客户端(已初始化,可直接使用) + redisClient, err := fac.GetRedisClient() + if err != nil { + log.Printf("Redis not available: %v", err) + } else { + // 直接使用Redis客户端 + ctx := context.Background() + val, err := redisClient.Get(ctx, "test_key").Result() + if err != nil && err != redis.Nil { + log.Printf("Redis error: %v", err) + } else if err == redis.Nil { + fmt.Println("Redis key not found") + } else { + fmt.Printf("Redis value: %s\n", val) + } } // 获取邮件客户端(已初始化,可直接使用) diff --git a/factory/factory.go b/factory/factory.go index ef81cab..787ac3b 100644 --- a/factory/factory.go +++ b/factory/factory.go @@ -1,6 +1,7 @@ package factory import ( + "context" "fmt" "time" @@ -8,6 +9,7 @@ import ( "git.toowon.com/jimmy/go-common/email" "git.toowon.com/jimmy/go-common/logger" "git.toowon.com/jimmy/go-common/sms" + "github.com/redis/go-redis/v9" "gorm.io/driver/mysql" "gorm.io/driver/postgres" "gorm.io/driver/sqlite" @@ -113,9 +115,66 @@ func (f *Factory) GetDatabase() (*gorm.DB, error) { return db, nil } +// GetRedisClient 获取Redis客户端对象(已初始化) +// 返回已初始化的Redis客户端对象,可直接使用 +func (f *Factory) GetRedisClient() (*redis.Client, error) { + if f.cfg.Redis == nil { + return nil, fmt.Errorf("redis config is nil") + } + + // 获取Redis地址 + addr := f.cfg.GetRedisAddr() + if addr == "" { + return nil, fmt.Errorf("redis address is empty") + } + + // 设置默认值 + redisConfig := f.cfg.Redis + if redisConfig.PoolSize == 0 { + redisConfig.PoolSize = 10 // 默认连接池大小 + } + if redisConfig.MinIdleConns == 0 { + redisConfig.MinIdleConns = 5 // 默认最小空闲连接数 + } + if redisConfig.DialTimeout == 0 { + redisConfig.DialTimeout = 5 // 默认连接超时5秒 + } + if redisConfig.ReadTimeout == 0 { + redisConfig.ReadTimeout = 3 // 默认读取超时3秒 + } + if redisConfig.WriteTimeout == 0 { + redisConfig.WriteTimeout = 3 // 默认写入超时3秒 + } + + // 创建Redis客户端 + client := redis.NewClient(&redis.Options{ + Addr: addr, + Password: redisConfig.Password, + DB: redisConfig.Database, + PoolSize: redisConfig.PoolSize, + MinIdleConns: redisConfig.MinIdleConns, + MaxRetries: redisConfig.MaxRetries, + DialTimeout: time.Duration(redisConfig.DialTimeout) * time.Second, + ReadTimeout: time.Duration(redisConfig.ReadTimeout) * time.Second, + WriteTimeout: time.Duration(redisConfig.WriteTimeout) * time.Second, + }) + + // 测试连接 + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(redisConfig.DialTimeout)*time.Second) + defer cancel() + + _, err := client.Ping(ctx).Result() + if err != nil { + client.Close() // 连接失败时关闭客户端 + return nil, fmt.Errorf("failed to connect to redis: %w", err) + } + + return client, nil +} + // GetRedisConfig 获取Redis配置(用于创建Redis客户端) // 返回Redis配置对象,调用方可以使用此配置创建Redis客户端 -// 注意:Go标准库没有Redis客户端,需要调用方使用第三方库(如go-redis/redis)创建 +// 注意:推荐使用 GetRedisClient 方法直接获取已初始化的客户端 func (f *Factory) GetRedisConfig() *config.RedisConfig { return f.cfg.Redis } diff --git a/go.mod b/go.mod index d1c24c1..ce83448 100644 --- a/go.mod +++ b/go.mod @@ -4,11 +4,14 @@ go 1.21 require ( gorm.io/driver/mysql v1.5.2 + gorm.io/driver/postgres v1.6.0 gorm.io/driver/sqlite v1.6.0 gorm.io/gorm v1.30.0 ) require ( + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/go-sql-driver/mysql v1.7.1 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect @@ -17,8 +20,8 @@ require ( github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/mattn/go-sqlite3 v1.14.22 // indirect + github.com/redis/go-redis/v9 v9.17.1 // indirect golang.org/x/crypto v0.31.0 // indirect golang.org/x/sync v0.10.0 // indirect golang.org/x/text v0.21.0 // indirect - gorm.io/driver/postgres v1.6.0 // indirect ) diff --git a/go.sum b/go.sum index 60e1838..71b0e3d 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,10 @@ +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= @@ -16,20 +22,25 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/redis/go-redis/v9 v9.17.1 h1:7tl732FjYPRT9H9aNfyTwKg9iTETjWjGKEJ2t/5iWTs= +github.com/redis/go-redis/v9 v9.17.1/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= -golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/mysql v1.5.2 h1:QC2HRskSE75wBuOxe0+iCkyJZ+RqpudsQtqkp+IMuXs= gorm.io/driver/mysql v1.5.2/go.mod h1:pQLhh1Ut/WUAySdTHwBpBv6+JKcj+ua4ZFx1QQTBzb8= gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=