讓go程序以后臺(tái)進(jìn)程或daemon方式運(yùn)行方法探究
引言
本文探討了如何通過Go代碼實(shí)現(xiàn)在后臺(tái)運(yùn)行的程序。最近我用Go語言開發(fā)了一個(gè)WebSocket服務(wù),我希望它能在后臺(tái)運(yùn)行,并在異常退出時(shí)自動(dòng)重新啟動(dòng)。我的整體思路是將程序轉(zhuǎn)為后臺(tái)進(jìn)程,也就是守護(hù)進(jìn)程(daemon)。它不處理具體的業(yè)務(wù)邏輯,而是再次使用相同的參數(shù)調(diào)用自身,啟動(dòng)一個(gè)子進(jìn)程來處理業(yè)務(wù)邏輯。守護(hù)進(jìn)程監(jiān)視子進(jìn)程的狀態(tài),如果子進(jìn)程退出,則再次啟動(dòng)一個(gè)新的子進(jìn)程。這樣就能保證在服務(wù)異常終止時(shí)及時(shí)重啟。
我在網(wǎng)上找到了一個(gè)開源庫,github.com/sevlyar/go-daemon,它很方便地實(shí)現(xiàn)了在后臺(tái)啟動(dòng)一個(gè)新的進(jìn)程,但如果后臺(tái)進(jìn)程再次嘗試作為另一個(gè)后臺(tái)進(jìn)程啟動(dòng),會(huì)出現(xiàn)錯(cuò)誤。
后來我閱讀了源代碼才發(fā)現(xiàn):為了區(qū)分當(dāng)前進(jìn)程是父進(jìn)程還是子進(jìn)程,作者巧妙地設(shè)計(jì)了一個(gè)環(huán)境變量標(biāo)識(shí)。正是因?yàn)檫@種識(shí)別策略,該庫只能啟動(dòng)一次自身作為后臺(tái)進(jìn)程,無法連續(xù)啟動(dòng)自身為后臺(tái)進(jìn)程。
不過,這種使用環(huán)境變量來區(qū)分進(jìn)程身份的思路給我啟發(fā)很大?;谶@個(gè)想法,我通過延伸和優(yōu)化,最終實(shí)現(xiàn)了在保持參數(shù)不變的情況下連續(xù)啟動(dòng)自身為后臺(tái)進(jìn)程。我對(duì)作者表示敬意。
此外,我還找到了一些其他的庫,它們的思路有所不同,主要通過添加特殊參數(shù)來標(biāo)記進(jìn)程身份。但是,這些方法并沒有完美地解決讓進(jìn)程啟動(dòng)自身的問題,令我有些遺憾。
最終,我決定自己實(shí)現(xiàn)一個(gè)庫來解決我的項(xiàng)目需求,并希望它是一個(gè)通用的庫,可以快速方便地將用Go語言編寫的服務(wù)程序轉(zhuǎn)為后臺(tái)運(yùn)行或守護(hù)進(jìn)程模式運(yùn)行。本文總結(jié)了我在這次探索中的經(jīng)驗(yàn)和收獲。
后臺(tái)運(yùn)行和守護(hù)進(jìn)程區(qū)別
首先,讓我們區(qū)分一下兩個(gè)概念:后臺(tái)運(yùn)行和守護(hù)進(jìn)程。平常交流時(shí),我們可能不太區(qū)分或區(qū)分不夠清晰。在本文中,我想明確如下定義:
后臺(tái)運(yùn)行:指進(jìn)程在操作系統(tǒng)中以非顯示方式運(yùn)行,沒有與任何命令行終端或程序界面相關(guān)聯(lián)。這種方式下運(yùn)行的進(jìn)程稱為后臺(tái)進(jìn)程,比如沒有與任何終端相關(guān)聯(lián)的命令行程序進(jìn)程。
守護(hù)進(jìn)程:也稱為守護(hù)進(jìn)程,它首先以后臺(tái)運(yùn)行方式啟動(dòng),然后還有額外的職責(zé)。在本文中,我的定義是守護(hù)進(jìn)程可以監(jiān)視Go服務(wù)程序進(jìn)程的狀態(tài),如果異常退出,可以自動(dòng)重新啟動(dòng)。這樣守護(hù)進(jìn)程可以確保服務(wù)程序一直在后臺(tái)運(yùn)行,即使它在某些情況下崩潰或意外終止。
接下來,我將介紹如何使用Go代碼來實(shí)現(xiàn)在后臺(tái)運(yùn)行的程序,并將其轉(zhuǎn)化為一個(gè)守護(hù)進(jìn)程。
后臺(tái)運(yùn)行程序
要將Go程序在后臺(tái)運(yùn)行,可以使用一些操作系統(tǒng)級(jí)別的方法。以下是一種簡單的方法:
package main
import (
"fmt"
"os"
"os/exec"
"syscall"
)
func main() {
if os.Getppid() != 1 {
cmd := exec.Command(os.Args[0])
cmd.Start()
fmt.Println("Background process ID:", cmd.Process.Pid)
os.Exit(0)
}
// 在這里寫入具體的業(yè)務(wù)邏輯代碼
fmt.Println("Running in background...")
select {}
}在上面的代碼中,我們首先使用os.Getppid()函數(shù)獲取當(dāng)前進(jìn)程的父進(jìn)程ID。如果父進(jìn)程不是1,說明當(dāng)前進(jìn)程不是守護(hù)進(jìn)程,而是從終端啟動(dòng)的。在這種情況下,我們創(chuàng)建一個(gè)新的命令,使用相同的參數(shù)再次啟動(dòng)程序,并在后臺(tái)運(yùn)行。我們打印出新進(jìn)程的PID,并退出初始進(jìn)程。
如果進(jìn)程的父進(jìn)程是1,那么說明當(dāng)前進(jìn)程已經(jīng)是守護(hù)進(jìn)程了,我們可以在此處寫入具體的業(yè)務(wù)邏輯代碼。
使用這種方法,我們可以確保程序在后臺(tái)運(yùn)行,而且還可以檢查是否已經(jīng)啟動(dòng)了一個(gè)后臺(tái)進(jìn)程。
守護(hù)進(jìn)程
將程序轉(zhuǎn)化為守護(hù)進(jìn)程需要額外的步驟,我們需要?jiǎng)?chuàng)建一個(gè)監(jiān)聽子進(jìn)程狀態(tài)的循環(huán),并在子進(jìn)程異常退出時(shí)重新啟動(dòng)它。以下是一個(gè)簡單的守護(hù)進(jìn)程實(shí)現(xiàn):
package main
import (
"fmt"
"os"
"os/exec"
"syscall"
)
func main() {
if os.Getppid() != 1 {
cmd := exec.Command(os.Args[0])
cmd.Start()
fmt.Println("Background process ID:", cmd.Process.Pid)
os.Exit(0)
}
// 在這里寫入具體的業(yè)務(wù)邏輯代碼
fmt.Println("Running in background...")
for {
cmd := exec.Command(os.Args[0])
cmd.Start()
exitCh := make(chan error)
go func() {
exitCh <- cmd.Wait()
}()
err := <-exitCh
if err != nil {
fmt.Println("Process exited with error:", err)
} else {
fmt.Println("Process exited successfully")
}
select {
case <-exitCh:
default:
}
}
}在上面的代碼中,我們添加了一個(gè)循環(huán),用于監(jiān)聽子進(jìn)程的狀態(tài)。在每次子進(jìn)程退出之后,我們使用相同的參數(shù)再次啟動(dòng)守護(hù)進(jìn)程,并重新開始監(jiān)聽。這樣就可以確保服務(wù)程序在異常退出時(shí)能夠自動(dòng)重新啟動(dòng)。
這只是一個(gè)簡單的守護(hù)進(jìn)程實(shí)現(xiàn),你可以根據(jù)自己的需求進(jìn)行擴(kuò)展和優(yōu)化。
以上就是讓go程序以后臺(tái)進(jìn)程或daemon方式運(yùn)行方法探究的詳細(xì)內(nèi)容,更多關(guān)于go以后臺(tái)進(jìn)程daemon方式運(yùn)行的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Go項(xiàng)目編寫Makefile規(guī)則文件概述
這篇文章主要為大家介紹了Go項(xiàng)目編寫Makefile文件規(guī)則概述,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04
go語言實(shí)現(xiàn)一個(gè)簡單的http客戶端抓取遠(yuǎn)程url的方法
這篇文章主要介紹了go語言實(shí)現(xiàn)一個(gè)簡單的http客戶端抓取遠(yuǎn)程url的方法,實(shí)例分析了Go語言http操作技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-03-03
go-micro微服務(wù)domain層開發(fā)示例詳解
這篇文章主要為大家介紹了go-micro微服務(wù)domain層開發(fā)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01
Go中g(shù)in框架的*gin.Context參數(shù)常見實(shí)用方法
*gin.Context是處理HTTP請求的核心,ctx代表"context"(上下文),它包含了處理請求所需的所有信息和方法,例如請求數(shù)據(jù)、響應(yīng)構(gòu)建器、路由參數(shù)等,這篇文章主要介紹了Go中g(shù)in框架的*gin.Context參數(shù)常見實(shí)用方法,需要的朋友可以參考下2024-07-07
Go 語言中和類型Sum Types的創(chuàng)新實(shí)現(xiàn)方案詳解
本文介紹了Go語言中如何通過ProjectionStructs和SafeUnsafeConversion來實(shí)現(xiàn)和類型(SumTypes),該方案通過內(nèi)存布局相同的多個(gè)結(jié)構(gòu)體投影同一塊數(shù)據(jù),實(shí)現(xiàn)零自定義編解碼、編譯時(shí)類型安全和IDE自動(dòng)補(bǔ)全友好,感興趣的朋友跟隨小編一起看看吧2026-02-02
Golang棧結(jié)構(gòu)和后綴表達(dá)式實(shí)現(xiàn)計(jì)算器示例
這篇文章主要為大家介紹了Golang棧結(jié)構(gòu)和后綴表達(dá)式實(shí)現(xiàn)計(jì)算器示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07

