1. 程式人生 > >golang的facebookgo grace優雅重啟原理分析

golang的facebookgo grace優雅重啟原理分析

從原理上來說是這樣一個過程:

    1)釋出新的bin檔案去覆蓋老的bin檔案
    2)傳送一個訊號量,告訴正在執行的程序,進行重啟
    3)正在執行的程序收到訊號後,會以子程序的方式啟動新的bin檔案
    4)新程序接受新請求,並處理
    5)老程序不再接受請求,但是要等正在處理的請求處理完成,所有在處理的請求處理完之後,便自動退出
    6)新程序在老程序退出之後,由init程序收養,但是會繼續服務。

所以一步一步來看,關鍵是從第2步開始之後怎麼做,所以我們先來看看第2步的實現,這個應該說很簡單,傳送訊號量到一個程序,使用kill命令即可,在facebook這個專案中傳送的訊號量有3個:SIGINT,SIGTERM,SIGUSR2,前面兩個訊號收到後程序會直接退出,後面一個訊號SIGUSR2才會執行所謂的優雅重啟。
第3步,正在執行的程序收到SIGUSR2訊號後,會以子程序的方式啟動新的bin檔案。先直接上程式碼看:https://github.com/facebookgo/grace/blob/master/gracehttp/http.go

func (a *app) signalHandler(wg *sync.WaitGroup) {
    ch := make(chan os.Signal, 10)
    signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR2)
    for {
        sig := <-ch
        switch sig {
        case syscall.SIGINT, syscall.SIGTERM:  
            // this ensures a subsequent INT/TERM will trigger standard go behaviour of
            // terminating. 執行標準的go終止行為,程式就結束了
            signal.Stop(ch)
            a.term(wg)
            return
        case syscall.SIGUSR2: // 這裡開始執行優雅重啟
            err := a.preStartProcess()  
            // 這個函式在原始碼中沒有具體實現功能,只是預留了一個鉤子函式,使用者可以註冊自己的函式,可以在重啟之前做些自定義的事情。一般情況下也沒有什麼可以做的,除非有些特殊的服務環境或是狀態儲存之類的,至少目前,我們的server還沒有遇到
            if err != nil {
                a.errors <- err
            }
            // we only return here if there's an error, otherwise the new process
            // will send us a TERM when it's ready to trigger the actual shutdown.
            if _, err := a.net.StartProcess(); err != nil { // 這裡開始正式所謂的優雅重啟            
                a.errors <- err
            }
        }
    }
}

a.net.StartProcess的過程我們來看看基本過程:

func (n *Net) StartProcess() (int, error) {
    listeners, err := n.activeListeners() // 獲取目前在監聽的埠,這塊也是重點,下面重點介紹
    if err != nil {
        return 0, err
    }

    // Extract the fds from the listeners.  從監聽埠中把檔案描述符取出來
    files := make([]*os.File, len(listeners))
    for i, l := range listeners {
        files[i], err = l.(filer).File()
        if err != nil {
            return 0, err
        }
        defer files[i].Close()
    }

    // Use the original binary location. This works with symlinks such that if
    // the file it points to has been changed we will use the updated symlink.
    // 獲取可執行bin檔案的路勁,也可以是連結路勁,會使用最新的連結路徑作為啟動檔案路勁的
    argv0, err := exec.LookPath(os.Args[0])
    if err != nil {
        return 0, err
    }

    // Pass on the environment and replace the old count key with the new one.
    // 獲取 LISTEN_FDS 換進變數值 
    var env []string
    for _, v := range os.Environ() {
        if !strings.HasPrefix(v, envCountKeyPrefix) {
            env = append(env, v)
        }
    }
    env = append(env, fmt.Sprintf("%s%d", envCountKeyPrefix, len(listeners)))

    allFiles := append([]*os.File{os.Stdin, os.Stdout, os.Stderr}, files...)
    // 這裡呼叫一個golang底層的程序啟動函式,來指定,上面獲取的引數來啟動程序
    process, err := os.StartProcess(argv0, os.Args, &os.ProcAttr{
        Dir:   originalWD,
        Env:   env,
        Files: allFiles,
    })
    if err != nil {
        return 0, err
    }
    // 返回新程序id。
    return process.Pid, nil 
}

以上是啟動新程序,並且接管監聽埠的過程, 一般情況下埠是不可以重複監聽的,所以這裡就要需要使用比較特別的辦法,從上面的程式碼來看就是讀取監聽埠的檔案描述符,並且把監聽埠的檔案描述符傳遞給子程序,子程序裡從這個檔案描述符實現對埠的監聽

另外還有一個比較特別的地方就是老的介面怎麼關閉的問題,關閉必須要把已經收到的請求處理完成之後再關閉。為此facebook的同學另外開了一個專案httpdown,繼承了原始的httpserver,但是多了對各種連結狀態的維護和處理,這部分後面在分析。

相關推薦

golang的facebookgo grace優雅原理分析

從原理上來說是這樣一個過程: 1)釋出新的bin檔案去覆蓋老的bin檔案 2)傳送一個訊號量,告訴正在執行的程序,進行重啟 3)正在執行的程序收到訊號後,會以子程序的方式啟動新的bin檔案 4)新程序接受新請求,並處理 5)老程序不再

Golang開發支持平滑升級(優雅)的HTTP服務

response def files all 結束 wait stop error: 如何 Golang開發支持平滑升級(優雅重啟)的HTTP服務 前段時間用Golang在做一個HTTP的接口,因編譯型語言的特性,修改了代碼需要重新編譯可執行文件,關閉正在運行的老程序,並

彈出SD卡導致的問題分析

2018-10-10   在Android8.0上,安裝SD卡,設定為內部儲存,然後把安裝的應用move到SD卡上,開啟應用,然後在setting裡彈出(reject)SD卡,會發生重啟。 log 01-01 21:04:55.321858 1050 1081 D Pack

Android patch後問題分析

在合入Android patch過程中會遇到Crash重啟的情況,在logcat日誌也只會列印一些崩潰的堆疊,這些資訊很難 幫助我們定位問題。      首先先看一下我遇到的一個logcat關於Crash的列印資訊: 如: 952 555

平滑和平滑原理

什麼是平滑重啟? 平滑重啟不同於普通的重啟,平滑重啟可以做到在不影響使用者的情況下重啟服務,以便重新載入PHP程式,完成業務程式碼更新。 平滑重啟一般應用於業務更新或者版本釋出過程中,能夠避免因為程式碼釋出重啟服務導致的暫時性服務不可用的影響。 注意:只有子程序執行過程

android展訊平臺 案例分析(二)

./0000.log  watchdog重啟 01-02 02:01:02.458   551  1010 W Watchdog: *** WATCHDOG KILLING SYSTEM PROCESS: Blocked in monitor com.android.s

notification使用不當導致的宕機問題分析(Could not copy bitmap to parcel blob. )

前言 前段時間遇到了一個宕機重啟問題,比較複雜,涉及到多方面的知識,我也分析了很長的時間,期間學到了很多東西,現在把分析的過程整理一下,希望可以給大家一點幫助和啟發,同時也幫助自己再鞏固一下。 一、問題的復現 首先說一下問題最開始的分析思路以及復現的過程,log 中最核心的部

Android原因分析

重啟原因分類 1.上層造成重啟 system_server被殺watchdog重啟重要執行緒阻塞2.kernel造成重啟 空指標非法地址3.kernel watchdog造成重啟,原因不確定 記憶體原因nand驅動log檢視步驟及關鍵字 1. 重啟後的kernel.log或

JNI引用溢位導致的問題分析

問題描述 JNI全域性引用異常導致的重啟問題JNI ERROR (app bug): global reference table overflow (max=51200)' Log pid: 1279, tid: 2518, name: Binder:1279_9

Android 系統(74)--Android原因分析

重啟原因分類1.上層造成重啟system_server被殺watchdog重啟重要執行緒阻塞2.kernel造成重啟空指標非法地址3.kernel watchdog造成重啟,原因不確定記憶體原因nand驅動log檢視步驟及關鍵字1. 重啟後的kernel.log或misc/cmdline.log在log最前面

21. log分析

案例一 : kernel重啟 - mt6580.dtsi 現象 : 平臺 : androidN,MTK6580 排查過程: 1. 打串列埠log,發現如下: [ 1.607970] <2>.(2)[1:swapper/0]musb-hdrc

ELF格式的定位原理分析

前面有篇文章分析了ELF格式,也只是讓我們對目標檔案有了一個大概的瞭解,並沒有說明一個十分重要的問題:重定位,今天重新看了下重定位的資料,終於弄懂了重定位的過程,下面來做一個分析。 我們將使用下面兩個

Android 系統(126)---Android的宕機、問題分析方法

Android的宕機、重啟問題分析方法當手機長時間無法再被使用者控制操作時,我們稱為宕機。在這裡我們強調長時間,如果是短時間,歸結為效能問題。* 使用者操作手機無任何響應,如觸控式螢幕幕,按鍵操作等。* 手機螢幕黑屏,無法點亮螢幕。* 手機介面顯示內容和使用者輸入不相干。1. 系統簡圖當用戶對手機進行操作時,

MySQL無限原因分析

最近在CentOS配置LAMP環境,裝完MySQL執行/usr/local/mysql/bin/mysqld_safe --user=mysql &,MySQL無限重啟: “number of

linux 優雅的退出/關閉/gunicorn進程

inux HR down ref 通過 AS 銷毀 發生 html 在工作中,會發現gunicorn啟動的web服務,無論怎麽使用kill -9 進程號都是無法殺死gunicorn,經過我一番百度和谷歌,發現想要刪除gunicorn進程其實很簡單。 第一步獲取Gunicor

Spring Boot 1.X和2.X優雅彩38平臺出租實戰

main pau ble eas ren 超過 kill -9 tst 兼容 項目在彩38平臺出租 haozbbs.com Q1446595067 重新發布的過程中,如果有的請求時間比較長,還沒執行完成,此時重啟的話就會導致請求中斷,影響業務功能,優雅重啟可以保證在停止的時

生產環境優雅基於Nginx、Tornado的Web服務進程

進程關閉 nod add ocs unix 內部 start logfile stop Nginx是一個高效的Web服務器及代理服務器,Tornado是一個基於epoll的異步Web開發框架,通常使用Nginx做為Web服務器時,都會以FastCGI模式,而我們從開發、調試

優雅的退出/關閉/gunicorn進程

unicorn ati local 進程id 命令 怎麽 gun 百度 hup 在工作中,會發現gunicorn啟動的web服務,無論怎麽使用kill -9 進程號都是無法殺死gunicorn,經過我一番百度和谷歌,發現想要刪除gunicorn進程其實很簡單。 第一步獲

1.報出問題: Please change caller according to com.intellij.openapi.project. IndexNotReadyException documentation 2.分析問題: 提示資訊,不影響編譯 3.解決方案: AS

1.報出問題: Please change caller according to com.intellij.openapi.project. IndexNotReadyException documentation 2.分析問題: 提示資訊,不影響編譯 3.解

Spring Developer Tools 原始碼分析:三、自動配置'

接上文 Spring Developer Tools 原始碼分析:二、類路徑監控,接下來看看前面提到的這些類是如何配置,如何啟動的。 spring-boot-devtools 使用了 Spring Boot 的自動配置方式,我們先關注本地開發環境中自動重啟的部分。 在 LocalDevToolsAut