Golang sharing configurations between packages

huangapple go评论83阅读模式
英文:

Golang sharing configurations between packages

问题

所以我刚开始学习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
}

这是我的main函数:

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函数中看到的,我从文件中初始化了相关的配置,这是没问题的。但问题是,我该如何在数据库包中使用同一个config对象呢?因为我需要设置主机、端口等等。我可以再次解析文件,但我更希望从一开始就共享那个对象。请指点我正确的方向。

英文:

So I just started learning the Go programming language and have spent hours on end looking at examples, references and so forth. As most of you would agree there is no better way to learn a language than to dive in and make something, which is what I am attempting to do at the moment. I am building a Restful web service. I have managed to get the basics running as well as inserting into db, registering routes etc. However for the past two days I have been struggling to implement application configurations/properties. It could just be that since I'm newbie my Go project architecture is all wrong hence why I am having such difficulty with this. Without further a due here is my project structure

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

Here is my 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

This is my main

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))
 }

And finally this is where my routes get mapped

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
}

So as you can see in my Main I initialise the relevant configurations from a file which is fine. But the issue is how would I go about using that same config object from main in the database package,since I will need to set Host,Ports etc ? I could parse the file again but I would prefer if I could share that one object from the start. Please point me in the right direction

答案1

得分: 5

我建议在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]) // 在这里进行配置初始化
}

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中对init()的解释

英文:

What I would suggest is declaring a global variable in config.go and initialize it using the init() function. That way, you know that the variable will always be initialized when any package imports it. Here's some code:

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() is going to run before init(), but init() will run before the code in the package importing it. So if main.go imports the models package, then init() in models will run before any code inside main.go and thus the variable Configuration will be initialized before it is used.

Effective Go explanation of init()

答案2

得分: 2

现在你想要的只是提供一个可以在另一个包中使用的变量,解决方案很简单,记住,如果你声明一个以大写字母[A-Z]开头的变量名,这个变量就可以在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))
}

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

英文:

Now what you want just is to provide a variable can be used in another package,the solution is easy,remember that if you declare a variable name begins with uppercase letter:[A-Z],this variable can be visible and used in another package in go.

So you just need to rename config in your main.go to Config and extract it as global variable:

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

Config = models.NewConfig(args[0])
port := Config.Server[&quot;PORT&quot;]

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

log.Fatal(http.ListenAndServe(&quot;:&quot;+port,router))
}

when you want to use it in another package,just call &lt;package name&gt;.Config,the package name is the package name which your main.go belongs to,maybe main in your case.

答案3

得分: 0

我已经为此构建了一个库,一个工具箱工厂(单例)和一个通用的配置解析器(支持yaml、json和toml),请查看一下,看看这个示例:SpareBox

英文:

I've built a library just for that, a toolbox factory (singletons) & an agnostic config parser (yaml, json and toml), give it a look, watch the example: SpareBox

huangapple
  • 本文由 发表于 2016年4月10日 17:33:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/36528091.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定