Linux系统启动Go服务前释放占用端口,杀死进程后服务无法启动问题
问题原因
- 未处理
server.ListenAndServe()的错误
该方法只要监听失败就会返回非空错误,你当前的代码没有接收、打印该错误,也没有重试逻辑。杀死旧进程后端口还没完全释放时调用该方法,会直接绑定失败,main函数执行结束后程序就直接退出了。 - 端口释放延迟
Linux系统中进程被终止后,它占用的端口会进入TIME_WAIT状态,通常会持续几十秒到两分钟,这个阶段直接尝试绑定端口仍然会报占用错误。 - 监听地址不统一
你首次测试监听用的是:8070(监听所有网卡),但HTTP服务配置的是127.0.0.1:8070(仅监听本地环回),虽然不是核心问题,但可能引发预期外的行为。
修复方案
你可以在杀死进程后增加等待重试逻辑,同时捕获服务启动的错误,也可以通过设置端口复用选项跳过TIME_WAIT限制,修复后的参考代码如下:
func main() { port := "8070" addr := "127.0.0.1:" + port server := http.Server{ Addr: addr, } http.Handle("/www/", http.StripPrefix("/www/", http.FileServer(http.Dir("./www")))) ln, err := net.Listen("tcp", addr) if err != nil { fmt.Fprintf(os.Stderr, "Can't listen on port %q: %s \n", port, err) // 杀死端口占用进程 _, err := exec.Command("fuser", "-k", port + "/tcp").Output() if err != nil { fmt.Printf("Failed to kill process at Port %q\n", port) os.Exit(1) } fmt.Printf("TCP Port %q is available, waiting for port release...\n", port) // 等待2秒让系统释放端口 time.Sleep(2 * time.Second) // 再次启动服务,打印错误 if err := server.ListenAndServe(); err != nil { fmt.Printf("Failed to start server: %v\n", err) os.Exit(1) } } else { ln.Close() if err := server.ListenAndServe(); err != nil { fmt.Printf("Failed to start server: %v\n", err) os.Exit(1) } } }
如果想要彻底避免TIME_WAIT的影响,可以自定义监听器,开启SO_REUSEADDR选项:
config := &net.ListenConfig{ Control: func(network, address string, c syscall.RawConn) error { return c.Control(func(fd uintptr) { syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1) }) }, } ln, err := config.Listen(context.Background(), "tcp", addr)
内容的提问来源于stack exchange,提问作者Hasan A Yousef
相关产品推荐
相关产品推荐

