mirror of
https://gitee.com/johng/gf
synced 2026-06-07 02:12:11 +08:00
Merge branch 'qiangg_comment' into develop
This commit is contained in:
60
RELEASE.MD
60
RELEASE.MD
@ -1,3 +1,63 @@
|
||||
# `v1.7.0`
|
||||
## 新功能/改进
|
||||
1. 重构改进`glog`模块:
|
||||
- 去掉日志模块所有的锁机制,改为无锁设计,执行性能更加高效
|
||||
- 增加日志内容的异步输出特性:https://goframe.org/os/glog/async
|
||||
- 增加日志输出内容的`Json`格式支持:https://goframe.org/os/glog/json
|
||||
- 增加`Flags`额外特性支持,包括文件行号打印、自定义时间格式、异步输出等特性控制:https://goframe.org/os/glog/flags
|
||||
- 增加`Writer`接口支持,便于开发者进行自定义的日志功能扩展,或者与第三方服务/模块对接集成:https://goframe.org/os/glog/writer
|
||||
- 修改`SetStdPrint`方法名为`SetStdoutPrint`
|
||||
- 修改链式方法`StdPrint`方法名为`Stdout`
|
||||
- 标记淘汰`*fln`日志输出方法,`*f`方法支持自动的换行输出
|
||||
- 新增更多的链式方法支持:https://goframe.org/os/glog/chain
|
||||
1. 重构改进`gmap`模块:
|
||||
- 增加更多数据格式支持:`HashMap`/`ListMap`/`TreeMap`
|
||||
- 简化类型名称,如`gmap.StringInterfaceMap`简化为`gmap.StrAnyMap`
|
||||
- 改进`Map/Keys/Values`方法以提高性能
|
||||
- 修改`BatchSet`/`BatchRemove`方法名为`Sets`/`Removes`
|
||||
- 新增更多功能方法支持:https://goframe.org/container/gmap/index
|
||||
1. 改进`gtime`时间模块:
|
||||
- 增加并完善更多的类`PHP`时间格式支持
|
||||
- 新增更多功能方法,如`FormatTo`/`LayoutTo`等等
|
||||
- 详见开发文档:https://goframe.org/os/gtime/index
|
||||
1. 改进`gdb`数据库模块:
|
||||
- 增加对继承结构体的数据转换支持:https://goframe.org/database/gdb/senior
|
||||
- 新增`GetLastSql`方法,用以在调试模式下获取最近一条执行的SQL语句
|
||||
- 其他的细节处理改进
|
||||
1. 改进`gtcp`通信模块:
|
||||
- 完善处理细节,提高通信性能;
|
||||
- 增加`TLS`服务端/客户端通信支持:https://goframe.org/net/gtcp/tls
|
||||
- 增加简单协议支持,便于开发者封包/解包,并解决粘包/半包问题:https://goframe.org/net/gtcp/conn/pkg
|
||||
- TCP服务端增加`Close`方法
|
||||
- 更多细节查看开发文档:https://goframe.org/net/gtcp/index
|
||||
1. 改进`gconv`类型转换模块
|
||||
- 修改`gconv.TimeDuration`转换方法名称为`gconv.Duration`
|
||||
- 新增`gconv.StructDeep`及`gconv.MapDeep`方法,支持递归转换
|
||||
- 详见开发文档:https://goframe.org/util/gconv/struct
|
||||
1. 改进`ghttp`模块:
|
||||
- 日志输出增加`http/https`字段:https://goframe.org/net/ghttp/logs
|
||||
- 新增`ghttp.Server.SetKeepAlive`设置方法,用以开启/关闭`KeepAlive`特性
|
||||
- 增加`ghttp.Request.GetUrl`方法,用以获取当前完整的URL请求地址
|
||||
- `ghttp.Client`客户端支持开发者自定义`Transport`属性,`ghttp.Client.Post`方法支持`浏览器模式`:https://goframe.org/net/ghttp/client
|
||||
1. 新增`gtree`树形数据结构容器支持:https://goframe.org/container/gtree/index
|
||||
1. 改进`gudp`通信模块,具体请参考开发文档:https://goframe.org/net/gudp/index
|
||||
1. 改进`gcfg`配置管理模块,所有`Get*`方法增加默认值支持:https://goframe.org/os/gcfg/index
|
||||
1. `gredis`模块新增`DoVar`/`ReceiveVar`方法以便于开发者对执行结果进行灵活的数据格式转换:https://goframe.org/database/gredis/index
|
||||
1. `gcache`模块`BatchSet`/`BatchRemove`方法名修改为`Sets`/`Removes`
|
||||
1. 改进`gjson`/`gparser`模块,增加更多方法:https://goframe.org/encoding/gjson/index
|
||||
1. 改进`gfile.MainPkgPath`方法,以支持不同平台的开发环境;
|
||||
1. 改进`grpool`协程池模块,提高执行性能:https://goframe.org/os/grpool/index
|
||||
1. 改进`TryCatch`方法,当开发者不传递`Catch`参数时,默认抑制并忽略错误的处理
|
||||
1. 改进`gmlock`模块,增加`TryLockFunc`/`TryRLockFunc`方法,并且为`gmlock.Mutex`高级互斥锁对象增加`TryLockFunc`/`TryRLockFunc`方法
|
||||
1. 去除`gvar.VarRead`接口类型支持
|
||||
|
||||
## Bug Fix
|
||||
1. 解决`gdb`模块与其他第三方`ORM`模块同时使用的冲突;
|
||||
1. 修复`gcron.AddOnce`方法的细节逻辑问题;
|
||||
1. 修复内部`empty`模块的`IsEmpty`方法对结构体属性的空校验错误;
|
||||
1. 修复`gview`模板引擎的并发安全问题;
|
||||
1. 修复`ghttp.Server`的SESSION初始化过期时间问题;
|
||||
|
||||
# `v1.6.0` (2019-04-09)
|
||||
|
||||
## 新功能/改进
|
||||
|
||||
2
TODO.MD
2
TODO.MD
@ -47,7 +47,7 @@
|
||||
1. gtimer增加DelayAdd*方法返回Entry对象,以便DelayAdd*的定时任务也能进行状态控制;gcron同理需要改进;
|
||||
1. 改进gdb对pgsql/mssql/oracle的支持,使用方法覆盖的方式改进操作,而不是完全依靠正则替换的方式;
|
||||
1. gdb的Cache缓存功能增加可自定义缓存接口,以便支持外部缓存功能,缓存接口可以通过io.ReadWriter接口实现;
|
||||
|
||||
1. grpool增加支持阻塞添加任务接口;
|
||||
|
||||
|
||||
|
||||
|
||||
@ -4,7 +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 gchan provides graceful channel for safe operations.
|
||||
// Package gchan provides graceful channel for no panic operations.
|
||||
//
|
||||
// It's safe to call Chan.Push/Close functions repeatedly.
|
||||
package gchan
|
||||
@ -14,12 +14,13 @@ import (
|
||||
"github.com/gogf/gf/g/container/gtype"
|
||||
)
|
||||
|
||||
// Graceful channel.
|
||||
type Chan struct {
|
||||
channel chan interface{}
|
||||
closed *gtype.Bool
|
||||
}
|
||||
|
||||
// New creates a graceful channel with given limit.
|
||||
// New creates a graceful channel with given <limit>.
|
||||
func New(limit int) *Chan {
|
||||
return &Chan {
|
||||
channel : make(chan interface{}, limit),
|
||||
@ -31,7 +32,7 @@ func New(limit int) *Chan {
|
||||
// It is safe to be called repeatedly.
|
||||
func (c *Chan) Push(value interface{}) error {
|
||||
if c.closed.Val() {
|
||||
return errors.New("closed")
|
||||
return errors.New("channel is closed")
|
||||
}
|
||||
c.channel <- value
|
||||
return nil
|
||||
@ -39,6 +40,7 @@ func (c *Chan) Push(value interface{}) error {
|
||||
|
||||
// Pop pops value from channel.
|
||||
// If there's no value in channel, it would block to wait.
|
||||
// If the channel is closed, it will return a nil value immediately.
|
||||
func (c *Chan) Pop() interface{} {
|
||||
return <- c.channel
|
||||
}
|
||||
|
||||
@ -37,8 +37,8 @@ const (
|
||||
)
|
||||
|
||||
// New returns an empty queue object.
|
||||
// Optional parameter <limit> is used to limit the size of the queue, which is unlimited by default.
|
||||
// When <limit> is given, the queue will be static and high performance which is comparable with stdlib chan.
|
||||
// Optional parameter <limit> is used to limit the size of the queue, which is unlimited in default.
|
||||
// When <limit> is given, the queue will be static and high performance which is comparable with stdlib channel.
|
||||
func New(limit...int) *Queue {
|
||||
q := &Queue {
|
||||
closed : make(chan struct{}, 0),
|
||||
@ -103,7 +103,7 @@ func (q *Queue) Pop() interface{} {
|
||||
|
||||
// Close closes the queue.
|
||||
// Notice: It would notify all goroutines return immediately,
|
||||
// which are being blocked reading by Pop method.
|
||||
// which are being blocked reading using Pop method.
|
||||
func (q *Queue) Close() {
|
||||
close(q.C)
|
||||
close(q.events)
|
||||
|
||||
@ -9,152 +9,172 @@
|
||||
package gset_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/garray"
|
||||
"github.com/gogf/gf/g/container/gset"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
"github.com/gogf/gf/g/container/garray"
|
||||
"github.com/gogf/gf/g/container/gset"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSet_New(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s := gset.New()
|
||||
s.Add(1).Add(1).Add(2)
|
||||
s.Add([]interface{}{3, 4}...)
|
||||
gtest.Assert(s.Size(), 4)
|
||||
gtest.AssertIN(1, s.Slice())
|
||||
gtest.AssertIN(2, s.Slice())
|
||||
gtest.AssertIN(3, s.Slice())
|
||||
gtest.AssertIN(4, s.Slice())
|
||||
gtest.AssertNI(0, s.Slice())
|
||||
gtest.Assert(s.Contains(4), true)
|
||||
gtest.Assert(s.Contains(5), false)
|
||||
s.Remove(1)
|
||||
gtest.Assert(s.Size(), 3)
|
||||
s.Clear()
|
||||
gtest.Assert(s.Size(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSet_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s := gset.NewSet()
|
||||
s.Add(1).Add(1).Add(2)
|
||||
s.Add([]interface{}{3,4}...)
|
||||
gtest.Assert(s.Size(), 4)
|
||||
gtest.AssertIN(1, s.Slice())
|
||||
gtest.AssertIN(2, s.Slice())
|
||||
gtest.AssertIN(3, s.Slice())
|
||||
gtest.AssertIN(4, s.Slice())
|
||||
gtest.AssertNI(0, s.Slice())
|
||||
gtest.Assert(s.Contains(4), true)
|
||||
gtest.Assert(s.Contains(5), false)
|
||||
s.Remove(1)
|
||||
gtest.Assert(s.Size(), 3)
|
||||
s.Clear()
|
||||
gtest.Assert(s.Size(), 0)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
s := gset.NewSet()
|
||||
s.Add(1).Add(1).Add(2)
|
||||
s.Add([]interface{}{3, 4}...)
|
||||
gtest.Assert(s.Size(), 4)
|
||||
gtest.AssertIN(1, s.Slice())
|
||||
gtest.AssertIN(2, s.Slice())
|
||||
gtest.AssertIN(3, s.Slice())
|
||||
gtest.AssertIN(4, s.Slice())
|
||||
gtest.AssertNI(0, s.Slice())
|
||||
gtest.Assert(s.Contains(4), true)
|
||||
gtest.Assert(s.Contains(5), false)
|
||||
s.Remove(1)
|
||||
gtest.Assert(s.Size(), 3)
|
||||
s.Clear()
|
||||
gtest.Assert(s.Size(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSet_Iterator(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s := gset.NewSet()
|
||||
s.Add(1).Add(2).Add(3)
|
||||
gtest.Assert(s.Size(), 3)
|
||||
gtest.Case(t, func() {
|
||||
s := gset.NewSet()
|
||||
s.Add(1).Add(2).Add(3)
|
||||
gtest.Assert(s.Size(), 3)
|
||||
|
||||
a1 := garray.New()
|
||||
a2 := garray.New()
|
||||
s.Iterator(func(v interface{}) bool {
|
||||
a1.Append(1)
|
||||
return false
|
||||
})
|
||||
s.Iterator(func(v interface{}) bool {
|
||||
a2.Append(1)
|
||||
return true
|
||||
})
|
||||
gtest.Assert(a1.Len(), 1)
|
||||
gtest.Assert(a2.Len(), 3)
|
||||
})
|
||||
a1 := garray.New()
|
||||
a2 := garray.New()
|
||||
s.Iterator(func(v interface{}) bool {
|
||||
a1.Append(1)
|
||||
return false
|
||||
})
|
||||
s.Iterator(func(v interface{}) bool {
|
||||
a2.Append(1)
|
||||
return true
|
||||
})
|
||||
gtest.Assert(a1.Len(), 1)
|
||||
gtest.Assert(a2.Len(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSet_LockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s := gset.NewSet()
|
||||
s.Add(1).Add(2).Add(3)
|
||||
gtest.Assert(s.Size(), 3)
|
||||
s.LockFunc(func(m map[interface{}]struct{}) {
|
||||
delete(m, 1)
|
||||
})
|
||||
gtest.Assert(s.Size(), 2)
|
||||
s.RLockFunc(func(m map[interface{}]struct{}) {
|
||||
gtest.Assert(m, map[interface{}]struct{}{
|
||||
3 : struct{}{},
|
||||
2 : struct{}{},
|
||||
})
|
||||
})
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
s := gset.NewSet()
|
||||
s.Add(1).Add(2).Add(3)
|
||||
gtest.Assert(s.Size(), 3)
|
||||
s.LockFunc(func(m map[interface{}]struct{}) {
|
||||
delete(m, 1)
|
||||
})
|
||||
gtest.Assert(s.Size(), 2)
|
||||
s.RLockFunc(func(m map[interface{}]struct{}) {
|
||||
gtest.Assert(m, map[interface{}]struct{}{
|
||||
3: struct{}{},
|
||||
2: struct{}{},
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSet_Equal(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := gset.NewSet()
|
||||
s2 := gset.NewSet()
|
||||
s3 := gset.NewSet()
|
||||
s1.Add(1).Add(2).Add(3)
|
||||
s2.Add(1).Add(2).Add(3)
|
||||
s3.Add(1).Add(2).Add(3).Add(4)
|
||||
gtest.Assert(s1.Equal(s2), true)
|
||||
gtest.Assert(s1.Equal(s3), false)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
s1 := gset.NewSet()
|
||||
s2 := gset.NewSet()
|
||||
s3 := gset.NewSet()
|
||||
s1.Add(1).Add(2).Add(3)
|
||||
s2.Add(1).Add(2).Add(3)
|
||||
s3.Add(1).Add(2).Add(3).Add(4)
|
||||
gtest.Assert(s1.Equal(s2), true)
|
||||
gtest.Assert(s1.Equal(s3), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSet_IsSubsetOf(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := gset.NewSet()
|
||||
s2 := gset.NewSet()
|
||||
s3 := gset.NewSet()
|
||||
s1.Add(1).Add(2)
|
||||
s2.Add(1).Add(2).Add(3)
|
||||
s3.Add(1).Add(2).Add(3).Add(4)
|
||||
gtest.Assert(s1.IsSubsetOf(s2), true)
|
||||
gtest.Assert(s2.IsSubsetOf(s3), true)
|
||||
gtest.Assert(s1.IsSubsetOf(s3), true)
|
||||
gtest.Assert(s2.IsSubsetOf(s1), false)
|
||||
gtest.Assert(s3.IsSubsetOf(s2), false)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
s1 := gset.NewSet()
|
||||
s2 := gset.NewSet()
|
||||
s3 := gset.NewSet()
|
||||
s1.Add(1).Add(2)
|
||||
s2.Add(1).Add(2).Add(3)
|
||||
s3.Add(1).Add(2).Add(3).Add(4)
|
||||
gtest.Assert(s1.IsSubsetOf(s2), true)
|
||||
gtest.Assert(s2.IsSubsetOf(s3), true)
|
||||
gtest.Assert(s1.IsSubsetOf(s3), true)
|
||||
gtest.Assert(s2.IsSubsetOf(s1), false)
|
||||
gtest.Assert(s3.IsSubsetOf(s2), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSet_Union(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := gset.NewSet()
|
||||
s2 := gset.NewSet()
|
||||
s1.Add(1).Add(2)
|
||||
s2.Add(3).Add(4)
|
||||
s3 := s1.Union(s2)
|
||||
gtest.Assert(s3.Contains(1), true)
|
||||
gtest.Assert(s3.Contains(2), true)
|
||||
gtest.Assert(s3.Contains(3), true)
|
||||
gtest.Assert(s3.Contains(4), true)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
s1 := gset.NewSet()
|
||||
s2 := gset.NewSet()
|
||||
s1.Add(1).Add(2)
|
||||
s2.Add(3).Add(4)
|
||||
s3 := s1.Union(s2)
|
||||
gtest.Assert(s3.Contains(1), true)
|
||||
gtest.Assert(s3.Contains(2), true)
|
||||
gtest.Assert(s3.Contains(3), true)
|
||||
gtest.Assert(s3.Contains(4), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSet_Diff(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := gset.NewSet()
|
||||
s2 := gset.NewSet()
|
||||
s1.Add(1).Add(2).Add(3)
|
||||
s2.Add(3).Add(4).Add(5)
|
||||
s3 := s1.Diff(s2)
|
||||
gtest.Assert(s3.Contains(1), true)
|
||||
gtest.Assert(s3.Contains(2), true)
|
||||
gtest.Assert(s3.Contains(3), false)
|
||||
gtest.Assert(s3.Contains(4), false)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
s1 := gset.NewSet()
|
||||
s2 := gset.NewSet()
|
||||
s1.Add(1).Add(2).Add(3)
|
||||
s2.Add(3).Add(4).Add(5)
|
||||
s3 := s1.Diff(s2)
|
||||
gtest.Assert(s3.Contains(1), true)
|
||||
gtest.Assert(s3.Contains(2), true)
|
||||
gtest.Assert(s3.Contains(3), false)
|
||||
gtest.Assert(s3.Contains(4), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSet_Intersect(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := gset.NewSet()
|
||||
s2 := gset.NewSet()
|
||||
s1.Add(1).Add(2).Add(3)
|
||||
s2.Add(3).Add(4).Add(5)
|
||||
s3 := s1.Intersect(s2)
|
||||
gtest.Assert(s3.Contains(1), false)
|
||||
gtest.Assert(s3.Contains(2), false)
|
||||
gtest.Assert(s3.Contains(3), true)
|
||||
gtest.Assert(s3.Contains(4), false)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
s1 := gset.NewSet()
|
||||
s2 := gset.NewSet()
|
||||
s1.Add(1).Add(2).Add(3)
|
||||
s2.Add(3).Add(4).Add(5)
|
||||
s3 := s1.Intersect(s2)
|
||||
gtest.Assert(s3.Contains(1), false)
|
||||
gtest.Assert(s3.Contains(2), false)
|
||||
gtest.Assert(s3.Contains(3), true)
|
||||
gtest.Assert(s3.Contains(4), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSet_Complement(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := gset.NewSet()
|
||||
s2 := gset.NewSet()
|
||||
s1.Add(1).Add(2).Add(3)
|
||||
s2.Add(3).Add(4).Add(5)
|
||||
s3 := s1.Complement(s2)
|
||||
gtest.Assert(s3.Contains(1), false)
|
||||
gtest.Assert(s3.Contains(2), false)
|
||||
gtest.Assert(s3.Contains(4), true)
|
||||
gtest.Assert(s3.Contains(5), true)
|
||||
})
|
||||
}
|
||||
gtest.Case(t, func() {
|
||||
s1 := gset.NewSet()
|
||||
s2 := gset.NewSet()
|
||||
s1.Add(1).Add(2).Add(3)
|
||||
s2.Add(3).Add(4).Add(5)
|
||||
s3 := s1.Complement(s2)
|
||||
gtest.Assert(s3.Contains(1), false)
|
||||
gtest.Assert(s3.Contains(2), false)
|
||||
gtest.Assert(s3.Contains(4), true)
|
||||
gtest.Assert(s3.Contains(5), true)
|
||||
})
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
package gaes_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/encoding/gbase64"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g/crypto/gaes"
|
||||
@ -16,52 +17,111 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
content = []byte("pibigstar")
|
||||
content = []byte("pibigstar")
|
||||
content_16, _ = gbase64.Decode("v1jqsGHId/H8onlVHR8Vaw==")
|
||||
content_24, _ = gbase64.Decode("0TXOaj5KMoLhNWmJ3lxY1A==")
|
||||
content_32, _ = gbase64.Decode("qM/Waw1kkWhrwzek24rCSA==")
|
||||
content_16_iv, _ = gbase64.Decode("DqQUXiHgW/XFb6Qs98+hrA==")
|
||||
content_32_iv, _ = gbase64.Decode("ZuLgAOii+lrD5KJoQ7yQ8Q==")
|
||||
// iv 长度必须等于blockSize,只能为16
|
||||
iv = []byte("Hello My GoFrame")
|
||||
key_16 = []byte("1234567891234567")
|
||||
key_24 = []byte("123456789123456789123456")
|
||||
key_32 = []byte("12345678912345678912345678912345")
|
||||
keys = []byte("12345678912345678912345678912346")
|
||||
iv = []byte("Hello My GoFrame")
|
||||
key_16 = []byte("1234567891234567")
|
||||
key_17 = []byte("12345678912345670")
|
||||
key_24 = []byte("123456789123456789123456")
|
||||
key_32 = []byte("12345678912345678912345678912345")
|
||||
keys = []byte("12345678912345678912345678912346")
|
||||
key_err = []byte("1234")
|
||||
key_32_err = []byte("1234567891234567891234567891234 ")
|
||||
)
|
||||
|
||||
func TestEncrypt(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
_, err := gaes.Encrypt(content, key_16)
|
||||
data, err := gaes.Encrypt(content, key_16)
|
||||
gtest.Assert(err, nil)
|
||||
_, err = gaes.Encrypt(content, key_24)
|
||||
gtest.Assert(data, []byte(content_16))
|
||||
data, err = gaes.Encrypt(content, key_24)
|
||||
gtest.Assert(err, nil)
|
||||
_, err = gaes.Encrypt(content, key_32)
|
||||
gtest.Assert(data, []byte(content_24))
|
||||
data, err = gaes.Encrypt(content, key_32)
|
||||
gtest.Assert(err, nil)
|
||||
_, err = gaes.Encrypt(content, key_16, iv)
|
||||
gtest.Assert(data, []byte(content_32))
|
||||
data, err = gaes.Encrypt(content, key_16, iv)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(data, []byte(content_16_iv))
|
||||
data, err = gaes.Encrypt(content, key_32, iv)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(data, []byte(content_32_iv))
|
||||
})
|
||||
}
|
||||
|
||||
func TestDecrypt(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
encrypt, err := gaes.Encrypt(content, key_16)
|
||||
decrypt, err := gaes.Decrypt(encrypt, key_16)
|
||||
decrypt, err := gaes.Decrypt([]byte(content_16), key_16)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(string(decrypt), string(content))
|
||||
gtest.Assert(decrypt, content)
|
||||
|
||||
encrypt, err = gaes.Encrypt(content, key_24)
|
||||
decrypt, err = gaes.Decrypt(encrypt, key_24)
|
||||
decrypt, err = gaes.Decrypt([]byte(content_24), key_24)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(string(decrypt), string(content))
|
||||
gtest.Assert(decrypt, content)
|
||||
|
||||
encrypt, err = gaes.Encrypt(content, key_32)
|
||||
decrypt, err = gaes.Decrypt(encrypt, key_32)
|
||||
decrypt, err = gaes.Decrypt([]byte(content_32), key_32)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(string(decrypt), string(content))
|
||||
gtest.Assert(decrypt, content)
|
||||
|
||||
encrypt, err = gaes.Encrypt(content, key_32, iv)
|
||||
decrypt, err = gaes.Decrypt(encrypt, key_32, iv)
|
||||
decrypt, err = gaes.Decrypt([]byte(content_16_iv), key_16, iv)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(string(decrypt), string(content))
|
||||
gtest.Assert(decrypt, content)
|
||||
|
||||
encrypt, err = gaes.Encrypt(content, key_32, iv)
|
||||
decrypt, err = gaes.Decrypt(encrypt, keys, iv)
|
||||
decrypt, err = gaes.Decrypt([]byte(content_32_iv), key_32, iv)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(decrypt, content)
|
||||
|
||||
decrypt, err = gaes.Decrypt([]byte(content_32_iv), keys, iv)
|
||||
gtest.Assert(err, "invalid padding")
|
||||
})
|
||||
}
|
||||
|
||||
func TestEncryptErr(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
// encrypt key error
|
||||
_, err := gaes.Encrypt(content, key_err)
|
||||
gtest.AssertNE(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDecryptErr(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
// decrypt key error
|
||||
encrypt, err := gaes.Encrypt(content, key_16)
|
||||
_, err = gaes.Decrypt(encrypt, key_err)
|
||||
gtest.AssertNE(err, nil)
|
||||
|
||||
// decrypt content too short error
|
||||
_, err = gaes.Decrypt([]byte("test"), key_16)
|
||||
gtest.AssertNE(err, nil)
|
||||
|
||||
// decrypt content size error
|
||||
_, err = gaes.Decrypt(key_17, key_16)
|
||||
gtest.AssertNE(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPKCS5UnPaddingErr(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
// PKCS5UnPadding blockSize zero
|
||||
_, err := gaes.PKCS5UnPadding(content, 0)
|
||||
gtest.AssertNE(err, nil)
|
||||
|
||||
// PKCS5UnPadding src len zero
|
||||
_, err = gaes.PKCS5UnPadding([]byte(""), 16)
|
||||
gtest.AssertNE(err, nil)
|
||||
|
||||
// PKCS5UnPadding src len > blockSize
|
||||
_, err = gaes.PKCS5UnPadding(key_17, 16)
|
||||
gtest.AssertNE(err, nil)
|
||||
|
||||
// PKCS5UnPadding src len > blockSize
|
||||
_, err = gaes.PKCS5UnPadding(key_32_err, 32)
|
||||
gtest.AssertNE(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
@ -9,13 +9,14 @@
|
||||
package gcrc32_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/crypto/gmd5"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g/crypto/gcrc32"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
)
|
||||
|
||||
func TestEncrypt(t *testing.T) {
|
||||
func TestEncryptString(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s := "pibigstar"
|
||||
result := 693191136
|
||||
@ -25,3 +26,19 @@ func TestEncrypt(t *testing.T) {
|
||||
gtest.AssertEQ(int(encrypt2), result)
|
||||
})
|
||||
}
|
||||
|
||||
func TestEncrypt(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s := "pibigstar"
|
||||
result := 693191136
|
||||
encrypt1 := gcrc32.Encrypt(s)
|
||||
encrypt2 := gcrc32.Encrypt([]byte(s))
|
||||
gtest.AssertEQ(int(encrypt1), result)
|
||||
gtest.AssertEQ(int(encrypt2), result)
|
||||
|
||||
strmd5 := gmd5.Encrypt(s)
|
||||
test1 := gcrc32.Encrypt(strmd5)
|
||||
test2 := gcrc32.Encrypt([]byte(strmd5))
|
||||
gtest.AssertEQ(test2, test1)
|
||||
})
|
||||
}
|
||||
|
||||
@ -58,7 +58,7 @@ func (s *Session) init() {
|
||||
// 否则执行初始化创建
|
||||
s.id = s.request.Cookie.MakeSessionId()
|
||||
s.data = gmap.NewStrAnyMap()
|
||||
s.server.sessions.Set(s.id, s.data, s.server.GetSessionMaxAge())
|
||||
s.server.sessions.Set(s.id, s.data, s.server.GetSessionMaxAge()*1000)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -85,3 +85,36 @@ func Test_Router_Hook_Priority(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Router_Hook_Multi(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.BindHandler("/multi-hook", func(r *ghttp.Request) {
|
||||
r.Response.Write("show")
|
||||
})
|
||||
|
||||
s.BindHookHandlerByMap("/multi-hook", map[string]ghttp.HandlerFunc {
|
||||
"BeforeServe" : func(r *ghttp.Request) {
|
||||
r.Response.Write("1")
|
||||
},
|
||||
})
|
||||
s.BindHookHandlerByMap("/multi-hook", map[string]ghttp.HandlerFunc {
|
||||
"BeforeServe" : func(r *ghttp.Request) {
|
||||
r.Response.Write("2")
|
||||
},
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(time.Second)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/multi-hook"), "12show")
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -7,81 +7,100 @@
|
||||
// Package gcache provides high performance and concurrent-safe in-memory cache for process.
|
||||
package gcache
|
||||
|
||||
// 全局缓存管理对象
|
||||
// Default cache object.
|
||||
var cache = New()
|
||||
|
||||
// (使用全局KV缓存对象)设置kv缓存键值对,过期时间单位为**毫秒**
|
||||
|
||||
// Set sets cache with <key>-<value> pair, which is expired after <expire> milliseconds.
|
||||
// If <expire> <=0 means it does not expire.
|
||||
func Set(key interface{}, value interface{}, expire int) {
|
||||
cache.Set(key, value, expire)
|
||||
}
|
||||
|
||||
// 当键名不存在时写入,并返回true;否则返回false。
|
||||
// 常用来做对并发性要求不高的内存锁。
|
||||
// SetIfNotExist sets cache with <key>-<value> pair if <key> does not exist in the cache,
|
||||
// which is expired after <expire> milliseconds.
|
||||
// If <expire> <=0 means it does not expire.
|
||||
func SetIfNotExist(key interface{}, value interface{}, expire int) bool {
|
||||
return cache.SetIfNotExist(key, value, expire)
|
||||
}
|
||||
|
||||
// (使用全局KV缓存对象)批量设置kv缓存键值对,过期时间单位为**毫秒**
|
||||
// Sets batch sets cache with key-value pairs by <data>, which is expired after <expire> milliseconds.
|
||||
// If <expire> <=0 means it does not expire.
|
||||
func Sets(data map[interface{}]interface{}, expire int) {
|
||||
cache.Sets(data, expire)
|
||||
}
|
||||
|
||||
// (使用全局KV缓存对象)获取指定键名的值
|
||||
// Get returns the value of <key>.
|
||||
// It returns nil if it does not exist or its value is nil.
|
||||
func Get(key interface{}) interface{} {
|
||||
return cache.Get(key)
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值
|
||||
// GetOrSet returns the value of <key>,
|
||||
// or sets <key>-<value> pair and returns <value> if <key> does not exist in the cache.
|
||||
// The key-value pair expires after <expire> milliseconds.
|
||||
// If <expire> <=0 means it does not expire.
|
||||
func GetOrSet(key interface{}, value interface{}, expire int) interface{} {
|
||||
return cache.GetOrSet(key, value, expire)
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成
|
||||
|
||||
// GetOrSetFunc returns the value of <key>,
|
||||
// or sets <key> with result of function <f> and returns its result
|
||||
// if <key> does not exist in the cache.
|
||||
// The key-value pair expires after <expire> milliseconds.
|
||||
// If <expire> <=0 means it does not expire.
|
||||
func GetOrSetFunc(key interface{}, f func() interface{}, expire int) interface{} {
|
||||
return cache.GetOrSetFunc(key, f, expire)
|
||||
}
|
||||
|
||||
// 与GetOrSetFunc不同的是,f是在写锁机制内执行
|
||||
// GetOrSetFuncLock returns the value of <key>,
|
||||
// or sets <key> with result of function <f> and returns its result
|
||||
// if <key> does not exist in the cache.
|
||||
// The key-value pair expires after <expire> milliseconds.
|
||||
// If <expire> <=0 means it does not expire.
|
||||
//
|
||||
// Note that the function <f> is executed within writing mutex lock.
|
||||
func GetOrSetFuncLock(key interface{}, f func() interface{}, expire int) interface{} {
|
||||
return cache.GetOrSetFuncLock(key, f, expire)
|
||||
}
|
||||
|
||||
// 是否存在指定的键名,true表示存在,false表示不存在。
|
||||
// Contains returns true if <key> exists in the cache, or else returns false.
|
||||
func Contains(key interface{}) bool {
|
||||
return cache.Contains(key)
|
||||
}
|
||||
|
||||
// (使用全局KV缓存对象)删除指定键值对
|
||||
// Remove deletes the <key> in the cache, and returns its value.
|
||||
func Remove(key interface{}) interface{} {
|
||||
return cache.Remove(key)
|
||||
}
|
||||
|
||||
// (使用全局KV缓存对象)批量删除指定键值对
|
||||
// Removes deletes <keys> in the cache.
|
||||
func Removes(keys []interface{}) {
|
||||
cache.Removes(keys)
|
||||
}
|
||||
|
||||
// 返回缓存的所有数据键值对(不包含已过期数据)
|
||||
// Data returns a copy of all key-value pairs in the cache as map type.
|
||||
func Data() map[interface{}]interface{} {
|
||||
return cache.Data()
|
||||
}
|
||||
|
||||
// 获得所有的键名,组成数组返回
|
||||
// Keys returns all keys in the cache as slice.
|
||||
func Keys() []interface{} {
|
||||
return cache.Keys()
|
||||
}
|
||||
|
||||
// 获得所有的键名,组成字符串数组返回
|
||||
// KeyStrings returns all keys in the cache as string slice.
|
||||
func KeyStrings() []string {
|
||||
return cache.KeyStrings()
|
||||
}
|
||||
|
||||
// 获得所有的值,组成数组返回
|
||||
// Values returns all values in the cache as slice.
|
||||
func Values() []interface{} {
|
||||
return cache.Values()
|
||||
}
|
||||
|
||||
// 获得缓存对象的键值对数量
|
||||
// Size returns the size of the cache.
|
||||
func Size() int {
|
||||
return cache.Size()
|
||||
}
|
||||
|
||||
@ -13,13 +13,12 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// 缓存对象。
|
||||
// 底层只有一个缓存对象,如果需要提高并发性能,可新增缓存对象无锁哈希表,用键名做固定分区。
|
||||
// Cache struct.
|
||||
type Cache struct {
|
||||
*memCache
|
||||
}
|
||||
|
||||
// Cache对象按照缓存键名首字母做了分组
|
||||
// New creates and returns a new cache object.
|
||||
func New(lruCap...int) *Cache {
|
||||
c := &Cache {
|
||||
memCache : newMemCache(lruCap...),
|
||||
@ -28,10 +27,8 @@ func New(lruCap...int) *Cache {
|
||||
return c
|
||||
}
|
||||
|
||||
// 清空缓存中的所有数据
|
||||
// Clear clears all data of the cache.
|
||||
func (c *Cache) Clear() {
|
||||
// 使用原子操作替换缓存对象
|
||||
old := atomic.SwapPointer((*unsafe.Pointer)(unsafe.Pointer(&c.memCache)), unsafe.Pointer(newMemCache()))
|
||||
// 关闭旧的缓存对象
|
||||
(*memCache)(old).Close()
|
||||
}
|
||||
@ -18,21 +18,25 @@ import (
|
||||
)
|
||||
|
||||
|
||||
// 缓存对象
|
||||
// Internal cache object.
|
||||
type memCache struct {
|
||||
dataMu sync.RWMutex
|
||||
expireTimeMu sync.RWMutex
|
||||
expireSetMu sync.RWMutex
|
||||
|
||||
cap int // 控制缓存池大小,超过大小则按照LRU算法进行缓存过期处理(默认为0表示不进行限制)
|
||||
data map[interface{}]memCacheItem // 缓存数据(所有的缓存数据存放哈希表)
|
||||
expireTimes map[interface{}]int64 // 键名对应的分组过期时间(用于相同键名过期时间快速更新),键值为1秒级时间戳
|
||||
expireSets map[int64]*gset.Set // 分组过期时间对应的键名列表(用于自动过期快速删除),键值为1秒级时间戳
|
||||
// <cap> limits the size of the cache pool.
|
||||
// If the size of the cache exceeds the <cap>,
|
||||
// the cache expiration process is performed according to the LRU algorithm.
|
||||
// It is 0 in default which means no limits.
|
||||
cap int
|
||||
data map[interface{}]memCacheItem // Underlying cache data which is stored in a hash table.
|
||||
expireTimes map[interface{}]int64 // Expiring key mapping to its timestamp, which is used for quick indexing and deleting.
|
||||
expireSets map[int64]*gset.Set // Expiring timestamp mapping to its key set, which is used for quick indexing and deleting.
|
||||
|
||||
lru *memCacheLru // LRU缓存限制(只有限定cap池大小时才启用)
|
||||
lruGetList *glist.List // Get操作的LRU记录
|
||||
eventList *glist.List // 异步处理队列
|
||||
closed *gtype.Bool // 关闭事件通知
|
||||
lru *memCacheLru // LRU object, which is enabled when <cap> > 0.
|
||||
lruGetList *glist.List // LRU history according with Get function.
|
||||
eventList *glist.List // Asynchronous event list for internal data synchronization.
|
||||
closed *gtype.Bool // Is this cache closed or not.
|
||||
}
|
||||
|
||||
// 缓存数据项
|
||||
|
||||
@ -9,57 +9,265 @@
|
||||
package gcache_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/os/gcache"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
"time"
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/container/gset"
|
||||
"github.com/gogf/gf/g/os/gcache"
|
||||
"github.com/gogf/gf/g/os/grpool"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
//clear 用于清除全局缓存,因gcache api 暂未暴露 Clear 方法
|
||||
//暂定所有测试用例key的集合为1,2,3,避免不同测试用例间因全局cache共享带来的问题,每个测试用例在测试gcache.XXX之前,先调用clear()
|
||||
func clear() {
|
||||
gcache.Removes(g.Slice{1, 2, 3})
|
||||
}
|
||||
|
||||
func TestCache_Set(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
cache := gcache.New()
|
||||
cache.Set(1, 11, 0)
|
||||
gtest.Assert(cache.Get(1), 11)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
cache := gcache.New()
|
||||
cache.Set(1, 11, 0)
|
||||
gtest.Assert(cache.Get(1), 11)
|
||||
gtest.Assert(cache.Contains(1), true)
|
||||
|
||||
clear()
|
||||
gcache.Set(1, 11, 0)
|
||||
gtest.Assert(gcache.Get(1), 11)
|
||||
gtest.Assert(gcache.Contains(1), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCache_Set_Expire(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
cache := gcache.New()
|
||||
cache.Set(2, 22, 100)
|
||||
gtest.Assert(cache.Get(2), 22)
|
||||
time.Sleep(200*time.Millisecond)
|
||||
gtest.Assert(cache.Get(2), nil)
|
||||
time.Sleep(3*time.Second)
|
||||
gtest.Assert(cache.Size(), 0)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
cache := gcache.New()
|
||||
cache.Set(2, 22, 100)
|
||||
gtest.Assert(cache.Get(2), 22)
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
gtest.Assert(cache.Get(2), nil)
|
||||
time.Sleep(3 * time.Second)
|
||||
gtest.Assert(cache.Size(), 0)
|
||||
cache.Close()
|
||||
})
|
||||
}
|
||||
|
||||
func TestCache_Keys_Values(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
cache := gcache.New()
|
||||
for i := 0; i < 10; i++ {
|
||||
cache.Set(i, i*10, 0)
|
||||
}
|
||||
gtest.Assert(len(cache.Keys()), 10)
|
||||
gtest.Assert(len(cache.Values()), 10)
|
||||
gtest.AssertIN(0, cache.Keys())
|
||||
gtest.AssertIN(90, cache.Values())
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
cache := gcache.New()
|
||||
for i := 0; i < 10; i++ {
|
||||
cache.Set(i, i*10, 0)
|
||||
}
|
||||
gtest.Assert(len(cache.Keys()), 10)
|
||||
gtest.Assert(len(cache.Values()), 10)
|
||||
gtest.AssertIN(0, cache.Keys())
|
||||
gtest.AssertIN(90, cache.Values())
|
||||
})
|
||||
}
|
||||
|
||||
func TestCache_LRU(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
cache := gcache.New(2)
|
||||
for i := 0; i < 10; i++ {
|
||||
cache.Set(i, i, 0)
|
||||
}
|
||||
gtest.Case(t, func() {
|
||||
cache := gcache.New(2)
|
||||
for i := 0; i < 10; i++ {
|
||||
cache.Set(i, i, 0)
|
||||
}
|
||||
gtest.Assert(cache.Size(), 10)
|
||||
gtest.Assert(cache.Get(6), 6)
|
||||
time.Sleep(4 * time.Second)
|
||||
gtest.Assert(cache.Size(), 2)
|
||||
gtest.Assert(cache.Get(6), 6)
|
||||
gtest.Assert(cache.Get(1), nil)
|
||||
cache.Close()
|
||||
})
|
||||
}
|
||||
|
||||
gtest.Assert(cache.Size(), 10)
|
||||
gtest.Assert(cache.Get(6), 6)
|
||||
time.Sleep(4*time.Second)
|
||||
gtest.Assert(cache.Size(), 2)
|
||||
gtest.Assert(cache.Get(6), 6)
|
||||
gtest.Assert(cache.Get(1), nil)
|
||||
})
|
||||
}
|
||||
func TestCache_LRU_expire(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
cache := gcache.New(2)
|
||||
cache.Set(1, nil, 1000)
|
||||
gtest.Assert(cache.Size(), 1)
|
||||
gtest.Assert(cache.Get(1), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCache_SetIfNotExist(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
cache := gcache.New()
|
||||
cache.SetIfNotExist(1, 11, 0)
|
||||
gtest.Assert(cache.Get(1), 11)
|
||||
cache.SetIfNotExist(1, 22, 0)
|
||||
gtest.Assert(cache.Get(1), 11)
|
||||
cache.SetIfNotExist(2, 22, 0)
|
||||
gtest.Assert(cache.Get(2), 22)
|
||||
|
||||
clear()
|
||||
gcache.SetIfNotExist(1, 11, 0)
|
||||
gtest.Assert(gcache.Get(1), 11)
|
||||
gcache.SetIfNotExist(1, 22, 0)
|
||||
gtest.Assert(gcache.Get(1), 11)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCache_Sets(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
cache := gcache.New()
|
||||
cache.Sets(g.MapAnyAny{1: 11, 2: 22}, 0)
|
||||
gtest.Assert(cache.Get(1), 11)
|
||||
|
||||
clear()
|
||||
gcache.Sets(g.MapAnyAny{1: 11, 2: 22}, 0)
|
||||
gtest.Assert(gcache.Get(1), 11)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCache_GetOrSet(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
cache := gcache.New()
|
||||
cache.GetOrSet(1, 11, 0)
|
||||
gtest.Assert(cache.Get(1), 11)
|
||||
cache.GetOrSet(1, 111, 0)
|
||||
gtest.Assert(cache.Get(1), 11)
|
||||
|
||||
clear()
|
||||
gcache.GetOrSet(1, 11, 0)
|
||||
gtest.Assert(gcache.Get(1), 11)
|
||||
gcache.GetOrSet(1, 111, 0)
|
||||
gtest.Assert(gcache.Get(1), 11)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCache_GetOrSetFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
cache := gcache.New()
|
||||
cache.GetOrSetFunc(1, func() interface{} {
|
||||
return 11
|
||||
}, 0)
|
||||
gtest.Assert(cache.Get(1), 11)
|
||||
cache.GetOrSetFunc(1, func() interface{} {
|
||||
return 111
|
||||
}, 0)
|
||||
gtest.Assert(cache.Get(1), 11)
|
||||
|
||||
clear()
|
||||
gcache.GetOrSetFunc(1, func() interface{} {
|
||||
return 11
|
||||
}, 0)
|
||||
gtest.Assert(gcache.Get(1), 11)
|
||||
gcache.GetOrSetFunc(1, func() interface{} {
|
||||
return 111
|
||||
}, 0)
|
||||
gtest.Assert(gcache.Get(1), 11)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCache_GetOrSetFuncLock(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
cache := gcache.New()
|
||||
cache.GetOrSetFuncLock(1, func() interface{} {
|
||||
return 11
|
||||
}, 0)
|
||||
gtest.Assert(cache.Get(1), 11)
|
||||
cache.GetOrSetFuncLock(1, func() interface{} {
|
||||
return 111
|
||||
}, 0)
|
||||
gtest.Assert(cache.Get(1), 11)
|
||||
|
||||
clear()
|
||||
gcache.GetOrSetFuncLock(1, func() interface{} {
|
||||
return 11
|
||||
}, 0)
|
||||
gtest.Assert(gcache.Get(1), 11)
|
||||
gcache.GetOrSetFuncLock(1, func() interface{} {
|
||||
return 111
|
||||
}, 0)
|
||||
gtest.Assert(gcache.Get(1), 11)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCache_Clear(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
cache := gcache.New()
|
||||
cache.Sets(g.MapAnyAny{1: 11, 2: 22}, 0)
|
||||
cache.Clear()
|
||||
gtest.Assert(cache.Size(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCache_SetConcurrency(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
cache := gcache.New()
|
||||
pool := grpool.New(4)
|
||||
go func() {
|
||||
for {
|
||||
pool.Add(func() {
|
||||
cache.SetIfNotExist(1, 11, 10)
|
||||
})
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Log("first part end")
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
pool.Add(func() {
|
||||
cache.SetIfNotExist(1, nil, 10)
|
||||
})
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Log("second part end")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestCache_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
{
|
||||
cache := gcache.New()
|
||||
cache.Sets(g.MapAnyAny{1: 11, 2: 22}, 0)
|
||||
gtest.Assert(cache.Contains(1), true)
|
||||
gtest.Assert(cache.Get(1), 11)
|
||||
data := cache.Data()
|
||||
gtest.Assert(data[1], 11)
|
||||
gtest.Assert(data[2], 22)
|
||||
gtest.Assert(data[3], nil)
|
||||
gtest.Assert(cache.Size(), 2)
|
||||
keys := cache.Keys()
|
||||
gtest.Assert(gset.NewFrom(g.Slice{1, 2}).Equal(gset.NewFrom(keys)), true)
|
||||
keyStrs := cache.KeyStrings()
|
||||
gtest.Assert(gset.NewFrom(g.Slice{"1", "2"}).Equal(gset.NewFrom(keyStrs)), true)
|
||||
values := cache.Values()
|
||||
gtest.Assert(gset.NewFrom(g.Slice{11, 22}).Equal(gset.NewFrom(values)), true)
|
||||
removeData1 := cache.Remove(1)
|
||||
gtest.Assert(removeData1, 11)
|
||||
gtest.Assert(cache.Size(), 1)
|
||||
cache.Removes(g.Slice{2})
|
||||
gtest.Assert(cache.Size(), 0)
|
||||
}
|
||||
|
||||
clear()
|
||||
{
|
||||
gcache.Sets(g.MapAnyAny{1: 11, 2: 22}, 0)
|
||||
gtest.Assert(gcache.Contains(1), true)
|
||||
gtest.Assert(gcache.Get(1), 11)
|
||||
data := gcache.Data()
|
||||
gtest.Assert(data[1], 11)
|
||||
gtest.Assert(data[2], 22)
|
||||
gtest.Assert(data[3], nil)
|
||||
gtest.Assert(gcache.Size(), 2)
|
||||
keys := gcache.Keys()
|
||||
gtest.Assert(gset.NewFrom(g.Slice{1, 2}).Equal(gset.NewFrom(keys)), true)
|
||||
keyStrs := gcache.KeyStrings()
|
||||
gtest.Assert(gset.NewFrom(g.Slice{"1", "2"}).Equal(gset.NewFrom(keyStrs)), true)
|
||||
values := gcache.Values()
|
||||
gtest.Assert(gset.NewFrom(g.Slice{11, 22}).Equal(gset.NewFrom(values)), true)
|
||||
removeData1 := gcache.Remove(1)
|
||||
gtest.Assert(removeData1, 11)
|
||||
gtest.Assert(gcache.Size(), 1)
|
||||
gcache.Removes(g.Slice{2})
|
||||
gtest.Assert(gcache.Size(), 0)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
43
g/os/genv/genv_test.go
Normal file
43
g/os/genv/genv_test.go
Normal file
@ -0,0 +1,43 @@
|
||||
package genv_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/os/genv"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Genv_All(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(os.Environ(), genv.All())
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Genv_Get(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
key := "TEST_GET_ENV"
|
||||
err := os.Setenv(key, "TEST")
|
||||
gtest.Assert(err, nil)
|
||||
gtest.AssertEQ(genv.Get(key), "TEST")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Genv_Set(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
key := "TEST_SET_ENV"
|
||||
err := genv.Set(key, "TEST")
|
||||
gtest.Assert(err, nil)
|
||||
gtest.AssertEQ(os.Getenv(key), "TEST")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Genv_Remove(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
key := "TEST_REMOVE_ENV"
|
||||
err := os.Setenv(key, "TEST")
|
||||
gtest.Assert(err, nil)
|
||||
err = genv.Remove(key)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.AssertEQ(os.Getenv(key), "")
|
||||
})
|
||||
}
|
||||
@ -425,8 +425,7 @@ func MainPkgPath() string {
|
||||
continue
|
||||
}
|
||||
// separator of <file> '/' will be converted to Separator.
|
||||
path = Dir(file)
|
||||
for path[len(path) - 1] != os.PathSeparator {
|
||||
for path = Dir(file); len(path) > 1 && Exists(path) && path[len(path) - 1] != os.PathSeparator; {
|
||||
files, _ := ScanDir(path, "*.go")
|
||||
for _, v := range files {
|
||||
if gregex.IsMatchString(`package\s+main`, GetContents(v)) {
|
||||
|
||||
@ -243,6 +243,13 @@ func (l *Logger) print(std io.Writer, lead string, value...interface{}) {
|
||||
if len(timeFormat) > 0 {
|
||||
buffer.WriteString(time.Now().Format(timeFormat))
|
||||
}
|
||||
// Lead string.
|
||||
if len(lead) > 0 {
|
||||
buffer.WriteString(lead)
|
||||
if len(value) > 0 {
|
||||
buffer.WriteByte(' ')
|
||||
}
|
||||
}
|
||||
// Caller path.
|
||||
callerPath := ""
|
||||
if l.flags & F_FILE_LONG > 0 {
|
||||
@ -259,12 +266,6 @@ func (l *Logger) print(std io.Writer, lead string, value...interface{}) {
|
||||
buffer.WriteString(l.prefix + " ")
|
||||
}
|
||||
}
|
||||
if len(lead) > 0 {
|
||||
buffer.WriteString(lead)
|
||||
if len(value) > 0 {
|
||||
buffer.WriteByte(' ')
|
||||
}
|
||||
}
|
||||
for k, v := range value {
|
||||
if k > 0 {
|
||||
buffer.WriteByte(' ')
|
||||
|
||||
@ -14,10 +14,10 @@ import (
|
||||
|
||||
// Goroutine Pool
|
||||
type Pool struct {
|
||||
limit int // Max goroutine count limit.
|
||||
count *gtype.Int // Current running goroutine count.
|
||||
list *glist.List // Job list.
|
||||
closed *gtype.Bool // Is pool closed or not.
|
||||
limit int // Max goroutine count limit.
|
||||
count *gtype.Int // Current running goroutine count.
|
||||
list *glist.List // Job list for asynchronous job adding purpose.
|
||||
closed *gtype.Bool // Is pool closed or not.
|
||||
}
|
||||
|
||||
// Default goroutine pool.
|
||||
@ -33,7 +33,7 @@ func New(limit...int) *Pool {
|
||||
list : glist.New(),
|
||||
closed : gtype.NewBool(),
|
||||
}
|
||||
if len(limit) > 0 {
|
||||
if len(limit) > 0 && limit[0] > 0 {
|
||||
p.limit = limit[0]
|
||||
}
|
||||
return p
|
||||
@ -72,6 +72,7 @@ func (p *Pool) Add(f func()) {
|
||||
p.fork()
|
||||
}
|
||||
|
||||
|
||||
// Size returns current goroutine count of the pool.
|
||||
func (p *Pool) Size() int {
|
||||
return p.count.Val()
|
||||
|
||||
@ -5,8 +5,6 @@
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gtime provides functionality for measuring and displaying time.
|
||||
//
|
||||
// 时间管理.
|
||||
package gtime
|
||||
|
||||
import (
|
||||
|
||||
@ -15,6 +15,10 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
PatternErr = `([\d+`
|
||||
)
|
||||
|
||||
func Test_Quote(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := `[foo]` //`\[foo\]`
|
||||
@ -40,6 +44,8 @@ func Test_IsMatch(t *testing.T) {
|
||||
gtest.Assert(gregex.IsMatch(pattern, s1), false)
|
||||
s1 = []byte(`sfs:`)
|
||||
gtest.Assert(gregex.IsMatch(pattern, s1), false)
|
||||
// error pattern
|
||||
gtest.Assert(gregex.IsMatch(PatternErr, s1), false)
|
||||
})
|
||||
}
|
||||
|
||||
@ -52,6 +58,8 @@ func Test_IsMatchString(t *testing.T) {
|
||||
gtest.Assert(gregex.IsMatchString(pattern, s1), false)
|
||||
s1 = `sfs:`
|
||||
gtest.Assert(gregex.IsMatchString(pattern, s1), false)
|
||||
// error pattern
|
||||
gtest.Assert(gregex.IsMatchString(PatternErr, s1), false)
|
||||
})
|
||||
}
|
||||
|
||||
@ -68,6 +76,9 @@ func Test_Match(t *testing.T) {
|
||||
if string(subs[1]) != "aab" {
|
||||
t.Fatalf("Match(%q)[1] = %q; want %q", s, subs[1], "aab")
|
||||
}
|
||||
// error pattern
|
||||
_, err = gregex.Match(PatternErr, []byte(s))
|
||||
gtest.AssertNE(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
@ -84,6 +95,9 @@ func Test_MatchString(t *testing.T) {
|
||||
if string(subs[1]) != "aab" {
|
||||
t.Fatalf("Match(%q)[1] = %q; want %q", s, subs[1], "aab")
|
||||
}
|
||||
// error pattern
|
||||
_, err = gregex.MatchString(PatternErr, s)
|
||||
gtest.AssertNE(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
@ -108,6 +122,9 @@ func Test_MatchAll(t *testing.T) {
|
||||
if string(subs[1][1]) != "aab" {
|
||||
t.Fatalf("Match(%q)[1] = %q; want %q", s, subs[1][1], "aab")
|
||||
}
|
||||
// error pattern
|
||||
_, err = gregex.MatchAll(PatternErr, []byte(s))
|
||||
gtest.AssertNE(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
@ -131,6 +148,9 @@ func Test_MatchAllString(t *testing.T) {
|
||||
if string(subs[1][1]) != "aab" {
|
||||
t.Fatalf("Match(%q)[1] = %q; want %q", s, subs[1][1], "aab")
|
||||
}
|
||||
// error pattern
|
||||
_, err = gregex.MatchAllString(PatternErr, s)
|
||||
gtest.AssertNE(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
@ -146,6 +166,9 @@ func Test_Replace(t *testing.T) {
|
||||
if string(replacedStr) != wanted {
|
||||
t.Fatalf("regex:%s,old:%s; want %q", re, s, wanted)
|
||||
}
|
||||
// error pattern
|
||||
_, err = gregex.Replace(PatternErr, []byte(replace), []byte(s))
|
||||
gtest.AssertNE(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
@ -161,6 +184,9 @@ func Test_ReplaceString(t *testing.T) {
|
||||
if replacedStr != wanted {
|
||||
t.Fatalf("regex:%s,old:%s; want %q", re, s, wanted)
|
||||
}
|
||||
// error pattern
|
||||
_, err = gregex.ReplaceString(PatternErr, replace, s)
|
||||
gtest.AssertNE(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
@ -182,6 +208,11 @@ func Test_ReplaceFun(t *testing.T) {
|
||||
if string(replacedStr) != wanted {
|
||||
t.Fatalf("regex:%s,old:%s; want %q", re, s, wanted)
|
||||
}
|
||||
// error pattern
|
||||
_, err = gregex.ReplaceFunc(PatternErr, []byte(s), func(s []byte) []byte {
|
||||
return []byte("")
|
||||
})
|
||||
gtest.AssertNE(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
@ -209,6 +240,11 @@ func Test_ReplaceFuncMatch(t *testing.T) {
|
||||
})
|
||||
gtest.Assert(e3, nil)
|
||||
gtest.Assert(s3, []byte("7890"))
|
||||
// error pattern
|
||||
_, err := gregex.ReplaceFuncMatch(PatternErr, s, func(match [][]byte) []byte {
|
||||
return match[3]
|
||||
})
|
||||
gtest.AssertNE(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
@ -230,6 +266,11 @@ func Test_ReplaceStringFunc(t *testing.T) {
|
||||
if replacedStr != wanted {
|
||||
t.Fatalf("regex:%s,old:%s; want %q", re, s, wanted)
|
||||
}
|
||||
// error pattern
|
||||
_, err = gregex.ReplaceStringFunc(PatternErr, s, func(s string) string {
|
||||
return ""
|
||||
})
|
||||
gtest.AssertNE(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
@ -257,6 +298,11 @@ func Test_ReplaceStringFuncMatch(t *testing.T) {
|
||||
})
|
||||
gtest.Assert(e3, nil)
|
||||
gtest.Assert(s3, "7890")
|
||||
// error pattern
|
||||
_, err := gregex.ReplaceStringFuncMatch(PatternErr, s, func(match []string) string {
|
||||
return ""
|
||||
})
|
||||
gtest.AssertNE(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
@ -288,6 +334,9 @@ func Test_Split(t *testing.T) {
|
||||
if items[0] != s {
|
||||
t.Fatalf("regex:%s,Split(%q) want %q", re, s, item0)
|
||||
}
|
||||
// error pattern
|
||||
items = gregex.Split(PatternErr, s)
|
||||
gtest.AssertEQ(items, nil)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@ -157,7 +157,9 @@ func StructDeep(params interface{}, pointer interface{}, mapping...map[string]st
|
||||
trv := rv.Field(i)
|
||||
switch trv.Kind() {
|
||||
case reflect.Struct:
|
||||
StructDeep(params, trv, mapping...)
|
||||
if err := StructDeep(params, trv, mapping...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -239,7 +241,9 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}) er
|
||||
switch structFieldValue.Kind() {
|
||||
// 属性为结构体
|
||||
case reflect.Struct:
|
||||
Struct(value, structFieldValue)
|
||||
if err := Struct(value, structFieldValue); err != nil {
|
||||
structFieldValue.Set(reflect.ValueOf(value))
|
||||
}
|
||||
|
||||
// 属性为数组类型
|
||||
case reflect.Slice: fallthrough
|
||||
@ -253,11 +257,15 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}) er
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
if t.Kind() == reflect.Ptr {
|
||||
e := reflect.New(t.Elem()).Elem()
|
||||
Struct(v.Index(i).Interface(), e)
|
||||
if err := Struct(v.Index(i).Interface(), e); err != nil {
|
||||
e.Set(reflect.ValueOf(v.Index(i).Interface()))
|
||||
}
|
||||
a.Index(i).Set(e.Addr())
|
||||
} else {
|
||||
e := reflect.New(t).Elem()
|
||||
Struct(v.Index(i).Interface(), e)
|
||||
if err := Struct(v.Index(i).Interface(), e); err != nil {
|
||||
e.Set(reflect.ValueOf(v.Index(i).Interface()))
|
||||
}
|
||||
a.Index(i).Set(e)
|
||||
}
|
||||
}
|
||||
@ -267,11 +275,15 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}) er
|
||||
t := a.Index(0).Type()
|
||||
if t.Kind() == reflect.Ptr {
|
||||
e := reflect.New(t.Elem()).Elem()
|
||||
Struct(value, e)
|
||||
if err := Struct(value, e); err != nil {
|
||||
e.Set(reflect.ValueOf(value))
|
||||
}
|
||||
a.Index(0).Set(e.Addr())
|
||||
} else {
|
||||
e := reflect.New(t).Elem()
|
||||
Struct(value, e)
|
||||
if err := Struct(value, e); err != nil {
|
||||
e.Set(reflect.ValueOf(value))
|
||||
}
|
||||
a.Index(0).Set(e)
|
||||
}
|
||||
}
|
||||
@ -280,7 +292,9 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}) er
|
||||
// 属性为指针类型
|
||||
case reflect.Ptr:
|
||||
e := reflect.New(structFieldValue.Type().Elem()).Elem()
|
||||
Struct(value, e)
|
||||
if err := Struct(value, e); err != nil {
|
||||
e.Set(reflect.ValueOf(value))
|
||||
}
|
||||
structFieldValue.Set(e.Addr())
|
||||
|
||||
default:
|
||||
|
||||
@ -7,12 +7,12 @@
|
||||
package gconv_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"testing"
|
||||
"time"
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_Struct_Basic1(t *testing.T) {
|
||||
@ -104,6 +104,26 @@ func Test_Struct_Basic2(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// 带有指针的基础类型属性
|
||||
func Test_Struct_Basic3(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Uid int
|
||||
Name *string
|
||||
}
|
||||
user := new(User)
|
||||
params := g.Map {
|
||||
"uid" : 1,
|
||||
"Name" : "john",
|
||||
}
|
||||
if err := gconv.Struct(params, user); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
gtest.Assert(user.Uid, 1)
|
||||
gtest.Assert(*user.Name, "john")
|
||||
})
|
||||
}
|
||||
|
||||
// slice类型属性的赋值
|
||||
func Test_Struct_Attr_Slice(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
|
||||
@ -5,6 +5,6 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
glog.Line().Println("this is the short file name with its line number")
|
||||
glog.Line(true).Println("lone file name with line number")
|
||||
glog.Line().Debug("this is the short file name with its line number")
|
||||
glog.Line(true).Debug("lone file name with line number")
|
||||
}
|
||||
|
||||
@ -1,19 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/net/ghttp"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
rs :="GF(Go Frame)是一款模块化、松耦合、生产级的Go应用开发框架。提供了常用的核心开发组件,如:缓存、日志、文件、时间、队列、数组、集合、字符串、定时器、命令行、文件锁、内存锁、对象池、连接池、数据校验、数据编码、文件监控、定时任务、数据库ORM、TCP/UDP组件、进程管理/通信、 并发安全容器等等。并提供了Web服务开发的系列核心组件,如:Router、Cookie、Session、路由注册、配置管理、模板引擎等等,支持热重启、热更新、多域名、多端口、多服务、HTTPS、Rewrite等特性。"
|
||||
//此行压测会提示map并发错误 webbench -c 8000 -t 60 http://IP 局域网两台机器测试
|
||||
r.Response.WriteTplContent(rs, g.Map{
|
||||
"Contentb": 1,
|
||||
})
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
fmt.Println(gtime.Now().Format("U"))
|
||||
fmt.Println(gtime.Second())
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package gf
|
||||
|
||||
const VERSION = "v1.6.17"
|
||||
const VERSION = "v1.7.0"
|
||||
const AUTHORS = "john<john@goframe.org>"
|
||||
|
||||
Reference in New Issue
Block a user