0

0

如何更酷地实现 Go 程序热开关功能

Go语言进阶学习

Go语言进阶学习

发布时间:2023-07-21 12:00:03

|

1209人浏览过

|

来源于Go语言进阶学习

转载

开发中,我们经常会有热开关的需求,即特定功能在程序运行中的适当时候对它进行打开或关闭。例如性能分析中使用的 pprof 采样,就是一种典型的热开关。本文将讨论如何将这种热开关做得更酷。

在介绍新方案之前,我们回顾一下,在 Go 程序中,pprof 是如何做的。

接口调用

对程序进行性能采样,可能会影响到它的服务能力。因此,线上采样一般是在指定的小块时间范围内进行,需要有效的开关控制。

为了做到这点,我们一般采用在代码中引入 net/http/pprof 包(其 init 函数绑定了路由功能函数)的方式。外部访问指定端口的 HTTP 服务时开启采样功能,采样时间结束后,即关闭采集。

实现代码如下所示

package main

import (
 "net/http"
 _ "net/http/pprof"
)

func main() {
 go func() {
  _ = http.ListenAndServe(":8080", nil)
 }()
 ...
}

这种做法,当然没什么问题。我们可以学习它,将其他的开关功能也做成 HTTP 服务。但,还有其他更酷的方式吗?

信号通知

信号处理与 Go 程序的优雅退出一文中,我们谈论过信号机制,它用以向应用程序发送某种事件通知。

我们可以将基于接口触发的方式改为信号通知。

首先,构造采样功能函数(对应于 net/http/pprof 包下 init 函数中绑定的路由功能函数)。

func RegisterSignalForProfiling(sig os.Signal) {
 ch := make(chan os.Signal)
 started := false
 signal.Notify(ch, sig)

 go func() {
  var memoryProfile, cpuProfile, traceProfile *os.File
  for range ch {
   if started {
    pprof.StopCPUProfile()
    trace.Stop()
    pprof.WriteHeapProfile(memoryProfile)
    memoryProfile.Close()
    cpuProfile.Close()
    traceProfile.Close()
    started = false
   } else {
    cpuProfile, _ = os.Create("cpu.pprof")
    memoryProfile, _ = os.Create("memory.pprof")
    traceProfile, _ = os.Create("runtime.trace")
    pprof.StartCPUProfile(cpuProfile)
    trace.Start(traceProfile)
    started = true
   }
  }
 }()
}

在上述函数中,我们定义了接收信号通道ch,通过signal.Notify(ch, sig)将指定的通知信号sigch进行绑定。for range ch 将阻塞等待外部信号sig,随着sig信号的到来,交替进入开启或关闭采样的逻辑。

main函数中,就可以这样替代http.ListenAndServe(":8080", nil)了。

package main

import (
  "syscall"
  ...
)

func main() {
  RegisterSignalForProfiling(syscall.Signal(31))
 ...
}

在 linux 系统,可以通过kill -signal_number pid命令向程序发送指定信号。

如上代码所示,我们硬编码指定的采样开关信号值是 31。因此,当程序运行起来后,我们在控制台输入kill -31 pid 命令,即可开启采样,再次输入kill -31 pid命令,就关闭了采样。

依葫芦画瓢,我们再来一个打印 goroutine 堆栈信息的热开关函数,是不是很酷?

func RegisterSignalForPrintStack(sig os.Signal) {
 ch := make(chan os.Signal)
 signal.Notify(ch, sig)

 go func() {
  for range ch {
   buffer := make([]byte, 1024*1024*4)
   runtime.Stack(buffer, true)
   fmt.Println(string(buffer))
  }
 }()
}

总结

热开关是一个很简单常用的功能,无非是选择何种触发与等待方式。基于接口的调用更适合于远程控制,基于信号则便于本地控制。

相关专题

更多
硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

989

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

50

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

201

2025.12.29

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

366

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

561

2023.08.10

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

366

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

561

2023.08.10

http500解决方法
http500解决方法

http500解决方法有检查服务器日志、检查代码错误、检查服务器配置、检查文件和目录权限、检查资源不足、更新软件版本、重启服务器或寻求专业帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

266

2023.11.09

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

3

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Django 教程
Django 教程

共28课时 | 2.6万人学习

Excel 教程
Excel 教程

共162课时 | 10.1万人学习

Kotlin 教程
Kotlin 教程

共23课时 | 2.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号