我们在生产环境下运行的系统要求优雅退出,即程序接收退出通知后,会有机会先执行一段清理代码,将收尾工作做完后再真正退出。我们采用系统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\n", 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\n", sig)
os.Exit()
}
}
}

上例中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/

Golang -- Signal处理的更多相关文章

  1. golang处理signal

    signal一般用来实现优雅重启,或者重新加载配置文件等操作. 废话不多说,上表格 动作 号码 信号 golang kill pid 15 SIGTERM terminated kill -9 pid ...

  2. Golang的 signal

    在实际项目中我们可能有下面的需求: 1.修改了配置文件后,希望在不重启进程的情况下重新加载配置文件: 2.当用 Ctrl + C 强制关闭应用后,做一些必要的处理: 这时候就需要通过信号传递来进行处理 ...

  3. golang语言中os/signal包的学习与使用

    package main; import ( "os" "os/signal" "fmt" ) //signal包中提供了两个函数 //No ...

  4. golang 管道

    2.管道简介Golang的原子并发特性使得它很容易构造流数据管道,这使得Golang可有效的使用I/O和多CPU特性.本文提出一些关于管道的示例,在这个过程中突出了操作失败的微妙之处和介绍处理失败的具 ...

  5. Ubuntu14.04+RabbitMQ3.6.3+Golang的最佳实践

    目录 [TOC] 1.RabbitMQ介绍 1.1.什么是RabbitMQ?   RabbitMQ 是由 LShift 提供的一个 Advanced Message Queuing Protocol ...

  6. golang内存分配

    golang内存分配 new一个对象的时候,入口函数是malloc.go中的newobject函数 func newobject(typ *_type) unsafe.Pointer { flags ...

  7. golang channel 用法转的

    一.Golang并发基础理论 Golang在并发设计方面参考了C.A.R Hoare的CSP,即Communicating Sequential Processes并发模型理论.但就像John Gra ...

  8. 深入学习golang(2)—channel

    Channel 1. 概述 “网络,并发”是Go语言的两大feature.Go语言号称“互联网的C语言”,与使用传统的C语言相比,写一个Server所使用的代码更少,也更简单.写一个Server除了网 ...

  9. [转]50 Shades of Go: Traps, Gotchas, and Common Mistakes for New Golang Devs

    http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/ 50 Shades of Go: Traps, Gotc ...

随机推荐

  1. 【1】【leetcode-130】 被围绕的区域

    (DFS思路对,写复杂了) 给定一个二维的矩阵,包含 'X' 和 'O'(字母 O). 找到所有被 'X' 围绕的区域,并将这些区域里所有的 'O' 用 'X' 填充. 示例: X X X X X O ...

  2. js/java常用正则表达式及写法

    不定时增加正则表达式. 其中 Js验证写法: var str = "待验证文本"; var regular = new RegExp(/^这里是正则表达式/); if (regul ...

  3. SQL结构化查询语句

    SQL结构化查询语句 SQL定义了查询所有关系型数据库的规则. 1.通用语法 SQL语句可以单行或者多行书写,以分号结尾 可以使用空格和缩进增强可读性 不区分大小写,但是关键字建议大写 3种注释 注释 ...

  4. Java入门系列 Lambda表达式

    https://blog.csdn.net/bitcarmanlee/article/details/70195403

  5. Excel列名序号互转

    public static int ToIndex(string columnName) { if (!Regex.IsMatch(columnName.ToUpper(), @"[A-Z] ...

  6. 设计前沿:16款扁平风格 iOS 7 图标设计

    最新发布的 iOS 7 抛弃了曾经倍受追捧的拟物化设计,采用扁平化,极简设计的界面.对于 iOS 平台来说,采用没有阴影的平面设计其实是有道理的.以前的屏幕分辨率有限,必要的阴影可以填补小尺寸,分辨率 ...

  7. 字符设备驱动(一)---led

    一.总体架构 二.硬件电路 1.硬件原理图 2.寄存器配置 三.代码编写 写linux的设备驱动操作的是系统的虚拟地址,并不是像裸机程序一样操作的是物理地址. 物理地址要映射成虚拟地址,就要用到ior ...

  8. [C++]Linux之读取计算机网络数据[/proc/net/dev]

    #include<stdlib.h> #include<stdio.h> #define BUFFER_SIZE 256 int main(){ FILE *stream; c ...

  9. Ubuntu18.04终端设置为zsh后的问题记录

    1. 在将终端从bash切换成zsh后,需要将 .bashrc 下的一些配置迁移到 .zshrc 中: 例如,笔者在使用zsh中使用virtualenv及virtualenvwrapper的相关命令时 ...

  10. webpack中resolve用法

    如果想在页面中使用 bootstrap 的样式,一般会在入口文件中引入 import 'bootstrap' 但是这样引入的 bootstrap 默认引入的是JS文件,所以页面的样式是不会生效的,下面 ...