我们因不同的意图去封闭服务。有时,封闭服务器的主要意图是用来更新装备。在Golang中,有很多关于高雅关机的帖子。然而,我发现他们并没有提到热重载装备。一般来说,shutdown graceful和hot reload是由信号控制的。这便是我把它们放在一个帖子里的原因。

起先,一个服务器因为各种原因需求封闭,常见的是操作系统的中止,我们希望能高雅地封闭服务器。最后也是最基本的是假如一个应用程序需求更新,只需求更新装备。这种状况总是产生。那个时候,假如你不需求重启服务器,那将是十分惊人的。你只需求更新你的装备,然后发送一个信号。之后,服务器将热重新加载,你的新装备就会作业。上述场景可以在Golang中运用信号包来完结。让我们动手吧。

代码实现

首要,我界说了一个Config结构并声明了一个conf变量。其代码如下:

type Config struct {
 Message string
}
var conf = &Config{Message: "Before hot reload"}

上面的代码只是一个简单的装备样本,你可以依据自己的需求界说一个复杂的结构。

其次,界说一个路由器函数,用来绑定和监听8080端口。在热重载装备完结后,它也被用来显现成果。代码如下:

func router() {
 log.Println("starting up....")
 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  _, _ = w.Write([]byte(conf.Message))
 })
 go func() {
  log.Fatal(http.ListenAndServe(":8080", nil))
 }()
}

下一步是服务器关机和热重载装备。当一个服务器封闭时,它应该停止接纳新的恳求,同时完结正在进行的恳求,返回其呼应,然后封闭。像其他文章相同,我将在这儿运用信号包。

sigCh := make(chan os.Signal, 1)

之后,我也会运用signal.Notify()。可是我一同发送更多的信号。

signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)

上述代码中,当程序被中止时,signal.Notify将向sigCh通道发送一个信号。

syscall.SIGHUPsyscall.SIGINTsyscall.SIGTERM是什么意思?

syscall.SIGINT是用来在Ctrl+C时高雅地封闭的,它也相当于os.Interrupt

syscall.SIGTERM是常用的停止信号,也是docker容器的默认信号,Kubernetes也运用它。

syscall.SIGHUP用于热重载装备。

这一部分的悉数代码如下:

func main() {
   router()
   sigCh := make(chan os.Signal, 1)
   signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
   for {
      multiSignalHandler(<-sigCh)
   }
}

因为这个帖子也包含高雅地封闭,multiSignalHandler(<-sigCh)被用来接纳chan值,然后,它将决议运转代码的哪一部分。multiSignalHandler的代码如下:

func multiSignalHandler(signal os.Signal) {
   switch signal {
   case syscall.SIGHUP:
      log.Println("Signal:", signal.String())
      log.Println("After hot reload")
      conf.Message = "Hot reload has been finished."
   case syscall.SIGINT:
      log.Println("Signal:", signal.String())
      log.Println("Interrupt by Ctrl+C")
      os.Exit(0)
   case syscall.SIGTERM:
      log.Println("Signal:", signal.String())
      log.Println("Process is killed.")
      os.Exit(0)
   default:
      log.Println("Unhandled/unknown signal")
   }
}

以上一切的代码将使高雅地关机和热重载装备作业。我把整个代码粘贴在下面,你可以很容易地把它粘贴到你的环境中。

package main
import (
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
)
type Config struct {
	Message string
}
var conf = &Config{Message: "Before hot reload"}
func router() {
	log.Println("starting up....")
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		_, _ = w.Write([]byte(conf.Message))
	})
	go func() {
		log.Fatal(http.ListenAndServe(":8080", nil))
	}()
}
func main() {
	router()
	sigCh := make(chan os.Signal, 1)
	signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
	for {
		multiSignalHandler(<-sigCh)
	}
}
func multiSignalHandler(signal os.Signal) {
	switch signal {
	case syscall.SIGHUP:
		log.Println("Signal:", signal.String())
		log.Println("After hot reload")
		conf.Message = "Hot reload has been finished."
	case syscall.SIGINT:
		log.Println("Signal:", signal.String())
		log.Println("Interrupt by Ctrl+C")
		os.Exit(0)
	case syscall.SIGTERM:
		log.Println("Signal:", signal.String())
		log.Println("Process is killed.")
		os.Exit(0)
	default:
		log.Println("Unhandled/unknown signal")
	}
}

测验高雅封闭和热重载装备

这与之前运用Golang的自动测验不同,此时我手动进行测验。这个测验需求翻开两个终端窗口。

测验高雅封闭

首要,在终端一中运转服务器。假如你按上述过程操作,你会看到与运转服务器后的截图类似的成果:

Golang热重载和优雅地关闭

第二,在第二终端发送一个curl恳求。

curl localhost:8080

假如你按上述过程操作,运转服务器后,你会看到与截图类似的成果。

Golang热重载和优雅地关闭

上面的截图意味着服务器在作业。

让我们先用Ctrl+C测验一下中止。

第三,回到榜首终端,然后,用键盘输入Ctrl+C。你会看到下面类似的屏幕截图。

Golang热重载和优雅地关闭

从下面的代码中输出 “经过Ctrl+C中止”,你可以在函数multiSignalHandler()中轻松找到它们。

热重载

首要,因为服务器已经被高雅地封闭了,我们应该在终端一中再次运转服务器。

第二,让我们到第二终端。发送curl恳求,以保证服务器作业。

第三,在第二终端,运转

lsof -i tcp:port-number

上面的指令是用来检查当前端口号在本地的PID。

Golang热重载和优雅地关闭

第四,在第二终端,运转

kill -SIGHUP 43587

43587与截图中的PID相同。

第五,回到榜首终端,假如你墨守成规,你会看到类似下面的截图。

Golang热重载和优雅地关闭

现在你会看到 “After hot reload “已经在终端一打印出来了。让我们回到终端二,发送一个 curl 恳求来检查成果。

Golang热重载和优雅地关闭

如截图所示,我们知道装备已经被热重载。

在完结上述一切作业后,我们可以用两种方法来停止程序。一种方法,在终端一中运用 “Ctrl+C “来做。另一种方法,运用

kill -9 port-number

在二号终端中,停止程序。