# 如何配置Go根据端口号启动程序,并守护该进程 ## 前言 在现代服务器应用开发中,我们经常需要让程序监听特定端口提供服务。对于Go语言开发者而言,如何优雅地实现基于端口号启动程序并确保进程稳定运行是一个常见需求。本文将深入探讨以下内容: 1. Go程序监听端口的实现原理 2. 通过命令行参数指定端口的方法 3. 使用第三方库创建守护进程 4. 系统级守护方案(systemd/supervisor) 5. 生产环境最佳实践 ## 一、Go程序监听端口的基础实现 ### 1.1 标准库net/http示例 ```go package main import ( "fmt" "net/http" ) func handler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, you've requested: %s\n", r.URL.Path) } func main() { http.HandleFunc("/", handler) port := "8080" // 默认端口 fmt.Printf("Starting server on port %s...\n", port) http.ListenAndServe(":"+port, nil) } package main import ( "flag" "fmt" "net/http" "os" ) var port string func init() { flag.StringVar(&port, "port", "8080", "server listening port") flag.Parse() } func main() { fmt.Printf("Starting server on port %s...\n", port) if err := http.ListenAndServe(":"+port, nil); err != nil { fmt.Printf("Server failed: %v\n", err) os.Exit(1) } } 使用方式:
go run main.go -port=9090 package main import ( "log" "os" "os/exec" "syscall" ) func daemonize() { cmd := exec.Command(os.Args[0], os.Args[1:]...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Stdin = os.Stdin cmd.SysProcAttr = &syscall.SysProcAttr{ Setsid: true, } if err := cmd.Start(); err != nil { log.Fatalf("Failed to daemonize: %v", err) } os.Exit(0) } func main() { // 调用守护函数 daemonize() // 主程序逻辑... } 安装:
go get github.com/sevlyar/go-daemon 示例代码:
package main import ( "flag" "log" "os" "time" "github.com/sevlyar/go-daemon" ) var ( port = flag.String("port", "8080", "server port") daemon = flag.Bool("d", false, "run as daemon") ) func main() { flag.Parse() if *daemon { cntxt := &daemon.Context{ WorkDir: "./", Umask: 027, } child, err := cntxt.Reborn() if err != nil { log.Fatal("Unable to run: ", err) } if child != nil { return } defer cntxt.Release() } // 主程序逻辑 for { log.Printf("Server running on port %s (PID: %d)", *port, os.Getpid()) time.Sleep(10 * time.Second) } } 创建服务文件 /etc/systemd/system/myapp.service:
[Unit] Description=My Go Application After=network.target [Service] Type=simple User=appuser WorkingDirectory=/opt/myapp ExecStart=/opt/myapp/myapp -port=8080 Restart=always RestartSec=10 StandardOutput=syslog StandardError=syslog SyslogIdentifier=myapp [Install] WantedBy=multi-user.target 管理命令:
# 重载配置 sudo systemctl daemon-reload # 启动服务 sudo systemctl start myapp # 设置开机启动 sudo systemctl enable myapp # 查看状态 sudo systemctl status myapp 安装Supervisor后,创建配置文件 /etc/supervisor/conf.d/myapp.conf:
[program:myapp] command=/opt/myapp/myapp -port=8080 directory=/opt/myapp user=appuser autostart=true autorestart=true startsecs=10 startretries=3 stdout_logfile=/var/log/myapp.log stdout_logfile_maxbytes=50MB stdout_logfile_backups=10 stderr_logfile=/var/log/myapp_err.log stderr_logfile_maxbytes=50MB stderr_logfile_backups=10 environment=GIN_MODE="release" 管理命令:
# 更新配置 sudo supervisorctl update # 启动/停止 sudo supervisorctl start myapp sudo supervisorctl stop myapp # 查看状态 sudo supervisorctl status package main import ( "net/http" "time" ) func healthCheck() { ticker := time.NewTicker(30 * time.Second) defer ticker.Stop() for range ticker.C { resp, err := http.Get("http://localhost:" + port + "/health") if err != nil || resp.StatusCode != http.StatusOK { // 健康检查失败处理 restartService() } } } func main() { go healthCheck() // 主程序逻辑... } package main import ( "context" "log" "net/http" "os" "os/signal" "syscall" "time" ) func main() { server := &http.Server{ Addr: ":" + port, Handler: nil, } // 优雅关闭 quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) go func() { <-quit log.Println("Shutting down server...") ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := server.Shutdown(ctx); err != nil { log.Fatal("Server forced to shutdown:", err) } }() log.Println("Server started on port", port) if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatal("ListenAndServe:", err) } log.Println("Server exiting") } import ( "github.com/sirupsen/logrus" "gopkg.in/natefinch/lumberjack.v2" ) func setupLogger() { logger := logrus.New() logger.SetOutput(&lumberjack.Logger{ Filename: "/var/log/myapp.log", MaxSize: 100, // MB MaxBackups: 10, MaxAge: 30, // days Compress: true, }) logger.SetFormatter(&logrus.JSONFormatter{}) } package main import ( "net" "net/http" "time" ) func createHTTPClient() *http.Client { return &http.Client{ Transport: &http.Transport{ DialContext: (&net.Dialer{ Timeout: 30 * time.Second, KeepAlive: 30 * time.Second, }).DialContext, MaxIdleConns: 100, MaxIdleConnsPerHost: 100, IdleConnTimeout: 90 * time.Second, }, Timeout: time.Minute, } } 推荐使用Prometheus监控:
import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" ) var ( requestsTotal = prometheus.NewCounterVec( prometheus.CounterOpts{ Name: "http_requests_total", Help: "Total number of HTTP requests", }, []string{"method", "path", "status"}, ) ) func init() { prometheus.MustRegister(requestsTotal) } func main() { http.Handle("/metrics", promhttp.Handler()) // 其他路由... } 本文详细介绍了Go程序基于端口号启动的多种实现方式,以及各种进程守护方案。实际生产环境中,建议:
通过合理配置,可以确保Go应用程序稳定可靠地长期运行。根据实际需求选择最适合的方案,才能达到最佳效果。
字数统计:约3050字 “`
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。