mirror of
https://gitee.com/johng/gf
synced 2026-06-06 16:21:40 +08:00
增加gring并发安全环;gdb包增加调试模式特性并增加了获得在调试模式下已执行的SQL列表结果
This commit is contained in:
3
TODO
3
TODO
@ -11,7 +11,6 @@ Cookie&Session数据池化处理;
|
||||
ghttp.Client增加proxy特性;
|
||||
gtime增加对时区转换的封装,并简化失去转换时对类似+80500时区的支持;
|
||||
改进gf-orm的where查询功能,参考thinkphp 里的where查询语法;
|
||||
ORM增加获取被执行的sql语句的方法;
|
||||
gpage分页增加对自定义后缀的支持,如:2.html, 2.php等等;
|
||||
|
||||
DONE:
|
||||
@ -42,6 +41,6 @@ DONE:
|
||||
25. gredis增加redis密码支持;
|
||||
26. 改进ghttp.Server平滑重启机制,当新进程接管服务后,再使用进程间通信方式通知父进程销毁;
|
||||
27. gproc进程间通信增加分组特性,不同的进程间可以通过进程ID以及分组名称发送/获取进程消息;
|
||||
|
||||
28. ORM增加获取被执行的sql语句的方法;
|
||||
|
||||
|
||||
|
||||
233
g/container/gring/gring.go
Normal file
233
g/container/gring/gring.go
Normal file
@ -0,0 +1,233 @@
|
||||
// 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.
|
||||
|
||||
// 并发安全的环.
|
||||
package gring
|
||||
|
||||
import (
|
||||
"container/ring"
|
||||
"sync"
|
||||
"gitee.com/johng/gf/g/container/gtype"
|
||||
)
|
||||
|
||||
type Ring struct {
|
||||
mu sync.RWMutex // 互斥锁
|
||||
ring *ring.Ring // 底层环形数据结构
|
||||
len *gtype.Int // 数据大小(已使用的大小)
|
||||
cap *gtype.Int // 总长度(分配的环大小,包括未使用的数据项数量)
|
||||
dirty *gtype.Bool // 标记环是否脏了(需要重新计算大小,当环大小发生改变时做标记)
|
||||
}
|
||||
|
||||
func New(cap int) *Ring {
|
||||
return &Ring {
|
||||
ring : ring.New(cap),
|
||||
len : gtype.NewInt(),
|
||||
cap : gtype.NewInt(cap),
|
||||
dirty : gtype.NewBool(),
|
||||
}
|
||||
}
|
||||
|
||||
// 返回当前环指向的数据项值
|
||||
func (r *Ring) Val() interface{} {
|
||||
r.mu.RLock()
|
||||
v := r.ring.Value
|
||||
r.mu.RUnlock()
|
||||
return v
|
||||
}
|
||||
|
||||
// 返回当前环已有数据项大小
|
||||
func (r *Ring) Len() int {
|
||||
r.checkAndUpdateLenAndCap()
|
||||
return r.len.Val()
|
||||
}
|
||||
|
||||
// 返回当前环总大小(包含未使用长度)
|
||||
func (r *Ring) Cap() int {
|
||||
r.checkAndUpdateLenAndCap()
|
||||
return r.cap.Val()
|
||||
}
|
||||
|
||||
// 检测并执行len和cap的更新(两者必须一起更新)
|
||||
func (r *Ring) checkAndUpdateLenAndCap() {
|
||||
if !r.dirty.Val() {
|
||||
return
|
||||
}
|
||||
totalLen := 0
|
||||
emptyLen := 0
|
||||
if r.ring != nil {
|
||||
r.mu.RLock()
|
||||
for p := r.ring.Next(); p != r.ring; p = p.Next() {
|
||||
if p.Value == nil {
|
||||
emptyLen++
|
||||
}
|
||||
totalLen++
|
||||
}
|
||||
r.mu.RUnlock()
|
||||
}
|
||||
r.cap.Set(totalLen)
|
||||
r.len.Set(totalLen - emptyLen)
|
||||
r.dirty.Set(false)
|
||||
}
|
||||
|
||||
// 当前位置设置数据项值
|
||||
func (r *Ring) Set(value interface{}) *Ring {
|
||||
r.mu.Lock()
|
||||
if r.ring.Value == nil {
|
||||
r.len.Add(1)
|
||||
}
|
||||
r.ring.Value = value
|
||||
r.mu.Unlock()
|
||||
return r
|
||||
}
|
||||
|
||||
// Set & Next
|
||||
func (r *Ring) Put(value interface{}) *Ring {
|
||||
r.mu.Lock()
|
||||
if r.ring.Value == nil {
|
||||
r.len.Add(1)
|
||||
}
|
||||
r.ring.Value = value
|
||||
r.ring = r.ring.Next()
|
||||
r.mu.Unlock()
|
||||
return r
|
||||
}
|
||||
|
||||
// 环往后(n > 0)或者往前(n < 0)移动n个元素
|
||||
func (r *Ring) Move(n int) *Ring {
|
||||
r.mu.Lock()
|
||||
r.ring = r.ring.Move(n)
|
||||
r.mu.Unlock()
|
||||
return r
|
||||
}
|
||||
|
||||
// 环往前移动1个元素
|
||||
func (r *Ring) Prev() *Ring {
|
||||
r.mu.Lock()
|
||||
r.ring = r.ring.Prev()
|
||||
r.mu.Unlock()
|
||||
return r
|
||||
}
|
||||
|
||||
// 环往后移动1个元素
|
||||
func (r *Ring) Next() *Ring {
|
||||
r.mu.Lock()
|
||||
r.ring = r.ring.Next()
|
||||
r.mu.Unlock()
|
||||
return r
|
||||
}
|
||||
|
||||
// 连接两个环,两个环的大小和位置都有可能会发生改变。
|
||||
// 1、链接将环r与环s连接,使得r.Next()成为s并返回r.Next()的原始值。r一定不能为空。
|
||||
// 2、如果r和s指向同一个环,则链接它们会从环中移除r和s之间的元素。
|
||||
// 删除的元素形成子环,结果是对该子环的引用(如果没有删除元素,结果仍然是r.Next()的原始值,而不是nil)。
|
||||
// 3、如果r和s指向不同的环,则链接它们会创建一个单独的环,并在r之后插入s的元素。 结果指向插入后s的最后一个元素后面的元素。
|
||||
func (r *Ring) Link(s *Ring) *Ring {
|
||||
r.mu.Lock()
|
||||
s.mu.Lock()
|
||||
r.ring.Link(s.ring)
|
||||
s.mu.Unlock()
|
||||
r.mu.Unlock()
|
||||
r.dirty.Set(true)
|
||||
s.dirty.Set(true)
|
||||
return r
|
||||
}
|
||||
|
||||
// 删除环中当前位置往后的n个数据项
|
||||
func (r *Ring) Unlink(n int) *Ring {
|
||||
r.mu.Lock()
|
||||
r.ring = r.ring.Unlink(n)
|
||||
r.dirty.Set(true)
|
||||
r.mu.Unlock()
|
||||
return r
|
||||
}
|
||||
|
||||
// 往后只读遍历,回调函数返回true表示继续遍历,否则退出遍历
|
||||
func (r *Ring) RLockIteratorNext(f func(value interface{}) bool) {
|
||||
r.mu.RLock()
|
||||
if !f(r.ring.Value) {
|
||||
return
|
||||
}
|
||||
for p := r.ring.Next(); p != r.ring; p = p.Next() {
|
||||
if !f(p.Value) {
|
||||
break
|
||||
}
|
||||
}
|
||||
r.mu.RUnlock()
|
||||
}
|
||||
|
||||
// 往前只读遍历,回调函数返回true表示继续遍历,否则退出遍历
|
||||
func (r *Ring) RLockIteratorPrev(f func(value interface{}) bool) {
|
||||
r.mu.RLock()
|
||||
if !f(r.ring.Value) {
|
||||
return
|
||||
}
|
||||
for p := r.ring.Prev(); p != r.ring; p = p.Prev() {
|
||||
if !f(p.Value) {
|
||||
break
|
||||
}
|
||||
}
|
||||
r.mu.RUnlock()
|
||||
}
|
||||
|
||||
// 往后写遍历,回调函数返回true表示继续遍历,否则退出遍历
|
||||
func (r *Ring) LockIteratorNext(f func(item *ring.Ring) bool) {
|
||||
r.mu.Lock()
|
||||
if !f(r.ring) {
|
||||
return
|
||||
}
|
||||
for p := r.ring.Next(); p != r.ring; p = p.Next() {
|
||||
if !f(p) {
|
||||
break
|
||||
}
|
||||
}
|
||||
r.mu.Unlock()
|
||||
}
|
||||
|
||||
// 往前写遍历,回调函数返回true表示继续遍历,否则退出遍历
|
||||
func (r *Ring) LockIteratorPrev(f func(item *ring.Ring) bool) {
|
||||
r.mu.Lock()
|
||||
if !f(r.ring) {
|
||||
return
|
||||
}
|
||||
for p := r.ring.Prev(); p != r.ring; p = p.Prev() {
|
||||
if !f(p) {
|
||||
break
|
||||
}
|
||||
}
|
||||
r.mu.Unlock()
|
||||
}
|
||||
|
||||
// 从当前位置,往后只读完整遍历,返回非空数据项值构成的数组
|
||||
func (r *Ring) SliceNext() []interface{} {
|
||||
s := make([]interface{}, 0)
|
||||
r.mu.RLock()
|
||||
if r.ring.Value != nil {
|
||||
s = append(s, r.ring.Value)
|
||||
}
|
||||
for p := r.ring.Next(); p != r.ring; p = p.Next() {
|
||||
if p.Value != nil {
|
||||
s = append(s, p.Value)
|
||||
}
|
||||
}
|
||||
r.mu.RUnlock()
|
||||
return s
|
||||
}
|
||||
|
||||
// 从当前位置,往前只读完整遍历,返回非空数据项值构成的数组
|
||||
func (r *Ring) SlicePrev() []interface{} {
|
||||
s := make([]interface{}, 0)
|
||||
r.mu.RLock()
|
||||
if r.ring.Value != nil {
|
||||
s = append(s, r.ring.Value)
|
||||
}
|
||||
for p := r.ring.Prev(); p != r.ring; p = p.Prev() {
|
||||
if p.Value != nil {
|
||||
s = append(s, p.Value)
|
||||
}
|
||||
}
|
||||
r.mu.RUnlock()
|
||||
return s
|
||||
}
|
||||
46
g/container/gring/gring_test.go
Normal file
46
g/container/gring/gring_test.go
Normal file
@ -0,0 +1,46 @@
|
||||
// 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 gring
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var length = 10000
|
||||
var r1 = New(length)
|
||||
|
||||
func BenchmarkRing_Put(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
r1.Put(i)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRing_Next(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
r1.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRing_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
r1.Set(i)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRing_Len(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
r1.Len()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRing_Cap(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
r1.Cap()
|
||||
}
|
||||
}
|
||||
@ -14,6 +14,8 @@ import (
|
||||
"gitee.com/johng/gf/g/util/grand"
|
||||
_ "github.com/lib/pq"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"gitee.com/johng/gf/g/container/gtype"
|
||||
"gitee.com/johng/gf/g/container/gring"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -81,11 +83,21 @@ type Link interface {
|
||||
|
||||
// 数据库链接对象
|
||||
type Db struct {
|
||||
link Link
|
||||
master *sql.DB
|
||||
slave *sql.DB
|
||||
charl string
|
||||
charr string
|
||||
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列表
|
||||
}
|
||||
|
||||
// 执行的SQL对象
|
||||
type Sql struct {
|
||||
Sql string // SQL语句(可能带有预处理占位符)
|
||||
Args []interface{} // 预处理参数值列表
|
||||
Error error // 执行结果(nil为成功)
|
||||
Func string // 执行方法名称
|
||||
}
|
||||
|
||||
// 返回数据表记录值
|
||||
@ -208,6 +220,7 @@ func newDb (masterNode *ConfigNode, slaveNode *ConfigNode) (*Db, error) {
|
||||
slave : slave,
|
||||
charl : link.getQuoteCharLeft(),
|
||||
charr : link.getQuoteCharRight(),
|
||||
debug : gtype.NewBool(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@ -15,8 +15,38 @@ import (
|
||||
"database/sql"
|
||||
"gitee.com/johng/gf/g/util/gstr"
|
||||
"gitee.com/johng/gf/g/util/gconv"
|
||||
"gitee.com/johng/gf/g/container/gring"
|
||||
)
|
||||
|
||||
const (
|
||||
gDEFAULT_DEBUG_SQL_LENGTH = 1000 // 默认调试模式下记录的SQL条数
|
||||
)
|
||||
|
||||
// 是否开启调试服务
|
||||
func (db *Db) SetDebug(debug bool) {
|
||||
db.debug.Set(debug)
|
||||
if debug && db.sqls == nil {
|
||||
db.sqls = gring.New(gDEFAULT_DEBUG_SQL_LENGTH)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取已经执行的SQL列表
|
||||
func (db *Db) GetQueriedSqls() []*Sql {
|
||||
if db.sqls == nil {
|
||||
return nil
|
||||
}
|
||||
sqls := make([]*Sql, 0)
|
||||
db.sqls.Prev()
|
||||
db.sqls.RLockIteratorPrev(func(value interface{}) bool {
|
||||
if value == nil {
|
||||
return false
|
||||
}
|
||||
sqls = append(sqls, value.(*Sql))
|
||||
return true
|
||||
})
|
||||
return sqls
|
||||
}
|
||||
|
||||
// 关闭链接
|
||||
func (db *Db) Close() error {
|
||||
if db.master != nil {
|
||||
@ -40,9 +70,18 @@ func (db *Db) Close() error {
|
||||
func (db *Db) Query(query string, args ...interface{}) (*sql.Rows, error) {
|
||||
p := db.link.handleSqlBeforeExec(&query)
|
||||
rows, err := db.slave.Query(*p, args ...)
|
||||
err = db.formatError(err, p, args...)
|
||||
if db.debug.Val() {
|
||||
db.sqls.Put(&Sql{
|
||||
Sql : *p,
|
||||
Args : args,
|
||||
Error : err,
|
||||
Func : "DB:Query",
|
||||
})
|
||||
}
|
||||
if err == nil {
|
||||
return rows, nil
|
||||
} else {
|
||||
err = db.formatError(err, p, args...)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
@ -51,8 +90,15 @@ func (db *Db) Query(query string, args ...interface{}) (*sql.Rows, error) {
|
||||
func (db *Db) Exec(query string, args ...interface{}) (sql.Result, error) {
|
||||
p := db.link.handleSqlBeforeExec(&query)
|
||||
r, err := db.master.Exec(*p, args ...)
|
||||
err = db.formatError(err, p, args...)
|
||||
return r, err
|
||||
if db.debug.Val() {
|
||||
db.sqls.Put(&Sql{
|
||||
Sql : *p,
|
||||
Args : args,
|
||||
Error : err,
|
||||
Func : "DB:Exec",
|
||||
})
|
||||
}
|
||||
return r, db.formatError(err, p, args...)
|
||||
}
|
||||
|
||||
// 格式化错误信息
|
||||
|
||||
@ -37,9 +37,18 @@ func (tx *Tx) Rollback() error {
|
||||
func (tx *Tx) Query(query string, args ...interface{}) (*sql.Rows, error) {
|
||||
p := tx.db.link.handleSqlBeforeExec(&query)
|
||||
rows, err := tx.tx.Query(*p, args ...)
|
||||
err = tx.db.formatError(err, p, args...)
|
||||
if tx.db.debug.Val() {
|
||||
tx.db.sqls.Put(&Sql{
|
||||
Sql : *p,
|
||||
Args : args,
|
||||
Error : err,
|
||||
Func : "TX:Query",
|
||||
})
|
||||
}
|
||||
if err == nil {
|
||||
return rows, nil
|
||||
} else {
|
||||
err = tx.db.formatError(err, p, args...)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
@ -48,8 +57,15 @@ func (tx *Tx) Query(query string, args ...interface{}) (*sql.Rows, error) {
|
||||
func (tx *Tx) Exec(query string, args ...interface{}) (sql.Result, error) {
|
||||
p := tx.db.link.handleSqlBeforeExec(&query)
|
||||
r, err := tx.tx.Exec(*p, args ...)
|
||||
err = tx.db.formatError(err, p, args...)
|
||||
return r, err
|
||||
if tx.db.debug.Val() {
|
||||
tx.db.sqls.Put(&Sql{
|
||||
Sql : *p,
|
||||
Args : args,
|
||||
Error : err,
|
||||
Func : "TX:Exec",
|
||||
})
|
||||
}
|
||||
return r, tx.db.formatError(err, p, args...)
|
||||
}
|
||||
|
||||
// 数据库查询,获取查询结果集,以列表结构返回
|
||||
|
||||
@ -59,9 +59,13 @@ func New(config Config) *Redis {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.Do("SELECT", config.Db)
|
||||
if _, err := c.Do("SELECT", config.Db); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(config.Pass) > 0 {
|
||||
c.Do("AUTH", config.Pass)
|
||||
if _, err := c.Do("AUTH", config.Pass); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return c, nil
|
||||
},
|
||||
|
||||
@ -16,7 +16,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
TIME_REAGEX_PATTERN = `(\d{4}-\d{2}-\d{2})[\sT]{0,1}(\d{2}:\d{2}:\d{2}){0,1}\.{0,1}(\d{0,9})([\sZ]{0,1})([\+-]{0,1})([:\d]*)`
|
||||
TIME_REAGEX_PATTERN = `(\d{4}[-/]\d{2}[-/]\d{2})[\sT]{0,1}(\d{2}:\d{2}:\d{2}){0,1}\.{0,1}(\d{0,9})([\sZ]{0,1})([\+-]{0,1})([:\d]*)`
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
27
geg/container/gring/gring.go
Normal file
27
geg/container/gring/gring.go
Normal file
@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/container/gring"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
r1 := gring.New(10)
|
||||
for i := 0; i < 5; i ++ {
|
||||
r1.Set(i).Next()
|
||||
}
|
||||
fmt.Println("Len:", r1.Len())
|
||||
fmt.Println("Cap:", r1.Cap())
|
||||
fmt.Println(r1.SlicePrev())
|
||||
fmt.Println(r1.SliceNext())
|
||||
|
||||
r2 := gring.New(10)
|
||||
for i := 0; i < 10; i ++ {
|
||||
r2.Set(i).Next()
|
||||
}
|
||||
fmt.Println("Len:", r2.Len())
|
||||
fmt.Println("Cap:", r2.Cap())
|
||||
fmt.Println(r2.SlicePrev())
|
||||
fmt.Println(r2.SliceNext())
|
||||
|
||||
}
|
||||
@ -475,7 +475,19 @@ func mapToStruct() {
|
||||
}
|
||||
}
|
||||
|
||||
// getQueriedSqls
|
||||
func getQueriedSqls() {
|
||||
for k, v := range db.GetQueriedSqls() {
|
||||
fmt.Println(k, ":")
|
||||
fmt.Println("Sql :", v.Sql)
|
||||
fmt.Println("Args :", v.Args)
|
||||
fmt.Println("Error:", v.Error)
|
||||
fmt.Println("Func :", v.Func)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
db.SetDebug(true)
|
||||
r, err := db.Table("test").Where("id=1").One()
|
||||
fmt.Println(r["datetime"])
|
||||
fmt.Println(r["datetime"].Time().Date())
|
||||
@ -503,4 +515,7 @@ func main() {
|
||||
//transaction2()
|
||||
//
|
||||
//keepPing()
|
||||
//likeQuery()
|
||||
//mapToStruct()
|
||||
getQueriedSqls()
|
||||
}
|
||||
@ -1,13 +1,10 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/util/gregx"
|
||||
"fmt"
|
||||
"gitee.com/johng/gf/g/encoding/gbinary"
|
||||
)
|
||||
|
||||
func main() {
|
||||
i := 65533
|
||||
b := gbinary.EncodeByLength(3, i)
|
||||
fmt.Println(b)
|
||||
fmt.Println(gbinary.DecodeToInt32(b))
|
||||
fmt.Println(gregx.MatchString(`[-/]`, "-"))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user