文章较长,如果需要解决方案
问题简述
最近在golang项目中连接达梦数据库的时候遇到了如下报错:
2023-12-28 10:05:50,302 [ERROR] 1:common.InitDb initcnf.go:166
QueryAppInfo fail, err = Error 6001: 网络通信异常 dial address: localhost:5236
stack info:
1). github.com/mzky/dm.dm_build_348\n /home/user/go/pkg/mod/github.com/mzky/dm@v0.0.0-20210201024716-58aaa968f460/a.go:55\n
驱动是官网驱动,连接方式使用的是官网demo中的连接方式
dbuser:="root"
dbPassWd:="password"
dbAddrs:="NORMAL"
port:=5236
dbName:="test"
dataSource = fmt.Sprintf("dm://%s:%s@%s:%d?schema=%s", dbUser, dbPassWd, dbAddrs, port, dbName)
这里的地址使用了别名,但是报错的时候却说是连接到localhost:5236
网络错误,这个就很神奇了。手头正好有达梦golang驱动的代码,就进去搜索了一下。
dm驱动校验host的逻辑
if group, ok := ServerGroupMap[strings.ToLower(host)]; ok {
c.group = group
} else {
host, port, err := net.SplitHostPort(host)
if err != nil || net.ParseIP(host) == nil {
c.host = hostDef //hostDef先前定义为了“localhost”
} else {
c.host = host
}
tmpPort, err := strconv.Atoi(port)
if err != nil {
c.port = portDef
} else {
c.port = int32(tmpPort)
}
c.group = newEPGroup(c.host+":"+strconv.Itoa(int(c.port)), []*ep{newEP(c.host, c.port)})
}
这里就大概明白了,驱动中会校验传入的host是否为合法的ip地址(这个函数ParseIP
,甚至还能校验ipv6),对于非法的host,就会被强制替换为localhost。这个设计很难评价。
配置服务组及踩坑
不过上面有个ServerGroupMap
,如果host在里面就不会走这个逻辑了,进一步往下看。这个ServerGroupMap
是读取了指定目录下的dm_svc.conf
,从代码逻辑看应该是解析了这个文件。
// filePath: dm_svc.conf 文件路径
func load(filePath string) {
if filePath == "" {
switch runtime.GOOS {
case "windows":
filePath = os.Getenv("SystemRoot") + "\\system32\\dm_svc.conf"
case "linux":
filePath = "/etc/dm_svc.conf"
default:
return
}
}
file, err := os.Open(filePath)
defer file.Close()
if err != nil {
return
}
fileReader := bufio.NewReader(file)
// GlobalProperties = NewProperties()
var groupProps *Properties
var line string //dm_svc.conf读取到的一行
for line, err = fileReader.ReadString('\n'); line != "" && (err == nil || err == io.EOF); line, err = fileReader.ReadString('\n') {
// 去除#标记的注释
if notesIndex := strings.IndexByte(line, '#'); notesIndex != -1 {
line = line[:notesIndex]
}
// 去除前后多余的空格
line = strings.TrimSpace(line)
if line == "" {
continue
}
if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
groupName := strings.ToLower(line[1 : len(line)-1])
dbGroup, ok := ServerGroupMap[groupName]
if groupName == "" || !ok {
continue
}
...
连接时用host匹配解析出的服务组,匹配到了就按照匹配的服务组连接数据库。于是就去配置文件/etc/dm_svc.conf
里面按照官网写上了服务组的配置:
NORMAL=(192.168.0.1:5000,192.168.0.2:5236)
结果还是解析到了localhost,最后发现这个host应该是截取连接里面“@”到“?”这部分
dm://%s:%s@%s:%d?schema=%s
按照目前的方式,程序会先匹配NORMAL:5236
,发现配置文件里面只有NORMAL,不匹配。然后继续走校验ip的流程。
解决方案
使用别名连接,需要去修改配置文件(没有则新建)
地址:
- windows :%SystemRoot%\system32\dm_svc.conf
- linux :filePath = "/etc/dm_svc.conf
填写方式
连接别名=(IP1:PORT1,IP2:PORT2)
在连接时只允许这种
dm://用户名:密码@IP:端口?schema=库名
dm://用户名:密码@连接别名?schema=库名
参考资料:https://eco.dameng.com/community/question/c8ba0b48062bd35401edb388e41ab598