add instance management feature for gdb/gredis; add customized configuration content management feature for gcfg; update gjson for data content type check

This commit is contained in:
John
2019-04-02 14:37:46 +08:00
parent b1804fc346
commit 07476a4349
24 changed files with 472 additions and 140 deletions

View File

@ -14,6 +14,7 @@ import (
"database/sql"
"errors"
"fmt"
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/container/gring"
"github.com/gogf/gf/g/container/gtype"
"github.com/gogf/gf/g/container/gvar"
@ -155,22 +156,26 @@ const (
gDEFAULT_BATCH_NUM = 10
// 默认的连接池连接存活时间(秒)
gDEFAULT_CONN_MAX_LIFE_TIME = 30
)
var (
// 单例对象Map
instances = gmap.NewStringInterfaceMap()
)
// 使用默认/指定分组配置进行连接数据库集群配置项default
func New(groupName ...string) (db DB, err error) {
group := config.d
if len(groupName) > 0 {
group = groupName[0]
func New(name...string) (db DB, err error) {
group := configs.defaultGroup
if len(name) > 0 {
group = name[0]
}
config.RLock()
defer config.RUnlock()
configs.RLock()
defer configs.RUnlock()
if len(config.c) < 1 {
if len(configs.config) < 1 {
return nil, errors.New("empty database configuration")
}
if _, ok := config.c[group]; ok {
if _, ok := configs.config[group]; ok {
if node, err := getConfigNodeByGroup(group, true); err == nil {
base := &dbBase {
group : group,
@ -204,9 +209,25 @@ func New(groupName ...string) (db DB, err error) {
}
}
// 获得数据库操作对象单例
func Instance(name...string) (db DB, err error) {
group := configs.defaultGroup
if len(name) > 0 {
group = name[0]
}
v := instances.GetOrSetFuncLock(group, func() interface{} {
db, err = New(group)
return db
})
if v != nil {
return v.(DB), nil
}
return
}
// 获取指定数据库角色的一个配置项,内部根据权重计算负载均衡
func getConfigNodeByGroup(group string, master bool) (*ConfigNode, error) {
if list, ok := config.c[group]; ok {
if list, ok := configs.config[group]; ok {
// 将master, slave集群列表拆分出来
masterList := make(ConfigGroup, 0)
slaveList := make(ConfigGroup, 0)
@ -319,17 +340,17 @@ func (bs *dbBase) getSqlDb(master bool) (sqlDb *sql.DB, err error) {
return
}
// 切换操作的数据库(注意该切换是全局的)
// 切换当前数据库对象操作的数据库。
func (bs *dbBase) SetSchema(schema string) {
bs.schema.Set(schema)
}
// 创建底层数据库master链接对象
// 创建底层数据库master链接对象
func (bs *dbBase) Master() (*sql.DB, error) {
return bs.getSqlDb(true)
}
// 创建底层数据库slave链接对象
// 创建底层数据库slave链接对象
func (bs *dbBase) Slave() (*sql.DB, error) {
return bs.getSqlDb(false)
}

View File

@ -17,14 +17,7 @@ const (
DEFAULT_GROUP_NAME = "default" // 默认配置名称
)
// 数据库配置包内对象
var config struct {
sync.RWMutex
c Config // 数据库配置
d string // 默认数据库分组名称
}
// 数据库配置
// 数据库分组配置
type Config map[string]ConfigGroup
// 数据库集群配置
@ -41,12 +34,19 @@ type ConfigNode struct {
Role string // (可选默认为master)数据库的角色用于主从操作分离至少需要有一个master参数值master, slave
Charset string // (可选,默认为 utf8)编码,默认为 utf8
Priority int // (可选)用于负载均衡的权重计算,当集群中只有一个节点时,权重没有任何意义
Linkinfo string // (可选)自定义链接信息,当该字段被设置值时,以上链接字段(Host,Port,User,Pass,Name)将失效(该字段是一个扩展功能)
LinkInfo string // (可选)自定义链接信息,当该字段被设置值时,以上链接字段(Host,Port,User,Pass,Name)将失效(该字段是一个扩展功能)
MaxIdleConnCount int // (可选)连接池最大限制的连接数
MaxOpenConnCount int // (可选)连接池最大打开的连接数
MaxConnLifetime int // (可选,单位秒)连接对象可重复使用的时间长度
}
// 数据库配置包内对象
var configs struct {
sync.RWMutex // 并发安全互斥锁
config Config // 数据库分组配置
defaultGroup string // 默认数据库分组名称
}
// 数据库集群配置示例,支持主从处理,多数据库集群支持
/*
var DatabaseConfiguration = Config {
@ -80,29 +80,32 @@ var DatabaseConfiguration = Config {
// 包初始化
func init() {
config.c = make(Config)
config.d = DEFAULT_GROUP_NAME
configs.config = make(Config)
configs.defaultGroup = DEFAULT_GROUP_NAME
}
// 设置当前应用的数据库配置信息,进行全局数据库配置覆盖操作
func SetConfig (c Config) {
config.Lock()
defer config.Unlock()
config.c = c
func SetConfig (config Config) {
defer instances.Clear()
configs.Lock()
defer configs.Unlock()
configs.config = config
}
// 添加数据库服务器集群配置
func AddConfigGroup (group string, nodes ConfigGroup) {
config.Lock()
config.c[group] = nodes
config.Unlock()
defer instances.Clear()
configs.Lock()
defer configs.Unlock()
configs.config[group] = nodes
}
// 添加一台数据库服务器配置
func AddConfigNode (group string, node ConfigNode) {
config.Lock()
config.c[group] = append(config.c[group], node)
config.Unlock()
defer instances.Clear()
configs.Lock()
defer configs.Unlock()
configs.config[group] = append(configs.config[group], node)
}
// 添加默认链接的一台数据库服务器配置
@ -117,16 +120,25 @@ func AddDefaultConfigGroup (nodes ConfigGroup) {
// 添加一台数据库服务器配置
func GetConfig (group string) ConfigGroup {
config.RLock()
defer config.RUnlock()
return config.c[group]
configs.RLock()
defer configs.RUnlock()
return configs.config[group]
}
// 设置默认链接的数据库链接配置项(默认是 default)
func SetDefaultGroup (groupName string) {
config.Lock()
config.d = groupName
config.Unlock()
func SetDefaultGroup (name string) {
defer instances.Clear()
configs.Lock()
defer configs.Unlock()
configs.defaultGroup = name
}
// 获取默认链接的数据库链接配置项(默认是 default)
func GetDefaultGroup() string {
defer instances.Clear()
configs.Lock()
defer configs.Unlock()
return configs.defaultGroup
}
// 设置数据库连接池中空闲链接的大小
@ -147,8 +159,8 @@ func (bs *dbBase) SetConnMaxLifetime(n int) {
// 节点配置转换为字符串
func (node *ConfigNode) String() string {
if node.Linkinfo != "" {
return node.Linkinfo
if node.LinkInfo != "" {
return node.LinkInfo
}
return fmt.Sprintf(`%s@%s:%s,%s,%s,%s,%s,%d-%d-%d`, node.User, node.Host, node.Port,
node.Name, node.Type, node.Role, node.Charset,

View File

@ -30,8 +30,8 @@ type dbMssql struct {
// 创建SQL操作对象
func (db *dbMssql) Open(config *ConfigNode) (*sql.DB, error) {
source := ""
if config.Linkinfo != "" {
source = config.Linkinfo
if config.LinkInfo != "" {
source = config.LinkInfo
} else {
source = fmt.Sprintf("user id=%s;password=%s;server=%s;port=%s;database=%s;encrypt=disable",
config.User, config.Pass, config.Host, config.Port, config.Name)

View File

@ -20,8 +20,8 @@ type dbMysql struct {
// 创建SQL操作对象内部采用了lazy link处理
func (db *dbMysql) Open (config *ConfigNode) (*sql.DB, error) {
var source string
if config.Linkinfo != "" {
source = config.Linkinfo
if config.LinkInfo != "" {
source = config.LinkInfo
} else {
source = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s&multiStatements=true",
config.User, config.Pass, config.Host, config.Port, config.Name, config.Charset)

View File

@ -30,8 +30,8 @@ type dbOracle struct {
// 创建SQL操作对象
func (db *dbOracle) Open(config *ConfigNode) (*sql.DB, error) {
var source string
if config.Linkinfo != "" {
source = config.Linkinfo
if config.LinkInfo != "" {
source = config.LinkInfo
} else {
source = fmt.Sprintf("%s/%s@%s", config.User, config.Pass, config.Name)
}

View File

@ -26,8 +26,8 @@ type dbPgsql struct {
// 创建SQL操作对象内部采用了lazy link处理
func (db *dbPgsql) Open (config *ConfigNode) (*sql.DB, error) {
var source string
if config.Linkinfo != "" {
source = config.Linkinfo
if config.LinkInfo != "" {
source = config.LinkInfo
} else {
source = fmt.Sprintf("user=%s password=%s host=%s port=%s dbname=%s", config.User, config.Pass, config.Host, config.Port, config.Name)
}

View File

@ -24,8 +24,8 @@ type dbSqlite struct {
func (db *dbSqlite) Open(config *ConfigNode) (*sql.DB, error) {
var source string
if config.Linkinfo != "" {
source = config.Linkinfo
if config.LinkInfo != "" {
source = config.LinkInfo
} else {
source = config.Name
}

View File

@ -0,0 +1,28 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gdb_test
import (
"github.com/gogf/gf/g/database/gdb"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
func Test_Instance(t *testing.T) {
gtest.Case(t, func() {
_, err := gdb.Instance("none")
gtest.AssertNE(err, nil)
db, err := gdb.Instance()
gtest.Assert(err, nil)
err1 := db.PingMaster()
err2 := db.PingSlave()
gtest.Assert(err1, nil)
gtest.Assert(err2, nil)
})
}

View File

@ -4,10 +4,11 @@
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// Package gredis provides client for redis server.
// Package gredis provides convenient client for redis server.
//
// Redis客户端.
// Redis中文手册文档请参考http://redisdoc.com/ , Redis官方命令请参考https://redis.io/commands
// Redis中文手册请参考http://redisdoc.com/
// Redis官方命令请参考https://redis.io/commands
package gredis
import (
@ -24,8 +25,9 @@ const (
// Redis客户端(管理连接池)
type Redis struct {
pool *redis.Pool
config Config
pool *redis.Pool // 底层连接池
group string // 配置分组
config Config // 配置对象
}
// Redis连接对象(连接池中的单个连接)
@ -48,13 +50,17 @@ type PoolStats struct {
redis.PoolStats
}
// 连接池map
var pools = gmap.NewStringInterfaceMap()
var (
// 单例对象Map
instances = gmap.NewStringInterfaceMap()
// 连接池Map
pools = gmap.NewStringInterfaceMap()
)
// New creates a redis client object with given configuration.
// Redis client maintains a connection pool automatically.
//
// 创建redis操作对象.
// 创建redis操作对象,底层根据配置信息公用的连接池(连接池单例)。
func New(config Config) *Redis {
if config.IdleTimeout == 0 {
config.IdleTimeout = gDEFAULT_POOL_IDLE_TIMEOUT
@ -96,11 +102,41 @@ func New(config Config) *Redis {
}
}
// Instance returns an instance of redis client with specified group.
// The <group> param is unnecessary, if <group> is not passed,
// return redis instance with default group.
//
// 获取指定分组名称的Redis单例对象底层根据配置信息公用的连接池连接池单例
func Instance(name...string) *Redis {
group := DEFAULT_GROUP_NAME
if len(name) > 0 {
group = name[0]
}
v := instances.GetOrSetFuncLock(group, func() interface{} {
if config, ok := GetConfig(group); ok {
r := New(config)
r.group = group
return r
}
return nil
})
if v != nil {
return v.(*Redis)
}
return nil
}
// Close closes the redis connection pool,
// it will release all connections reserved by this pool.
// It always not necessary to call Close manually.
//
// 关闭redis管理对象将会关闭底层的连接池。
// 往往没必要手动调用,跟随进程销毁即可。
func (r *Redis) Close() error {
if r.group != "" {
// 如果是单例对象那么需要从单例对象Map中删除
instances.Remove(r.group)
}
pools.Remove(fmt.Sprintf("%v", r.config))
return r.pool.Close()
}

View File

@ -0,0 +1,69 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gredis
import "github.com/gogf/gf/g/container/gmap"
const (
// 默认分组名称
DEFAULT_GROUP_NAME = "default"
)
var (
// 分组配置
configs = gmap.NewStringInterfaceMap()
)
// SetConfig sets the global configuration for specified group.
// If <name> is not passed, it sets configuration for the default group name.
//
// 设置全局分组配置name为非必需参数默认为默认分组名称。
func SetConfig(config Config, name...string) {
group := DEFAULT_GROUP_NAME
if len(name) > 0 {
group = name[0]
}
configs.Set(group, config)
instances.Remove(group)
}
// GetConfig returns the global configuration with specified group.
// If <group> is not passed, it returns configuration of the default group name.
//
// 获取指定全局分组配置group为非必需参数默认为默认分组名称。
func GetConfig(name...string) (config Config, ok bool) {
group := DEFAULT_GROUP_NAME
if len(name) > 0 {
group = name[0]
}
if v := configs.Get(group); v != nil {
return v.(Config), true
}
return Config{}, false
}
// RemoveConfig removes the global configuration with specified group.
// If <name> is not passed, it removes configuration of the default group name.
//
// 删除指定全局分组配置name为非必需参数默认为默认分组名称。
func RemoveConfig(name...string) {
group := DEFAULT_GROUP_NAME
if len(name) > 0 {
group = name[0]
}
configs.Remove(group)
instances.Remove(group)
}
// ClearConfig removes all configurations and instances of redis.
//
// 清除所有的配置内容。
func ClearConfig() {
configs.Clear()
instances.Clear()
}

View File

@ -99,6 +99,33 @@ func Test_Conn(t *testing.T) {
conn := redis.Conn()
defer conn.Close()
r, err := conn.Do("GET", "k")
gtest.Assert(err, nil)
gtest.Assert(r, []byte("v"))
_, err = conn.Do("DEL", "k")
gtest.Assert(err, nil)
r, err = conn.Do("GET", "k")
gtest.Assert(err, nil)
gtest.Assert(r, nil)
})
}
func Test_Instance(t *testing.T) {
gtest.Case(t, func() {
group := "my-test"
gredis.SetConfig(config, group)
defer gredis.RemoveConfig(group)
redis := gredis.Instance(group)
defer redis.Close()
conn := redis.Conn()
defer conn.Close()
_, err := conn.Do("SET", "k", "v")
gtest.Assert(err, nil)
r, err := conn.Do("GET", "k")
gtest.Assert(err, nil)
gtest.Assert(r, []byte("v"))

View File

@ -138,9 +138,9 @@ func LoadContent(data interface{}, dataType...string) (*Json, error) {
t = "json"
} else if gregex.IsMatch(`<.+>.*</.+>`, b) {
t = "xml"
} else if gregex.IsMatch(`\w+\s*:\s*.+`, b) {
} else if gregex.IsMatch(`\n[\s\t]*\w+\s*:\s*.+`, b) {
t = "yml"
} else if gregex.IsMatch(`\w+\s*=\s*.+`, b) {
} else if gregex.IsMatch(`\n[\s\t]*\w+\s*=\s*.+`, b) {
t = "toml"
}
}
@ -169,6 +169,10 @@ func LoadContent(data interface{}, dataType...string) (*Json, error) {
if err := decoder.Decode(&result); err != nil {
return nil, err
}
switch result.(type) {
case string, []byte:
return nil, fmt.Errorf(`json decoding failed for content: %s`, string(b))
}
}
return New(result), nil
}

View File

@ -4,10 +4,7 @@
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// Package gins provides instances management and some core components.
//
// 单例对象管理.
// 框架内置了一些核心对象获取方法并且可以通过Set和Get方法实现IoC以及对内置核心对象的自定义替换
// Package gins provides instances management and core components management.
package gins
import (
@ -21,9 +18,9 @@ import (
"github.com/gogf/gf/g/os/gfsnotify"
"github.com/gogf/gf/g/os/glog"
"github.com/gogf/gf/g/os/gview"
"github.com/gogf/gf/g/text/gregex"
"github.com/gogf/gf/g/text/gstr"
"github.com/gogf/gf/g/util/gconv"
"github.com/gogf/gf/g/text/gregex"
"time"
)
@ -101,7 +98,8 @@ func Config(file...string) *gcfg.Config {
if len(file) > 0 {
configFile = file[0]
}
return instances.GetOrSetFuncLock(fmt.Sprintf("%s.%s", gFRAME_CORE_COMPONENT_NAME_CONFIG, configFile),
key := fmt.Sprintf("%s.%s", gFRAME_CORE_COMPONENT_NAME_CONFIG, configFile)
return instances.GetOrSetFuncLock(key,
func() interface{} {
// 默认当前工作目录
config := gcfg.New(gfile.Pwd(), configFile)
@ -139,62 +137,66 @@ func Database(name...string) gdb.DB {
for group, v := range m {
cg := gdb.ConfigGroup{}
if list, ok := v.([]interface{}); ok {
for _, nodev := range list {
node := gdb.ConfigNode{}
nodem := nodev.(map[string]interface{})
if value, ok := nodem["host"]; ok {
for _, nodeValue := range list {
node := gdb.ConfigNode{}
nodeMap := nodeValue.(map[string]interface{})
if value, ok := nodeMap["host"]; ok {
node.Host = gconv.String(value)
}
if value, ok := nodem["port"]; ok {
if value, ok := nodeMap["port"]; ok {
node.Port = gconv.String(value)
}
if value, ok := nodem["user"]; ok {
if value, ok := nodeMap["user"]; ok {
node.User = gconv.String(value)
}
if value, ok := nodem["pass"]; ok {
if value, ok := nodeMap["pass"]; ok {
node.Pass = gconv.String(value)
}
if value, ok := nodem["name"]; ok {
if value, ok := nodeMap["name"]; ok {
node.Name = gconv.String(value)
}
if value, ok := nodem["type"]; ok {
if value, ok := nodeMap["type"]; ok {
node.Type = gconv.String(value)
}
if value, ok := nodem["role"]; ok {
if value, ok := nodeMap["role"]; ok {
node.Role = gconv.String(value)
}
if value, ok := nodem["charset"]; ok {
if value, ok := nodeMap["charset"]; ok {
node.Charset = gconv.String(value)
}
if value, ok := nodem["priority"]; ok {
if value, ok := nodeMap["priority"]; ok {
node.Priority = gconv.Int(value)
}
// Deprecated
if value, ok := nodem["linkinfo"]; ok {
node.Linkinfo = gconv.String(value)
}
if value, ok := nodem["linkInfo"]; ok {
node.Linkinfo = gconv.String(value)
if value, ok := nodeMap["linkinfo"]; ok {
node.LinkInfo = gconv.String(value)
}
// Deprecated
if value, ok := nodem["max-idle"]; ok {
if value, ok := nodeMap["link-info"]; ok {
node.LinkInfo = gconv.String(value)
}
if value, ok := nodeMap["linkInfo"]; ok {
node.LinkInfo = gconv.String(value)
}
// Deprecated
if value, ok := nodeMap["max-idle"]; ok {
node.MaxIdleConnCount = gconv.Int(value)
}
if value, ok := nodem["maxIdle"]; ok {
if value, ok := nodeMap["maxIdle"]; ok {
node.MaxIdleConnCount = gconv.Int(value)
}
// Deprecated
if value, ok := nodem["max-open"]; ok {
if value, ok := nodeMap["max-open"]; ok {
node.MaxOpenConnCount = gconv.Int(value)
}
if value, ok := nodem["maxOpen"]; ok {
if value, ok := nodeMap["maxOpen"]; ok {
node.MaxOpenConnCount = gconv.Int(value)
}
// Deprecated
if value, ok := nodem["max-lifetime"]; ok {
if value, ok := nodeMap["max-lifetime"]; ok {
node.MaxConnLifetime = gconv.Int(value)
}
if value, ok := nodem["maxLifetime"]; ok {
if value, ok := nodeMap["maxLifetime"]; ok {
node.MaxConnLifetime = gconv.Int(value)
}
cg = append(cg, node)
@ -202,10 +204,7 @@ func Database(name...string) gdb.DB {
}
gdb.AddConfigGroup(group, cg)
}
// 使用gfsnotify进行文件监控当配置文件有任何变化时清空数据库配置缓存
gfsnotify.Add(config.GetFilePath(), func(event *gfsnotify.Event) {
instances.Remove(key)
})
addConfigMonitor(key)
}
if db, err := gdb.New(name...); err == nil {
return db
@ -254,10 +253,12 @@ func Redis(name...string) *gredis.Redis {
if v, ok := parse["maxConnLifetime"]; ok {
config.MaxConnLifetime = gconv.TimeDuration(v)*time.Second
}
addConfigMonitor(key)
return gredis.New(config)
}
array, _ = gregex.MatchString(`(.+):(\d+),{0,1}(\d*),{0,1}(.*)`, line)
if len(array) == 5 {
addConfigMonitor(key)
return gredis.New(gredis.Config{
Host : array[1],
Port : gconv.Int(array[2]),
@ -281,6 +282,16 @@ func Redis(name...string) *gredis.Redis {
return nil
}
// 添加对单例对象的配置文件inotify监控
func addConfigMonitor(key string) {
// 使用gfsnotify进行文件监控当配置文件有任何变化时清空对象单例缓存
if path := Config().GetFilePath(); path != "" {
gfsnotify.Add(path, func(event *gfsnotify.Event) {
instances.Remove(key)
})
}
}
// 模板内置方法config
func funcConfig(pattern string, file...string) string {
return Config().GetString(pattern, file...)

View File

@ -30,7 +30,7 @@ var serverMapping = gmap.NewStringInterfaceMap()
// 获取/创建一个空配置的TCP Server
// 单例模式请保证name的唯一性
func GetServer(name...interface{}) (*Server) {
func GetServer(name...interface{}) *Server {
serverName := gDEFAULT_SERVER
if len(name) > 0 {
serverName = gconv.String(name[0])

View File

@ -4,10 +4,7 @@
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// Package gcfg provides reading, caching and managing for configuration files.
//
// 配置管理,
// 配置文件格式支持json, xml, toml, yaml/yml
// Package gcfg provides reading, caching and managing for configuration files/contents.
package gcfg
import (
@ -26,21 +23,20 @@ import (
)
const (
// 默认的配置管理文件名称
// Default configuration file name.
DEFAULT_CONFIG_FILE = "config.toml"
)
// 配置管理对象
// Configuration struct.
type Config struct {
name *gtype.String // 默认配置文件名称
paths *garray.StringArray // 搜索目录路径
jsons *gmap.StringInterfaceMap // 配置文件对象
vc *gtype.Bool // 层级检索是否执行分隔符冲突检测(默认为false检测会比较影响检索效率)
name *gtype.String // Default configuration file name.
paths *garray.StringArray // Searching path array.
jsons *gmap.StringInterfaceMap // The pared JSON objects for configuration files.
vc *gtype.Bool // Whether do violence check in value index searching.
// It affects the performance when set true(false in default).
}
// New returns a new configuration management object.
//
// 生成一个配置管理对象
func New(path string, file...string) *Config {
name := DEFAULT_CONFIG_FILE
if len(file) > 0 {
@ -59,8 +55,6 @@ func New(path string, file...string) *Config {
}
// filePath returns the absolute configuration file path for the given filename by <file>.
//
// 判断从哪个配置文件中获取内容,返回配置文件的绝对路径
func (c *Config) filePath(file...string) (path string) {
name := c.name.Val()
if len(file) > 0 {
@ -228,19 +222,30 @@ func (c *Config) getJson(file...string) *gjson.Json {
name = file[0]
}
r := c.jsons.GetOrSetFuncLock(name, func() interface{} {
filePath := c.filePath(file...)
if filePath == "" {
return nil
content := ""
filePath := ""
if content = GetContent(name); content == "" {
filePath = c.filePath(name)
if filePath == "" {
return nil
}
content = gfile.GetContents(filePath)
}
if j, err := gjson.Load(filePath); err == nil {
if j, err := gjson.LoadContent(content); err == nil {
j.SetViolenceCheck(c.vc.Val())
// 添加配置文件监听,如果有任何变化,删除文件内容缓存,下一次查询会自动更新
gfsnotify.Add(filePath, func(event *gfsnotify.Event) {
c.jsons.Remove(name)
})
if filePath != "" {
gfsnotify.Add(filePath, func(event *gfsnotify.Event) {
c.jsons.Remove(name)
})
}
return j
} else {
glog.Criticalfln(`[gcfg] Load config file "%s" failed: %s`, filePath, err.Error())
if filePath != "" {
glog.Criticalfln(`[gcfg] Load config file "%s" failed: %s`, filePath, err.Error())
} else {
glog.Criticalfln(`[gcfg] Load configuration failed: %s`, err.Error())
}
}
return nil
})

34
g/os/gcfg/gcfg_config.go Normal file
View File

@ -0,0 +1,34 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gcfg
import "github.com/gogf/gf/g/container/gmap"
var (
// Customized configuration content.
configs = gmap.NewStringStringMap()
)
// SetContent sets customized configuration content for specified <file>.
// The <file> is unnecessary param, default is DEFAULT_CONFIG_FILE.
func SetContent(content string, file...string) {
name := DEFAULT_CONFIG_FILE
if len(file) > 0 {
name = file[0]
}
configs.Set(name, content)
}
// GetContent returns customized configuration content for specified <file>.
// The <file> is unnecessary param, default is DEFAULT_CONFIG_FILE.
func GetContent(file...string) string {
name := DEFAULT_CONFIG_FILE
if len(file) > 0 {
name = file[0]
}
return configs.Get(name)
}

View File

@ -75,3 +75,58 @@ array = [1,2,3]
})
}
func Test_Content(t *testing.T) {
content := `
v1 = 1
v2 = "true"
v3 = "off"
v4 = "1.23"
array = [1,2,3]
[redis]
disk = "127.0.0.1:6379,0"
cache = "127.0.0.1:6379,1"
`
gcfg.SetContent(content)
gtest.Case(t, func() {
c := gcfg.New(".")
gtest.Assert(c.Get("v1"), 1)
gtest.AssertEQ(c.GetInt("v1"), 1)
gtest.AssertEQ(c.GetInt8("v1"), int8(1))
gtest.AssertEQ(c.GetInt16("v1"), int16(1))
gtest.AssertEQ(c.GetInt32("v1"), int32(1))
gtest.AssertEQ(c.GetInt64("v1"), int64(1))
gtest.AssertEQ(c.GetUint("v1"), uint(1))
gtest.AssertEQ(c.GetUint8("v1"), uint8(1))
gtest.AssertEQ(c.GetUint16("v1"), uint16(1))
gtest.AssertEQ(c.GetUint32("v1"), uint32(1))
gtest.AssertEQ(c.GetUint64("v1"), uint64(1))
gtest.AssertEQ(c.GetVar("v1").String(), "1")
gtest.AssertEQ(c.GetVar("v1").Bool(), true)
gtest.AssertEQ(c.GetVar("v2").String(), "true")
gtest.AssertEQ(c.GetVar("v2").Bool(), true)
gtest.AssertEQ(c.GetString("v1"), "1")
gtest.AssertEQ(c.GetFloat32("v4"), float32(1.23))
gtest.AssertEQ(c.GetFloat64("v4"), float64(1.23))
gtest.AssertEQ(c.GetString("v2"), "true")
gtest.AssertEQ(c.GetBool("v2"), true)
gtest.AssertEQ(c.GetBool("v3"), false)
gtest.AssertEQ(c.Contains("v1"), true)
gtest.AssertEQ(c.Contains("v2"), true)
gtest.AssertEQ(c.Contains("v3"), true)
gtest.AssertEQ(c.Contains("v4"), true)
gtest.AssertEQ(c.Contains("v5"), false)
gtest.AssertEQ(c.GetInts("array"), []int{1,2,3})
gtest.AssertEQ(c.GetStrings("array"), []string{"1","2","3"})
gtest.AssertEQ(c.GetArray("array"), []interface{}{"1","2","3"})
gtest.AssertEQ(c.GetInterfaces("array"), []interface{}{"1","2","3"})
gtest.AssertEQ(c.GetMap("redis"), map[string]interface{}{
"disk" : "127.0.0.1:6379,0",
"cache" : "127.0.0.1:6379,1",
})
})
}

View File

@ -141,11 +141,6 @@ func (c *Cron) Search(name string) *Entry {
return nil
}
// 根据指定名称删除定时任务
func (c *Cron) Remove(name string) {
c.entries.Remove(name)
}
// 开启定时任务执行(可以指定特定名称的一个或若干个定时任务)
func (c *Cron) Start(name...string) {
if len(name) > 0 {
@ -172,6 +167,13 @@ func (c *Cron) Stop(name...string) {
}
}
// 根据指定名称删除定时任务。
func (c *Cron) Remove(name string) {
if v := c.entries.Get(name); v != nil {
v.(*Entry).Close()
}
}
// 关闭定时任务
func (c *Cron) Close() {
c.status.Set(STATUS_CLOSED)

View File

@ -87,7 +87,7 @@ func (entry *Entry) Stop() {
// 关闭定时任务
func (entry *Entry) Close() {
entry.cron.Remove(entry.Name)
entry.cron.entries.Remove(entry.Name)
entry.entry.Close()
}
@ -96,6 +96,7 @@ func (entry *Entry) check() {
if entry.schedule.meet(time.Now()) {
path := entry.cron.GetLogPath()
level := entry.cron.GetLogLevel()
// 检查定时任务对象状态(非任务状态)
switch entry.cron.status.Val() {
case STATUS_STOPPED:
return

View File

@ -71,6 +71,24 @@ func TestCron_Basic(t *testing.T) {
})
}
func TestCron_Remove(t *testing.T) {
gtest.Case(t, func() {
cron := gcron.New()
array := garray.New()
cron.Add("* * * * * *", func() {
array.Append(1)
}, "add")
gtest.Assert(array.Len(), 0)
time.Sleep(1200*time.Millisecond)
gtest.Assert(array.Len(), 1)
cron.Remove("add")
gtest.Assert(array.Len(), 1)
time.Sleep(1200*time.Millisecond)
gtest.Assert(array.Len(), 1)
})
}
func TestCron_AddSingleton(t *testing.T) {
// un used, can be removed
gtest.Case(t, func() {

View File

@ -0,0 +1,19 @@
package main
import (
"net/http"
"time"
)
func main() {
s := &http.Server{
Addr: ":8199",
ReadTimeout: 2 * time.Second,
WriteTimeout: 2 * time.Second,
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hi"))
time.Sleep(3*time.Second)
})
s.ListenAndServe()
}

View File

@ -9,9 +9,8 @@ func main() {
gudp.NewServer("127.0.0.1:8999", func(conn *gudp.Conn) {
defer conn.Close()
for {
if data, _ := conn.Recv(-1); len(data) > 0 {
fmt.Println(string(data))
}
data, err := conn.Recv(-1)
fmt.Println(err, string(data))
}
}).Run()
}

View File

@ -1,18 +1,9 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
)
import "fmt"
func main() {
value := interface{}(nil)
data := []byte(`{"n": 123456789}`)
decoder := json.NewDecoder(bytes.NewReader(data))
decoder.UseNumber()
err := decoder.Decode(&value)
//err := json.Unmarshal(data, &value)
fmt.Println(err)
fmt.Println(value)
x := uintptr(1)
fmt.Println(x^0)
fmt.Println(2^0)
}

View File

@ -1 +1 @@
`GF` self-maintains its thirdparty-packages, developers need no worry about the dependences.
`GF` self-maintains its third-party-packages, developers need no worry about the dependencies.