This commit is contained in:
wenzi1
2018-07-04 11:20:03 +08:00
30 changed files with 277 additions and 241 deletions

6
TODO
View File

@ -8,6 +8,11 @@ orm增加更多数据库支持
增加可选择性的orm tag特性用以数据表记录与struct对象转换的键名属性映射
ghttp.Response增加输出内容后自动退出当前请求机制不需要用户手动return参考beego如何实现
Cookie&Session数据池化处理
ghttp.Client增加proxy特性
gtime增加对时区转换的封装并简化失去转换时对类似+80500时区的支持
改进gf-orm的where查询功能参考thinkphp 里的where查询语法
gproc进程间通信增加分组特性不同的进程见可以通过进程ID以及分组名称发送/获取消息;
DONE:
1. gconv完善针对不同类型的判断例如尽量减少sprintf("%v", xxx)来执行string类型的转换
@ -35,6 +40,7 @@ DONE:
23. 平滑重启机制改进,以便于开发阶段调试;
24. 对grpool进行优化改进包括属性原子操作封装采用gtype实现修正设计BUGhttps://github.com/johng-cn/gf/issues/6
25. gredis增加redis密码支持
26. 改进ghttp.Server平滑重启机制当新进程接管服务后再使用进程间通信方式通知父进程销毁

View File

@ -40,39 +40,40 @@ func NewIntArray(size int, cap ... int) *IntArray {
// 获取指定索引的数据项, 调用方注意判断数组边界
func (a *IntArray) Get(index int) int {
a.mu.RLock()
defer a.mu.RUnlock()
value := a.array[index]
a.mu.RUnlock()
return value
}
// 设置指定索引的数据项, 调用方注意判断数组边界
func (a *IntArray) Set(index int, value int) {
a.mu.Lock()
defer a.mu.RUnlock()
a.array[index] = value
a.mu.Unlock()
}
// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界
func (a *IntArray) Insert(index int, value int) {
a.mu.Lock()
defer a.mu.RUnlock()
rear := append([]int{}, a.array[index : ]...)
a.array = append(a.array[0 : index], value)
a.array = append(a.array, rear...)
a.mu.Unlock()
}
// 删除指定索引的数据项, 调用方注意判断数组边界
func (a *IntArray) Remove(index int) {
a.mu.Lock()
defer a.mu.RUnlock()
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
a.mu.Unlock()
}
// 追加数据项
func (a *IntArray) Append(value int) {
a.mu.Lock()
defer a.mu.RUnlock()
a.array = append(a.array, value)
a.mu.Unlock()
}
// 数组长度
@ -94,12 +95,12 @@ func (a *IntArray) Slice() []int {
// 清空数据数组
func (a *IntArray) Clear() {
a.mu.Lock()
defer a.mu.RUnlock()
if a.cap > 0 {
a.array = make([]int, a.size, a.cap)
} else {
a.array = make([]int, a.size)
}
a.mu.Unlock()
}
// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1
@ -140,13 +141,13 @@ func (a *IntArray) Search(value int) int {
// 使用自定义方法执行加锁修改操作
func (a *IntArray) LockFunc(f func(array []int)) {
a.mu.Lock()
defer a.mu.Unlock()
f(a.array)
a.mu.Unlock()
}
// 使用自定义方法执行加锁读取操作
func (a *IntArray) RLockFunc(f func(array []int)) {
a.mu.RLock()
defer a.mu.Unlock()
f(a.array)
a.mu.RUnlock()
}
}

View File

@ -30,32 +30,32 @@ func NewArray(size int, cap ... int) *Array {
// 获取指定索引的数据项, 调用方注意判断数组边界
func (a *Array) Get(index int) interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
value := a.array[index]
a.mu.RUnlock()
return value
}
// 设置指定索引的数据项, 调用方注意判断数组边界
func (a *Array) Set(index int, value interface{}) {
a.mu.Lock()
defer a.mu.Unlock()
a.array[index] = value
a.mu.Unlock()
}
// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界
func (a *Array) Insert(index int, value interface{}) {
a.mu.Lock()
defer a.mu.Unlock()
rear := append([]interface{}{}, a.array[index : ]...)
a.array = append(a.array[0 : index], value)
a.array = append(a.array, rear...)
a.mu.Unlock()
}
// 删除指定索引的数据项, 调用方注意判断数组边界
func (a *Array) Remove(index int) {
a.mu.Lock()
defer a.mu.Unlock()
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
a.mu.Unlock()
}
// 追加数据项

View File

@ -70,16 +70,16 @@ func (a *SortedIntArray) Add(value int) {
// 获取指定索引的数据项, 调用方注意判断数组边界
func (a *SortedIntArray) Get(index int) int {
a.mu.RLock()
defer a.mu.RUnlock()
value := a.array[index]
a.mu.RUnlock()
return value
}
// 删除指定索引的数据项, 调用方注意判断数组边界
func (a *SortedIntArray) Remove(index int) {
a.mu.Lock()
defer a.mu.Unlock()
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
a.mu.Unlock()
}
// 数组长度

View File

@ -55,16 +55,16 @@ func (a *SortedArray) Add(value interface{}) {
// 获取指定索引的数据项, 调用方注意判断数组边界
func (a *SortedArray) Get(index int) interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
value := a.array[index]
a.mu.RUnlock()
return value
}
// 删除指定索引的数据项, 调用方注意判断数组边界
func (a *SortedArray) Remove(index int) {
a.mu.Lock()
defer a.mu.Unlock()
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
a.mu.Unlock()
}
// 数组长度

View File

@ -65,16 +65,16 @@ func (a *SortedStringArray) Add(value string) {
// 获取指定索引的数据项, 调用方注意判断数组边界
func (a *SortedStringArray) Get(index int) string {
a.mu.RLock()
defer a.mu.RUnlock()
value := a.array[index]
a.mu.RUnlock()
return value
}
// 删除指定索引的数据项, 调用方注意判断数组边界
func (a *SortedStringArray) Remove(index int) {
a.mu.Lock()
defer a.mu.Unlock()
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
a.mu.Unlock()
}
// 数组长度

View File

@ -6,25 +6,24 @@
// go test *.go -bench=".*"
package gmap_test
package gmap
import (
"testing"
"gitee.com/johng/gf/g/container/gmap"
"strconv"
)
var ibm = gmap.NewIntBoolMap()
var iim = gmap.NewIntIntMap()
var iifm = gmap.NewIntInterfaceMap()
var ism = gmap.NewIntStringMap()
var ififm = gmap.NewInterfaceInterfaceMap()
var sbm = gmap.NewStringBoolMap()
var sim = gmap.NewStringIntMap()
var sifm = gmap.NewStringInterfaceMap()
var ssm = gmap.NewStringStringMap()
var uifm = gmap.NewUintInterfaceMap()
var ibm = NewIntBoolMap()
var iim = NewIntIntMap()
var iifm = NewIntInterfaceMap()
var ism = NewIntStringMap()
var ififm = NewInterfaceInterfaceMap()
var sbm = NewStringBoolMap()
var sim = NewStringIntMap()
var sifm = NewStringInterfaceMap()
var ssm = NewStringStringMap()
var uifm = NewUintInterfaceMap()
func BenchmarkIntBoolMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {

View File

@ -148,7 +148,7 @@ func (this *IntIntMap) Size() int {
// 哈希表是否为空
func (this *IntIntMap) IsEmpty() bool {
this.mu.RLock()
empty := (len(this.m) == 0)
empty := len(this.m) == 0
this.mu.RUnlock()
return empty
}

View File

@ -9,7 +9,6 @@ package gmap
import (
"sync"
"gitee.com/johng/gf/g/util/gconv"
)
type IntInterfaceMap struct {
@ -81,30 +80,6 @@ func (this *IntInterfaceMap) GetWithDefault(key int, value interface{}) interfac
return val
}
func (this *IntInterfaceMap) GetBool(key int) bool {
return gconv.Bool(this.Get(key))
}
func (this *IntInterfaceMap) GetInt(key int) int {
return gconv.Int(this.Get(key))
}
func (this *IntInterfaceMap) GetUint (key int) uint {
return gconv.Uint(this.Get(key))
}
func (this *IntInterfaceMap) GetFloat32 (key int) float32 {
return gconv.Float32(this.Get(key))
}
func (this *IntInterfaceMap) GetFloat64 (key int) float64 {
return gconv.Float64(this.Get(key))
}
func (this *IntInterfaceMap) GetString (key int) string {
return gconv.String(this.Get(key))
}
// 删除键值对
func (this *IntInterfaceMap) Remove(key int) {
this.mu.Lock()

View File

@ -9,7 +9,6 @@ package gmap
import (
"sync"
"gitee.com/johng/gf/g/util/gconv"
)
type InterfaceInterfaceMap struct {
@ -81,30 +80,6 @@ func (this *InterfaceInterfaceMap) GetWithDefault(key interface{}, value interfa
return val
}
func (this *InterfaceInterfaceMap) GetBool(key interface{}) bool {
return gconv.Bool(this.Get(key))
}
func (this *InterfaceInterfaceMap) GetInt(key interface{}) int {
return gconv.Int(this.Get(key))
}
func (this *InterfaceInterfaceMap) GetUint (key interface{}) uint {
return gconv.Uint(this.Get(key))
}
func (this *InterfaceInterfaceMap) GetFloat32 (key interface{}) float32 {
return gconv.Float32(this.Get(key))
}
func (this *InterfaceInterfaceMap) GetFloat64 (key interface{}) float64 {
return gconv.Float64(this.Get(key))
}
func (this *InterfaceInterfaceMap) GetString (key interface{}) string {
return gconv.String(this.Get(key))
}
// 删除键值对
func (this *InterfaceInterfaceMap) Remove(key interface{}) {
this.mu.Lock()

View File

@ -9,7 +9,6 @@ package gmap
import (
"sync"
"gitee.com/johng/gf/g/util/gconv"
)
type StringInterfaceMap struct {
@ -81,30 +80,6 @@ func (this *StringInterfaceMap) GetWithDefault(key string, value interface{}) in
return val
}
func (this *StringInterfaceMap) GetBool(key string) bool {
return gconv.Bool(this.Get(key))
}
func (this *StringInterfaceMap) GetInt(key string) int {
return gconv.Int(this.Get(key))
}
func (this *StringInterfaceMap) GetUint (key string) uint {
return gconv.Uint(this.Get(key))
}
func (this *StringInterfaceMap) GetFloat32 (key string) float32 {
return gconv.Float32(this.Get(key))
}
func (this *StringInterfaceMap) GetFloat64 (key string) float64 {
return gconv.Float64(this.Get(key))
}
func (this *StringInterfaceMap) GetString (key string) string {
return gconv.String(this.Get(key))
}
// 删除键值对
func (this *StringInterfaceMap) Remove(key string) {
this.mu.Lock()

View File

@ -9,7 +9,6 @@ package gmap
import (
"sync"
"gitee.com/johng/gf/g/util/gconv"
)
type UintInterfaceMap struct {
@ -80,30 +79,6 @@ func (this *UintInterfaceMap) GetWithDefault(key uint, value interface{}) interf
return val
}
func (this *UintInterfaceMap) GetBool(key uint) bool {
return gconv.Bool(this.Get(key))
}
func (this *UintInterfaceMap) GetInt(key uint) int {
return gconv.Int(this.Get(key))
}
func (this *UintInterfaceMap) GetUint (key uint) uint {
return gconv.Uint(this.Get(key))
}
func (this *UintInterfaceMap) GetFloat32 (key uint) float32 {
return gconv.Float32(this.Get(key))
}
func (this *UintInterfaceMap) GetFloat64 (key uint) float64 {
return gconv.Float64(this.Get(key))
}
func (this *UintInterfaceMap) GetString (key uint) string {
return gconv.String(this.Get(key))
}
// 删除键值对
func (this *UintInterfaceMap) Remove(key uint) {
this.mu.Lock()

View File

@ -234,7 +234,7 @@ func (db *Db) insert(table string, data Map, option uint8) (sql.Result, error) {
for k, v := range data {
fields = append(fields, db.charl + k + db.charr)
values = append(values, "?")
params = append(params, v)
params = append(params, gconv.String(v))
}
operation := db.getInsertOperationByOption(option)
updatestr := ""
@ -247,7 +247,10 @@ func (db *Db) insert(table string, data Map, option uint8) (sql.Result, error) {
}
return db.Exec(
fmt.Sprintf("%s INTO %s%s%s(%s) VALUES(%s) %s",
operation, db.charl, table, db.charr, strings.Join(fields, ","), strings.Join(values, ","), updatestr), params...
operation, db.charl, table, db.charr, strings.Join(fields, ","),
strings.Join(values, ","),
updatestr),
params...
)
}
@ -283,35 +286,43 @@ func (db *Db) batchInsert(table string, list List, batch int, option uint8) (sql
keys = append(keys, k)
values = append(values, "?")
}
var kstr = db.charl + strings.Join(keys, db.charl + "," + db.charr) + db.charr
keyStr := db.charl + strings.Join(keys, db.charl + "," + db.charr) + db.charr
valueHolderStr := "(" + strings.Join(values, ",") + ")"
// 操作判断
operation := db.getInsertOperationByOption(option)
updatestr := ""
if option == OPTION_SAVE {
var updates []string
for _, k := range keys {
updates = append(updates, fmt.Sprintf("%s=VALUES(%s)", db.charl, k, db.charr, k))
updates = append(updates, fmt.Sprintf("%s%s%s=VALUES(%s)", db.charl, k, db.charr, k))
}
updatestr = fmt.Sprintf(" ON DUPLICATE KEY UPDATE %s", strings.Join(updates, ","))
}
// 构造批量写入数据格式(注意map的遍历是无序的)
for i := 0; i < size; i++ {
for _, k := range keys {
params = append(params, list[i][k])
params = append(params, gconv.String(list[i][k]))
}
bvalues = append(bvalues, "(" + strings.Join(values, ",") + ")")
bvalues = append(bvalues, valueHolderStr)
if len(bvalues) == batch {
r, err := db.Exec(fmt.Sprintf("%s INTO %s%s%s(%s) VALUES%s %s", operation, db.charl, table, db.charr, kstr, strings.Join(bvalues, ","), updatestr), params...)
r, err := db.Exec(fmt.Sprintf("%s INTO %s%s%s(%s) VALUES%s %s",
operation, db.charl, table, db.charr, keyStr, strings.Join(bvalues, ","),
updatestr),
params...)
if err != nil {
return result, err
}
result = r
params = params[:0]
bvalues = bvalues[:0]
}
}
// 处理最后不构成指定批量的数据
if len(bvalues) > 0 {
r, err := db.Exec(fmt.Sprintf("%s INTO %s%s%s(%s) VALUES%s %s", operation, db.charl, table, db.charr, kstr, strings.Join(bvalues, ","), updatestr), params...)
r, err := db.Exec(fmt.Sprintf("%s INTO %s%s%s(%s) VALUES%s %s",
operation, db.charl, table, db.charr, keyStr, strings.Join(bvalues, ","),
updatestr),
params...)
if err != nil {
return result, err
}

View File

@ -169,7 +169,7 @@ func (tx *Tx) insert(table string, data Map, option uint8) (sql.Result, error) {
for k, v := range data {
keys = append(keys, tx.db.charl + k + tx.db.charr)
values = append(values, "?")
params = append(params, v)
params = append(params, gconv.String(v))
}
operation := tx.db.getInsertOperationByOption(option)
updatestr := ""
@ -182,7 +182,10 @@ func (tx *Tx) insert(table string, data Map, option uint8) (sql.Result, error) {
}
return tx.Exec(
fmt.Sprintf("%s INTO %s%s%s(%s) VALUES(%s) %s",
operation, tx.db.charl, table, tx.db.charr, strings.Join(keys, ","), strings.Join(values, ","), updatestr), params...
operation, tx.db.charl, table, tx.db.charr, strings.Join(keys, ","),
strings.Join(values, ","),
updatestr),
params...
)
}
@ -218,35 +221,43 @@ func (tx *Tx) batchInsert(table string, list List, batch int, option uint8) (sql
keys = append(keys, k)
values = append(values, "?")
}
var kstr = tx.db.charl + strings.Join(keys, tx.db.charl + "," + tx.db.charr) + tx.db.charr
keyStr := tx.db.charl + strings.Join(keys, tx.db.charl + "," + tx.db.charr) + tx.db.charr
valueHolderStr := "(" + strings.Join(values, ",") + ")"
// 操作判断
operation := tx.db.getInsertOperationByOption(option)
updatestr := ""
if option == OPTION_SAVE {
var updates []string
for _, k := range keys {
updates = append(updates, fmt.Sprintf("%s=VALUES(%s)", tx.db.charl, k, tx.db.charr, k))
updates = append(updates, fmt.Sprintf("%s%s%s=VALUES(%s)", tx.db.charl, k, tx.db.charr, k))
}
updatestr = fmt.Sprintf(" ON DUPLICATE KEY UPDATE %s", strings.Join(updates, ","))
}
// 构造批量写入数据格式(注意map的遍历是无序的)
for i := 0; i < size; i++ {
for _, k := range keys {
params = append(params, list[i][k])
params = append(params, gconv.String(list[i][k]))
}
bvalues = append(bvalues, "(" + strings.Join(values, ",") + ")")
bvalues = append(bvalues, valueHolderStr)
if len(bvalues) == batch {
r, err := tx.Exec(fmt.Sprintf("%s INTO %s%s%s(%s) VALUES%s %s", operation, tx.db.charl, table, tx.db.charr, kstr, strings.Join(bvalues, ","), updatestr), params...)
r, err := tx.Exec(fmt.Sprintf("%s INTO %s%s%s(%s) VALUES%s %s",
operation, tx.db.charl, table, tx.db.charr, keyStr, strings.Join(bvalues, ","),
updatestr),
params...)
if err != nil {
return result, err
}
result = r
params = params[:0]
bvalues = bvalues[:0]
}
}
// 处理最后不构成指定批量的数据
if len(bvalues) > 0 {
r, err := tx.Exec(fmt.Sprintf("%s INTO %s%s%s(%s) VALUES%s %s", operation, tx.db.charl, table, tx.db.charr, kstr, strings.Join(bvalues, ","), updatestr), params...)
r, err := tx.Exec(fmt.Sprintf("%s INTO %s%s%s(%s) VALUES%s %s",
operation, tx.db.charl, table, tx.db.charr, keyStr, strings.Join(bvalues, ","),
updatestr),
params...)
if err != nil {
return result, err
}

View File

@ -21,6 +21,7 @@ import (
"gitee.com/johng/gf/g/encoding/gyaml"
"gitee.com/johng/gf/g/encoding/gtoml"
"gitee.com/johng/gf/g/util/gstr"
"time"
)
const (
@ -32,7 +33,7 @@ type Json struct {
mu sync.RWMutex
p *interface{} // 注意这是一个指针
c byte // 层级分隔符,默认为"."
vc bool // 是否执行分隔符冲突检测(默认为true检测会比较影响检索效率)
vc bool // 层级检索是否执行分隔符冲突检测(默认为false检测会比较影响检索效率)
}
// 将变量转换为Json对象进行处理该变量至少应当是一个map或者array否者转换没有意义
@ -42,13 +43,13 @@ func New(value interface{}) *Json {
return &Json{
p : &value,
c : byte(gDEFAULT_SPLIT_CHAR),
vc : true ,
vc : false ,
}
case []interface{}:
return &Json{
p : &value,
c : byte(gDEFAULT_SPLIT_CHAR),
vc : true ,
vc : false ,
}
default:
// 这里效率会比较低
@ -57,7 +58,7 @@ func New(value interface{}) *Json {
return &Json{
p : &v,
c : byte(gDEFAULT_SPLIT_CHAR),
vc : true,
vc : false,
}
}
}
@ -101,9 +102,13 @@ func Load (path string) (*Json, error) {
}
// 支持的配置文件格式xml, json, yaml/yml, toml
func LoadContent (data []byte, t string) (*Json, error) {
func LoadContent (data []byte, dataType...string) (*Json, error) {
var err error
var result interface{}
t := "json"
if len(dataType) > 0 {
t = dataType[0]
}
switch t {
case "xml": fallthrough
case ".xml":
@ -140,7 +145,8 @@ func (j *Json) SetSplitChar(char byte) {
j.mu.Unlock()
}
// 设置自定义的层级分隔符号
// 设置是否执行层级冲突检查,当键名中存在层级符号时需要开启该特性,默认为关闭。
// 开启比较耗性能,也不建议允许键名中存在分隔符,最好在应用端避免这种情况。
func (j *Json) SetViolenceCheck(check bool) {
j.mu.Lock()
j.vc = check
@ -201,6 +207,19 @@ func (j *Json) GetString(pattern string) string {
return gconv.String(j.Get(pattern))
}
// 返回指定json中的strings(转换为[]string数组)
func (j *Json) GetStrings(pattern string) []string {
return gconv.Strings(j.Get(pattern))
}
func (j *Json) GetTime(pattern string, format ... string) time.Time {
return gconv.Time(j.Get(pattern), format...)
}
func (j *Json) GetTimeDuration(pattern string) time.Duration {
return gconv.TimeDuration(j.Get(pattern))
}
// 返回指定json中的bool(false:"", 0, false, off)
func (j *Json) GetBool(pattern string) bool {
return gconv.Bool(j.Get(pattern))
@ -456,18 +475,22 @@ func (j *Json) setPointerWithValue(pointer *interface{}, key string, value inter
return pointer
}
// 根据约定字符串方式访问json解析数据参数形如 "items.name.first", "list.0"
// 返回的结果类型的interface{},因此需要自己做类型转换
// 如果找不到对应节点的数据返回nil
func (j *Json) Get(pattern string) interface{} {
// 根据约定字符串方式访问json解析数据参数形如 "items.name.first", "list.0"; 当pattern为空时表示获取所有数据;
// 返回的结果类型的interface{},因此需要自己做类型转换;
// 如果找不到对应节点的数据返回nil;
func (j *Json) Get(pattern...string) interface{} {
j.mu.RLock()
defer j.mu.RUnlock()
queryPattern := ""
if len(pattern) > 0 {
queryPattern = pattern[0]
}
var result *interface{}
if j.vc {
result = j.getPointerByPattern(pattern)
result = j.getPointerByPattern(queryPattern)
} else {
result = j.getPointerByPatternWithoutSplitCharViolenceCheck(pattern)
result = j.getPointerByPatternWithoutSplitCharViolenceCheck(queryPattern)
}
if result != nil {
return *result

View File

@ -9,6 +9,7 @@ package gparser
import (
"gitee.com/johng/gf/g/encoding/gjson"
"time"
)
type Parser struct {
@ -32,9 +33,9 @@ func Load (path string) (*Parser, error) {
}
}
// 支持的配置文件格式xml, json, yaml/yml, toml
func LoadContent (data []byte, fileType string) (*Parser, error) {
if j, e := gjson.LoadContent(data, fileType); e == nil {
// 支持的数据内容格式json(默认), xml, yaml/yml, toml
func LoadContent (data []byte, dataType...string) (*Parser, error) {
if j, e := gjson.LoadContent(data, dataType...); e == nil {
return &Parser{j}, nil
} else {
return nil, e
@ -46,9 +47,10 @@ func (p *Parser) SetSplitChar(char byte) {
p.json.SetSplitChar(char)
}
// 设置自定义的层级分隔符号
// 设置是否执行层级冲突检查,当键名中存在层级符号时需要开启该特性,默认为关闭。
// 开启比较耗性能,也不建议允许键名中存在分隔符,最好在应用端避免这种情况。
func (p *Parser) SetViolenceCheck(check bool) {
p.SetViolenceCheck(check)
p.json.SetViolenceCheck(check)
}
// 将指定的json内容转换为指定结构返回查找失败或者转换失败目标对象转换为nil
@ -74,6 +76,18 @@ func (p *Parser) GetString(pattern string) string {
return p.json.GetString(pattern)
}
func (p *Parser) GetStrings(pattern string) []string {
return p.json.GetStrings(pattern)
}
func (p *Parser) GetTime(pattern string, format ... string) time.Time {
return p.json.GetTime(pattern, format...)
}
func (p *Parser) GetTimeDuration(pattern string) time.Duration {
return p.json.GetTimeDuration(pattern)
}
// 返回指定json中的bool(false:"", 0, false, off)
func (p *Parser) GetBool(pattern string) bool {
return p.json.GetBool(pattern)
@ -138,11 +152,11 @@ func (p *Parser) Remove(pattern string) error {
return p.json.Remove(pattern)
}
// 根据约定字符串方式访问json解析数据参数形如 "items.name.first", "list.0"
// 返回的结果类型的interface{},因此需要自己做类型转换
// 如果找不到对应节点的数据返回nil
func (p *Parser) Get(pattern string) interface{} {
return p.json.Get(pattern)
// 根据约定字符串方式访问json解析数据参数形如 "items.name.first", "list.0"; 当pattern为空时表示获取所有数据
// 返回的结果类型的interface{},因此需要自己做类型转换;
// 如果找不到对应节点的数据返回nil;
func (p *Parser) Get(pattern...string) interface{} {
return p.json.Get(pattern...)
}
// 转换为map[string]interface{}类型,如果转换失败返回nil

View File

@ -25,6 +25,8 @@ import (
"gitee.com/johng/gf/g/os/gfile"
"gitee.com/johng/gf/g/os/genv"
"github.com/gorilla/websocket"
"gitee.com/johng/gf/g/os/gtime"
"time"
)
const (
@ -127,6 +129,8 @@ func init() {
// 信号量管理操作监听
go handleProcessSignal()
// 异步监听进程间消息
go handleProcessMessage()
}
// 获取/创建一个默认配置的HTTP Server(默认监听端口是80)
@ -213,6 +217,13 @@ func (s *Server) Start() error {
s.startServer(nil)
}
// 如果是子进程,那么服务开启后通知父进程销毁
if gproc.IsChild() {
gtime.SetTimeout(2*time.Second, func() {
gproc.Send(gproc.PPid(), []byte("exit"))
})
}
// 开启异步关闭队列处理循环
s.startCloseQueueLoop()
return nil

View File

@ -22,14 +22,15 @@ import (
"gitee.com/johng/gf/g/util/gconv"
"time"
"runtime"
"bytes"
)
const (
gADMIN_ACTION_INTERVAL_LIMIT = 2000 // (毫秒)服务开启后允许执行管理操作的间隔限制
gADMIN_ACTION_RESTARTING = 1
gADMIN_ACTION_SHUTINGDOWN = 2
gADMIN_ACTION_RELOAD_ENVKEY = "gf.server.reload"
gADMIN_ACTION_RESTART_ENVKEY = "gf.server.restart"
gADMIN_ACTION_RELOAD_ENVKEY = "GF_SERVER_RELOAD"
gADMIN_ACTION_RESTART_ENVKEY = "GF_SERVER_RESTART"
)
// 用于服务管理的对象
@ -240,8 +241,6 @@ func restartWebServers(newExeFilePath...string) {
})
} else {
forkReloadProcess(newExeFilePath...)
go gracefulShutdownWebServers()
doneChan <- struct{}{}
}
}
@ -278,4 +277,17 @@ func forcedlyCloseWebServers() {
}
}
})
}
// 异步监听进程间消息
func handleProcessMessage() {
for {
if msg := gproc.Receive(); msg != nil {
if bytes.EqualFold(msg.Data, []byte("exit")) {
gracefulShutdownWebServers()
doneChan <- struct{}{}
return
}
}
}
}

View File

@ -12,7 +12,6 @@ package ghttp
import (
"sync"
"time"
"strings"
"net/http"
"gitee.com/johng/gf/g/os/gtime"
)
@ -43,7 +42,7 @@ func GetCookie(r *Request) *Cookie {
}
c := &Cookie {
data : make(map[string]CookieItem),
domain : strings.Split(r.Host, ":")[0],
domain : r.GetHost(),
server : r.Server,
request : r,
response : r.Response,
@ -53,15 +52,13 @@ func GetCookie(r *Request) *Cookie {
return c
}
// 从请求流中初始化
// 从请求流中初始化,无锁
func (c *Cookie) init() {
c.mu.Lock()
for _, v := range c.request.Cookies() {
c.data[v.Name] = CookieItem {
v.Value, v.Domain, v.Path, v.Expires.Second(), v.HttpOnly,
}
}
c.mu.Unlock()
}
// 获取所有的Cookie并构造成map返回
@ -137,6 +134,7 @@ func (c *Cookie) Close() {
func (c *Cookie) Output() {
c.mu.RLock()
for k, v := range c.data {
// 只有expire != 0的才是服务端在本地请求中设置的cookie
if v.expire == 0 {
continue
}

View File

@ -86,6 +86,10 @@ func (s *Session) GetUint (k string) uint {
return gconv.Uint(s.Get(k))
}
func (s *Session) GetUint8 (k string) uint8 {
return gconv.Uint8(s.Get(k))
}
func (s *Session) GetFloat32 (k string) float32 {
return gconv.Float32(s.Get(k))
}

View File

@ -26,6 +26,7 @@ type Config struct {
paths *gspath.SPath // 搜索目录路径
jsons *gmap.StringInterfaceMap // 配置文件对象
closed *gtype.Bool // 是否已经被close
vc *gtype.Bool // 层级检索是否执行分隔符冲突检测(默认为false检测会比较影响检索效率)
}
// 生成一个配置管理对象
@ -41,6 +42,7 @@ func New(path string, file...string) *Config {
paths : s,
jsons : gmap.NewStringInterfaceMap(),
closed : gtype.NewBool(),
vc : gtype.NewBool(),
}
}
@ -62,6 +64,13 @@ func (c *Config) SetPath(path string) error {
return nil
}
// 设置是否执行层级冲突检查,当键名中存在层级符号时需要开启该特性,默认为关闭。
// 开启比较耗性能,也不建议允许键名中存在分隔符,最好在应用端避免这种情况。
func (c *Config) SetViolenceCheck(check bool) {
c.vc.Set(check)
c.Reload()
}
// 添加配置管理器的配置文件搜索路径
func (c *Config) AddPath(path string) error {
if err := c.paths.Add(path); err != nil {
@ -91,6 +100,7 @@ func (c *Config) getJson(file...string) *gjson.Json {
return r.(*gjson.Json)
}
if j, err := gjson.Load(fpath); err == nil {
j.SetViolenceCheck(c.vc.Val())
c.addMonitor(fpath)
c.jsons.Set(fpath, j)
return j
@ -132,6 +142,13 @@ func (c *Config) GetString(pattern string, file...string) string {
return ""
}
func (c *Config) GetStrings(pattern string, file...string) []string {
if j := c.getJson(file...); j != nil {
return j.GetStrings(pattern)
}
return nil
}
// 返回指定json中的bool
func (c *Config) GetBool(pattern string, file...string) bool {
if j := c.getJson(file...); j != nil {

View File

@ -110,7 +110,7 @@ func (c *gCmdOption) GetBool(key string) bool {
return false
}
// 绑定命令行参数及对应的命令函数,注意参数是函数的内存地址
// 绑定命令行参数及对应的命令函数,注意命令函数参数是函数的内存地址
// 如果操作失败返回错误信息
func BindHandle (cmd string, f func()) error {
if _, ok := cmdFuncMap[cmd]; ok {

View File

@ -17,8 +17,9 @@ import (
)
const (
gPROC_ENV_KEY_PPID_KEY = "gproc.ppid"
gPROC_TEMP_DIR_ENV_KEY = "gproc.tempdir"
gPROC_ENV_KEY_PPID_KEY = "GPROC_PPID"
gPROC_ENV_KEY_COMM_KEY = "GPROC_COMM_ENABLED"
gPROC_TEMP_DIR_ENV_KEY = "GPROC_TEMP_DIR"
)
// 进程开始执行时间
@ -36,7 +37,7 @@ func PPid() int {
}
// gPROC_ENV_KEY_PPID_KEY为gproc包自定义的父进程
ppidValue := os.Getenv(gPROC_ENV_KEY_PPID_KEY)
if ppidValue != "" {
if ppidValue != "" && ppidValue != "0" {
return gconv.Int(ppidValue)
}
return PPidOS()
@ -49,7 +50,8 @@ func PPidOS() int {
// 判断当前进程是否为gproc创建的子进程
func IsChild() bool {
return os.Getenv(gPROC_ENV_KEY_PPID_KEY) != ""
ppidValue := os.Getenv(gPROC_ENV_KEY_PPID_KEY)
return ppidValue != "" && ppidValue != "0"
}
// 设置gproc父进程ID当ppid为0时表示该进程为gproc主进程否则为gproc子进程

View File

@ -37,7 +37,10 @@ type sendQueueItem struct {
// 进程管理/通信初始化操作
func init() {
go startTcpListening()
// 默认下为空("")
if os.Getenv(gPROC_ENV_KEY_COMM_KEY) != "0" {
go startTcpListening()
}
}
// 获取指定进程的通信文件地址
@ -47,7 +50,7 @@ func getCommFilePath(pid int) string {
// 获取进程间通信目录地址
func getCommDirPath() string {
tempDir := os.Getenv("gproc.tempdir")
tempDir := os.Getenv(gPROC_TEMP_DIR_ENV_KEY)
if tempDir == "" {
tempDir = gfile.TempDir()
}

View File

@ -9,10 +9,7 @@ package gproc
import (
"os"
"strings"
"os/exec"
"gitee.com/johng/gf/g/container/gmap"
"fmt"
)
// 进程管理器
@ -27,40 +24,6 @@ func NewManager() *Manager {
}
}
// 创建一个进程(不执行)
func NewProcess(path string, args []string, environment []string) *Process {
env := make([]string, len(environment) + 1)
for k, v := range environment {
env[k] = v
}
env[len(env) - 1] = fmt.Sprintf("%s=%s", gPROC_TEMP_DIR_ENV_KEY, os.TempDir())
p := &Process {
Manager : nil,
PPid : os.Getpid(),
Cmd : exec.Cmd {
Args : []string{path},
Path : path,
Stdin : os.Stdin,
Stdout : os.Stdout,
Stderr : os.Stderr,
Env : env,
ExtraFiles : make([]*os.File, 0),
},
}
// 当前工作目录
if d, err := os.Getwd(); err == nil {
p.Dir = d
}
if len(args) > 0 {
start := 0
if strings.EqualFold(path, args[0]) {
start = 1
}
p.Args = append(p.Args, args[start : ]...)
}
return p
}
// 创建一个进程(不执行)
func (m *Manager) NewProcess(path string, args []string, environment []string) *Process {
p := NewProcess(path, args, environment)

View File

@ -11,13 +11,49 @@ import (
"fmt"
"os/exec"
"errors"
"strings"
)
// 子进程
type Process struct {
exec.Cmd
Manager *Manager // 所属进程管理器
PPid int // 自定义关联的父进程ID
Manager *Manager // 所属进程管理器
PPid int // 自定义关联的父进程ID
DisableComm bool // 是否关闭TCP通信监听服务
}
// 创建一个进程(不执行)
func NewProcess(path string, args []string, environment []string) *Process {
env := make([]string, len(environment) + 1)
for k, v := range environment {
env[k] = v
}
env[len(env) - 1] = fmt.Sprintf("%s=%s", gPROC_TEMP_DIR_ENV_KEY, os.TempDir())
p := &Process {
Manager : nil,
PPid : os.Getpid(),
Cmd : exec.Cmd {
Args : []string{path},
Path : path,
Stdin : os.Stdin,
Stdout : os.Stdout,
Stderr : os.Stderr,
Env : env,
ExtraFiles : make([]*os.File, 0),
},
}
// 当前工作目录
if d, err := os.Getwd(); err == nil {
p.Dir = d
}
if len(args) > 0 {
start := 0
if strings.EqualFold(path, args[0]) {
start = 1
}
p.Args = append(p.Args, args[start : ]...)
}
return p
}
// 开始执行(非阻塞)
@ -25,9 +61,12 @@ func (p *Process) Start() (int, error) {
if p.Process != nil {
return p.Pid(), nil
}
if p.PPid > 0 {
p.Env = append(p.Env, fmt.Sprintf("%s=%d", gPROC_ENV_KEY_PPID_KEY, p.PPid))
commEnabled := 1
if p.DisableComm {
commEnabled = 0
}
p.Env = append(p.Env, fmt.Sprintf("%s=%d", gPROC_ENV_KEY_PPID_KEY, p.PPid))
p.Env = append(p.Env, fmt.Sprintf("%s=%d", gPROC_ENV_KEY_COMM_KEY, commEnabled))
if err := p.Cmd.Start(); err == nil {
if p.Manager != nil {
p.Manager.processes.Set(p.Process.Pid, p)

View File

@ -15,6 +15,10 @@ import (
"errors"
)
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]*)`
)
var (
// 用于time.Time转换使用防止多次Compile
timeRegex *regexp.Regexp
@ -22,7 +26,7 @@ var (
func init() {
// 使用正则判断会比直接使用ParseInLocation挨个轮训判断要快很多
timeRegex, _ = regexp.Compile(`(\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]*)`)
timeRegex, _ = regexp.Compile(TIME_REAGEX_PATTERN)
}
@ -88,6 +92,15 @@ func Format(format string, timestamps...int64) string {
}
// 字符串转换为时间对象需要给定字符串时间格式format格式形如2006-01-02 15:04:05
// 不传递自定义格式下默认支持的标准时间格式:
// "2017-12-14 04:51:34 +0805 LMT",
// "2006-01-02T15:04:05Z07:00",
// "2014-01-17T01:19:15+08:00",
// "2018-02-09T20:46:17.897Z",
// "2018-02-09 20:46:17.897",
// "2018-02-09T20:46:17Z",
// "2018-02-09 20:46:17",
// "2018-02-09",
func StrToTime(str string, format...string) (time.Time, error) {
// 优先使用用户输入日期格式进行转换
if len(format) > 0 {

View File

@ -12,13 +12,14 @@ import (
"fmt"
"time"
"strconv"
"gitee.com/johng/gf/g/encoding/gbinary"
"gitee.com/johng/gf/g/util/gstr"
"encoding/json"
"gitee.com/johng/gf/g/os/gtime"
"gitee.com/johng/gf/g/util/gstr"
"gitee.com/johng/gf/g/encoding/gbinary"
)
// 将变量i转换为字符串指定的类型t
func Convert(i interface{}, t string) interface{} {
func Convert(i interface{}, t string, params...interface{}) interface{} {
switch t {
case "int": return Int(i)
case "int8": return Int8(i)
@ -35,7 +36,12 @@ func Convert(i interface{}, t string) interface{} {
case "bool": return Bool(i)
case "string": return String(i)
case "[]byte": return Bytes(i)
case "time.Time": return Time(i)
case "time.Time":
if len(params) > 0 {
return Time(i, String(params[0]))
}
return Time(i)
case "time.Duration": return TimeDuration(i)
default:
return i
@ -106,13 +112,15 @@ func String(i interface{}) string {
case uint16: return strconv.FormatUint(uint64(value), 10)
case uint32: return strconv.FormatUint(uint64(value), 10)
case uint64: return strconv.FormatUint(uint64(value), 10)
case float32: return strconv.FormatFloat(float64(value), 'f', -1, 64)
case float32: return strconv.FormatFloat(float64(value), 'f', -1, 32)
case float64: return strconv.FormatFloat(value, 'f', -1, 64)
case bool: return strconv.FormatBool(value)
case string: return value
case []byte: return string(value)
default:
return fmt.Sprintf("%v", value)
// 默认使用json进行字符串转换
jsonContent, _ := json.Marshal(value)
return string(jsonContent)
}
}
@ -289,7 +297,7 @@ func Float32 (i interface{}) float32 {
if v, ok := i.(float32); ok {
return v
}
v, _ := strconv.ParseFloat(String(i), 32)
v, _ := strconv.ParseFloat(String(i), 64)
return float32(v)
}

View File

@ -9,7 +9,7 @@ import (
)
func main() {
clientMax := 10
clientMax := 1000
requestMax := 1000
failureNum := gtype.NewInt64()
successNum := gtype.NewInt64()
@ -21,7 +21,7 @@ func main() {
go func(clientId int) {
url := "http://127.0.0.1:8199/"
for i := 0; i < requestMax; i++ {
url = fmt.Sprintf("http://127.0.0.1:8199/%d_%d", clientId, i)
//url = fmt.Sprintf("http://127.0.0.1:8199/%d_%d", clientId, i)
if c, e := ghttp.Get(url); e == nil {
c.Close()
successNum.Add(1)

View File

@ -1,11 +1,12 @@
package main
import (
"time"
"fmt"
"gitee.com/johng/gf/g/encoding/gjson"
)
func main() {
t := time.Now().Zone()
fmt.Println(z)
}
content := `[0.00000000059, 1.598877777409]`
j, _ := gjson.LoadContent([]byte(content), "json")
fmt.Println(j.GetString("0"))
}