GO 的優雅終止姿勢

最近優化了一版程序:用到了 golang 的優雅退出機制。

程序使用 etcd 的election sdk做高可用選主,需要在節點意外下線的時候,主動去 etcd 卸任 (刪除 10s 租約), 否則已經下線的節點還會被 etcd 認爲是 leader。

所以在這裏,優雅退出是技術剛需。

另外根據《雲原生十二要素方法論》 第 9 條: 快速啓動和優雅終止可最大化健壯性 , 也推薦各位遵守實踐。
Fast startup and shutdown are advocated for a more robust and resilient system.

粗淺的認知方案:捕獲程序的終止信號, 主動去卸任

標準信號 [1] Linux 支持如下標準信號,第二列指示該信號遵守的標準。

   Signal      Standard   Action   Comment
   ────────────────────────────────────────────────────────────────────────
   SIGABRT      P1990      Core    Abort signal from abort(3)
   SIGALRM      P1990      Term    Timer signal from alarm(2)
   SIGBUS       P2001      Core    Bus error (bad memory access)
   SIGCHLD      P1990      Ign     Child stopped or terminated
   SIGCLD         -        Ign     A synonym for SIGCHLD
   SIGCONT      P1990      Cont    Continue if stopped
   SIGEMT         -        Term    Emulator trap
   SIGFPE       P1990      Core    Floating-point exception
   SIGHUP       P1990      Term    Hangup detected on controlling terminal
                                   or death of controlling process
   SIGILL       P1990      Core    Illegal Instruction
   SIGINFO        -                A synonym for SIGPWR
   SIGINT       P1990      Term    Interrupt from keyboard


   SIGIO          -        Term    I/O now possible (4.2BSD)
   SIGIOT         -        Core    IOT trap. A synonym for SIGABRT
   SIGKILL      P1990      Term    Kill signal
   SIGLOST        -        Term    File lock lost (unused)
   SIGPIPE      P1990      Term    Broken pipe: write to pipe with no
                                   readers; see pipe(7)
   SIGPOLL      P2001      Term    Pollable event (Sys V);
                                   synonym for SIGIO
   SIGPROF      P2001      Term    Profiling timer expired
   SIGPWR         -        Term    Power failure (System V)
   SIGQUIT      P1990      Core    Quit from keyboard
   SIGSEGV      P1990      Core    Invalid memory reference
   SIGSTKFLT      -        Term    Stack fault on coprocessor (unused)
   SIGSTOP      P1990      Stop    Stop process
   SIGTSTP      P1990      Stop    Stop typed at terminal
   SIGSYS       P2001      Core    Bad system call (SVr4);
                                   see also seccomp(2)
   SIGTERM      P1990      Term    Termination signal
   SIGTRAP      P2001      Core    Trace/breakpoint trap
   SIGTTIN      P1990      Stop    Terminal input for background process
   SIGTTOU      P1990      Stop    Terminal output for background process
   SIGUNUSED      -        Core    Synonymous with SIGSYS
   SIGURG       P2001      Ign     Urgent condition on socket (4.2BSD)
   SIGUSR1      P1990      Term    User-defined signal 1
   SIGUSR2      P1990      Term    User-defined signal 2
   SIGVTALRM    P2001      Term    Virtual alarm clock (4.2BSD)
   SIGXCPU      P2001      Core    CPU time limit exceeded (4.2BSD);
                                   see setrlimit(2)
   SIGXFSZ      P2001      Core    File size limit exceeded (4.2BSD);
                                   see setrlimit(2)
   SIGWINCH       -        Ign     Window resize signal (4.3BSD, Sun)

其中SIGKILL,SIGSTOP信號不能被捕獲、阻塞、忽略。

我們常見的三種終止程序的操作:

1.CTRL+C 實際是發送SIGINT信號,2.kill pid的作用是向指定進程發送SIGTERM信號 (這是 kill 默認發送的信息), 若應用程序沒有捕獲並響應該信號的邏輯,則該信號默認動作是 kill 掉進程,這是終止進程的推薦做法。3.kill -9 pid 則是向指定進程發送SIGKILL信號,SIGKILL 信號既不能被應用程序捕獲,也不能被阻塞或忽略,

故要達成我們的目的,這裏捕獲 SIGINT SIGTREM信號就可滿足需求。

golang 提供signal包來監聽並反饋收到的信號。

可針對長時間運行的程序,新開協程,持續監聽信號,並插入優雅關閉的代碼。

c := make(chan os.Signal)
signal.Notify(c, syscall.SIGTERM, syscall.SIGINT)
go func() {
        select  {
            case sig:= <-c: {
                log.Infof("Got %s signal. Aborting...\n", sig)
                eCli.Close()    // 利用 etcd election sdk主動卸任
                os.Exit(1)    
            }
        }
    }()

是不是依舊適配容器?

我們得看 DOCKER 官方docker stop,docker kill命令的定義。

docker stop[2]: The main process inside the container will receiver SIGTREM, and after a grace period,SIGKILL .(default grace period =10s)

docker kill[3]:The main process inside the container is sent SIGKILL signal (default), or the signal that is specified with the --signal option

我們常用的 docker stop 命令:向容器內進程發送SIGTREM信號,10s 後發送SIGKILL信號,這 10s 時間給了程序做優雅關閉的時機,所以上面代碼的邏輯是能適配容器的。

Ref:  十二要素 App 方法論

引用鏈接

[1] 標準信號: https://www.man7.org/linux/man-pages/man7/signal.7.html
[2] docker stop: https://docs.docker.com/engine/reference/commandline/stop/
[3] docker kill: https://docs.docker.com/engine/reference/commandline/kill/

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/gHjDBo0xWw51qGqr33cKuQ