diff --git a/config/configs.go b/config/configs.go index 129c69b..2a35eea 100644 --- a/config/configs.go +++ b/config/configs.go @@ -9,23 +9,29 @@ import ( ) type ( + + // Config dotweb app config define Config struct { - XMLName xml.Name `xml:"config" json:"-" yaml:"-"` - App *AppNode `xml:"app"` - AppSets []*AppSetNode `xml:"appset>set"` - Offline *OfflineNode `xml:"offline"` - Server *ServerNode `xml:"server"` - Session *SessionNode `xml:"session"` - Routers []*RouterNode `xml:"routers>router"` - Groups []*GroupNode `xml:"groups>group"` - Middlewares []*MiddlewareNode `xml:"middlewares>middleware"` - AppSetConfig *core.ItemContext `json:"-" yaml:"-"` + XMLName xml.Name `xml:"config" json:"-" yaml:"-"` + App *AppNode `xml:"app"` + ConfigSetNodes []*ConfigSetNode `xml:"configset>set"` + Offline *OfflineNode `xml:"offline"` + Server *ServerNode `xml:"server"` + Session *SessionNode `xml:"session"` + Routers []*RouterNode `xml:"routers>router"` + Groups []*GroupNode `xml:"groups>group"` + Middlewares []*MiddlewareNode `xml:"middlewares>middleware"` + ConfigSet core.ReadonlyMap `json:"-" yaml:"-"` } + + // OfflineNode dotweb app offline config OfflineNode struct { Offline bool `xml:"offline,attr"` //是否维护,默认false OfflineText string `xml:"offlinetext,attr"` //当设置为维护,默认显示内容,如果设置url,优先url OfflineUrl string `xml:"offlineurl,attr"` //当设置为维护,默认维护页地址,如果设置url,优先url } + + // AppNode dotweb app global config AppNode struct { LogPath string `xml:"logpath,attr"` //文件方式日志目录,如果为空,默认当前目录 EnabledLog bool `xml:"enabledlog,attr"` //是否启用日志记录 @@ -33,12 +39,8 @@ type ( PProfPort int `xml:"pprofport,attr"` //pprof-server 端口,不能与主Server端口相同 EnabledPProf bool `xml:"enabledpprof,attr"` //是否启用pprof server,默认不启用 } - //update for issue #16 配置文件 - AppSetNode struct { - Key string `xml:"key,attr"` - Value string `xml:"value,attr"` - } + // ServerNode dotweb app's httpserver config ServerNode struct { EnabledListDir bool `xml:"enabledlistdir,attr"` //设置是否启用目录浏览,仅对Router.ServerFile有效,若设置该项,则可以浏览目录文件,默认不开启 EnabledRequestID bool `xml:"enabledrequestid,attr"` //设置是否启用唯一请求ID,默认不开启,开启后使用32位UUID @@ -55,6 +57,7 @@ type ( EnabledDetailRequestData bool `xml:"enableddetailrequestdata,attr"` //设置状态数据是否启用详细页面统计,默认不启用,请特别对待,如果站点url过多,会导致数据量过大 } + // SessionNode dotweb app's session config SessionNode struct { EnabledSession bool `xml:"enabled,attr"` //启用Session SessionMode string `xml:"mode,attr"` //session模式,目前支持runtime、redis @@ -64,6 +67,7 @@ type ( Password string `xml:"password,attr"` //远程session password } + // RouterNode dotweb app's router config RouterNode struct { Method string `xml:"method,attr"` Path string `xml:"path,attr"` @@ -72,6 +76,7 @@ type ( IsUse bool `xml:"isuse,attr"` //是否启用,默认false } + // GroupNode dotweb app's group router config GroupNode struct { Path string `xml:"path,attr"` Routers []*RouterNode `xml:"router"` @@ -79,6 +84,7 @@ type ( IsUse bool `xml:"isuse,attr"` //是否启用,默认false } + // MiddlewareNode dotweb app's middleware config MiddlewareNode struct { Name string `xml:"name,attr"` IsUse bool `xml:"isuse,attr"` //是否启用,默认false @@ -86,19 +92,51 @@ type ( ) const ( - ConfigType_Xml = "xml" - ConfigType_Json = "json" + // ConfigType_XML xml config file + ConfigType_XML = "xml" + // ConfigType_JSON json config file + ConfigType_JSON = "json" + // ConfigType_Yaml yaml config file ConfigType_Yaml = "yaml" ) +// NewConfig create new config func NewConfig() *Config { return &Config{ - App: NewAppNode(), - Offline: NewOfflineNode(), - Server: NewServerNode(), - Session: NewSessionNode(), - AppSetConfig: core.NewItemContext(), + App: NewAppNode(), + Offline: NewOfflineNode(), + Server: NewServerNode(), + Session: NewSessionNode(), + ConfigSet: core.NewReadonlyMap(), + } +} + +// IncludeConfigSet include ConfigSet file to Dotweb.Config.ConfigSet, can use ctx.ConfigSet to use your data +// same key will cover oldest value +// support xml\json\yaml +func (conf *Config) IncludeConfigSet(configFile string, confType string) error { + var parseItem core.ConcurrenceMap + var err error + if confType == ConfigType_XML { + parseItem, err = ParseConfigSetXML(configFile) + } + if confType == ConfigType_JSON { + parseItem, err = ParseConfigSetJSON(configFile) + } + if confType == ConfigType_Yaml { + parseItem, err = ParseConfigSetYaml(configFile) + } + if err != nil { + return err + } + items := conf.ConfigSet.(*core.ItemMap) + if items == nil { + return errors.New("init config items error") + } + for k, v := range parseItem.GetCurrentMap() { + items.Set(k, v) } + return nil } func NewAppNode() *AppNode { @@ -151,15 +189,15 @@ func InitConfig(configFile string, confType ...interface{}) (config *Config, err } } - cType := ConfigType_Xml - if len(confType) > 0 && confType[0] == ConfigType_Json { - cType = ConfigType_Json + cType := ConfigType_XML + if len(confType) > 0 && confType[0] == ConfigType_JSON { + cType = ConfigType_JSON } if len(confType) > 0 && confType[0] == ConfigType_Yaml { cType = ConfigType_Yaml } - if cType == ConfigType_Xml { + if cType == ConfigType_XML { config, err = initConfig(realFile, cType, UnmarshalXML) } else if cType == ConfigType_Yaml { config, err = initConfig(realFile, cType, UnmarshalYaml) @@ -187,11 +225,11 @@ func InitConfig(configFile string, confType ...interface{}) (config *Config, err config.Offline = NewOfflineNode() } - tmpAppSetMap := core.NewItemContext() - for _, v := range config.AppSets { - tmpAppSetMap.Set(v.Key, v.Value) + tmpConfigSetMap := core.NewConcurrenceMap() + for _, v := range config.ConfigSetNodes { + tmpConfigSetMap.Set(v.Key, v.Value) } - config.AppSetConfig = tmpAppSetMap + config.ConfigSet = tmpConfigSetMap //deal config default value dealConfigDefaultSet(config) diff --git a/config/configs_test.go b/config/configs_test.go index b88e4b5..c35fe66 100644 --- a/config/configs_test.go +++ b/config/configs_test.go @@ -15,8 +15,8 @@ func TestInitConfig(t *testing.T) { test.NotNil(t,conf) test.NotNil(t,conf.App) test.NotNil(t,conf.App.LogPath) - test.NotNil(t,conf.AppSets) - test.Equal(t,4, len(conf.AppSets)) + test.NotNil(t,conf.ConfigSet) + test.Equal(t,4, conf.ConfigSet.Len()) } //该测试方法报错... @@ -28,6 +28,6 @@ func TestInitConfigWithXml(t *testing.T) { test.NotNil(t,conf) test.NotNil(t,conf.App) test.NotNil(t,conf.App.LogPath) - test.NotNil(t,conf.AppSets) - test.Equal(t,4, len(conf.AppSets)) + test.NotNil(t,conf.ConfigSet) + test.Equal(t,4, conf.ConfigSet.Len()) } \ No newline at end of file diff --git a/config/configset.go b/config/configset.go new file mode 100644 index 0000000..50dc1d6 --- /dev/null +++ b/config/configset.go @@ -0,0 +1,63 @@ +package config + +import ( + "encoding/xml" + "errors" + "github.com/devfeel/dotweb/core" + "io/ioutil" +) + +type ( + // ConfigSet 单元配置组,包含一系列单元配置节点 + ConfigSet struct { + XMLName xml.Name `xml:"config" json:"-" yaml:"-"` + Name string `xml:"name,attr"` + ConfigSetNodes []*ConfigSetNode `xml:"set"` + } + + // ConfigSetNode update for issue #16 配置文件 + ConfigSetNode struct { + Key string `xml:"key,attr"` + Value string `xml:"value,attr"` + } +) + +// ParseConfigSetXML include ConfigSet xml file +func ParseConfigSetXML(configFile string) (core.ConcurrenceMap, error) { + return parseConfigSetFile(configFile, ConfigType_XML) +} + +// ParseConfigSetJSON include ConfigSet json file +func ParseConfigSetJSON(configFile string) (core.ConcurrenceMap, error) { + return parseConfigSetFile(configFile, ConfigType_JSON) +} + +// ParseConfigSetYaml include ConfigSet yaml file +func ParseConfigSetYaml(configFile string) (core.ConcurrenceMap, error) { + return parseConfigSetFile(configFile, ConfigType_Yaml) +} + +func parseConfigSetFile(configFile string, confType string) (core.ConcurrenceMap, error) { + content, err := ioutil.ReadFile(configFile) + if err != nil { + return nil, errors.New("DotWeb:Config:parseConfigSetFile 配置文件[" + configFile + ", " + confType + "]无法解析 - " + err.Error()) + } + set := new(ConfigSet) + if confType == ConfigType_XML { + err = UnmarshalXML(content, set) + } + if confType == ConfigType_JSON { + err = UnmarshalJSON(content, set) + } + if confType == ConfigType_Yaml { + err = UnmarshalYaml(content, set) + } + if err != nil { + return nil, errors.New("DotWeb:Config:parseConfigSetFile 配置文件[" + configFile + ", " + confType + "]无法解析 - " + err.Error()) + } + item := core.NewConcurrenceMap() + for _, s := range set.ConfigSetNodes { + item.Set(s.Key, s.Value) + } + return item, nil +} diff --git a/const/const.go b/const/const.go index 338a9de..9b6834e 100644 --- a/const/const.go +++ b/const/const.go @@ -2,5 +2,5 @@ package _const //dotweb const const ( - Version = "1.4.7" + Version = "1.4.8" ) diff --git a/context.go b/context.go index 0f5b443..bc53811 100644 --- a/context.go +++ b/context.go @@ -13,8 +13,8 @@ import ( "github.com/devfeel/dotweb/session" "os" "path/filepath" - "time" "strconv" + "time" ) const ( @@ -39,11 +39,11 @@ type ( RouterNode() RouterNode RouterParams() Params Handler() HttpHandle - AppContext() *core.ItemContext + AppItems() core.ConcurrenceMap Cache() cache.Cache - Items() *core.ItemContext - AppSetConfig() *core.ItemContext - ViewData() *core.ItemContext + Items() core.ConcurrenceMap + ConfigSet() core.ReadonlyMap + ViewData() core.ConcurrenceMap SessionID() string Session() (state *session.SessionState) Hijack() (*HijackConn, error) @@ -103,9 +103,9 @@ type ( isEnd bool //表示当前处理流程是否需要终止 httpServer *HttpServer sessionID string - innerItems *core.ItemContext - items *core.ItemContext - viewData *core.ItemContext + innerItems core.ConcurrenceMap + items core.ConcurrenceMap + viewData core.ConcurrenceMap features *xFeatureTools handler HttpHandle startTime time.Time @@ -225,11 +225,11 @@ func (ctx *HttpContext) Features() *xFeatureTools { // AppContext get application's global appcontext // issue #3 -func (ctx *HttpContext) AppContext() *core.ItemContext { +func (ctx *HttpContext) AppItems() core.ConcurrenceMap { if ctx.HttpServer != nil { - return ctx.httpServer.DotApp.AppContext + return ctx.httpServer.DotApp.Items } else { - return core.NewItemContext() + return core.NewConcurrenceMap() } } @@ -240,33 +240,33 @@ func (ctx *HttpContext) Cache() cache.Cache { // getInnerItems get request's inner item context // lazy init when first use -func (ctx *HttpContext) getInnerItems() *core.ItemContext { +func (ctx *HttpContext) getInnerItems() core.ConcurrenceMap { if ctx.innerItems == nil { - ctx.innerItems = core.NewItemContext() + ctx.innerItems = core.NewConcurrenceMap() } return ctx.innerItems } // Items get request's item context // lazy init when first use -func (ctx *HttpContext) Items() *core.ItemContext { +func (ctx *HttpContext) Items() core.ConcurrenceMap { if ctx.items == nil { - ctx.items = core.NewItemContext() + ctx.items = core.NewConcurrenceMap() } return ctx.items } // AppSetConfig get appset from config file // update for issue #16 配置文件 -func (ctx *HttpContext) AppSetConfig() *core.ItemContext { - return ctx.HttpServer().DotApp.Config.AppSetConfig +func (ctx *HttpContext) ConfigSet() core.ReadonlyMap { + return ctx.HttpServer().DotApp.Config.ConfigSet } // ViewData get view data context // lazy init when first use -func (ctx *HttpContext) ViewData() *core.ItemContext { +func (ctx *HttpContext) ViewData() core.ConcurrenceMap { if ctx.viewData == nil { - ctx.viewData = core.NewItemContext() + ctx.viewData = core.NewConcurrenceMap() } return ctx.viewData } @@ -330,8 +330,8 @@ func (ctx *HttpContext) QueryInt(key string) int { if param == "" { return 0 } - val, err:=strconv.Atoi(param) - if err != nil{ + val, err := strconv.Atoi(param) + if err != nil { return 0 } return val @@ -344,14 +344,13 @@ func (ctx *HttpContext) QueryInt64(key string) int64 { if param == "" { return 0 } - val, err:=strconv.ParseInt(param, 10, 64) - if err != nil{ + val, err := strconv.ParseInt(param, 10, 64) + if err != nil { return 0 } return val } - /* * 根据指定key获取包括在post、put和get内的值 */ @@ -551,7 +550,7 @@ func (ctx *HttpContext) WriteBlobC(code int, contentType string, b []byte) error _, err := ctx.hijackConn.WriteBlob(b) return err } else { - _, err := ctx.response.Write(code, b) + _, err := ctx.response.Write(code, b) return err } } @@ -564,7 +563,7 @@ func (ctx *HttpContext) WriteJson(i interface{}) error { // WriteJsonC write (httpCode, json string) to response // auto convert interface{} to json string -func (ctx *HttpContext) WriteJsonC(code int, i interface{}) error{ +func (ctx *HttpContext) WriteJsonC(code int, i interface{}) error { b, err := json.Marshal(i) if err != nil { return err diff --git a/context_test.go b/context_test.go index cc10e47..e81a360 100644 --- a/context_test.go +++ b/context_test.go @@ -74,7 +74,7 @@ func TestWriteString(t *testing.T) { //call function //这里是一个interface数组,用例需要小心. - _,contextErr:=context.WriteString(string(animalJson)) + contextErr:=context.WriteString(string(animalJson)) test.Nil(t,contextErr) //header @@ -111,7 +111,7 @@ func TestWriteJson(t *testing.T) { test.Nil(t,err) //call function - _,contextErr:=context.WriteJson(exceptedObject) + contextErr:=context.WriteJson(exceptedObject) test.Nil(t,contextErr) //header @@ -146,7 +146,7 @@ func TestWriteJsonp(t *testing.T) { callback:="jsonCallBack" //call function - _,err:=context.WriteJsonp(callback,exceptedObject) + err:=context.WriteJsonp(callback,exceptedObject) test.Nil(t,err) //check result diff --git a/core/concurrenceMap.go b/core/concurrenceMap.go new file mode 100644 index 0000000..542afc3 --- /dev/null +++ b/core/concurrenceMap.go @@ -0,0 +1,143 @@ +package core + +import ( + "fmt" + "sync" +) + +type ( + // ReadonlyMap only support readonly method for map + ReadonlyMap interface { + Get(key string) (value interface{}, exists bool) + GetString(key string) string + GetInt(key string) int + GetUInt64(key string) uint64 + Exists(key string) bool + Len() int + } + + // ReadonlyMap support concurrence for map + ConcurrenceMap interface { + Get(key string) (value interface{}, exists bool) + GetString(key string) string + GetInt(key string) int + GetUInt64(key string) uint64 + Exists(key string) bool + GetCurrentMap() map[string]interface{} + Len() int + Set(key string, value interface{}) error + Remove(key string) + Once(key string) (value interface{}, exists bool) + } +) + +// ItemMap concurrence map +type ItemMap struct { + innerMap map[string]interface{} + *sync.RWMutex +} + +// NewItemMap create new ItemMap +func NewItemMap() *ItemMap { + return &ItemMap{ + innerMap: make(map[string]interface{}), + RWMutex: new(sync.RWMutex), + } +} + +// NewConcurrenceMap create new ConcurrenceMap +func NewConcurrenceMap() ConcurrenceMap { + return &ItemMap{ + innerMap: make(map[string]interface{}), + RWMutex: new(sync.RWMutex), + } +} + +// NewReadonlyMap create new ReadonlyMap +func NewReadonlyMap() ReadonlyMap { + return &ItemMap{ + innerMap: make(map[string]interface{}), + RWMutex: new(sync.RWMutex), + } +} + +// Set 以key、value置入ItemMap +func (ctx *ItemMap) Set(key string, value interface{}) error { + ctx.Lock() + ctx.innerMap[key] = value + ctx.Unlock() + return nil +} + +// Get 读取指定key在ItemMap中的内容 +func (ctx *ItemMap) Get(key string) (value interface{}, exists bool) { + ctx.RLock() + value, exists = ctx.innerMap[key] + ctx.RUnlock() + return value, exists +} + +// Remove remove item by gived key +// if not exists key, do nothing... +func (ctx *ItemMap) Remove(key string) { + ctx.Lock() + delete(ctx.innerMap, key) + ctx.Unlock() +} + +// Once get item by gived key, and remove it +// only can be read once, it will be locked +func (ctx *ItemMap) Once(key string) (value interface{}, exists bool) { + ctx.Lock() + defer ctx.Unlock() + value, exists = ctx.innerMap[key] + if exists { + delete(ctx.innerMap, key) + } + return value, exists +} + + +// GetString 读取指定key在AppContext中的内容,以string格式输出 +func (ctx *ItemMap) GetString(key string) string { + value, exists := ctx.Get(key) + if !exists { + return "" + } + return fmt.Sprint(value) +} + + +// GetInt 读取指定key在AppContext中的内容,以int格式输出 +func (ctx *ItemMap) GetInt(key string) int { + value, exists := ctx.Get(key) + if !exists { + return 0 + } + return value.(int) +} + +// GetUInt64 读取指定key在AppContext中的内容,以int格式输出 +func (ctx *ItemMap) GetUInt64(key string) uint64 { + value, exists := ctx.Get(key) + if !exists { + return 0 + } + return value.(uint64) +} + +// Exists check exists key +func (ctx *ItemMap) Exists(key string) bool { + _, exists := ctx.innerMap[key] + return exists +} + +// GetCurrentMap get current map, returns map[string]interface{} +func (ctx *ItemMap) GetCurrentMap() map[string]interface{} { + return ctx.innerMap +} + +// Len get context length +func (ctx *ItemMap) Len() int { + return len(ctx.innerMap) +} diff --git a/core/context_test.go b/core/concurrenceMap_test.go similarity index 64% rename from core/context_test.go rename to core/concurrenceMap_test.go index 38f6896..c25c7d2 100644 --- a/core/context_test.go +++ b/core/concurrenceMap_test.go @@ -1,27 +1,28 @@ package core import ( - "testing" "fmt" + "strconv" "sync" + "testing" "time" - "strconv" ) -var ic *ItemContext +var ic ConcurrenceMap var keys []string + func init() { - ic = NewItemContext() - for i := 0; i < 10000000 ; i++ { + ic = NewConcurrenceMap() + for i := 0; i < 10000000; i++ { keys = append(keys, time.Now().String()) } - fmt.Println("len of keys ",len(keys)) + fmt.Println("len of keys ", len(keys)) } func TestItemContext_Get_Set(t *testing.T) { - t.Log(ic.Set("foo","bar")) + t.Log(ic.Set("foo", "bar")) t.Log(ic.Get("foo")) t.Log(ic.Exists("foo")) @@ -30,16 +31,16 @@ func TestItemContext_Get_Set(t *testing.T) { } func TestItemContext_Get_Once(t *testing.T) { - ic.Set("foo","bar") + ic.Set("foo", "bar") t.Log(ic.Once("foo")) t.Log(ic.Get("foo")) } func TestItemContext_Remove(t *testing.T) { - ic.Set("foo","bar") - ic.Set("foo1","bar1") - t.Log(len(ic.contextMap)) + ic.Set("foo", "bar") + ic.Set("foo1", "bar1") + t.Log(len(ic.GetCurrentMap())) ic.Remove("foo") t.Log(ic.GetString("foo")) } @@ -47,22 +48,21 @@ func TestItemContext_Remove(t *testing.T) { func TestItemContext_Current(t *testing.T) { lock := &sync.Mutex{} j := 0 - for i := 0; i < 9;i++ { + for i := 0; i < 9; i++ { go func() { lock.Lock() - fmt.Println("go",j) + fmt.Println("go", j) j++ v := "bar" + strconv.Itoa(j) fmt.Println(v) - ic.Set(strconv.Itoa(j),v) + ic.Set(strconv.Itoa(j), v) lock.Unlock() }() } - time.Sleep(3*time.Second) - - t.Log(ic.contextMap) + time.Sleep(3 * time.Second) + t.Log(ic.GetCurrentMap()) } @@ -72,36 +72,35 @@ func TestItemContext_Current(t *testing.T) { func BenchmarkItemContext_Set_1(b *testing.B) { var num uint64 = 1 for i := 0; i < b.N; i++ { - ic.Set(string(num),num) + ic.Set(string(num), num) } } //并发效率 func BenchmarkItemContext_Set_Parallel(b *testing.B) { - b.RunParallel(func (pb *testing.PB){ + b.RunParallel(func(pb *testing.PB) { var num uint64 = 1 - for pb.Next(){ - ic.Set(string(num),num) + for pb.Next() { + ic.Set(string(num), num) } }) } //基准测试 func BenchmarkItemContext_Get_1(b *testing.B) { - ic.Set("foo","bar") - for i := 0; i < b.N; i++{ + ic.Set("foo", "bar") + for i := 0; i < b.N; i++ { ic.Get("foo") } } //并发效率 func BenchmarkItemContext_Get_Parallel(b *testing.B) { - ic.Set("foo","bar") - b.RunParallel(func (pb *testing.PB){ + ic.Set("foo", "bar") + b.RunParallel(func(pb *testing.PB) { for pb.Next() { ic.Get("foo") } }) } - diff --git a/core/context.go b/core/context.go deleted file mode 100644 index 2d79545..0000000 --- a/core/context.go +++ /dev/null @@ -1,108 +0,0 @@ -package core - -import ( - "fmt" - "sync" -) - -//自带锁,并发安全的Map -type ItemContext struct { - contextMap map[string]interface{} - *sync.RWMutex -} - -func NewItemContext() *ItemContext { - return &ItemContext{ - contextMap: make(map[string]interface{}), - RWMutex: new(sync.RWMutex), - } -} - -/* -* 以key、value置入AppContext - */ -func (ctx *ItemContext) Set(key string, value interface{}) error { - ctx.Lock() - ctx.contextMap[key] = value - ctx.Unlock() - return nil -} - -/* -* 读取指定key在AppContext中的内容 - */ -func (ctx *ItemContext) Get(key string) (value interface{}, exists bool) { - ctx.RLock() - value, exists = ctx.contextMap[key] - ctx.RUnlock() - return value, exists -} - -//remove item by gived key -//if not exists key, do nothing... -func (ctx *ItemContext) Remove(key string) { - ctx.Lock() - delete(ctx.contextMap, key) - ctx.Unlock() -} - -//get item by gived key, and remove it -//only can be read once, it will be locked -func (ctx *ItemContext) Once(key string) (value interface{}, exists bool) { - ctx.Lock() - defer ctx.Unlock() - value, exists = ctx.contextMap[key] - if exists { - delete(ctx.contextMap, key) - } - return value, exists -} - -/* -* 读取指定key在AppContext中的内容,以string格式输出 - */ -func (ctx *ItemContext) GetString(key string) string { - value, exists := ctx.Get(key) - if !exists { - return "" - } - return fmt.Sprint(value) -} - -/* -* 读取指定key在AppContext中的内容,以int格式输出 - */ -func (ctx *ItemContext) GetInt(key string) int { - value, exists := ctx.Get(key) - if !exists { - return 0 - } - return value.(int) -} - -/* -* 读取指定key在AppContext中的内容,以int格式输出 - */ -func (ctx *ItemContext) GetUInt64(key string) uint64 { - value, exists := ctx.Get(key) - if !exists { - return 0 - } - return value.(uint64) -} - -//check exists key -func (ctx *ItemContext) Exists(key string) bool { - _, exists := ctx.contextMap[key] - return exists -} - -//get current map, returns map[string]interface{} -func (ctx *ItemContext) GetCurrentMap() map[string]interface{} { - return ctx.contextMap -} - -//get context length -func (ctx *ItemContext) Len() int { - return len(ctx.contextMap) -} diff --git a/core/state.go b/core/state.go index d42060e..162aea8 100644 --- a/core/state.go +++ b/core/state.go @@ -1,6 +1,7 @@ package core import ( + "github.com/devfeel/dotweb/const" "github.com/devfeel/dotweb/framework/json" "net/http" "strconv" @@ -8,7 +9,6 @@ import ( "sync" "sync/atomic" "time" - "github.com/devfeel/dotweb/const" ) var GlobalState *ServerStateInfo @@ -25,12 +25,12 @@ func init() { ServerStartTime: time.Now(), TotalRequestCount: 0, TotalErrorCount: 0, - IntervalRequestData: NewItemContext(), - DetailRequestURLData: NewItemContext(), - IntervalErrorData: NewItemContext(), - DetailErrorPageData: NewItemContext(), - DetailErrorData: NewItemContext(), - DetailHTTPCodeData: NewItemContext(), + IntervalRequestData: NewItemMap(), + DetailRequestURLData: NewItemMap(), + IntervalErrorData: NewItemMap(), + DetailErrorPageData: NewItemMap(), + DetailErrorData: NewItemMap(), + DetailHTTPCodeData: NewItemMap(), dataChan_Request: make(chan *RequestInfo, 1000), dataChan_Error: make(chan *ErrorInfo, 1000), dataChan_HttpCode: make(chan *HttpCodeInfo, 1000), @@ -93,19 +93,19 @@ type ServerStateInfo struct { //该运行期间总访问次数 TotalRequestCount uint64 //单位时间内请求数据 - 按分钟为单位 - IntervalRequestData *ItemContext + IntervalRequestData *ItemMap //明细请求页面数据 - 以不带参数的访问url为key - DetailRequestURLData *ItemContext + DetailRequestURLData *ItemMap //该运行期间异常次数 TotalErrorCount uint64 //单位时间内异常次数 - 按分钟为单位 - IntervalErrorData *ItemContext + IntervalErrorData *ItemMap //明细异常页面数据 - 以不带参数的访问url为key - DetailErrorPageData *ItemContext + DetailErrorPageData *ItemMap //明细异常数据 - 以不带参数的访问url为key - DetailErrorData *ItemContext + DetailErrorData *ItemMap //明细Http状态码数据 - 以HttpCode为key,例如200、500等 - DetailHTTPCodeData *ItemContext + DetailHTTPCodeData *ItemMap dataChan_Request chan *RequestInfo dataChan_Error chan *ErrorInfo diff --git a/dotweb.go b/dotweb.go index 46556d0..f069b09 100644 --- a/dotweb.go +++ b/dotweb.go @@ -33,7 +33,7 @@ type ( ExceptionHandler ExceptionHandle NotFoundHandler StandardHandle // NotFoundHandler 支持自定义404处理代码能力 MethodNotAllowedHandler StandardHandle // MethodNotAllowedHandler fixed for #64 增加MethodNotAllowed自定义处理 - AppContext *core.ItemContext + Items core.ConcurrenceMap middlewareMap map[string]MiddlewareFunc middlewareMutex *sync.RWMutex StartMode string @@ -51,11 +51,17 @@ type ( ) const ( - DefaultHTTPPort = 8080 //DefaultHTTPPort default http port; fixed for #70 UPDATE default http port 80 to 8080 - RunMode_Development = "development" - RunMode_Production = "production" + // DefaultHTTPPort default http port; fixed for #70 UPDATE default http port 80 to 8080 + DefaultHTTPPort = 8080 + // RunMode_Development app runmode in development mode + RunMode_Development = "development" + // RunMode_Production app runmode in production mode + RunMode_Production = "production" + + //StartMode_New app startmode in New mode StartMode_New = "New" + //StartMode_Classic app startmode in Classic mode StartMode_Classic = "Classic" ) @@ -65,7 +71,7 @@ func New() *DotWeb { HttpServer: NewHttpServer(), OfflineServer: servers.NewOfflineServer(), Middlewares: make([]Middleware, 0), - AppContext: core.NewItemContext(), + Items: core.NewConcurrenceMap(), Config: config.NewConfig(), middlewareMap: make(map[string]MiddlewareFunc), middlewareMutex: new(sync.RWMutex), @@ -405,11 +411,11 @@ func (app *DotWeb) initBindMiddleware() { //bind app middlewares for fullExpress, _ := range router.allRouterExpress { expresses := strings.Split(fullExpress, "_") - if len(expresses) < 2{ + if len(expresses) < 2 { continue } node := router.getNode(expresses[0], expresses[1]) - if node == nil{ + if node == nil { continue } @@ -422,7 +428,7 @@ func (app *DotWeb) initBindMiddleware() { logger.Logger().Debug("DotWeb initBindMiddleware [app] "+fullExpress+" "+reflect.TypeOf(m).String()+" match", LogTarget_HttpServer) } } - if len(node.middlewares) > 0{ + if len(node.middlewares) > 0 { firstMiddleware := &xMiddleware{} firstMiddleware.SetNext(node.middlewares[0]) node.middlewares = append([]Middleware{firstMiddleware}, node.middlewares...) @@ -434,7 +440,7 @@ func (app *DotWeb) initBindMiddleware() { xg := g.(*xGroup) if len(xg.middlewares) <= 0 { continue - }else{ + } else { firstMiddleware := &xMiddleware{} firstMiddleware.SetNext(xg.middlewares[0]) xg.middlewares = append([]Middleware{firstMiddleware}, xg.middlewares...) diff --git a/example/appcontext/main.go b/example/appcontext/main.go deleted file mode 100644 index 1d063e9..0000000 --- a/example/appcontext/main.go +++ /dev/null @@ -1,59 +0,0 @@ -package main - -import ( - "fmt" - "github.com/devfeel/dotweb" - "github.com/devfeel/dotweb/framework/file" - "strconv" -) - -func main() { - //初始化DotServer - app := dotweb.New() - - //设置dotserver日志目录 - app.SetLogPath(file.GetCurrentDirectory()) - - //设置路由 - InitRoute(app.HttpServer) - - //启动 监控服务 - //app.SetPProfConfig(true, 8081) - - //全局容器 - app.AppContext.Set("gstring", "gvalue") - app.AppContext.Set("gint", 1) - - // 开始服务 - port := 8080 - fmt.Println("dotweb.StartServer => " + strconv.Itoa(port)) - err := app.StartServer(port) - fmt.Println("dotweb.StartServer error => ", err) -} - -type TestContext struct { - UserName string - Sex int -} - -//you can curl http://127.0.0.1:8080/ -func Index(ctx dotweb.Context) error { - gstring := ctx.AppContext().GetString("gstring") - gint := ctx.AppContext().GetInt("gint") - ctx.AppContext().Set("index", "index-v") - ctx.AppContext().Set("user", "user-v") - return ctx.WriteString("index -> " + gstring + ";" + strconv.Itoa(gint)) -} - -//you can curl http://127.0.0.1:8080/2 -func Index2(ctx dotweb.Context) error { - gindex := ctx.AppContext().GetString("index") - ctx.AppContext().Remove("index") - user, _ := ctx.AppContext().Once("user") - return ctx.WriteString("index -> " + gindex + ";" + fmt.Sprint(user)) -} - -func InitRoute(server *dotweb.HttpServer) { - server.Router().GET("/", Index) - server.Router().GET("/2", Index2) -} diff --git a/example/config/main.go b/example/config/main.go index 7f24e0f..bc1b2c9 100644 --- a/example/config/main.go +++ b/example/config/main.go @@ -49,7 +49,7 @@ func Index(ctx dotweb.Context) error { func GetAppSet(ctx dotweb.Context) error { key := ctx.QueryString("key") - return ctx.WriteString(ctx.Request().Url(), " => key = ", ctx.AppSetConfig().GetString(key)) + return ctx.WriteString(ctx.Request().Url(), " => key = ", ctx.ConfigSet().GetString(key)) } func DefaultPanic(ctx dotweb.Context) error { diff --git a/example/configset/main.go b/example/configset/main.go new file mode 100644 index 0000000..009079a --- /dev/null +++ b/example/configset/main.go @@ -0,0 +1,50 @@ +package main + +import ( + "fmt" + "github.com/devfeel/dotweb" + "github.com/devfeel/dotweb/config" + "github.com/devfeel/dotweb/framework/file" + "strconv" +) + +func main() { + //初始化DotServer + app := dotweb.New() + + //设置dotserver日志目录 + app.SetLogPath(file.GetCurrentDirectory()) + + app.SetDevelopmentMode() + + app.HttpServer.SetEnabledIgnoreFavicon(true) + + //引入自定义ConfigSet + err := app.Config.IncludeConfigSet("d:/gotmp/userconf.xml", config.ConfigType_XML) + if err != nil { + fmt.Println(err.Error()) + return + } + + //设置路由 + InitRoute(app.HttpServer) + + // 开始服务 + port := 8080 + fmt.Println("dotweb.StartServer => " + strconv.Itoa(port)) + err = app.StartServer(port) + fmt.Println("dotweb.StartServer error => ", err) +} + +// ConfigSet +func ConfigSet(ctx dotweb.Context) error { + vkey1 := ctx.ConfigSet().GetString("set1") + vkey2 := ctx.ConfigSet().GetString("set2") + ctx.WriteString(ctx.Request().Path(), "key1=", vkey1, "key2=", vkey2) + return ctx.WriteString("\r\n") +} + +// InitRoute +func InitRoute(server *dotweb.HttpServer) { + server.GET("/c", ConfigSet) +} diff --git a/example/configset/userconf.xml b/example/configset/userconf.xml new file mode 100644 index 0000000..f5fb674 --- /dev/null +++ b/example/configset/userconf.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/example/httpmodule/main.go b/example/httpmodule/main.go deleted file mode 100644 index ffc8d8a..0000000 --- a/example/httpmodule/main.go +++ /dev/null @@ -1,107 +0,0 @@ -package main - -import ( - "fmt" - "github.com/devfeel/dotweb" - "github.com/devfeel/dotweb/framework/file" - "github.com/devfeel/dotweb/session" - "strconv" -) - -func main() { - //初始化DotServer - app := dotweb.New() - - //设置dotserver日志目录 - app.SetLogPath(file.GetCurrentDirectory()) - - //设置gzip开关 - //app.HttpServer.SetEnabledGzip(true) - - app.SetDevelopmentMode() - - //设置Session开关 - app.HttpServer.SetEnabledSession(true) - - app.HttpServer.SetEnabledIgnoreFavicon(true) - - //设置Session配置 - //runtime mode - app.HttpServer.SetSessionConfig(session.NewDefaultRuntimeConfig()) - //redis mode - //app.SetSessionConfig(session.NewDefaultRedisConfig("192.168.8.175:6379", "")) - - //设置路由 - InitRoute(app.HttpServer) - - //设置HttpModule - InitModule(app.HttpServer) - - //启动 监控服务 - //app.SetPProfConfig(true, 8081) - - //全局容器 - app.AppContext.Set("gstring", "gvalue") - app.AppContext.Set("gint", 1) - - // 开始服务 - port := 8080 - fmt.Println("dotweb.StartServer => " + strconv.Itoa(port)) - err := app.StartServer(port) - fmt.Println("dotweb.StartServer error => ", err) -} - -func Index(ctx dotweb.Context) error { - ctx.Items().Set("count", 2) - ctx.WriteString(ctx.Request().Path() + ":Items.Count=> " + ctx.Items().GetString("count")) - return ctx.WriteString("\r\n") -} - -func WHtml(ctx dotweb.Context) error { - ctx.WriteHtml("this is html response!") - return nil -} - -func InitRoute(server *dotweb.HttpServer) { - server.GET("/", Index) - server.GET("/m", Index) - server.GET("/h", WHtml) -} - -func InitModule(dotserver *dotweb.HttpServer) { - dotserver.RegisterModule(&dotweb.HttpModule{ - Name: "test change route", - OnBeginRequest: func(ctx dotweb.Context) { - if ctx.IsEnd() { - return - } - if ctx.Request().Path() == "/" && ctx.QueryString("change") == "1" { - //change route - ctx.WriteString("变更访问路由测试") - ctx.WriteString("\r\n") - ctx.Request().URL.Path = "/m" - } - - if ctx.Request().Path() == "/" { - ctx.Items().Set("count", 1) - ctx.WriteString("OnBeginRequest:Items.Count => ", ctx.Items().GetString("count")) - ctx.WriteString("\r\n") - } - if ctx.QueryString("skip") == "1" { - ctx.End() - } - }, - OnEndRequest: func(ctx dotweb.Context) { - if ctx.IsEnd() { - return - } - if ctx.Request().Path() == "/" { - if ctx.Items().Exists("count") { - ctx.WriteString("OnEndRequest:Items.Count => ", ctx.Items().GetString("count")) - } else { - ctx.WriteString("OnEndRequest:Items.Len => ", ctx.Items().Len()) - } - } - }, - }) -} diff --git a/example/main.go b/example/main.go index b1f79b6..19a1424 100644 --- a/example/main.go +++ b/example/main.go @@ -65,8 +65,8 @@ func main() { app.SetPProfConfig(true, 8081) //全局容器 - app.AppContext.Set("gstring", "gvalue") - app.AppContext.Set("gint", 1) + app.Items.Set("gstring", "gvalue") + app.Items.Set("gint", 1) // 开始服务 port := 8080 diff --git a/example/middleware/main.go b/example/middleware/main.go index eee3e2a..ed18c45 100644 --- a/example/middleware/main.go +++ b/example/middleware/main.go @@ -34,10 +34,6 @@ func main() { //启动 监控服务 app.SetPProfConfig(true, 8081) - //全局容器 - app.AppContext.Set("gstring", "gvalue") - app.AppContext.Set("gint", 1) - // 开始服务 port := 8080 fmt.Println("dotweb.StartServer => " + strconv.Itoa(port)) diff --git a/version.MD b/version.MD index 31cce20..bfa6831 100644 --- a/version.MD +++ b/version.MD @@ -1,5 +1,20 @@ ## dotweb版本记录: +#### Version 1.4.8 +* 调整:ItemContext更名为ItemMap,新增ConcurrenceMap、ReadonlyMap接口 +* 调整:Dotweb.AppContext变更为Dotweb.Items +* 调整:HttpContext.AppContext变更为HttpContext.AppItems +* 调整:HttpContext.AppSetConfig变更为HttpContext.ConfigSet +* 调整:config.AppSet变更为config.ConfigSet +* 新增: config.ParseConfigSetXML\ParseConfigSetJSON\ParseConfigSetYaml,用于解析常规Key\Value格式的配置文件 +* 新增:config.Config.IncludeConfigSet,用于向config.ConfigSet中导入Key\Value格式的配置文件,通过HttpContext.ConfigSet获取相关设置信息 +* ParseConfigSetXML:支持xml格式文件解析,返回core.ConcurrenceMap +* ParseConfigSetJSON:支持json格式文件解析,返回core.ConcurrenceMap +* ParseConfigSetYaml:支持yaml格式文件解析,返回core.ConcurrenceMap +* 具体配置文件格式可参考example/configset +* 新增示例代码 example/configset +* 2018-01-24 22:00 + #### Version 1.4.7 * BUG Fixed: 修复Middleware特定场景下无效问题 * 新增dotweb.IncludeDotwebGroup,用于自动集成dotweb相关路由