mirror of
https://gitee.com/johng/gf
synced 2026-07-06 13:42:46 +08:00
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:
@ -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)
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
28
g/database/gdb/gdb_unit_basic_test.go
Normal file
28
g/database/gdb/gdb_unit_basic_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
@ -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()
|
||||
}
|
||||
|
||||
69
g/database/gredis/gredis_config.go
Normal file
69
g/database/gredis/gredis_config.go
Normal 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()
|
||||
}
|
||||
|
||||
|
||||
@ -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"))
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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...)
|
||||
|
||||
@ -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])
|
||||
|
||||
@ -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
34
g/os/gcfg/gcfg_config.go
Normal 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)
|
||||
}
|
||||
@ -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",
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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() {
|
||||
|
||||
19
geg/net/ghttp/server/nethttp_server.go
Normal file
19
geg/net/ghttp/server/nethttp_server.go
Normal 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()
|
||||
}
|
||||
@ -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()
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
@ -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.
|
||||
Reference in New Issue
Block a user