beego源码解析之config模块
source link: https://segmentfault.com/a/1190000021283533
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
目标
config模块参考了database/sql中实现模式,接口和实现分离,本教程选取ini格式配置的实现,以及分析beego和config模块的集成方式,来实现下面三个目标。
- 理解config包的源码实现
- ini格式介绍
- 理解beego如何使用源码config
config模块相关文件
config在beego下config目录,本文中分析以下两个文件。
- config.go 定义实现的接口与包含接口的使用方式
- ini.go 为ini格式的具体实现
图1-1
config接口定义
config中定义了两个接口,Config和Configer。先简单描述下,详情见下文,Config负责文件解析并存储。Configer是对存储数据的操作。
图1-2
config实现方式
从config.go注释中,我们看到config的使用demo
// Usage: // import "github.com/astaxie/beego/config" //Examples. // // cnf, err := config.NewConfig("ini", "config.conf") /
看一下NewConfig函数
func NewConfig(adapterName, filename string) (Configer, error) { adapter, ok := adapters[adapterName] return adapter.Parse(filename) }
会调用到上图中Parse函数,由于我们作用的ini配置,我们接下来看ini中的具体实现了。
ini格式介绍(百度百科)
init格式文件实例
appname = beepkg httpaddr = "127.0.0.1" ; http port httpport = 9090 [mysql] mysqluser = "root" mysqlpass = "rootpass"
节(section)
节用方括号括起来,单独占一行,例如:
[section]
键(key)
键(key)又名属性(property),单独占一行用等号连接键名和键值,例如:
name=value
注释(comment)
注释使用英文分号(;)开头,单独占一行。在分号后面的文字,直到该行结尾都全部为注释,例如:
; comment text
ini配置实现
我们看到Parse会最终调用parseData.
func (ini *IniConfig) Parse(name string) (Configer, error) { return ini.parseFile(name) } func (ini *IniConfig) parseFile(name string) (*IniConfigContainer, error) { data, err := ioutil.ReadFile(name) return ini.parseData(filepath.Dir(name), data) }
分析下parseData
1,读取section,如果读取不到,作用默认section的name.
2, 读取行,按照=分割为两个值,key=>value
3, 赋值cfg.data[section][key]=value
数据会存储到map中
图1-3
func (ini *IniConfig) parseData(dir string, data []byte) (*IniConfigContainer, error) { cfg := &IniConfigContainer{ ... } cfg.Lock() defer cfg.Unlock() var comment bytes.Buffer buf := bufio.NewReader(bytes.NewBuffer(data)) section := defaultSection tmpBuf := bytes.NewBuffer(nil) for { tmpBuf.Reset() shouldBreak := false //读取一行 .... line := tmpBuf.Bytes() line = bytes.TrimSpace(line) //处理注释,忽略 ... //读取section名称 if bytes.HasPrefix(line, sectionStart) && bytes.HasSuffix(line, sectionEnd) { section = strings.ToLower(string(line[1 : len(line)-1])) // section name case insensitive if comment.Len() > 0 { cfg.sectionComment[section] = comment.String() comment.Reset() } if _, ok := cfg.data[section]; !ok { cfg.data[section] = make(map[string]string) } continue } //默认section if _, ok := cfg.data[section]; !ok { cfg.data[section] = make(map[string]string) } keyValue := bytes.SplitN(line, bEqual, 2) key := string(bytes.TrimSpace(keyValue[0])) // key name case insensitive key = strings.ToLower(key) //include 忽略 ... val := bytes.TrimSpace(keyValue[1]) .... cfg.data[section][key] = ExpandValueEnv(string(val)) .... } return cfg, nil }
如何读取数据呢,Parse会返回实现Configer的实例,见上图1-2,我们分析下String方法。
func (c *IniConfigContainer) String(key string) string { return c.getdata(key) }
看一下getdata方法,如果不指定section,去default取,指定,则从传入的section取。
func (c *IniConfigContainer) getdata(key string) string { if len(key) == 0 { return "" } c.RLock() defer c.RUnlock() var ( section, k string sectionKey = strings.Split(strings.ToLower(key), "::") ) if len(sectionKey) >= 2 { section = sectionKey[0] k = sectionKey[1] } else { section = defaultSection k = sectionKey[0] } if v, ok := c.data[section]; ok { if vv, ok := v[k]; ok { return vv } } return "" }
好了,到现在已经分析完config模块,接下来看下beego如何使用config模块的。
beego中使用config
在beego下的config.go中(不是config/config.go),我们看下init方法
func init() { //beego默认配置 BConfig = newBConfig() ... if err = parseConfig(appConfigPath); err != nil { panic(err) } }
看下parseConfig
func parseConfig(appConfigPath string) (err error) { //前文分析的config模块 AppConfig, err = newAppConfig(appConfigProvider, appConfigPath) return assignConfig(AppConfig) }
看一下 assignConfig
func assignConfig(ac config.Configer) error { for _, i := range []interface{}{BConfig, &BConfig.Listen, &BConfig.WebConfig, &BConfig.Log, &BConfig.WebConfig.Session} { assignSingleConfig(i, ac) } ... }
最后了,看下assignSingleConfig,是使用reflect实现的赋值。这里会优先使用文件中的配置,文件中没才会用默认配置。
func assignSingleConfig(p interface{}, ac config.Configer) { pt := reflect.TypeOf(p) ... pt = pt.Elem() .... pv := reflect.ValueOf(p).Elem() for i := 0; i < pt.NumField(); i++ { pf := pv.Field(i) if !pf.CanSet() { continue } name := pt.Field(i).Name switch pf.Kind() { case reflect.String: pf.SetString(ac.DefaultString(name, pf.String())) ... } } }
看一下beego的默认配置吧
func newBConfig() *Config { return &Config{ AppName: "beego", RunMode: PROD, RouterCaseSensitive: true, ServerName: "beegoServer:" + VERSION, RecoverPanic: true, RecoverFunc: recoverPanic, CopyRequestBody: false, EnableGzip: false, MaxMemory: 1 << 26, //64MB EnableErrorsShow: true, EnableErrorsRender: true, Listen: Listen{ Graceful: false, ServerTimeOut: 0, ListenTCP4: false, EnableHTTP: true, AutoTLS: false, Domains: []string{}, TLSCacheDir: ".", HTTPAddr: "", HTTPPort: 8080, EnableHTTPS: false, HTTPSAddr: "", HTTPSPort: 10443, HTTPSCertFile: "", HTTPSKeyFile: "", EnableAdmin: false, AdminAddr: "", AdminPort: 8088, EnableFcgi: false, EnableStdIo: false, }, WebConfig: WebConfig{ AutoRender: true, EnableDocs: false, FlashName: "BEEGO_FLASH", FlashSeparator: "BEEGOFLASH", DirectoryIndex: false, StaticDir: map[string]string{"/static": "static"}, StaticExtensionsToGzip: []string{".css", ".js"}, TemplateLeft: "{{", TemplateRight: "}}", ViewsPath: "views", EnableXSRF: false, XSRFKey: "beegoxsrf", XSRFExpire: 0, Session: SessionConfig{ SessionOn: false, SessionProvider: "memory", SessionName: "beegosessionID", SessionGCMaxLifetime: 3600, SessionProviderConfig: "", SessionDisableHTTPOnly: false, SessionCookieLifeTime: 0, //set cookie default is the browser life SessionAutoSetCookie: true, SessionDomain: "", SessionEnableSidInHTTPHeader: false, // enable store/get the sessionId into/from http headers SessionNameInHTTPHeader: "Beegosessionid", SessionEnableSidInURLQuery: false, // enable get the sessionId from Url Query params }, }, Log: LogConfig{ AccessLogs: false, EnableStaticLogs: false, AccessLogsFormat: "APACHE_FORMAT", FileLineNum: true, Outputs: map[string]string{"console": ""}, }, } }
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK