【问题标题】:Golang sharing configurations between packagesGolang 在包之间共享配置
【发布时间】:2016-07-31 09:34:32
【问题描述】:

所以我刚开始学习 Go 编程语言,并且花了几个小时查看示例、参考资料等。正如你们中的大多数人会同意的那样,学习一门语言没有比潜入并做点什么更好的方法了,这就是我目前正在尝试做的事情。我正在构建一个 Restful Web 服务。我已经设法让基础知识运行以及插入数据库、注册路由等。但是在过去的两天里,我一直在努力实现应用程序配置/属性。可能只是因为我是新手,所以我的 Go 项目架构都是错误的,因此为什么我会遇到这样的困难。不用多说,这里是我的项目结构

src
   server
      database
         dbaccess.go
         dbcomm.go
      handling
         handler.go
         handlercomm.go
      models
         config.go
         response.go
         user.go
      routing
         routes.go
      main.go

这是我的 config.go

package models

import (
   "io/ioutil"
   "encoding/json"
)

type Config struct  {
   Db map[string]string `json:"db"`
   Server map[string]string `json:"server"`
}


func NewConfig(fname string) *Config{
   data,err := ioutil.ReadFile(fname)
   if err != nil{
      panic(err)
   }
   config := Config{}
   err = json.Unmarshal(data,&config)
   if err != nil {
   panic(err)
}
return config

这是我的主线

func main(){
    args := os.Args[1:]
    if len(args) == 0{
       fmt.Println("********************\nMust specify a config file   in args\n********************")
    os.Exit(1)
   }

   config := models.NewConfig(args[0])
   port := config.Server["PORT"]

   router := routing.NewRouter()
   fmt.Printf(  "-------------------------------------------------\n"+
        "Listening and Serving on Port %s\n"+
        "-------------------------------------------------",port)

   log.Fatal(http.ListenAndServe(":"+port,router))
 }

最后这是我的路线被映射的地方

type Route struct {
   Name string
   Method string
   Pattern string
   HandlerFunc http.HandlerFunc
}

var routes = []Route{
   Route{
    "signup",
    "POST",
    "/signup",
    handling.PostSignUpUser,
   },

   Route{
    "authenticate",
    "POST",
    "/login",
    handling.PostLogin,
   },
}

func NewRouter() *mux.Router{
 router :=  mux.NewRouter().StrictSlash(true)
 for _,route := range routes{       
    router.Methods(route.Method)
          .Path(route.Pattern)
          .Name(route.Name)
          .Handler(route.HandlerFunc)
}

return router
}

正如您在我的 Main 中看到的,我从一个文件中初始化了相关配置,这很好。但问题是我将如何在数据库包中使用与 main 相同的配置对象,因为我需要设置主机、端口等?我可以再次解析文件,但如果我可以从一开始就共享一个对象,我会更喜欢。请指出正确的方向

【问题讨论】:

    标签: go gorilla


    【解决方案1】:

    我的建议是在config.go 中声明一个全局变量并使用init() 函数对其进行初始化。这样,您知道该变量将始终在任何包导入它时被初始化。这是一些代码:

    package models
    
    import (
       "io/ioutil"
       "encoding/json"
    )
    
    
    var (
    
        Configuration Config 
    )
    
    
    init() {
    
        args := os.Args[1:]
        if len(args) == 0{
           fmt.Println("********************\nMust specify a config file   in args\n********************")
           os.Exit(1)
       }
    
       Configuration = NewConfig(args[0]) // configuration initialized here
    }
    
    type Config struct  {
       Db map[string]string `json:"db"`
       Server map[string]string `json:"server"`
    }
    
    
    func NewConfig(fname string) *Config{
       data,err := ioutil.ReadFile(fname)
       if err != nil{
          panic(err)
       }
       config := Config{}
       err = json.Unmarshal(data,&config)
       if err != nil {
          panic(err)
       }
       return config
    }
    

    var() 将在init() 之前运行,但init() 将在在导入它的包中的代码之前运行。因此,如果main.go 导入了models 包,那么models 中的init() 将在main.go 中的任何代码之前运行,因此变量Configuration 将在使用之前被初始化。

    Effective Go explanation of init()

    【讨论】:

    • 避免在你的 golang 应用程序中使用全局变量和初始化函数
    【解决方案2】:

    现在你想要的只是提供一个变量可以在另一个包中使用,解决方法很简单,记住如果你声明一个变量名以大写字母开头:[AZ],这个变量可以在go中的另一个包。

    所以您只需将main.go 中的config 重命名为Config 并将其提取为全局变量:

    var Config *models.Config
    func main(){
    args := os.Args[1:]
    if len(args) == 0{
       fmt.Println("********************\nMust specify a config file   in args\n********************")
    os.Exit(1)
    }
    
    Config = models.NewConfig(args[0])
    port := Config.Server["PORT"]
    
    router := routing.NewRouter()
    fmt.Printf(  "-------------------------------------------------\n"+
        "Listening and Serving on Port %s\n"+
        "-------------------------------------------------",port)
    
    log.Fatal(http.ListenAndServe(":"+port,router))
    }
    

    当你想在另一个包中使用它时,只需调用<package name>.Config,包名就是你的main.go所属的包名,在你的情况下可能是main

    【讨论】:

    • 感谢您的回答,是的,我知道大写范围规则,但是当我尝试您的解决方案时,config 没有被其他软件包拾取。所以我的 main.gopackage main 我按照建议将变量重命名为 Config 然后尝试在 database/dbaccess.go 中引用它。但是我得到unresolved reference 似乎不起作用
    • @MrSSS16,对不起,你也应该声明为全局变量,我的答案已经更新了。
    • 我没有看到您对答案的更改,并且仍然存在其他软件包无法从main.go 检测到我的Config 的相同问题。所以我在我的主要var Config *models.Config 中声明,但我无法从其他包访问它,特别是database
    • @MrSSS16,可能是网络问题,我添加示例代码,根据您的最新问题,您是否通过添加代码import "xx/main"导入主包?
    • 对,但从我读到的内容来看,不鼓励这样做,而是在单独的包中定义“var Config *models.config”,然后从那里引用它,而不是将主包导入子包.所以我在'Config.go'中定义了全局配置。问题是当我从“数据库”访问变量时变量为空
    【解决方案3】:

    我为此构建了一个库,一个工具箱工厂(单例)和一个不可知的配置解析器(yaml、json 和 toml),看看它,观看示例:SpareBox

    【讨论】:

      猜你喜欢
      • 2023-04-10
      • 2016-08-03
      • 1970-01-01
      • 1970-01-01
      • 2017-01-05
      • 2016-09-19
      • 2016-06-15
      • 2015-09-03
      • 2013-07-03
      相关资源
      最近更新 更多