天问

golang http.ListenAndServe 阻塞导致if else不执行问题分析

先来看一段极其简单的代码,你可能很难发现其中的错误:

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
扫我捐助哦
喜欢 4

这篇文章有1条评论

  1. xiaotushaoxia 2023/1/18 #1 [REPLY]

    我也在想这个问题。可是你这个办法怎么保证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)都不会出现这问题”

发表评论