-
发生时间:2022-10-19
-
问题描述:将产线服务部署到服务器后,在服务器内部可以正常访问该服务,但在本地却无法访问,提示 “Connection Refused”。
-
问题类别:软件调试
-
原因分析:
- 首先使用
ps命令确认服务是在运行的,使用netstat命令确认服务也监听了我配置的 8093 端口。 - 在没有思路的情况下,尝试对 docker-compose 或 nginx 的端口映射配置进行各种修改,但始终无法在本地访问服务器上面的产线服务。
- 最后才开始怀疑产线服务程序监听的 IP 地址可能有问题。从代码中看到,自己是在调用 gin 的
Run()函数时传入主机地址的; 而该地址配置在.env文件中,检查.env文件发现自己配置了APP_HOST: localhost。是不是这个 localhost 导致的问题呢?
addr := fmt.Sprintf("%s:%s", config.AppHost(), config.AppPort()) return r.Run(addr)- 于是我用 go 语言写了一个最简单的 http 服务(如下所示),启动该服务时通过环境变量来设置不同的主机地址, 然后用我背后的那台主机来访问该服务,以观察该服务监听在不同的主机地址下的行为。
// http-server.go func hello(w http.ResponseWriter, req *http.Request) { fmt.Fprintf(w, "hello\n") } func main() { addr := fmt.Sprintf("%s:8888", os.Getenv("HOST")) http.HandleFunc("/hello", hello) http.ListenAndServe(addr, nil) }- 测试发现,当配置 HOST 为
127.0.0.1或localhost时,其他主机无法访问此服务;当配置 HOST 为空或0.0.0.0时,其他主机可以访问此服务。 说明确实是 IP 地址配置错误导致的。
- 首先使用
-
解决方案:将
.env文件中APP_HOST的取值设置为空,代表监听所有 IP 地址。 -
实施结果:修改 APP_HOST 配置后,重启服务,本地可以正常访问服务器上的产线服务了。
-
经验总结:
- 监听在 localhost 的服务是无法被其他主机访问的——即使在 nginx 配置了转发命令也没用。
关于特殊 IP 地址的说明
localhost 和 127.0.0.1
以下摘自 维基百科词条 localhost:
In computer networking, localhost is a hostname that refers to the current device used to access it. It is used to access the network services that are running on the host via the loopback network interface.
The name localhost normally resolves to the IPv4 loopback address 127.0.0.1, and to the IPv6 loopback address ::1.
可见,localhost 代表本地主机,被解析为本地环回地址——对于 ipv4,本地环回地址为 127.0.0.1;对于 ipv6,本地环回地址为 ::1。
Linux 系统下查看 /etc/hosts 文件可以看到 localhost 被解析为 127.0.0.1。以下是我的 Ubuntu 系统下 /etc/hosts 文件的部分内容:
127.0.0.1 localhost
127.0.1.1 along
# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
0.0.0.0
以下摘自 维基百科词条 0.0.0.0:
In the context of servers, 0.0.0.0 can mean “all IPv4 addresses on the local machine”. If a host has two IP addresses, 192.168.1.1 and 10.1.2.1, and a server running on the host is configured to listen on 0.0.0.0, it will be reachable at both of those IP addresses.
可见,0.0.0.0 代表任意 ipv4 地址。如果服务监听在此地址,则意味着该服务对 ip 地址没有任何限制。
在 go 标准库 net 中,如果没填写 ip 地址,也代表监听任意 ip 地址。详见 go 源码文件 src/net/dial.go 的 listen 函数的注释:
// Listen announces on the local network address.
//
// The network must be "tcp", "tcp4", "tcp6", "unix" or "unixpacket".
//
// For TCP networks, if the host in the address parameter is empty or
// a literal unspecified IP address, Listen listens on all available
// unicast and anycast IP addresses of the local system.
// ...
func Listen(network, address string) (Listener, error) {
var lc ListenConfig
return lc.Listen(context.Background(), network, address)
}