先来看一段极其简单的代码,你可能很难发现其中的错误:
err := http.ListenAndServe(":8080")
if err != nil {
log.Printf("端口被占用啦")
} else {
log.Printf("成功启动啦")
}
上面的代码很简单,golang 自带的http包,可以极其简单的启动一个web服务,也即是这个代码:
http.ListenAndServe(":8080")
1、如果服务启动成功,打印“成功启动”log,用户打开http://localhost:8080就可以访问页面。
2、如果启动失败就打印“端口被占用”的log。逻辑很简单!看上去没有任何问题!!
但是执行上述代码会发现:
1、端口被占用,打印“端口被占用”logo
2、但是服务启动了,且 err==nil, log.Printf("成功启动啦") 这段代码总是不会打印出来。这就奇葩了,我接触到的任何其他语言(C++/R/java/python)都不会出现这问题。
再看一次代码:
1、http.ListenAndServe 方法执行如果异常(go语言没有except/try/catch异常处理哦),则会执行下面的if语句,打印“端口被占用”。
2、如果服务启动, err==nil, goroutine 就会阻塞后续执行。也就是服务启动后,后面 if 整段代码都不会被执行!
在go语言中,有些 if 语句是没有 else 的,因为 else 压根就不会做。所以上述启动web 服务的代码就变成这样的(开源项目基本都这样写):
err := http.ListenAndServe(":8080")
if err != nil {
log.Fatal("端口被占用啦")
}
那么,这样的 if 语句还有什么作用呢?就像猴子没有尾巴,老虎掉了牙一样。if作为条件选择语句,分支语句。上述情形中,就没法选择了!也即是“成功启动”没法打印出来!
那我怎么判断服务是否启动了?端口被占用会返回错误,端口不被占用就阻塞。这个goruntime被阻塞了,那可以新建一个 goroutine 协程ping一下是否可以访问咯:
go func() {
for {
time.Sleep(time.Second)
log.Println("Checking if started...")
resp, err := http.Get("http://localhost:8081")
if err != nil {
log.Println("Failed:", err)
continue
}
resp.Body.Close()
if resp.StatusCode != http.StatusOK {
log.Println("Not OK:", resp.StatusCode)
continue
}
break
}
log.Println("SERVER UP AND RUNNING!")
}()
log.Println("Starting server...")
time.Sleep(time.Second * 7)
log.Fatal(http.ListenAndServe(":8081", nil))
Example output:
2015/09/23 13:53:03 Starting server...
2015/09/23 13:53:04 Checking if started...
2015/09/23 13:53:06 Failed: Get http://localhost:8081: dial tcp [::1]:8081: connectex: No connection could be made because the target machine actively refused it.
2015/09/23 13:53:07 Checking if started...
2015/09/23 13:53:09 Failed: Get http://localhost:8081: dial tcp [::1]:8081: connectex: No connection could be made because the target machine actively refused it.
2015/09/23 13:53:10 Checking if started...
2015/09/23 13:53:10 SERVER UP AND RUNNING!
博客地址:http://blog.yoqi.me/?p=16889
我也在想这个问题。可是你这个办法怎么保证8081一定是你当前程序在监听呢。如果8081被别的程序用了,然后你就会打印一个SERVER UP AND RUNNING,然后ListenAndServe失败,打印8081已经被占用。
另外,这个问题其他语言也是一样的吧,我用python,不管是fastapi+uvicorn还是http.server.HTTPServer.serve_forever()都是阻塞的,和go一样,用java,好像spring boot的run也是直接main那边阻塞了,不知道你为什么说“我接触到的任何其他语言(C++/R/java/python)都不会出现这问题”