Go语言实现配置热加载的方法分享

 更新时间:2023年05月24日 11:41:22   作者:tracy小猫  
web项目,经常需要热启动各种各样的配置信息,一旦这些服务发生变更,我们需要重新启动web server,以使配置生效,实现配置热加载,本文为大家整理了几个方法实现这个需求,需要的可以参考下

概述

web项目,经常需要热启动各种各样的配置信息,一旦这些服务发生变更,我们需要重新启动web server,以使配置生效,实现配置热加载。这里有几种方法实现这个需求。

go 定时器协程实现

项目结构

首先来看一下整个项目的目录结构:

- dyconfig // 项目地址
  - config   // 配置文件目录
      - api.yaml  // 采用yaml格式文件
  - global // 代码文件夹
      - config
              - config_define
              - init
              - reload
  - go.mod // go package管理依赖的包文件
  - go.sum // go package管理打包产生的文件
  - main.go // web server的入口,主函数

代码细节

接下来依次看一下各文件的主体内容:

conf/api.yaml文件定义了配置项,包含server的host及port信息。

service:
    server:
          env: dev
          host: 127.0.0.1
          port: 9902

global/init.go

package global
import (
 "context"
 "path"
)
type Config struct {
 Conf struct {
    FilePath       string
    LastModifyTime int64
 }
 ctx    context.Context
 cancel context.CancelFunc
}
func NewConfig() (*Config, error) {
 conf := new(Config)
 conf.ctx, conf.cancel = context.WithCancel(context.Background())
 conf.Conf.FilePath = path.Join("./config", "api.yaml")
 APIconfig = conf.loadRoute()
 go conf.reload() //开启协程监听routeNotify
 go func() {
    for {
       select {
       case lastModifyTime, ok := <-routeNotify:
          if !ok {
             return
          }
          conf.Conf.LastModifyTime = lastModifyTime
          route := routeAtomic.Load().(*APIConf)
          if route != nil {
             APIconfig = route
          }
       }
    }
 }()
 return conf, nil
}
func (c *Config) Stop() {
 c.cancel()
}

定义Config 根据LastModifyTime 判断是否有发生变化,FilePath为文件路径

go conf.reload()

开启协程监听routeNotify,routeNotify内容是文件修改时间的timestamp

global/reload.go

package global
import (
   "fmt"
   "gopkg.in/yaml.v3"
   "io/ioutil"
   "os"
   "sync/atomic"
   "time"
)
const (
   CheckInterval = 5 * time.Second
)
var (
   routeAtomic atomic.Value //原子性,解决并发问题
   routeNotify = make(chan int64) //channel 放入timestamp
)
func (c *Config) reload() {
   ticker := time.NewTicker(CheckInterval)
   defer ticker.Stop()
   for {
      select {
      case <-c.ctx.Done():
         close(routeNotify)
         return
      case <-ticker.C:
         if f, err := os.Stat(c.Route.FilePath); err != nil {
            fmt.Println(err)
         } else if f.ModTime().Unix() != c.Route.LastModifyTime {
            if c.Route.LastModifyTime == 0 {
               c.Route.LastModifyTime = f.ModTime().Unix()
            } else {
               routeAtomic.Store(c.loadConfig())
               routeNotify <- f.ModTime().Unix()
               fmt.Println("配置文件发生变化")
            }
         }
      }
   }
}
func (c *Config) loadConfig() *APIConf {
   if fp, err := ioutil.ReadFile(c.Route.FilePath); err != nil {
      fmt.Println(err)
      return nil
   } else {
      route := new(APIConf)
      if err := yaml.Unmarshal(fp, &route); err != nil {
         return nil
      }
      return route
   }
}

定时器监听文件的修改时间与LastModifyTime是否相同,如果不同,则

package global
var (
   APIconfig = new(APIConf)
)
package global
type ServiceConf struct {
   Server struct {
      Env  string `yaml:"env"` 
      Host string `yaml:"host"`
      Port string  `yaml:"port"`
   } `yaml:"server"`
}
type APIConf struct { 
   Service ServiceConf `yaml:"service"`
}

mian

package main
import (
   "dyconfig/global"
   "fmt"
   "github.com/gin-gonic/gin"
)
func main() {
   if conf, err := global.NewConfig(); err != nil { // 初始化配置
      fmt.Println(err)
   } else {
      defer conf.Stop()
   }
   gin.SetMode(gin.DebugMode)
   r := gin.Default()
   r.GET("/ping", func(context *gin.Context) {
      fmt.Println("当前host是: ", global.APIconfig.Service.Server.Host)
      fmt.Println("当前port是: ", global.APIconfig.Service.Server.Port)
      context.JSON(
         200, gin.H{
            "host": global.APIconfig.Service.Server.Host,
            "port": &global.APIconfig.Service.Server.Port,
         })
   })
   port := global.APIconfig.Service.Server.Port
   fmt.Println("当前host是: ", global.APIconfig.Service.Server.Host)
   fmt.Println("当前port是: ", global.APIconfig.Service.Server.Port)
   port = ":" + port
   _ = r.Run(port)
}

调用示例

1.第一次调用,port为9902

2. 修改config ,port为9903

到此这篇关于Go语言实现配置热加载的方法分享的文章就介绍到这了,更多相关Go语言配置热加载内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Go语言学习之Switch语句的使用

    Go语言学习之Switch语句的使用

    这篇文章主要通过一些示例为大家介绍一下Go语言中Switch语句的基本语法以及使用,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一下
    2022-06-06
  • Go语言判断指定文件是否存在的方法

    Go语言判断指定文件是否存在的方法

    这篇文章主要介绍了Go语言判断指定文件是否存在的方法,实例分析了Go语言针对文件操作的技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-02-02
  • Go语言包管理工具dep的安装与使用

    Go语言包管理工具dep的安装与使用

    godep是解决包依赖的管理工具,下面这篇文章主要给大家介绍了关于Go语言包管理工具dep的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-07-07
  • Go语言copy()实现切片复制

    Go语言copy()实现切片复制

    本文主要介绍了Go语言copy()实现切片复制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04
  • 使用Go http重试请求的示例

    使用Go http重试请求的示例

    开发中对于http请求是经常遇到,一般可能网络延迟或接口返回超时,这篇文章主要介绍了使用Go http重试请求的示例,需要的朋友可以参考下
    2022-08-08
  • 一文带你了解Go中的内存对齐

    一文带你了解Go中的内存对齐

    一旦涉及到较为底层的编程,特别是与硬件交互,内存对齐是一个必修的课题,所以这篇文章小编就想来和大家聊一聊Go语言中的内存对齐,希望对大家有所帮助
    2023-10-10
  • go程序执行交叉编译的流程步骤

    go程序执行交叉编译的流程步骤

    go程序可用通过交叉编译的方式在一个平台输出多个平台可运行的二进制包,本文给大家详细介绍了go程序执行交叉编译的流程步骤,文中有详细的代码示例供大家参考,需要的朋友可以参考下
    2024-07-07
  • 一文教你学会Go中singleflight的使用

    一文教你学会Go中singleflight的使用

    缓存在项目中使用应该是非常频繁的,提到缓存只要了解过 singleflight ,基本都会用于缓存实现的一部分吧,下面就跟随小编一起来学习一下singleflight的使用吧
    2024-02-02
  • 使用Go语言编写一个极简版的容器Container

    使用Go语言编写一个极简版的容器Container

    Docker作为一种流行的容器化技术,对于每一个程序开发者而言都具有重要性和必要性,因为容器化相关技术的普及大大简化了开发环境配置、更好的隔离性和更高的安全性,对于部署项目和团队协作而言也更加方便,本文将尝试使用Go语言编写一个极简版的容器
    2023-10-10
  • Go语言实现管理多个数据库连接

    Go语言实现管理多个数据库连接

    在软件开发过程中,使用 MySQL、PostgreSQL 或其他数据库是很常见的,由于配置和要求不同,管理这些连接可能具有挑战性,下面就来和大家聊聊如何在Go中管理多个数据库连接吧
    2023-10-10

最新评论