本文目錄一覽:
- 1、go語言調試器有哪些官網
- 2、如何運行一個golang程序為守護進程
- 3、對於一個已有的docker容器,怎麼添加運行參數
- 4、Linux的too many open files解析
- 5、golang 進程創建,fork,以及熱重啟(無縫升級)
- 6、使用golang 還有必要使用 nginx 么
go語言調試器有哪些官網
可以去DELVE官網進行下載。
關於delve工具的介紹,這裡簡單給大家介紹一下。
delve在go項目及應用的開發中可以用來追蹤程序中的異常代碼,也可以通過打日誌的方式追查問題,但是更重要也是非常厲害的一點,就是delve可以直接分析程序執行的情況。這一點在後期或線上的問題排查中無疑是提供了一個非常大的便捷。
Go(又稱 Golang)是 Google 的 Robert Griesemer,Rob Pike 及 Ken Thompson 開發的一種靜態強類型、編譯型語言。
Go 語言語法與 C 相近,但功能上有:內存安全,GC(垃圾回收),結構形態及 CSP-style 並發計算。
Go的語法接近C語言,但對於變量的聲明有所不同。Go支持垃圾回收功能。Go的並行模型是以東尼·霍爾的通信順序進程(CSP)為基礎。
採取類似模型的其他語言包括Occam和Limbo,但它也具有Pi運算的特徵,比如通道傳輸。在1.8版本中開放插件(Plugin)的支持,這意味着現在能從Go中動態加載部分函數。
Delve常用命令
命令功能:
dlv attach後面跟 pid,用來Debug編譯好的Golang程序。
dlv core用於 coredump。
dlv debug後面跟要調試的 go 文件,進入 Debug。
dlv testDebug test 函數。
如何運行一個golang程序為守護進程
安裝daemonize
安裝git環境
1
yum install git -y
獲取daemonize
1
git clone git://github.com/a href=”;tn=44039180_cprfenlei=mv6quAkxTZn0IZRqIHckPjm4nH00T1YLP1cYrywWnjm1uAc3rjDz0ZwV5Hcvrjm3rH6sPfKWUMw85HfYnjn4nH6sgvPsT6KdThsqpZwYTjCEQLGCpyw9Uz4Bmy-bIi4WUvYETgN-TLwGUv3EPWcdnW6sn1nkn1f4n1fdrj6Y” target=”_blank” class=”baidu-highlight”bmc/a/daemonize.git
安裝daemonize
1
2
3
cd daemonize
./configure
make make install
查看是否安裝
1
daemonize -v
通過daemonize執行golang守護進程
需要打包golang程序為可執行文件(go build),並通過daemonize來執行它來實現守護進程,如:
1
daemonize -p /var/run/myapp.pid -l /var/lock/subsys/myapp -u nobody /path/to/myapp
對於一個已有的docker容器,怎麼添加運行參數
從util-linux版本2.23開始,nsenter工具就包含在其中。它用來訪問另一個進程的名字空間。nsenter要正常工作需要有root權限。很不幸,Ubuntu 14.4仍然使用的是util-linux版本2.20。安裝最新版本的util-linux(2.24)版,請按照以下步驟: 為了連接到容器,你還需要找到容器的第一個進程的PID。 docker inspect –format “{{ .State.Pid }}” container-id 通過這個PID,你就可以連接到這個容器: nsenter –target $PID –mount –uts –ipc –net –pid nsinit 從0.9版本開始,Docker自身就具有一個管理容器的庫,名字為 libcontainer。libcontainer中的nsinit工具允許用戶直接訪問linux名字空間和cgroup內核。在安裝nsinit之前,你首先需要安裝Go運行時環境: apt-get install git golang-go mkdir -p $HOME/go-dev/binmkdir -p $HOME/go-dev/src echo “export GOPATH=\$HOME/go-dev” ~/.profileecho “PATH=\$PATH:\$GOPATH/bin” ~/.profile source ~/.profile 接下來才安裝nsinit: mkdir -p $GOPATH/src/github.com/dotcloudcd $GOPATH/src/github.com/dotcloud git clone $GOPATH/src/github.com/dotcloud/docker /usr/bin/go get -v github.com/dotcloud/docker/vendor/src/github.com/docker/libcontainer/nsinit nsinit讀取的是位於/var/lib/docer/execdriver/native/container-id容器目錄下的配置數據。要運行nsinit,你需要切換到容器目錄下。由於/var/lib/docker目錄對於root用戶是只讀權限,因此你還需要root權限。通過docker的ps命令,你可以確定容器ID。一旦你進入/var/lib/docker目錄,你就可以連接容器了: nsinit exec /bin/bash lxc(-attach) 直到Docker 0.8.1版本為止,LXC一直是管理容器的基本工具,Docker一直支持這個工具。但是從0.9.0版本開始,Docker默認使用libcontainer管理容器,不再依賴LXC了。因此默認情況下,你不能使用lxc-attach了。 如果你仍然希望使用lxc-attach,那麼你需要使用-e lxc選項來重新啟動Docker服務進程。使用這個選項,Docker的內部將再次使用LXC管理容器了。完成這個任務最簡單的做法就是創建/etc/default/docker文件(如果這個文件仍然不存在),並添加以下內容: DOCKER_OPTS=” -e lxc” 現在你可以重新啟動Docker服務了。要連接容器,你需要知道完整的容器ID: docker ps –no-trunc 接下來,你就可以連接這個容器了。要完成下面工作,你還需要root權限: lxc-attach -n container-id — /bin/bash sshd 上面所有三種方法都要求具有主機系統的root權限。為了不採用root權限,通過ssh訪問容器將是一個很好的選擇。 要做到這一點,你需要構建一個支持SSH服務的基礎映像。此時,我們可能遇到這樣的問題:我們是不是用Docker CMD或者ENTRYPOINT運行一條命令就可以了?如果此時有sshd進程運行,那麼我們就不要再運行其他進程了。接下來的工作是創建一個腳本或者使用像supervisord這樣的進程管理工具來啟動其它所有需要啟動的進程。有關如何使用supervisord的 優秀的文檔可以在Docker的web站點上找到。一旦你啟動了具有sshd進程的容器,你就可以像以往一樣通過ssh客戶端了連接這個容器了。
Linux的too many open files解析
Linux中如果一個進程打開文件或者socket連接過多,有沒有及時處理和關閉掉文件或連接,當該進程打開文件的數量超過open files的數量時候,就會報too many open files的錯誤
Linux 的open files 是在一個同一個進程里限制的,當然也有全局的限制(查看/proc/sys/fs/file-max文件),ulimit -a pid命令可以看到open files進程級別限制的大小。
如果當前open files配置的是1024,則這個進程最多只能打開1024個文件,/proc/PID/fd 目錄下的打開文件描述符的數量不會超過1024, 使用 lsof -p PId | wc -l 來查看進程打開的文件數, 超過1024則報too many open files的錯誤,這時候其他進程仍然可以打開文件,進程之間互不影響。
可以臨時通過 ulimit -n 4096 這樣設置 open files為4096,然後在同一個用戶session下重新啟動程序。這樣的設置只能在Session級別生效,如果切換用戶或者切換shell session就失效了。如果要永久生效,需要修改/etc/security/limits.conf文件,在文件末尾添加下列參數並重啟機器:
noproc 是代表最大進程數
Golang等動態GC的語言,會通過GC來回收沒有正確關閉的文件(比如使用完文件後沒有調用Close()去關閉釋放資源),這樣就導致一些已經打開的文件又被GC關閉掉,然後此進程又可以打開另外的文件,從而會使/proc/PID/fd目錄下文件描述符會指向不同的文件,可能鏈接到不同的文件。
通過設置GOGC=off環境變量關閉GO GC, 再執行go程序,此問題解決, /proc/PID/fd目錄下的文件描述符一直保持在max open files 數量 1024,同時lsof -p pid|grep REG|wc -l 為1024.
打開文件後一定要記得f.Close()關閉,這樣就能避免產生too many open files的報錯。
golang 進程創建,fork,以及熱重啟(無縫升級)
一般來說,進程的操作使用的是一些系統的命令,所以go內部使用os包,進行一些運行系統命令的操作
os 包及其子包 os/exec 提供了創建進程的方法。
一般的,應該優先使用 os/exec 包。因為 os/exec 包依賴 os 包中關鍵創建進程的 API,為了便於理解,我們先探討 os 包中和進程相關的部分。
Unix :fork創建一個進程,(及其一些變種,如 vfork、clone)。
Go:Linux 下創建進程使用的系統調用是 clone。
允許一進程(父進程)創建一新進程(子進程)。具體做法是,新的子進程幾近於對父進程的翻版:子進程獲得父進程的棧、數據段、堆和執行文本段的拷貝。可將此視為把父進程一分為二。
終止一進程,將進程佔用的所有資源(內存、文件描述符等)歸還內核,交其進行再次分配。參數 status 為一整型變量,表示進程的退出狀態。父進程可使用系統調用 wait() 來獲取該狀態。
目的有二:其一,如果子進程尚未調用 exit() 終止,那麼 wait 會掛起父進程直至子進程終止;其二,子進程的終止狀態通過 wait 的 status 參數返回。
加載一個新程序(路徑名為 pathname,參數列表為 argv,環境變量列表為 envp)到當前進程的內存。這將丟棄現存的程序文本段,並為新程序重新創建棧、數據段以及堆。通常將這一動作稱為執行一個新程序。
沒有直接提供 fork 系統調用的封裝,而是將 fork 和 execve 合二為一,提供了 syscall.ForkExec。如果想只調用 fork,得自己通過 syscall.Syscall(syscall.SYS_FORK, 0, 0, 0) 實現。
os.Process 存儲了通過 StartProcess 創建的進程的相關信息。
一般通過 StartProcess 創建 Process 的實例,函數聲明如下:
它使用提供的程序名、命令行參數、屬性開始一個新進程。StartProcess 是一個低級別的接口。os/exec 包提供了高級別的接口,一般應該盡量使用 os/exec 包。如果出錯,錯誤的類型會是 *PathError。
屬性定義如下:
FindProcess 可以通過 pid 查找一個運行中的進程。該函數返回的 Process 對象可以用於獲取關於底層操作系統進程的信息。在 Unix 系統中,此函數總是成功,即使 pid 對應的進程不存在。
Process 提供了四個方法:Kill、Signal、Wait 和 Release。其中 Kill 和 Signal 跟信號相關,而 Kill 實際上就是調用 Signal,發送了 SIGKILL 信號,強制進程退出,關於信號,後續章節會專門講解。
Release 方法用於釋放 Process 對象相關的資源,以便將來可以被再使用。該方法只有在確定沒有調用 Wait 時才需要調用。Unix 中,該方法的內部實現只是將 Process 的 pid 置為 -1。
通過 os 包可以做到運行外部命令,如前面的例子。不過,Go 標準庫為我們封裝了更好用的包: os/exec,運行外部命令,應該優先使用它,它包裝了 os.StartProcess 函數以便更容易的重定向標準輸入和輸出,使用管道連接 I/O,以及作其它的一些調整。
exec.LookPath 函數在 PATH 指定目錄中搜索可執行程序,如 file 中有 /,則只在當前目錄搜索。該函數返回完整路徑或相對於當前路徑的一個相對路徑。
func LookPath(file string) (string, error)
如果在 PATH 中沒有找到可執行文件,則返回 exec.ErrNotFound。
Cmd 結構代表一個正在準備或者在執行中的外部命令,調用了 Run、Output 或 CombinedOutput 後,Cmd 實例不能被重用。
一般的,應該通過 exec.Command 函數產生 Cmd 實例:
用法
得到 * Cmd 實例後,接下來一般有兩種寫法:
前面講到,通過 Cmd 實例後,有兩種方式運行命令。有時候,我們不只是簡單的運行命令,還希望能控制命令的輸入和輸出。通過上面的 API 介紹,控制輸入輸出有幾種方法:
參考資料:
使用golang 還有必要使用 nginx 么
簡單學習了golang/go語言的基礎語法,做個定時切割nginx日誌的小腳本練習下,感覺挺好使的~
腳本代碼如下,install後將腳本加入到crontab定時運行,當然golang也可以自己定時執行,這裡加入到crontab運行,是因為golang進程有可能會被kill掉….
package main
import (
“fmt”
“os”
“path/filepath”
“syscall”
“time”
“strings”
“os/exec”
“io/ioutil”
)
func main(){
//日誌目錄
srcDirPath := “/usr/local/nginx/logs”
//存放切割日誌目錄
targetDirPath := “/usr/local/nginx/logs/history”
//ngixn進程ID文件
nginxPidPath := “/usr/local/nginx/logs/nginx.pid”
//檢查存放切割日誌目錄是否存在,如果不存在則創建
finfo, errFile := os.Stat(targetDirPath)
if errFile !=nil {
errFile := os.MkdirAll(targetDirPath, 0777)
if errFile != nil {
fmt.Println(“創建日誌目錄失敗:”+errFile.Error())
return
}
} else if !finfo.IsDir() {
fmt.Println(targetDirPath+”已經存在且不是一個目錄”)
return
}
//獲取當前日期,作為此次切割日誌根目錄
t := time.Now()
nowDateTime := t.Format(“2006-01-02”)
logPath := targetDirPath+”/”+nowDateTime
os.MkdirAll(logPath, 0777)
//獲取nginx的進程ID
pfile,err := os.Open(nginxPidPath)
defer pfile.Close()
if err != nil {
fmt.Println(“not found nginx pid file”)
return
}
pidData,_ := ioutil.ReadAll(pfile)
pid := string(pidData)
pid = strings.Replace(pid,”\n”,””,-1)
//遍曆日志目錄
filepath.Walk(srcDirPath,func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
return nil
} else {
//獲取切割日誌路徑
targetfilePath := strings.Replace(path,srcDirPath,logPath,1)
if strings.Index(targetfilePath,”nginx.pid”) != -1 {
return nil
}
//移動文件
syscall.Rename(path,targetfilePath)
//創建原文件,這裡不需要了,因為重啟nginx後會自動生成滴
// nFile,errCreate := os.Create(path)
// if errCreate != nil {
// fmt.Println(“create file faild:”+errCreate.Error())
// }
// defer nFile.Close()
}
return nil
})
//平滑重啟nginx
cmd := exec.Command(“kill”,”-USR1″,pid)
_, errCmd := cmd.Output()
if errCmd != nil {
fmt.Println(“重啟nginx失敗:”+errCmd.Error())
return;
}
fmt.Println(“success”)
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/232135.html