gva Viper配置
在运行时修改配置文件中的端口号没有生效
如果在运行时修改配置文件中的端口号没有生效,可能是因为应用程序在启动时已经绑定了端口,之后并没有重新绑定新的端口。一般情况下,端口绑定是在应用程序启动时完成的,动态更新端口号需要额外的处理逻辑。
下面是一个示例,展示如何在运行时动态更新端口号:
示例代码
package main
import (
"fmt"
"github.com/fsnotify/fsnotify"
"github.com/gin-gonic/gin"
"github.com/spf13/viper"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
var server *http.Server
func main() {
// 初始化 Viper
v := viper.New()
v.SetConfigFile("config.yaml")
v.SetConfigType("yaml")
err := v.ReadInConfig()
if err != nil {
panic(fmt.Errorf("Fatal error config file: %s \n", err))
}
// 监听配置文件变化
v.WatchConfig()
v.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("Config file changed:", e.Name)
restartServer(v)
})
// 启动服务器
startServer(v)
// 捕捉终止信号
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
<-sigChan
fmt.Println("Shutting down server...")
// 关闭服务器
if err := server.Close(); err != nil {
fmt.Println("Server Close:", err)
}
}
func startServer(v *viper.Viper) {
port := v.GetString("app.port")
if port == "" {
port = "8080"
}
r := gin.Default()
r.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "Hello, World!")
})
server = &http.Server{
Addr: fmt.Sprintf(":%s", port),
Handler: r,
}
go func() {
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
fmt.Printf("listen: %s\n", err)
}
}()
fmt.Printf("Server running on port %s\n", port)
}
func restartServer(v *viper.Viper) {
if server != nil {
if err := server.Close(); err != nil {
fmt.Println("Server Close:", err)
}
}
time.Sleep(1 * time.Second)
startServer(v)
}
解释
- 初始化 Viper:加载配置文件并设置监听配置文件变化。
- 启动服务器:读取配置文件中的端口号并启动服务 器。
- 监听配置文件变化:当配置文件发生变化时,调用
restartServer函数重新启动服务器。 - 捕捉终止信号:捕捉系统终止信号,优雅地关闭服务器。
注意事项
- 端口绑定:服务器启动时绑定的端口在运行时是不能动态改变的,因此需要重新启动服务器。
- 优雅关闭:确保在重新启动服务器前,优雅地关闭当前运行的服务器,以释放端口。
- 延迟处理:在关闭服务器和重新启动服务器之间加入适当的延迟,以避免端口未完全释放导致的绑定失败。
配置文件示例
app:
port: '8080'
总结
通过监听配置文件的变化并重新启动服务器,可以实现运行时动态更新端口号的功能。需要注意的是,重新启动服务器会导致短暂的服务中断,因此在生产环境中需要谨慎处理。
为什么捕捉终止信号
// 捕捉终止信号
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
<-sigChan
fmt.Println("Shutting down server...")
在服务器应用程序中捕捉终止信号是为了优雅地关闭服务器。这种做法可以确保在接收到系统终止信号(如 SIGINT 或 SIGTERM)时,服务器能够执行一些清理操作,比如关闭打开的连接、释放资源等,从而避免数据丢失或其他潜在问题。
详细解释
-
创建信号通道:
sigChan := make(chan os.Signal, 1)创建一个信号通道,用于接收系统信号。
-
注册信号通知:
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)signal.Notify函数将指定的系统信号(如SIGINT和SIGTERM)注册到信号通道sigChan。SIGINT通常是由用户按下Ctrl+C产生的,而SIGTERM通常是由系统发送的终止信号。 -
等待信号:
<-sigChan通过读取信号通道,程序会阻 塞在这里,直到接收到一个信号。
-
处理信号:
fmt.Println("Shutting down server...")当接收到信号时,程序会继续执行,打印出 "Shutting down server...",并执行后续的清理操作。
为什么要这么做
-
优雅关闭: 捕捉终止信号可以让你在程序退出前执行一些清理操作,比如关闭数据库连接、保存未处理的数据、释放资源等。
-
避免数据丢失: 直接终止程序可能会导致数据丢失或资源未释放,捕捉信号并优雅关闭可以最大限度地减少这些问题。
-
提高可靠性: 在服务器应用程序中,优雅关闭可以确保所有正在处理的请求被正确处理完毕,从而提高系统的可靠性和稳定性。
总结
通过捕捉终止信号并优雅地关闭服务器,可以确保在程序退出前完成必要的清理工作,从而提高应用程序的稳定性和可靠性。这种做法在生产环境中尤为重要,能够有效避免数据丢失和资源泄露。
<-sigChan 代码运行到这里,会阻塞掉,等信号?
是的,<-sigChan 这行代码会阻塞当前的 Goroutine,直到从 sigChan 通道接收到一个信号。这个机制允许程序在接收到系统终止信号(如 SIGINT 或 SIGTERM)之前保持运行状态。
详细解释
-
阻塞等待信号:
<-sigChan这行代码会使当前的 Goroutine 阻塞,直到从
sigChan通道接收到一个信号。当用户按下Ctrl+C或系统发送SIGTERM信号时,这个阻塞会被解除,程序继续执行后续代码。 -
保持服务器运行: 在服务器应用程序中,这种阻塞等待信号的机制通常用于保持服务器运行。服务器会在后台处理请求,而主 Goroutine 则等待终止信号以便执行优雅关闭操作。