golang中使用 request请求时候,url是域名,如何获取真实请求的IP地址?
在Go语言中,当使用域名发起HTTP请求时,可以通过以下两种方法获取实际连接的IP地址:
方法1:使用Client Trace(推荐)
利用http.Client
的Trace功能捕获连接建立时的IP地址:
package main
import (
"fmt"
"log"
"net/http"
"net/http/httptrace"
)
func main() {
url := "https://example.com"
// 创建Trace对象收集连接信息
var remoteAddr string
trace := &httptrace.ClientTrace{
GotConn: func(connInfo httptrace.GotConnInfo) {
remoteAddr = connInfo.Conn.RemoteAddr().String()
},
}
req, _ := http.NewRequest("GET", url, nil)
req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
// 发送请求
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal("Request error:", err)
}
defer resp.Body.Close()
fmt.Println("实际连接的IP地址:", remoteAddr) // 输出: 192.0.2.1:443
}
方法2:自定义Transport
通过自定义http.Transport
在连接时捕获IP:
package main
import (
"fmt"
"log"
"net"
"net/http"
)
type transport struct {
realTransport http.RoundTripper
remoteAddr string
}
func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) {
resp, err := t.realTransport.RoundTrip(req)
if err == nil {
t.remoteAddr = resp.Request.Context().Value(http.LocalAddrContextKey).(net.Addr).String()
}
return resp, err
}
func main() {
url := "https://example.com"
// 创建自定义Transport
customTransport := &transport{
realTransport: http.DefaultTransport,
}
client := &http.Client{
Transport: customTransport,
}
resp, err := client.Get(url)
if err != nil {
log.Fatal("Request error:", err)
}
defer resp.Body.Close()
fmt.Println("实际连接的IP地址:", customTransport.remoteAddr) // 输出: 192.0.2.1:443
}
关键说明:
- DNS解析过程:Go自动处理域名到IP的解析,以上方法捕获的是解析后的实际IP
- 输出格式:获取的是
IP:端口
格式(如192.0.2.1:443
) - 重定向处理:
- Trace方法自动处理重定向,每次连接都会更新IP
- Transport方法需手动处理重定向(可禁用重定向
client.CheckRedirect = func(...) error { return http.ErrUseLastResponse }
)
- 连接复用:如果使用连接池,相同域名的后续请求可能复用连接,不会触发新IP获取
应用场景:
- 调试DNS解析问题
- 记录请求的真实服务器IP
- 验证CDN/代理配置
- 地理位置服务(需配合IP数据库)
注意:如果域名对应多个IP,实际连接的IP由Go的DNS解析器决定(默认使用系统DNS),可能每次请求结果不同(负载均衡场景)。