>백엔드 개발 >Golang >Go 프로그램의 핫스위치 기능을 좀 더 멋지게 구현하는 방법

Go 프로그램의 핫스위치 기능을 좀 더 멋지게 구현하는 방법

Go语言进阶学习
Go语言进阶学习앞으로
2023-07-21 12:00:031149검색
개발 중에 열 스위치가 필요한 경우가 종종 있습니다. 즉, 프로그램이 실행되는 동안 적절한 시간에 특정 기능을 켜거나 끌 수 있습니다. 예를 들어 성능 분석에 사용되는 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
   }
  }
 }()
}

在上述函数中,我们定义了接收信号通道<span style="font-size: 15px;">ch</span>,通过<span style="font-size: 15px;">signal.Notify(ch, sig)</span>将指定的通知信号<span style="font-size: 15px;">sig</span><span style="font-size: 15px;">ch</span>进行绑定。<span style="font-size: 15px;">for range ch</span> 将阻塞等待外部信号<span style="font-size: 15px;">sig</span>,随着<span style="font-size: 15px;">sig</span>信号的到来,交替进入开启或关闭采样的逻辑。

<span style="font-size: 15px;">main</span>函数中,就可以这样替代<span style="font-size: 15px;">http.ListenAndServe(":8080", nil)</span>了。

package main

import (
  "syscall"
  ...
)

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

在 linux 系统,可以通过<span style="font-size: 15px;">kill -signal_number pid</span>命令向程序发送指定信号。

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

依葫芦画瓢,我们再来一个打印 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))
  }
 }()
}

总结

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

위 내용은 Go 프로그램의 핫스위치 기능을 좀 더 멋지게 구현하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 Go语言进阶学习에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제