• Golang -- Signal处理


    我们在生产环境下运行的系统要求优雅退出,即程序接收退出通知后,会有机会先执行一段清理代码,将收尾工作做完后再真正退出。我们采用系统Signal来 通知系统退出,即kill pragram-pid。我们在程序中针对一些系统信号设置了处理函数,当收到信号后,会执行相关清理程序或通知各个子进程做自清理。kill -9强制杀掉程序是不能被接受的,那样会导致某些处理过程被强制中断,留下无法恢复的现场,导致消息被破坏,影响下次系统启动运行。

    最近用Golang实现的一个代理程序也需要优雅退出,因此我尝试了解了一下Golang中对系统Signal的处理方式,这里和大家分享。Golang 的系统信号处理主要涉及os包、os.signal包以及syscall包。其中最主要的函数是signal包中的Notify函数:

    func Notify(c chan<- os.Signal, sig …os.Signal)

    该函数会将进程收到的系统Signal转发给channel c。转发哪些信号由该函数的可变参数决定,如果你没有传入sig参数,那么Notify会将系统收到的所有信号转发给c。如果你像下面这样调用Notify:

    signal.Notify(c, syscall.SIGINT, syscall.SIGUSR1, syscall.SIGUSR2)

    则Go只会关注你传入的Signal类型,其他Signal将会按照默认方式处理,大多都是进程退出。因此你需要在Notify中传入你要关注和处理的Signal类型,也就是拦截它们,提供自定义处理函数来改变它们的行为。

    下面是一个较为完整的例子:

    //signal.go
    
    package main
    
    import "fmt"
    import "time"
    import "os"
    import "os/signal"
    import "syscall"
    
    type signalHandler func(s os.Signal, arg interface{})
    
    type signalSet struct {
        m map[os.Signal]signalHandler
    }
    
    func signalSetNew()(*signalSet){
        ss := new(signalSet)
        ss.m = make(map[os.Signal]signalHandler)
        return ss
    }
    
    func (set *signalSet) register(s os.Signal, handler signalHandler) {
        if _, found := set.m[s]; !found {
            set.m[s] =  handler
        }
    }
    
    func (set *signalSet) handle(sig os.Signal, arg interface{})(err error) {
        if _, found := set.m[sig]; found {
            set.m[sig](sig, arg)
            return nil
        } else {
            return fmt.Errorf("No handler available for signal %v", sig)
        }
    
        panic("won't reach here")
    }
    
    func main() {
        go sysSignalHandleDemo()
        time.Sleep(time.Hour) // make the main goroutine wait!
    }
    
    func sysSignalHandleDemo() {
        ss := signalSetNew()
        handler := func(s os.Signal, arg interface{}) {
            fmt.Printf("handle signal: %v
    ", s)
        }
    
        ss.register(syscall.SIGINT, handler)
        ss.register(syscall.SIGUSR1, handler)
        ss.register(syscall.SIGUSR2, handler)
    
        for {
            c := make(chan os.Signal)
            var sigs []os.Signal
            for sig := range ss.m {
                sigs = append(sigs, sig)
            }
            signal.Notify(c)
            sig := <-c
    
            err := ss.handle(sig, nil)
            if (err != nil) {
                fmt.Printf("unknown signal received: %v
    ", sig)
                os.Exit(1)
            }
        }
    }

    上例中Notify函数只有一个参数,没有传入要关注的sig,因此程序会将收到的所有类型Signal都转发到channel c中。build该源文件并执行程序:

    $> go build signal.go
    $> signal  

    在另外一个窗口下执行如下命令:

    $> ps -ef|grep signal
    tonybai  25271  1087  0 16:27 pts/1    00:00:00 signal
    $> kill -n 2 25271
    $> kill -n 12 25271
    $> kill 25271

    我们在第一个窗口会看到如下输出:

    $> signal
    handle signal: interrupt
    handle signal: user defined signal 2
    unknown signal received: terminated

    在sysSignalHandleDemo中我们也可以为Notify传入我们所关注的Signal集合:

    signal.Notify(c, sigs…)

    这样只有在该集合中的信号我们才能捕获,收到未在集合中的信号时,程序多直接退出。上面只是一个Demo,只是说明了我们可以捕捉到我们所关注的信号,并未体现程序如何优雅退出,不同程序的退出方式不同,这里没有通用方法,就不细说了,你的程序需要你专门的设计。

    转自:http://tonybai.com/2012/09/21/signal-handling-in-go/

  • 相关阅读:
    一个通过JSONP跨域调用WCF REST服务的例子(以jQuery为例)
    步步为营UML建模系列七、表图(Data model diagram)
    步步为营UML建模系列六、类图(Class diagram)
    WebEx
    使用Nancy和Simple.Data两个轻量级的框架打造一个分布式开发系统
    细说 ASP.NET控制HTTP缓存
    WCSF vs ASP.NET MVC
    使用KTM(内核事务管理器)进行文件事务处理
    .net 2.0下的OOXML神器:NPOI.OpenXml4Net
    为什么System.Attribute的GetHashCode方法需要如此设计?
  • 原文地址:https://www.cnblogs.com/logo-fox/p/7201319.html
Copyright © 2020-2023  润新知