项目场景:
项目场景:微服务项目中使用consul作为配置中心存储mysql配置,获取配置后使用gorm连接mysql数据库时出现错误。
问题描述
错误:file=category/main.go:48 level= error Error 1045:Access denied for user ''@'xxx.xxx.xxx.xxx'' (using password: NO)
配置中心以及获取配置中心代码:
package main
func GetConsulConfig(host string, port int64, prefix string) (config.Config, error) {
consulSource := consul.NewSource(
consul.WithAddress(host+":"+strconv.FormatInt(port, 10)),
consul.WithPrefix(prefix),
consul.StripPrefix(true),
)
cf, err := config.NewConfig()
if err != nil {
return cf, err
}
err = config.Load(consulSource)
return cf, err
}
type MysqlConfig struct {
Host string `json:"host"`
User string `json:"user"`
Pwd string `json:"pwd"`
Database string `json:"database"`
Port int64 `json:"port"`
}
// 获取 mysql 的配置
func GetMysqlFromConsul(config config.Config, path ...string) *MysqlConfig {
mysqlConfig := &MysqlConfig{}
config.Get(path...).Scan(mysqlConfig)
return mysqlConfig
}
func main() {
// config center
consulConfig, err := common.GetConsulConfig("127.0.0.1", 8500, "/micro/config")
if err != nil {
log.Error(err)
}
// get mysql config
mysqlInfo := common.GetMysqlFromConsul(consulConfig, "mysql")
//"mysql",root+:123456@/db3?charset=utf8&parseTime=True&loc=Local"
db, err := gorm.Open("mysql", mysqlInfo.User+":"+mysqlInfo.Pwd+"@/"+mysqlInfo.Database+"?charset=utf8&parseTime=True&loc=Local")
if err != nil {
log.Error(err)
}
}
原因分析:
根据打印的日志:
file=category/main.go:48 level= error Error 1045:Access denied for user ''@'xxx.xxx.xxx.xxx' (using password: NO)
报错的地方为main函数的48行,也就是gorm连接mysql的地方:
db, err := gorm.Open("mysql", mysqlInfo.User+":"+mysqlInfo.Pwd+"@/"+mysqlInfo.Database+"?charset=utf8&parseTime=True&loc=Local")
if err != nil {
log.Error(err) // 记录了错误日志
}
gorm包已经引入, 那么肯定就是gorm连接地址出错,地址通过mysql配置拼接,找了半天并没有发现地址格式有误,于是开始向上查找,在往上就是从consul获取mysql配置,也许是这里出错了,就试着打印mysqlInfo中的信息,添加一行代码:
fmt.Printf("user:%s\npassword:%s\ndatabase:%s\nport:%d\nhost:%s\n", mysqlInfo.User, mysqlInfo.Pwd, mysqlInfo.Database, mysqlInfo.Port, mysqlInfo.Host)
output:
哦豁,获取不到consul中MySQL的配置 ,涉及到consul和获取consul中MySQL配置的只有两个函数,分别为:
func GetConsulConfig(host string, port int64, prefix string) (config.Config, error) {
consulSource := consul.NewSource(
consul.WithAddress(host+":"+strconv.FormatInt(port, 10)),
consul.WithPrefix(prefix),
consul.StripPrefix(true),
)
// 配置初始化
config, err := config.NewConfig()
if err != nil {
return config, err
}
// 加载配置
err = config.Load(consulSource)
return config, err
}
// 获取 mysql 的配置
func GetMysqlFromConsul(config config.Config, path ...string) *MysqlConfig {
mysqlConfig := &MysqlConfig{}
config.Get(path...).Scan(mysqlConfig)
return mysqlConfig
}
问题进一步缩小,当看到GetConsulConfig()函数时发现了错误,配置初始化config.NewConfig()返回了一个new config,用config变量接收,随后就是加载配置config.Load(consulSource),这里就是关键。
前面用config变量接收返回值,而有个包也叫config,config变量有个方法Load(),而config包中也有个Load()方法,就是因为调用错了才导致获取不到consul中的配置。
解决方案:
出现这样的问题就是变量命名和包名重名了,在调用时不小心调用错了方法,才导致获取不到consul中mysql的配置。
更改config.NewConfig()返回值接收对象名称为cf,然后调用cf的Load()方法即可:
func GetConsulConfig(host string, port int64, prefix string) (config.Config, error) {
consulSource := consul.NewSource(
consul.WithAddress(host+":"+strconv.FormatInt(port, 10)),
consul.WithPrefix(prefix),
consul.StripPrefix(true),
)
cf, err := config.NewConfig()
if err != nil {
return cf, err
}
//err = config.Load(consulSource) <-----原先错误的写法
err = cf.Load(consulSource)
return cf, err
}
再次运行项目:
此时就获取到了配置中心的MySQL配置。