From 5204193c5808e2647b0bb545afc7e7b97d491a1e Mon Sep 17 00:00:00 2001 From: John Date: Sat, 13 Oct 2018 20:29:27 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B9=E8=BF=9Bgdb=EF=BC=8C=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E6=95=B0=E6=8D=AE=E5=BA=93=E6=93=8D=E4=BD=9C=E8=87=AA?= =?UTF-8?q?=E5=8A=A8Close=E7=89=B9=E6=80=A7(=E4=BD=BF=E7=94=A8=E9=93=BE?= =?UTF-8?q?=E6=8E=A5=E6=B1=A0)=EF=BC=8C=E7=94=A8=E6=88=B7=E6=97=A0?= =?UTF-8?q?=E9=9C=80=E5=86=8Ddefer=20db.Close()=EF=BC=8C=E5=B9=B6=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0g.DB=E6=95=B0=E6=8D=AE=E5=BA=93=E5=AF=B9=E8=B1=A1?= =?UTF-8?q?=E5=8D=95=E4=BE=8B=E5=88=AB=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- g/container/gmap/gmap_int_interface_map.go | 7 +- .../gmap/gmap_interface_interface_map.go | 7 +- g/container/gmap/gmap_string_interface_map.go | 7 +- g/database/gdb/gdb.go | 215 ++++++++++-------- g/database/gdb/gdb_base.go | 118 ++++------ g/database/gdb/gdb_config.go | 16 ++ g/database/gdb/gdb_transaction.go | 13 +- g/database/gdb/test/gdb_bench_test.go | 31 --- g/frame/gins/gins.go | 120 ++++++++-- g/g_object.go | 89 +------- g/util/gconv/gconv.go | 30 ++- g/util/grand/grand.go | 47 ++-- geg/database/orm/mysql/gdb.go | 6 +- geg/database/orm/mysql/gdb_config.go | 16 ++ geg/frame/config.toml | 2 +- geg/other/test.go | 69 +----- 16 files changed, 397 insertions(+), 396 deletions(-) delete mode 100644 g/database/gdb/test/gdb_bench_test.go create mode 100644 geg/database/orm/mysql/gdb_config.go diff --git a/g/container/gmap/gmap_int_interface_map.go b/g/container/gmap/gmap_int_interface_map.go index a75d14ff2..3e68c2e37 100644 --- a/g/container/gmap/gmap_int_interface_map.go +++ b/g/container/gmap/gmap_int_interface_map.go @@ -94,9 +94,7 @@ func (this *IntInterfaceMap) GetOrSet(key int, value interface{}) interface{} { // 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成 func (this *IntInterfaceMap) GetOrSetFunc(key int, f func() interface{}) interface{} { if v := this.Get(key); v == nil { - v = f() - this.doSetWithLockCheck(key, v) - return v + return this.doSetWithLockCheck(key, f()) } else { return v } @@ -105,8 +103,7 @@ func (this *IntInterfaceMap) GetOrSetFunc(key int, f func() interface{}) interfa // 与GetOrSetFunc不同的是,f是在写锁机制内执行 func (this *IntInterfaceMap) GetOrSetFuncLock(key int, f func() interface{}) interface{} { if v := this.Get(key); v == nil { - this.doSetWithLockCheck(key, f) - return v + return this.doSetWithLockCheck(key, f) } else { return v } diff --git a/g/container/gmap/gmap_interface_interface_map.go b/g/container/gmap/gmap_interface_interface_map.go index 2cf371c04..4e8cafe52 100644 --- a/g/container/gmap/gmap_interface_interface_map.go +++ b/g/container/gmap/gmap_interface_interface_map.go @@ -96,9 +96,7 @@ func (this *InterfaceInterfaceMap) GetOrSet(key interface{}, value interface{}) // 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成 func (this *InterfaceInterfaceMap) GetOrSetFunc(key interface{}, f func() interface{}) interface{} { if v := this.Get(key); v == nil { - v = f() - this.doSetWithLockCheck(key, v) - return v + return this.doSetWithLockCheck(key, f()) } else { return v } @@ -107,8 +105,7 @@ func (this *InterfaceInterfaceMap) GetOrSetFunc(key interface{}, f func() interf // 与GetOrSetFunc不同的是,f是在写锁机制内执行 func (this *InterfaceInterfaceMap) GetOrSetFuncLock(key interface{}, f func() interface{}) interface{} { if v := this.Get(key); v == nil { - this.doSetWithLockCheck(key, f) - return v + return this.doSetWithLockCheck(key, f) } else { return v } diff --git a/g/container/gmap/gmap_string_interface_map.go b/g/container/gmap/gmap_string_interface_map.go index d284a8f26..1451211c8 100644 --- a/g/container/gmap/gmap_string_interface_map.go +++ b/g/container/gmap/gmap_string_interface_map.go @@ -96,9 +96,7 @@ func (this *StringInterfaceMap) GetOrSet(key string, value interface{}) interfac // 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成 func (this *StringInterfaceMap) GetOrSetFunc(key string, f func() interface{}) interface{} { if v := this.Get(key); v == nil { - v = f() - this.doSetWithLockCheck(key, v) - return v + return this.doSetWithLockCheck(key, f()) } else { return v } @@ -107,8 +105,7 @@ func (this *StringInterfaceMap) GetOrSetFunc(key string, f func() interface{}) i // 与GetOrSetFunc不同的是,f是在写锁机制内执行 func (this *StringInterfaceMap) GetOrSetFuncLock(key string, f func() interface{}) interface{} { if v := this.Get(key); v == nil { - this.doSetWithLockCheck(key, f) - return v + return this.doSetWithLockCheck(key, f) } else { return v } diff --git a/g/database/gdb/gdb.go b/g/database/gdb/gdb.go index 35ab37072..a0f4c6872 100644 --- a/g/database/gdb/gdb.go +++ b/g/database/gdb/gdb.go @@ -51,7 +51,7 @@ type Link interface { // 连接属性设置 SetMaxIdleConns(n int) SetMaxOpenConns(n int) - SetConnMaxLifetime(d time.Duration) + SetConnMaxLifetime(n int) // 开启事务操作 Begin() (*Tx, error) @@ -74,9 +74,6 @@ type Link interface { Table(tables string) *Model From(tables string) *Model - // 关闭数据库操作对象 - Close() error - // 内部方法 insert(table string, data Map, option uint8) (sql.Result, error) batchInsert(table string, list List, batch int, option uint8) (sql.Result, error) @@ -88,14 +85,16 @@ type Link interface { // 数据库链接对象 type Db struct { - link Link // 底层数据库类型管理对象 - master *sql.DB // 实例化数据库链接(master) - slave *sql.DB // 实例化数据库链接(slave,可能会与master相同) - charl string // SQL安全符号(左) - charr string // SQL安全符号(右) - debug *gtype.Bool // (默认关闭)是否开启调试模式,当开启时会启用一些调试特性 - sqls *gring.Ring // (debug=true时有效)已执行的SQL列表 - cache *gcache.Cache // 查询缓存,需要注意的是,事务查询不支持缓存 + link Link // 底层数据库类型管理对象 + group string // 配置分组名称 + charl string // SQL安全符号(左) + charr string // SQL安全符号(右) + debug *gtype.Bool // (默认关闭)是否开启调试模式,当开启时会启用一些调试特性 + sqls *gring.Ring // (debug=true时有效)已执行的SQL列表 + cache *gcache.Cache // 查询缓存,需要注意的是,事务查询不支持缓存 + maxIdleConnCount *gtype.Int // 连接池最大限制的连接数 + maxOpenConnCount *gtype.Int // 连接池最大打开的连接数 + maxConnLifetime *gtype.Int // (单位秒)连接对象可重复使用的时间长度 } // 执行的SQL对象 @@ -138,9 +137,9 @@ var dbCaches = gmap.NewStringInterfaceMap() // 使用默认/指定分组配置进行连接,数据库集群配置项:default func New(groupName ...string) (*Db, error) { - name := config.d + group := config.d if len(groupName) > 0 { - name = groupName[0] + group = groupName[0] } config.RLock() defer config.RUnlock() @@ -148,32 +147,63 @@ func New(groupName ...string) (*Db, error) { if len(config.c) < 1 { return nil, errors.New("empty database configuration") } - if list, ok := config.c[name]; ok { - // 将master, slave集群列表拆分出来 - masterList := make(ConfigGroup, 0) - slaveList := make(ConfigGroup, 0) - for i := 0; i < len(list); i++ { - if list[i].Role == "slave" { - slaveList = append(slaveList, list[i]) - } else { - // 默认配置项的角色为master - masterList = append(masterList, list[i]) - } - } - if len(masterList) < 1 { - return nil, errors.New("at least one master node configuration's need to make sense") - } - masterNode := getConfigNodeByPriority(masterList) - var slaveNode *ConfigNode - if len(slaveList) > 0 { - slaveNode = getConfigNodeByPriority(slaveList) - } - return newDb(masterNode, slaveNode, name) + if _, ok := config.c[group]; ok { + if node, err := getConfigNodeByGroup(group, true); err == nil { + link, err := getLinkByType(node.Type) + if err != nil { + return nil, err + } + db := &Db { + link : link, + group : group, + charl : link.getQuoteCharLeft(), + charr : link.getQuoteCharRight(), + debug : gtype.NewBool(), + maxIdleConnCount : gtype.NewInt(), + maxOpenConnCount : gtype.NewInt(), + maxConnLifetime : gtype.NewInt(), + } + db.cache = dbCaches.GetOrSetFuncLock(group, func() interface{} { + return gcache.New() + }).(*gcache.Cache) + return db, nil + } else { + return nil, err + } } else { - return nil, errors.New(fmt.Sprintf("empty database configuration for item name '%s'", name)) + return nil, errors.New(fmt.Sprintf("empty database configuration for item name '%s'", group)) } } +// 获取指定数据库角色的一个配置项,内部根据权重计算负载均衡 +func getConfigNodeByGroup(group string, master bool) (*ConfigNode, error) { + if list, ok := config.c[group]; ok { + // 将master, slave集群列表拆分出来 + masterList := make(ConfigGroup, 0) + slaveList := make(ConfigGroup, 0) + for i := 0; i < len(list); i++ { + if list[i].Role == "slave" { + slaveList = append(slaveList, list[i]) + } else { + masterList = append(masterList, list[i]) + } + } + if len(masterList) < 1 { + return nil, errors.New("at least one master node configuration's need to make sense") + } + if len(slaveList) < 1 { + slaveList = masterList + } + if master { + return getConfigNodeByPriority(masterList), nil + } else { + return getConfigNodeByPriority(slaveList), nil + } + } else { + return nil, errors.New(fmt.Sprintf("empty database configuration for item name '%s'", group)) + } +} + // 按照负载均衡算法(优先级配置)从数据库集群中选择一个配置节点出来使用 // 算法说明举例, // 1、假如2个节点的priority都是1,那么随机大小范围为[0, 199]; @@ -207,60 +237,63 @@ func getConfigNodeByPriority(cg ConfigGroup) *ConfigNode { return nil } -// 创建数据库链接对象 -func newDb(masterNode *ConfigNode, slaveNode *ConfigNode, groupName string) (*Db, error) { - var link Link - switch masterNode.Type { - case "mysql": - link = linkMysql - case "pgsql": - link = linkPgsql - case "sqlite": - link = linkSqlite - default: - return nil, errors.New(fmt.Sprintf("unsupported db type '%s'", masterNode.Type)) +// 根据配置的数据库;类型获得Link接口对象 +func getLinkByType(dbType string) (Link, error) { + switch dbType { + case "mysql": + return linkMysql, nil + case "pgsql": + return linkPgsql, nil + case "sqlite": + return linkSqlite, nil + default: + return nil, errors.New(fmt.Sprintf("unsupported db type '%s'", dbType)) } - master, err := link.Open(masterNode) - if err != nil { - return nil, err - } - slave := master - if slaveNode != nil { - slave, err = link.Open(slaveNode) - if err != nil { - return nil, err - } - } - db := &Db{ - link: link, - master: master, - slave: slave, - charl: link.getQuoteCharLeft(), - charr: link.getQuoteCharRight(), - debug: gtype.NewBool(), - } - // 设置连接属性,master和slave必须是一致的,所以这里使用的是master的属性设置 - if masterNode.MaxIdleConnCount > 0 { - db.SetMaxIdleConns(masterNode.MaxIdleConnCount) - } - if masterNode.MaxOpenConnCount > 0 { - db.SetMaxOpenConns(masterNode.MaxOpenConnCount) - } - if masterNode.MaxConnLifetime > 0 { - db.SetConnMaxLifetime(time.Duration(masterNode.MaxConnLifetime) * time.Second) - } - if v := dbCaches.Get(groupName); v == nil { - dbCaches.LockFunc(func(m map[string]interface{}) { - if v, ok := m[groupName]; !ok { - db.cache = gcache.New() - m[groupName] = db.cache - } else { - db.cache = v.(*gcache.Cache) - } - }) - } else { - db.cache = v.(*gcache.Cache) - } - - return db, nil +} + +// 获得底层数据库链接对象 +func (db *Db) getSqlDb(master bool) (*sql.DB, error) { + node, err := getConfigNodeByGroup(db.group, master) + if err != nil { + return nil, err + } + link, err := getLinkByType(node.Type) + if err != nil { + return nil, err + } + sqlDb, err := link.Open(node) + if err != nil { + return nil, err + } + if node.MaxIdleConnCount > 0 { + sqlDb.SetMaxIdleConns(node.MaxIdleConnCount) + } + if n := db.maxIdleConnCount.Val(); n > 0 { + sqlDb.SetMaxIdleConns(n) + } + + if node.MaxOpenConnCount > 0 { + sqlDb.SetMaxOpenConns(node.MaxOpenConnCount) + } + if n := db.maxOpenConnCount.Val(); n > 0 { + sqlDb.SetMaxOpenConns(n) + } + + if node.MaxConnLifetime > 0 { + sqlDb.SetConnMaxLifetime(time.Duration(node.MaxConnLifetime) * time.Second) + } + if n := db.maxConnLifetime.Val(); n > 0 { + sqlDb.SetConnMaxLifetime(time.Duration(n) * time.Second) + } + return sqlDb, nil +} + +// 创建底层数据库master链接对象 +func (db *Db) Master() (*sql.DB, error) { + return db.getSqlDb(true) +} + +// 创建底层数据库slave链接对象 +func (db *Db) Slave() (*sql.DB, error) { + return db.getSqlDb(false) } diff --git a/g/database/gdb/gdb_base.go b/g/database/gdb/gdb_base.go index 41a40b140..b47b74184 100644 --- a/g/database/gdb/gdb_base.go +++ b/g/database/gdb/gdb_base.go @@ -17,7 +17,6 @@ import ( "gitee.com/johng/gf/g/util/gconv" "gitee.com/johng/gf/g/container/gring" "gitee.com/johng/gf/g/os/gtime" - "time" "gitee.com/johng/gf/g/os/glog" "gitee.com/johng/gf/g/container/gvar" ) @@ -81,35 +80,22 @@ func (db *Db) printSql(v *Sql) { } } -// 关闭链接 -func (db *Db) Close() error { - if db.master != nil { - if err := db.master.Close(); err == nil { - db.master = nil - } else { - return err - } - } - if db.slave != nil { - if err := db.slave.Close(); err == nil { - db.slave = nil - } else { - return err - } - } - return nil -} - // 数据库sql查询操作,主要执行查询 func (db *Db) Query(query string, args ...interface{}) (*sql.Rows, error) { - var err error - var rows *sql.Rows + var err error + var rows *sql.Rows + var slave *sql.DB + slave, err = db.Slave(); + if err != nil { + return nil,err + } + defer slave.Close() p := db.link.handleSqlBeforeExec(&query) if db.debug.Val() { militime1 := gtime.Millisecond() - rows, err = db.slave.Query(*p, args ...) + rows, err = slave.Query(*p, args ...) militime2 := gtime.Millisecond() - s := &Sql{ + s := &Sql{ Sql : *p, Args : args, Error : err, @@ -120,7 +106,7 @@ func (db *Db) Query(query string, args ...interface{}) (*sql.Rows, error) { db.sqls.Put(s) db.printSql(s) } else { - rows, err = db.slave.Query(*p, args ...) + rows, err = slave.Query(*p, args ...) } if err == nil { return rows, nil @@ -134,12 +120,18 @@ func (db *Db) Query(query string, args ...interface{}) (*sql.Rows, error) { func (db *Db) Exec(query string, args ...interface{}) (sql.Result, error) { var err error var result sql.Result + var master *sql.DB + master, err = db.Master(); + if err != nil { + return nil,err + } + defer master.Close() p := db.link.handleSqlBeforeExec(&query) if db.debug.Val() { militime1 := gtime.Millisecond() - result, err = db.master.Exec(*p, args ...) + result, err = master.Exec(*p, args ...) militime2 := gtime.Millisecond() - s := &Sql{ + s := &Sql{ Sql : *p, Args : args, Error : err, @@ -150,7 +142,7 @@ func (db *Db) Exec(query string, args ...interface{}) (sql.Result, error) { db.sqls.Put(s) db.printSql(s) } else { - result, err = db.master.Exec(*p, args ...) + result, err = master.Exec(*p, args ...) } return result, db.formatError(err, p, args...) } @@ -269,58 +261,49 @@ func (db *Db) Select(tables, fields string, condition interface{}, groupBy, orde // sql预处理,执行完成后调用返回值sql.Stmt.Exec完成sql操作 // 记得调用sql.Stmt.Close关闭操作对象 func (db *Db) Prepare(query string) (*sql.Stmt, error) { - return db.master.Prepare(query) + if master, err := db.Master(); err != nil { + return nil, err + } else { + defer master.Close() + return master.Prepare(query) + } } // ping一下,判断或保持数据库链接(master) func (db *Db) PingMaster() error { - err := db.master.Ping() - return err + if master, err := db.Master(); err != nil { + return err + } else { + defer master.Close() + return master.Ping() + } } // ping一下,判断或保持数据库链接(slave) func (db *Db) PingSlave() error { - err := db.slave.Ping() - return err -} - -// 设置数据库连接池中空闲链接的大小 -func (db *Db) SetMaxIdleConns(n int) { - db.master.SetMaxIdleConns(n) - // 比较的是指向的变量地址 - if db.master != db.slave { - db.slave.SetMaxIdleConns(n) - } -} - -// 设置数据库连接池最大打开的链接数量 -func (db *Db) SetMaxOpenConns(n int) { - db.master.SetMaxOpenConns(n) - // 比较的是指向的变量地址 - if db.master != db.slave { - db.slave.SetMaxOpenConns(n) - } -} - -// 设置数据库连接可重复利用的时间,超过该时间则被关闭废弃 -// 如果 d <= 0 表示该链接会一直重复利用 -func (db *Db) SetConnMaxLifetime(d time.Duration) { - db.master.SetConnMaxLifetime(d) - // 比较的是指向的变量地址 - if db.master != db.slave { - db.slave.SetConnMaxLifetime(d) + if slave, err := db.Slave(); err != nil { + return err + } else { + defer slave.Close() + return slave.Ping() } } // 事务操作,开启,会返回一个底层的事务操作对象链接如需要嵌套事务,那么可以使用该对象,否则请忽略 +// 只有在tx.Commit/tx.Rollback时,链接会自动Close func (db *Db) Begin() (*Tx, error) { - if tx, err := db.master.Begin(); err == nil { - return &Tx { - db : db, - tx : tx, - }, nil - } else { + if master, err := db.Master(); err != nil { return nil, err + } else { + if tx, err := master.Begin(); err == nil { + return &Tx { + db : db, + tx : tx, + master : master, + }, nil + } else { + return nil, err + } } } @@ -521,5 +504,4 @@ func (db *Db) formatCondition(condition interface{}) (where string) { where += gconv.String(condition) } return -} - +} \ No newline at end of file diff --git a/g/database/gdb/gdb_config.go b/g/database/gdb/gdb_config.go index a753ed2d2..ae27208ef 100644 --- a/g/database/gdb/gdb_config.go +++ b/g/database/gdb/gdb_config.go @@ -119,3 +119,19 @@ func SetDefaultGroup (groupName string) { config.d = groupName config.Unlock() } + +// 设置数据库连接池中空闲链接的大小 +func (db *Db) SetMaxIdleConns(n int) { + db.maxIdleConnCount.Set(n) +} + +// 设置数据库连接池最大打开的链接数量 +func (db *Db) SetMaxOpenConns(n int) { + db.maxOpenConnCount.Set(n) +} + +// 设置数据库连接可重复利用的时间,超过该时间则被关闭废弃 +// 如果 d <= 0 表示该链接会一直重复利用 +func (db *Db) SetConnMaxLifetime(n int) { + db.maxConnLifetime.Set(n) +} \ No newline at end of file diff --git a/g/database/gdb/gdb_transaction.go b/g/database/gdb/gdb_transaction.go index 6d8281899..daebba228 100644 --- a/g/database/gdb/gdb_transaction.go +++ b/g/database/gdb/gdb_transaction.go @@ -20,18 +20,23 @@ import ( // 数据库事务对象 type Tx struct { - db *Db - tx *sql.Tx + db *Db + tx *sql.Tx + master *sql.DB } // 事务操作,提交 func (tx *Tx) Commit() error { - return tx.tx.Commit() + err := tx.tx.Commit() + tx.master.Close() + return err } // 事务操作,回滚 func (tx *Tx) Rollback() error { - return tx.tx.Rollback() + err := tx.tx.Rollback() + tx.master.Close() + return err } // (事务)数据库sql查询操作,主要执行查询 diff --git a/g/database/gdb/test/gdb_bench_test.go b/g/database/gdb/test/gdb_bench_test.go deleted file mode 100644 index 558a6cac2..000000000 --- a/g/database/gdb/test/gdb_bench_test.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2018 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf. - -// go test *.go -bench=".*" - -package test - -import ( - "testing" - "gitee.com/johng/gf/g" -) - -func init() { - // 这里需要修改为本地配置文件的目录地址 - g.Config().SetPath("/home/john/Workspace/Go/GOPATH/src/gitee.com/johng/gf/geg/frame") -} - -func Benchmark_New(b *testing.B) { - for i := 0; i < b.N; i++ { - g.Database() - } -} - -func Benchmark_NewAndClose(b *testing.B) { - for i := 0; i < b.N; i++ { - g.Database().Close() - } -} diff --git a/g/frame/gins/gins.go b/g/frame/gins/gins.go index d53084185..6bac41f70 100644 --- a/g/frame/gins/gins.go +++ b/g/frame/gins/gins.go @@ -15,32 +15,54 @@ import ( "gitee.com/johng/gf/g/os/gview" "gitee.com/johng/gf/g/os/gfile" "gitee.com/johng/gf/g/container/gmap" + "gitee.com/johng/gf/g/util/gconv" + "gitee.com/johng/gf/g/database/gdb" + "gitee.com/johng/gf/g/os/gfsnotify" + "fmt" ) const ( gFRAME_CORE_COMPONENT_NAME_VIEW = "gf.core.component.view" gFRAME_CORE_COMPONENT_NAME_CONFIG = "gf.core.component.config" + gFRAME_CORE_COMPONENT_NAME_DATABASE = "gf.core.component.database" ) // 单例对象存储器 var instances = gmap.NewStringInterfaceMap() // 获取单例对象 -func Get(k string) interface{} { - return instances.Get(k) +func Get(key string) interface{} { + return instances.Get(key) } // 设置单例对象 -func Set(k string, v interface{}) { - instances.Set(k, v) +func Set(key string, value interface{}) { + instances.Set(key, key) +} + +// 当键名存在时返回其键值,否则写入指定的键值 +func GetOrSet(key string, value interface{}) interface{} { + return instances.GetOrSet(key, value) +} + +// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成 +func GetOrSetFunc(key string, f func() interface{}) interface{} { + return instances.GetOrSetFunc(key, f) +} + +// 与GetOrSetFunc不同的是,f是在写锁机制内执行 +func GetOrSetFuncLock(key string, f func() interface{}) interface{} { + return instances.GetOrSetFuncLock(key, f) +} + +// 当键名不存在时写入,并返回true;否则返回false。 +func SetIfNotExist(key string, value interface{}) bool { + return instances.SetIfNotExist(key, value) } // 核心对象:View func View() *gview.View { - result := Get(gFRAME_CORE_COMPONENT_NAME_VIEW) - if result != nil { - return result.(*gview.View) - } else { + return instances.GetOrSetFuncLock(gFRAME_CORE_COMPONENT_NAME_VIEW, func() interface{} { path := gcmd.Option.Get("gf.viewpath") if path == "" { path = genv.Get("gf.viewpath") @@ -55,19 +77,14 @@ func View() *gview.View { } // 框架内置函数 view.BindFunc("config", funcConfig) - Set(gFRAME_CORE_COMPONENT_NAME_VIEW, view) return view - } - return nil + }).(*gview.View) } // 核心对象:Config // 配置文件目录查找依次为:启动参数cfgpath、当前程序运行目录 func Config() *gcfg.Config { - result := Get(gFRAME_CORE_COMPONENT_NAME_CONFIG) - if result != nil { - return result.(*gcfg.Config) - } else { + return instances.GetOrSetFuncLock(gFRAME_CORE_COMPONENT_NAME_CONFIG, func() interface{} { path := gcmd.Option.Get("gf.cfgpath") if path == "" { path = genv.Get("gf.cfgpath") @@ -80,9 +97,78 @@ func Config() *gcfg.Config { if p := gfile.MainPkgPath(); gfile.Exists(p) { config.AddPath(p) } - // 单例对象缓存控制 - Set(gFRAME_CORE_COMPONENT_NAME_CONFIG, config) return config + }).(*gcfg.Config) +} + +// 数据库操作对象,使用了连接池 +func Database(name...string) *gdb.Db { + config := Config() + db := instances.GetOrSetFuncLock(gFRAME_CORE_COMPONENT_NAME_DATABASE, func() interface{} { + m := config.GetMap("database") + if m == nil { + panic(fmt.Sprintf(`incomplete configuration for database: "database" node not found in config file "%s"`, config.GetFilePath())) + } + 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 { + node.Host = gconv.String(value) + } + if value, ok := nodem["port"]; ok { + node.Port = gconv.String(value) + } + if value, ok := nodem["user"]; ok { + node.User = gconv.String(value) + } + if value, ok := nodem["pass"]; ok { + node.Pass = gconv.String(value) + } + if value, ok := nodem["name"]; ok { + node.Name = gconv.String(value) + } + if value, ok := nodem["type"]; ok { + node.Type = gconv.String(value) + } + if value, ok := nodem["role"]; ok { + node.Role = gconv.String(value) + } + if value, ok := nodem["charset"]; ok { + node.Charset = gconv.String(value) + } + if value, ok := nodem["priority"]; ok { + node.Priority = gconv.Int(value) + } + if value, ok := nodem["max-idle"]; ok { + node.MaxIdleConnCount = gconv.Int(value) + } + if value, ok := nodem["max-open"]; ok { + node.MaxOpenConnCount = gconv.Int(value) + } + if value, ok := nodem["max-lifetime"]; ok { + node.MaxConnLifetime = gconv.Int(value) + } + cg = append(cg, node) + } + } + gdb.AddConfigGroup(group, cg) + } + // 使用gfsnotify进行文件监控,当配置文件有任何变化时,清空数据库配置缓存 + gfsnotify.Add(config.GetFilePath(), func(event *gfsnotify.Event) { + instances.Remove(gFRAME_CORE_COMPONENT_NAME_DATABASE) + }) + if db, err := gdb.New(name...); err == nil { + return db + } else { + panic(err) + } + return nil + }) + if db != nil { + return db.(*gdb.Db) } return nil } diff --git a/g/g_object.go b/g/g_object.go index 923cb8bc1..b22e06ecc 100644 --- a/g/g_object.go +++ b/g/g_object.go @@ -9,8 +9,6 @@ package g import ( "gitee.com/johng/gf/g/util/gconv" "gitee.com/johng/gf/g/database/gdb" - "gitee.com/johng/gf/g/os/gcache" - "gitee.com/johng/gf/g/os/gfsnotify" "gitee.com/johng/gf/g/database/gredis" "gitee.com/johng/gf/g/frame/gins" "gitee.com/johng/gf/g/net/ghttp" @@ -19,13 +17,7 @@ import ( "gitee.com/johng/gf/g/os/gview" "gitee.com/johng/gf/g/os/gcfg" "gitee.com/johng/gf/g/util/gregex" - "gitee.com/johng/gf/g/os/glog" - "fmt" ) -const ( - gIS_DATABASE_CONFIG_CACHED = "gf.core.component.database.cached" -) - // HTTPServer单例对象 func Server(name...interface{}) *ghttp.Server { @@ -55,79 +47,12 @@ func Config() *gcfg.Config { // 数据库操作对象,使用了连接池 func Database(name...string) *gdb.Db { - config := gins.Config() - if config == nil { - glog.Error("Config component init failed") - return nil - } - // 数据库配置是否已经设置到gdb模块中 - gcache.GetOrSetFuncLock(gIS_DATABASE_CONFIG_CACHED, func() interface{} { - if m := config.GetMap("database"); m != nil { - c := gdb.Config{} - 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 { - node.Host = gconv.String(value) - } - if value, ok := nodem["port"]; ok { - node.Port = gconv.String(value) - } - if value, ok := nodem["user"]; ok { - node.User = gconv.String(value) - } - if value, ok := nodem["pass"]; ok { - node.Pass = gconv.String(value) - } - if value, ok := nodem["name"]; ok { - node.Name = gconv.String(value) - } - if value, ok := nodem["type"]; ok { - node.Type = gconv.String(value) - } - if value, ok := nodem["role"]; ok { - node.Role = gconv.String(value) - } - if value, ok := nodem["charset"]; ok { - node.Charset = gconv.String(value) - } - if value, ok := nodem["priority"]; ok { - node.Priority = gconv.Int(value) - } - if value, ok := nodem["max-idle"]; ok { - node.MaxIdleConnCount = gconv.Int(value) - } - if value, ok := nodem["max-open"]; ok { - node.MaxOpenConnCount = gconv.Int(value) - } - if value, ok := nodem["max-lifetime"]; ok { - node.MaxConnLifetime = gconv.Int(value) - } - cg = append(cg, node) - } - } - c[group] = cg - } - gdb.SetConfig(c) - // 使用gfsnotify进行文件监控,当配置文件有任何变化时,清空数据库配置缓存 - gfsnotify.Add(Config().GetFilePath(), func(event *gfsnotify.Event) { - gcache.Remove(gIS_DATABASE_CONFIG_CACHED) - }) - return struct{}{} - } else { - glog.Error(fmt.Sprintf(`incomplete configuration for database: "database" node not found in config file "%s"`, config.GetFilePath())) - } - return nil - }, 0) - if db, err := gdb.New(name...); err == nil { - return db - } else { - glog.Error(err) - return nil - } + return gins.Database(name...) +} + +// (别名)Database +func DB(name...string) *gdb.Db { + return gins.Database(name...) } // Redis操作对象,使用了连接池 @@ -148,7 +73,7 @@ func Redis(name...string) *gredis.Redis { return gredis.New(gredis.Config{ Host : array[1], Port : gconv.Int(array[2]), - Db : gconv.Int(array[3]), + Db : gconv.Int(array[3]), Pass : array[4], }) } diff --git a/g/util/gconv/gconv.go b/g/util/gconv/gconv.go index 9da474dec..dfc435b65 100644 --- a/g/util/gconv/gconv.go +++ b/g/util/gconv/gconv.go @@ -173,11 +173,31 @@ func Uint(i interface{}) uint { return 0 } switch value := i.(type) { - case int: return uint(value) - case int8: return uint(value) - case int16: return uint(value) - case int32: return uint(value) - case int64: return uint(value) + case int: + if value < 0 { + value = -value + } + return uint(value) + case int8: + if value < 0 { + value = -value + } + return uint(value) + case int16: + if value < 0 { + value = -value + } + return uint(value) + case int32: + if value < 0 { + value = -value + } + return uint(value) + case int64: + if value < 0 { + value = -value + } + return uint(value) case uint: return value case uint8: return uint(value) case uint16: return uint(value) diff --git a/g/util/grand/grand.go b/g/util/grand/grand.go index d884544df..28868e116 100644 --- a/g/util/grand/grand.go +++ b/g/util/grand/grand.go @@ -9,18 +9,24 @@ package grand import ( "time" - "math/rand" + "gitee.com/johng/gf/g/util/gconv" ) + var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") var digits = []rune("0123456789") +var numChan = make(chan uint, 10000) -// 修改全局随机数seed -func Seed (seed...int64) { - if len(seed) > 0 { - rand.Seed(seed[0]) - } else { - rand.Seed(time.Now().UnixNano()) - } +func init() { + go func() { + for { + numChan <- gconv.Uint(time.Now().UnixNano()) + } + }() +} + +// 自定义的 rand.Intn +func intn (max int) int { + return int(<- numChan)%max } // 获得一个 min, max 之间的随机数(min <= x <= max) @@ -28,21 +34,28 @@ func Rand (min, max int) int { if min >= max { return min } - n := rand.Intn(max + 1) - if n < min { - return Rand(min, max) + if min == 0 { + return intn(max + 1) } - return n + if min > 0 { + // 数值往左平移,再使用底层随机方法获得随机数,随后将结果数值往右平移 + return intn(max - (min - 0) + 1) + (min - 0) + } + if min < 0 { + // 数值往右平移,再使用底层随机方法获得随机数,随后将结果数值往左平移 + return intn(max + (0 - min) + 1) - (0 - min) + } + return 0 } // 获得指定长度的随机字符串(可能包含数字和字母) func RandStr(n int) string { b := make([]rune, n) for i := range b { - if rand.Intn(2) == 1 { - b[i] = digits[rand.Intn(10)] + if intn(2) == 1 { + b[i] = digits[intn(10)] } else { - b[i] = letters[rand.Intn(52)] + b[i] = letters[intn(52)] } } return string(b) @@ -52,7 +65,7 @@ func RandStr(n int) string { func RandDigits(n int) string { b := make([]rune, n) for i := range b { - b[i] = digits[rand.Intn(10)] + b[i] = digits[intn(10)] } return string(b) } @@ -61,7 +74,7 @@ func RandDigits(n int) string { func RandLetters(n int) string { b := make([]rune, n) for i := range b { - b[i] = letters[rand.Intn(52)] + b[i] = letters[intn(52)] } return string(b) } diff --git a/geg/database/orm/mysql/gdb.go b/geg/database/orm/mysql/gdb.go index 21b4f4ee1..4431c764b 100644 --- a/geg/database/orm/mysql/gdb.go +++ b/geg/database/orm/mysql/gdb.go @@ -17,7 +17,7 @@ func init () { Host : "127.0.0.1", Port : "3306", User : "root", - Pass : "123456", + Pass : "8692651", Name : "test", Type : "mysql", Role : "master", @@ -511,8 +511,8 @@ func main() { //linkopUpdate3() //linkopUpdate4() // - //transaction1() - //transaction2() + transaction1() + transaction2() // //keepPing() //likeQuery() diff --git a/geg/database/orm/mysql/gdb_config.go b/geg/database/orm/mysql/gdb_config.go new file mode 100644 index 000000000..cc2569661 --- /dev/null +++ b/geg/database/orm/mysql/gdb_config.go @@ -0,0 +1,16 @@ +package main + +import ( + "fmt" + "gitee.com/johng/gf/g" +) + +func main() { + g.Config().AddPath("/home/john/Workspace/Go/GOPATH/src/gitee.com/johng/gf/geg/frame") + if r, err := g.DB().Table("user").Where("uid=?", 1).One(); err == nil { + fmt.Println(r["uid"].Int()) + fmt.Println(r["name"].String()) + } else { + fmt.Println(err) + } +} \ No newline at end of file diff --git a/geg/frame/config.toml b/geg/frame/config.toml index 20fe5ffc7..20d9dfd0c 100644 --- a/geg/frame/config.toml +++ b/geg/frame/config.toml @@ -16,7 +16,7 @@ viewpath = "/home/www/templates/" host = "127.0.0.1" port = "3306" user = "root" - pass = "" + pass = "8692651" name = "test" type = "mysql" role = "master" diff --git a/geg/other/test.go b/geg/other/test.go index ce6445183..49d48b439 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,70 +1,15 @@ package main import ( - "gitee.com/johng/gf/g" - "gitee.com/johng/gf/g/net/ghttp" "fmt" - "gitee.com/johng/gf/g/os/gfile" + "gitee.com/johng/gf/g/util/grand" + "os" ) -func init() { - fmt.Println(gfile.MainPkgPath()) -} - func main() { - s := g.Server() - - s.BindHookHandlerByMap("/*", map[string]ghttp.HandlerFunc{ - ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) { - if r.Router.Uri == "/logout" { - r.Response.Write("HOOK_SERV") - r.Exit() - } - }, - }) - - s.BindStatusHandler(404, func(r *ghttp.Request) { - r.Response.Writeln("This is customized 404 page") - }) - s.BindStatusHandler(500, func(r *ghttp.Request) { - r.Response.Writeln("This is customized 500 page") - }) - s.BindStatusHandler(403, func(r *ghttp.Request) { - r.Response.Writeln("This is customized 403 page") - }) - - s.BindHandler("/", func(r *ghttp.Request) { - r.Response.Write("Hello World") - }) - - p := &P{} - s.BindHandler("/login", p.Login) - s.BindHandler("/logout", p.Logout) - - s.BindHandler("/api/getuser", p.GetUser) - s.BindHandler("/api/:name", p.AnyName) - - s.SetPort(6655) - s.Run() + fmt.Println(uint(-1)) + os.Exit(0) + for i := 0; i < 10; i++ { + fmt.Println(grand.Rand(100, 200)) + } } - -type P struct { -} - -func (p *P) Login(c *ghttp.Request) { - c.Cookie.SetCookie("username", "sdf", "", "/", 300) - c.Response.Write("this is login") -} - -func (p *P) Logout(c *ghttp.Request) { - c.Response.Write("this is logout") -} - -func (p *P) GetUser(c *ghttp.Request) { - c.Cookie.Remove("username", "", "/") - c.Response.Write("this is GetUser") -} - -func (p *P) AnyName(c *ghttp.Request) { - c.Response.Write("this is AnyName") -} \ No newline at end of file