我是一个Go新手,试图在生产环境中调试一些网络问题,在一个应用程序上遇到一些问题,我怀疑这些问题与DNS解析有关。长话短说,在成功连接到redis集群并与之交互后,连接失败,应用程序开始记录大量dial: tcp :0: connection refused
错误。这条消息中的:0:
是我怀疑DNS的原因,因为据我所知,这表明它试图连接到一个零值远程地址,我真的不知道为什么会发生这种情况,除非主机名查找出错。
无论如何,如果有人对这个特定的问题有一些见解,我很乐意听到它,但这里的主要问题与我在尝试调试这个问题时遇到的问题有关:
为了了解主机名解析的实际情况,我向拨号器传递了一个自定义解析器,它从自己的拨号器返回一个 Package 的连接。连接 Package 器本质上只是记录在本机连接上读取和写入的字节,其他什么都没有。这里有一个简单的可复制的例子:
package main
import (
"context"
"fmt"
"net"
"time"
)
type ConnWrapper struct {
net.Conn
}
func (c *ConnWrapper) Write(bytes []byte) (int, error) {
n, err := c.Conn.Write(bytes)
if err != nil {
fmt.Printf("Failed to write bytes %s: %v\n", string(bytes), err)
} else {
fmt.Printf("Successfully wrote %d of bytes %x\n", n, bytes[:n])
}
return n, err
}
func (c *ConnWrapper) Read(bytes []byte) (int, error) {
n, err := c.Conn.Read(bytes)
if err != nil {
fmt.Printf("Failed to read bytes: %v\n", err)
} else {
fmt.Printf("Successfully read %d of bytes %x\n", n, bytes[:n])
}
return n, err
}
func (c *ConnWrapper) SetDeadline(t time.Time) error {
fmt.Printf("Setting deadline %v %s %s\n", t, c.LocalAddr(), c.RemoteAddr())
return c.Conn.SetDeadline(t)
}
func (c *ConnWrapper) SetReadDeadline(t time.Time) error {
fmt.Printf("Setting read deadline %v %s %s\n", t, c.LocalAddr(), c.RemoteAddr())
return c.Conn.SetReadDeadline(t)
}
func (c *ConnWrapper) SetWriteDeadline(t time.Time) error {
fmt.Printf("Setting write deadline %v %s %s\n", t, c.LocalAddr(), c.RemoteAddr())
return c.Conn.SetWriteDeadline(t)
}
func main() {
var d *net.Dialer
d = &net.Dialer{
Timeout: time.Duration(30) * time.Minute,
Resolver: &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
fmt.Printf("Redis dialing (from resolver) %s %s\n", network, address)
conn, err := d.DialContext(ctx, network, address)
if err != nil {
fmt.Printf("Redis resolver failed %v\n", err)
}
// When I return c2, the reads time out
c2 := &ConnWrapper{conn}
return c2, err
// When I return conn, everything is fine
//return conn, err
},
},
}
conn, err := d.DialContext(context.Background(), "tcp", "redis-node-0:6379")
if err != nil {
fmt.Printf("Redis dial failed %v\n", err)
} else {
fmt.Printf("Successfully dialed %s\n", conn.RemoteAddr())
}
}
字符串
我不明白的是,当我返回本机连接时,一切都按预期工作。当我返回 Package 连接时,连接在解析DNS名称时挂起/超时(特别是在conn.Read
调用中)。我不知道为什么会发生这种情况,但我对Go的实际经验也很少,所以我想知道是否有一个简单的解释,我只是没有看到。我试着问谷歌和ChatGPT没有用,所以希望社区里有人能给我一些建议
在我的系统上执行上面的程序会产生以下输出(使用Golang 1.21)
Redis dialing (from resolver) udp 1.1.1.1:53
Redis dialing (from resolver) udp 1.1.1.1:53
Setting deadline 2023-10-15 10:43:44.031475 -0400 AST m=+5.001741085 192.168.68.61:62862 1.1.1.1:53
Setting deadline 2023-10-15 10:43:44.031494 -0400 AST m=+5.001759751 192.168.68.61:57358 1.1.1.1:53
Successfully wrote 49 of bytes 002f53c8010000010000000000010c72656469732d6e6f64652d30056c6f63616c00001c000100002904d0000000000000
Successfully wrote 49 of bytes 002f29c8010000010000000000010c72656469732d6e6f64652d30056c6f63616c000001000100002904d0000000000000
Successfully read 2 of bytes 002f
Successfully read 2 of bytes 002f
Failed to read bytes: read udp 192.168.68.61:57358->1.1.1.1:53: i/o timeout
Redis dialing (from resolver) udp 1.0.0.1:53
Failed to read bytes: read udp 192.168.68.61:62862->1.1.1.1:53: i/o timeout
Redis dialing (from resolver) udp 1.0.0.1:53
Setting deadline 2023-10-15 10:43:49.033465 -0400 AST m=+10.003676876 192.168.68.61:64641 1.0.0.1:53
Setting deadline 2023-10-15 10:43:49.033817 -0400 AST m=+10.004027543 192.168.68.61:59264 1.0.0.1:53
Successfully wrote 49 of bytes 002f6612010000010000000000010c72656469732d6e6f64652d30056c6f63616c00001c000100002904d0000000000000
Successfully wrote 49 of bytes 002fd26c010000010000000000010c72656469732d6e6f64652d30056c6f63616c000001000100002904d0000000000000
Successfully read 2 of bytes 002f
Failed to read bytes: read udp 192.168.68.61:59264->1.0.0.1:53: i/o timeout
Redis dialing (from resolver) udp 1.1.1.1:53
Setting deadline 2023-10-15 10:43:54.036073 -0400 AST m=+15.006228210 192.168.68.61:56757 1.1.1.1:53
Failed to read bytes: read udp 192.168.68.61:64641->1.0.0.1:53: i/o timeout
Redis dialing (from resolver) udp 1.1.1.1:53
Successfully wrote 49 of bytes 002f4f6d010000010000000000010c72656469732d6e6f64652d30056c6f63616c00001c000100002904d0000000000000
Setting deadline 2023-10-15 10:43:54.036487 -0400 AST m=+15.006642126 192.168.68.61:58296 1.1.1.1:53
Successfully wrote 49 of bytes 002f404c010000010000000000010c72656469732d6e6f64652d30056c6f63616c000001000100002904d0000000000000
Successfully read 2 of bytes 002f
Successfully read 2 of bytes 002f
Failed to read bytes: read udp 192.168.68.61:58296->1.1.1.1:53: i/o timeout
Failed to read bytes: read udp 192.168.68.61:56757->1.1.1.1:53: i/o timeout
型
当我更新程序以简单地返回内置的Conn示例时,我得到以下结果(注意,我在一个redis主机不可用的环境中运行此程序,因此“没有这样的主机”实际上是这里预期的行为。关键是dns查找实际上完成了。)
Redis dialing (from resolver) udp 1.1.1.1:53
Redis dialing (from resolver) udp 1.1.1.1:53
Redis dialing (from resolver) udp 1.1.1.1:53
Redis dialing (from resolver) udp 1.1.1.1:53
Redis dial failed dial tcp: lookup redis-node-0 on 1.1.1.1:53: no such host
型
1条答案
按热度按时间xzabzqsa1#
解析程序区分net.PacketConn和net.Conn(文档)。如果拨号返回数据包连接,则返回数据包连接 Package 。
下面是 Package :
字符串
在dial函数中使用数据包 Package 器,如下所示:
型
https://go.dev/play/p/1KOr95FbDKD