diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..5c7247b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,7 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [] +} \ No newline at end of file diff --git a/README.md b/README.md index 079e127..7b641d8 100644 --- a/README.md +++ b/README.md @@ -5,5 +5,5 @@ golang 工具包。 包括: -`文件功能`,`leveldb`,`restful风格消息包头定义`,`cache缓存`,`绘图函数`,`elastic`,`echarts`,`http`,`日志`,`nsq抽取`,`线程安全队列`,`签名`,`gorm封装`,`时间函数`,`国际化i18n`,`gocui 界面类`,`驼峰命名转换工具`,`大驼峰到网络标准json串自动转换`,`剪切板`,`微信`,`ast`,`swagger 文档支持`,`mindoc/markdown 文档支持`,`分布式全局唯一id(myglobal)`,`ssh(支持tab自动补全)`,`zap logger`,`快递鸟` +`文件功能`,`leveldb`,`restful风格消息包头定义`,`cache缓存`,`绘图函数`,`elastic`,`echarts`,`http`,`日志`,`nsq抽取`,`线程安全队列`,`签名`,`gorm封装`,`时间函数`,`国际化i18n`,`gocui 界面类`,`驼峰命名转换工具`,`大驼峰到网络标准json串自动转换`,`剪切板`,`微信`,`ast`,`swagger 文档支持`,`mindoc/markdown 文档支持`,`分布式全局唯一id(myglobal)`,`ssh(支持tab自动补全)`,`zap logger`,`快递鸟`,`redis` ...... diff --git a/go.mod b/go.mod index 44892d6..44649c5 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/go-redis/redis/v8 v8.4.11 github.com/go-sql-driver/mysql v1.5.0 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 - github.com/gomodule/redigo v1.8.3 // indirect + github.com/gomodule/redigo v1.8.3 github.com/gookit/color v1.2.5 github.com/jander/golog v0.0.0-20150917071935-954a5be801fc github.com/jinzhu/gorm v1.9.12 @@ -28,6 +28,7 @@ require ( github.com/nsqio/go-nsq v1.0.8 github.com/olivere/elastic v6.2.31+incompatible github.com/pkg/errors v0.9.1 + github.com/prometheus/common v0.4.0 github.com/spf13/cobra v1.0.0 github.com/syndtr/goleveldb v1.0.0 github.com/xxjwxc/gowp v0.0.0-20200603130651-4d7368b0e285 diff --git a/go.sum b/go.sum index 9d425cc..a4c36e7 100644 --- a/go.sum +++ b/go.sum @@ -8,7 +8,9 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/ant0ine/go-json-rest v3.3.2+incompatible h1:nBixrkLFiDNAW0hauKDLc8yJI6XfrQumWvytE1Hk14E= github.com/ant0ine/go-json-rest v3.3.2+incompatible/go.mod h1:q6aCt0GfU6LhpBsnZ/2U+mwe+0XB5WStbmwyoPfc+sk= @@ -146,6 +148,7 @@ github.com/kardianos/service v1.0.0 h1:HgQS3mFfOlyntWX8Oke98JcJLqt1DBcHR4kxShpYe github.com/kardianos/service v1.0.0/go.mod h1:8CzDhVuCuugtsHyZoTvsOBuvonN/UDBvl0kH+BUxvbo= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -207,6 +210,7 @@ github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1: github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -217,6 +221,7 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -318,6 +323,7 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -352,6 +358,7 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/myredis/def.go b/myredis/def.go index a12f554..ff1dcf9 100644 --- a/myredis/def.go +++ b/myredis/def.go @@ -7,9 +7,13 @@ import ( "github.com/gomodule/redigo/redis" ) +const KeepTTL = -1 + // MyRedis redis配置项 type MyRedis struct { conf *redisOptions + con redis.Conn + pool *redis.Pool mtx sync.Mutex once sync.Once dial RedisDial @@ -20,11 +24,12 @@ var _default = &MyRedis{} // redisOption redisOption type redisOptions struct { - con redis.Conn timeout time.Duration groupName string pwd string + clientName string addrs []string + addrIdex int db int readTimeout time.Duration writeTimeout time.Duration diff --git a/myredis/my_test.go b/myredis/my_test.go deleted file mode 100644 index d8807c7..0000000 --- a/myredis/my_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package myredis - -import ( - "fmt" - "testing" - "time" -) - -func Test_cache(t *testing.T) { - //获取 - res, _ := NewRedis([]string{"192.155.1.150:6379"}, "Niren1015", "gggg", 0) - - fmt.Println(res.Clear()) - - aaa := "ccccc" - res.Add("aaaa", aaa, 20*time.Second) - res.Add("bbbb", aaa, 0) - fmt.Println(res.Delete("aaaa")) - - fmt.Print(res.IsExist("aaaa")) - - var tt string - res.Value("aaaa", &tt) - - time.Sleep(20 * time.Second) - fmt.Print(res.IsExist("aaaa")) - - fmt.Println(res.Clear()) - - fmt.Println(tt) - return -} diff --git a/myredis/myredis.go b/myredis/myredis.go index cde08e8..8b38259 100644 --- a/myredis/myredis.go +++ b/myredis/myredis.go @@ -1,9 +1,23 @@ package myredis -import "github.com/gomodule/redigo/redis" +import ( + "time" + + "github.com/gomodule/redigo/redis" +) // RedisDial 操作 type RedisDial interface { + Destory() // 析构 + GetRedisClient() redis.Conn // 获取一个原始的redis连接 + Ping() bool // 判断是否能ping通 + Add(key interface{}, value interface{}, lifeSpan time.Duration) error // 添加一个元素 + Value(key interface{}, value interface{}) error // 获取一个value + IsExist(key interface{}) bool // 判断是否存在 + Delete(key interface{}) error // 删除一个 + Clear() error // 清空 + GetKeyS(key interface{}) ([]string, error) // 查询所有key + Close() (err error) // 关闭连接 } // DefaultConf ... @@ -15,7 +29,7 @@ func DefaultConf() *MyRedis { } // InitDefaultRedis 初始化(必须要优先调用一次) -func InitDefaultRedis(ops ...Option) { +func InitDefaultRedis(ops ...Option) *MyRedis { var tmp = &redisOptions{} for _, o := range ops { o.apply(tmp) @@ -27,6 +41,7 @@ func InitDefaultRedis(ops ...Option) { _default.mtx.Lock() defer _default.mtx.Unlock() _default.conf = tmp + return _default } // InitRedis 初始化(必须要优先调用一次) @@ -54,20 +69,28 @@ func NewRedis(con *MyRedis) (dial RedisDial, err error) { con.once.Do(func() { // 创建连接 ReDialRedis(con) }) - - return nil, nil + + return con.dial, nil } // ReDialRedis 重新连接redis func ReDialRedis(con *MyRedis) { + if con.dial != nil { // 清理,关闭连接 + con.dial.Destory() + } + con.mtx.Lock() defer con.mtx.Unlock() - if con.conf.maxIdle > 0 { // 创建连接池 + if con.conf.maxIdle > 0 { // 创建连接池 + con.dial = &redisConPool{ + base: base{MyRedis: con}, + } return } - con.dial = // 创建单个连接 - redis.Dial("tcp",con.) + con.dial = &redisConOlny{ + base: base{MyRedis: con}, + } } diff --git a/myredis/myredis_test.go b/myredis/myredis_test.go new file mode 100644 index 0000000..a89b41e --- /dev/null +++ b/myredis/myredis_test.go @@ -0,0 +1,44 @@ +package myredis + +import ( + "fmt" + "testing" + "time" +) + +func Test_cache(t *testing.T) { + conf := InitRedis(WithAddr("192.155.1.150:6379"), WithClientName(""), + // WithPool(2, 2), + WithTimeout(10*time.Second), WithReadTimeout(10*time.Second), WithWriteTimeout(10*time.Second), + WithPwd("Niren1015"), WithGroupName("gggg"), WithDB(0)) + //获取 + res, err := NewRedis(conf) + + fmt.Println(err) + aaa := "ccccc" + res.Add("aaaa", aaa, 20*time.Second) + res.Add("bbbb", aaa, 0) + res.Close() + fmt.Println(res.Ping()) + + fmt.Print(res.IsExist("aaaa")) + fmt.Print(res.GetKeyS("*")) + fmt.Println(res.Clear()) + + fmt.Println(res.Delete("aaaa")) + + var tt string + res.Value("bbbb", &tt) + + var ww []int32 + res.Add("cccc", []int32{1, 2, 3, 4}, 0) + res.Value("cccc", &ww) + + // time.Sleep(20 * time.Second) + // fmt.Print(res.IsExist("aaaa")) + + // fmt.Println(res.Clear()) + + // fmt.Println(tt) + return +} diff --git a/myredis/option.go b/myredis/option.go index fd8b707..fedad49 100644 --- a/myredis/option.go +++ b/myredis/option.go @@ -60,3 +60,10 @@ func WithPool(maxIdle, maxActive int) Option { o.maxActive = maxActive }) } + +// WithClientName 指定Redis服务器连接使用的客户端名称 +func WithClientName(name string) Option { + return optionFunc(func(o *redisOptions) { + o.clientName = name + }) +} diff --git a/myredis/pool.go b/myredis/pool.go index 5a06cba..f2702df 100644 --- a/myredis/pool.go +++ b/myredis/pool.go @@ -1 +1,167 @@ package myredis + +import ( + "fmt" + "time" + + "github.com/gomodule/redigo/redis" + "github.com/xxjwxc/public/mylog" +) + +type redisConPool struct { + base +} + +func (mc *redisConPool) Destory() { + mc.mtx.Lock() + defer mc.mtx.Unlock() + + if mc.con != nil { + err := mc.con.Close() + if err != nil { + mylog.Error(err) + } + mc.con = nil + } +} + +func (mc *redisConPool) GetRedisClient() redis.Conn { + mc.mtx.Lock() + defer mc.mtx.Unlock() + if mc.pool == nil { // 创建连接 + mc.pool = &redis.Pool{ + MaxIdle: mc.conf.maxIdle, + MaxActive: mc.conf.maxActive, + Dial: func() (redis.Conn, error) { + return mc.Dial() + }, + TestOnBorrow: func(c redis.Conn, t time.Time) error { + _, err := c.Do("PING") + if err != nil { + mylog.Errorf("ping redis error: %s", err) + return err + } + return nil + }, + } + } + + return mc.pool.Get() +} + +// Ping 判断是否能ping通 +func (mc *redisConPool) Ping() bool { + return mc.ping(mc.GetRedisClient()) +} + +// Add 添加一个缓存 lifeSpan:缓存时间,0表示永不超时 +func (mc *redisConPool) Add(key interface{}, value interface{}, lifeSpan time.Duration) error { + var args []interface{} + args = append(args, mc.getKey(key), mc.encodeValue(value)) + if lifeSpan > 0 { + if usePrecise(lifeSpan) { + args = append(args, "px", formatMs(lifeSpan)) + } else { + args = append(args, "ex", formatSec(lifeSpan)) + } + } else if lifeSpan == KeepTTL { + args = append(args, "keepttl") + } + + repy, err := mc.Do(mc.GetRedisClient(), "SET", args...) + mylog.Info(redis.String(repy, err)) + if err != nil { + mylog.Error(err) + } + return err +} + +// Value 查找一个cache +func (mc *redisConPool) Value(key interface{}, value interface{}) (err error) { + repy, err := mc.Do(mc.GetRedisClient(), "GET", mc.getKey(key)) + if err != nil { + mylog.Error(err) + return err + } + return mc.decodeValue(repy, value) +} + +// IsExist 判断key是否存在 +func (mc *redisConPool) IsExist(key interface{}) bool { + repy, err := mc.Do(mc.GetRedisClient(), "EXISTS", mc.getKey(key)) + if err != nil { + mylog.Error(err) + return false + } + exist, err := redis.Bool(repy, err) // 转化bool格式 + if err != nil { + mylog.Error(err) + return false + } + + return exist +} + +// Delete 删除一个cache +func (mc *redisConPool) Delete(key interface{}) error { + _, err := mc.Do(mc.GetRedisClient(), "del", mc.getKey(key)) + if err != nil { + mylog.Error(err) + return err + } + return err +} + +// Clear 清空表內容 +func (mc *redisConPool) Clear() error { + out, err := mc.GetKeyS("*") + if err != nil { + return err + } + + for _, v := range out { + err = mc.Delete(v) + if err != nil { + return err + } + } + + return err +} + +// GetKeyS 查询所有key +func (mc *redisConPool) GetKeyS(key interface{}) ([]string, error) { + var keys []string + repy, err := mc.Do(mc.GetRedisClient(), "keys", mc.getKey(key)) + if err != nil { + mylog.Error(err) + return keys, err + } + + switch t := repy.(type) { + case []interface{}: + for _, v := range t { + out, err := redis.String(v, nil) + if err != nil { + mylog.Error(err) + } + keys = append(keys, mc.fixKeyGroupName(out)) + } + default: + return keys, fmt.Errorf("decodeValue err in type not find:%v", t) + } + + return keys, err +} + +// Close 关闭一个连接 +func (mc *redisConPool) Close() (err error) { + mc.mtx.Lock() + defer mc.mtx.Unlock() + if mc.con != nil { + err = mc.con.Close() + mc.con = nil + } + + return +} diff --git a/myredis/rediscon.go b/myredis/rediscon.go index 93c3cef..cf9a046 100644 --- a/myredis/rediscon.go +++ b/myredis/rediscon.go @@ -3,9 +3,13 @@ package myredis import ( "context" "fmt" + "strconv" + "strings" "time" "github.com/gomodule/redigo/redis" + "github.com/prometheus/common/log" + "github.com/xxjwxc/public/dev" "github.com/xxjwxc/public/mylog" "github.com/xxjwxc/public/tools" ) @@ -19,157 +23,346 @@ func (mc *base) getCtx() context.Context { } func (mc *base) getKey(key interface{}) string { + tmp := "" if len(mc.conf.groupName) > 0 { - return fmt.Sprintf("%v:%v", mc.conf.groupName, tools.JSONDecode(key)) + tmp = fmt.Sprintf("%v:", mc.conf.groupName) + } + switch t := key.(type) { + case []byte: + return fmt.Sprintf("%v%v", tmp, string(t)) + case string: + return fmt.Sprintf("%v%v", tmp, t) + default: + return fmt.Sprintf("%v%v", tmp, tools.JSONDecode(key)) } - return tools.JSONDecode(key) } -func initConOlny(cnf *MyRedis) *redisConOlny { - return &redisConOlny{ - base: base{MyRedis: cnf}, +func (mc *base) encodeValue(value interface{}) interface{} { + switch t := value.(type) { + case int32, byte, string, bool, int, uint, int8, int16, int64, uint16, uint32, uint64, float32, float64: // 基础类型 + return t + default: + return tools.JSONDecode(value) + // data, _ := serializing.Encode(value) + // return data } } +func (mc *base) decodeValue(in, out interface{}) (err error) { + if in == nil { + return fmt.Errorf("not fond") + } + + var reply string + switch t := in.(type) { + case []byte: + reply = string(t) + default: + return fmt.Errorf("decodeValue err in type not find:%v", t) + } + + switch o := out.(type) { + case *string: // string类型 + *o = reply + return nil + case *int32: + i64, err := strconv.ParseInt(reply, 10, 0) + *o = int32(i64) + return err + case *bool: + b, err := strconv.ParseBool(string(reply)) + *o = b + return err + case *int: + i64, err := strconv.ParseInt(reply, 10, 0) + *o = int(i64) + return err + case *int8: + i64, err := strconv.ParseInt(reply, 10, 0) + *o = int8(i64) + return err + case *int16: + i64, err := strconv.ParseInt(reply, 10, 0) + *o = int16(i64) + return err + case *int64: + i64, err := strconv.ParseInt(string(reply), 10, 64) + *o = int64(i64) + return err + case *uint: + i64, err := strconv.ParseUint(reply, 10, 0) + *o = uint(i64) + return err + case *uint8: + i64, err := strconv.ParseUint(reply, 10, 0) + *o = uint8(i64) + return err + case *uint16: + i64, err := strconv.ParseUint(reply, 10, 0) + *o = uint16(i64) + return err + case *uint32: + i64, err := strconv.ParseInt(string(reply), 10, 0) + *o = uint32(i64) + return err + case *uint64: + i64, err := strconv.ParseUint(reply, 10, 64) + *o = uint64(i64) + return err + case *float32: + f64, err := strconv.ParseFloat(string(reply), 32) + *o = float32(f64) + return err + case *float64: // 基础类型 + f64, err := strconv.ParseFloat(string(reply), 64) + *o = float64(f64) + return err + default: + tools.JSONEncode(reply, out) // 复杂类型 + return nil + //return serializing.Decode(t, out) + + } + + // return fmt.Errorf("decodeValue err not match:%v %v", in, out) +} + +func (mc *base) ping(con redis.Conn) bool { + if con == nil { + return false + } + + _, err := con.Do("PING") + if err != nil { + mylog.Errorf("ping redis error: %s", err) + return false + } + return true +} + +// Dial 获取一个链接 +func (mc *base) Dial() (redis.Conn, error) { + mc.mtx.Lock() + defer mc.mtx.Unlock() + if mc.con == nil { // 创建连接 + index := mc.conf.addrIdex + len := len(mc.conf.addrs) + b := false + var err error + for i := 0; i < len; i++ { + index = (mc.conf.addrIdex + i) % len + mc.con, err = redis.Dial("tcp", mc.conf.addrs[index], redis.DialClientName(mc.conf.clientName), + redis.DialConnectTimeout(mc.conf.timeout), redis.DialDatabase(mc.conf.db), + redis.DialPassword(mc.conf.pwd), redis.DialReadTimeout(mc.conf.readTimeout), redis.DialWriteTimeout(mc.conf.writeTimeout), + ) + if err != nil { + mylog.Error(err) + } + if mc.ping(mc.con) { + b = true + } + } + if b { + mc.conf.addrIdex = (index + 1) % len + } + return mc.con, err + } + + return mc.con, nil +} + +func (mc *base) Do(con redis.Conn, commandName string, args ...interface{}) (reply interface{}, err error) { + if dev.IsDev() { + cmd := commandName + for _, v := range args { + cmd += fmt.Sprintf(" %v", v) + } + log.Infof("redis req :%v", cmd) + } + + if con != nil { + reply, err = con.Do(commandName, args...) + // show log + if dev.IsDev() { + tmp := "" + switch reply := reply.(type) { + case []byte: + tmp = string(reply) + case string: + tmp = reply + case int64: + tmp = fmt.Sprintf("%v", reply) + } + log.Infof("redis resp:%v,%v", tmp, err) + } + return + } + return nil, fmt.Errorf("con is nil") +} + +func (mc *base) fixKeyGroupName(key string) string { + tmp := "" + if len(mc.conf.groupName) > 0 { + tmp = fmt.Sprintf("%v:", mc.conf.groupName) + } + return strings.TrimPrefix(key, tmp) +} + type redisConOlny struct { base } -// NewRedis 初始化一个cache cachename 缓存名字 -func NewRedis(addrs []string, pwd, groupName string, timeout time.Duration) (mc *MyRedis, err error) { - redis.Dial() - redis.SetLogger(&logger{}) - if len(addrs) <= 1 { - mc = &MyRedis{ - Cmdable: redis.NewClient(&redis.Options{ - Addr: addrs[0], - DialTimeout: timeout, - Password: pwd, // no password set - // DB: 0, // use default DB - }), - } - } else { - mc = &MyRedis{ - Cmdable: redis.NewClusterClient(&redis.ClusterOptions{ - Addrs: addrs, - Password: pwd, // no password set - DialTimeout: timeout, - }), +// Destory 析构 +func (mc *redisConOlny) Destory() { + mc.mtx.Lock() + defer mc.mtx.Unlock() + + if mc.con != nil { + err := mc.con.Close() + if err != nil { + mylog.Error(err) } + mc.con = nil } - mc.timeout = timeout - mc.groupName = groupName - - err = mc.Ping(mc.getCtx()).Err() - if err != nil { - mylog.Error(err) - } - return } -func (mc *MyRedis) getCtx() context.Context { - return context.Background() +// GetRedisClient ... +func (mc *redisConOlny) GetRedisClient() redis.Conn { + con, _ := mc.Dial() + return con } -func (mc *MyRedis) getKey(key interface{}) string { - if len(mc.groupName) > 0 { - return fmt.Sprintf("%v:%v", mc.groupName, tools.JSONDecode(key)) - } - return tools.JSONDecode(key) +// Ping 判断是否能ping通 +func (mc *redisConOlny) Ping() bool { + return mc.ping(mc.GetRedisClient()) } +// 判断是否能ping通 // Add 添加一个缓存 lifeSpan:缓存时间,0表示永不超时 -func (mc *MyRedis) Add(key interface{}, value interface{}, lifeSpan time.Duration) (err error) { - set := mc.Set(mc.getCtx(), mc.getKey(key), tools.JSONDecode(value), lifeSpan) - mylog.Info(set.Val()) - // redis.Expect(set.Err()).NotTo(HaveOccurred()) - // redis.Expect(set.Val()).To(Equal("OK")) - err = set.Err() +func (mc *redisConOlny) Add(key interface{}, value interface{}, lifeSpan time.Duration) error { + var args []interface{} + args = append(args, mc.getKey(key), mc.encodeValue(value)) + if lifeSpan > 0 { + if usePrecise(lifeSpan) { + args = append(args, "px", formatMs(lifeSpan)) + } else { + args = append(args, "ex", formatSec(lifeSpan)) + } + } else if lifeSpan == KeepTTL { + args = append(args, "keepttl") + } + + _, err := mc.Do(mc.GetRedisClient(), "SET", args...) if err != nil { mylog.Error(err) } - return + return err } // Value 查找一个cache -func (mc *MyRedis) Value(key interface{}, value interface{}) (err error) { - set := mc.Get(mc.getCtx(), mc.getKey(key)) - - mylog.Info(set.Val()) - // redis.Expect(set.Err()).NotTo(HaveOccurred()) - // redis.Expect(set.Val()).To(Equal("OK")) - err = set.Err() +func (mc *redisConOlny) Value(key interface{}, value interface{}) (err error) { + repy, err := mc.Do(mc.GetRedisClient(), "GET", mc.getKey(key)) if err != nil { mylog.Error(err) - return + return err } - - tools.JSONEncode(set.Val(), value) - return + return mc.decodeValue(repy, value) } // IsExist 判断key是否存在 -func (mc *MyRedis) IsExist(key interface{}) bool { - set := mc.Exists(mc.getCtx(), mc.getKey(key)) - mylog.Info(set.Val()) - // redis.Expect(set.Err()).NotTo(HaveOccurred()) - // redis.Expect(set.Val()).To(Equal("OK")) - err := set.Err() +func (mc *redisConOlny) IsExist(key interface{}) bool { + repy, err := mc.Do(mc.GetRedisClient(), "EXISTS", mc.getKey(key)) if err != nil { mylog.Error(err) + return false + } + exist, err := redis.Bool(repy, err) // 转化bool格式 + if err != nil { + mylog.Error(err) + return false } - return set.Val() == 1 + return exist } // Delete 删除一个cache -func (mc *MyRedis) Delete(key interface{}) error { - set := mc.Del(mc.getCtx(), mc.getKey(key)) - mylog.Info(set.Val()) - // redis.Expect(set.Err()).NotTo(HaveOccurred()) - // redis.Expect(set.Val()).To(Equal("OK")) - err := set.Err() +func (mc *redisConOlny) Delete(key interface{}) error { + _, err := mc.Do(mc.GetRedisClient(), "del", mc.getKey(key)) if err != nil { mylog.Error(err) + return err } - return err } -// GetRedisClient 获取原始cache2go操作类 -func (mc *MyRedis) GetRedisClient() redis.Cmdable { - return mc -} - // Clear 清空表內容 -func (mc *MyRedis) Clear() error { - key := "*" - if len(mc.groupName) > 0 { - key = fmt.Sprintf("%v:*", mc.groupName) +func (mc *redisConOlny) Clear() error { + out, err := mc.GetKeyS("*") + if err != nil { + return err } - set := mc.Pipeline().Do(mc.getCtx(), "KEYS", key) - mylog.Info(set.Val()) - // redis.Expect(set.Err()).NotTo(HaveOccurred()) - // redis.Expect(set.Val()).To(Equal("OK")) - err := set.Err() - if err != nil { - mylog.Error(err) - } - - return mc.delete(key) -} - -// delete 删除一个cache -func (mc *MyRedis) delete(key string) error { - set := mc.Del(mc.getCtx(), key) - mylog.Info(set.Val()) - // redis.Expect(set.Err()).NotTo(HaveOccurred()) - // redis.Expect(set.Val()).To(Equal("OK")) - err := set.Err() - if err != nil { - mylog.Error(err) + for _, v := range out { + err = mc.Delete(v) + if err != nil { + return err + } } return err } + +// GetKeyS 查询所有key +func (mc *redisConOlny) GetKeyS(key interface{}) ([]string, error) { + var keys []string + repy, err := mc.Do(mc.GetRedisClient(), "keys", mc.getKey(key)) + if err != nil { + mylog.Error(err) + return keys, err + } + + switch t := repy.(type) { + case []interface{}: + for _, v := range t { + out, err := redis.String(v, nil) + if err != nil { + mylog.Error(err) + } + keys = append(keys, mc.fixKeyGroupName(out)) + } + default: + return keys, fmt.Errorf("decodeValue err in type not find:%v", t) + } + + return keys, err +} + +// Close 关闭一个连接 +func (mc *redisConOlny) Close() (err error) { + mc.mtx.Lock() + defer mc.mtx.Unlock() + if mc.con != nil { + err = mc.con.Close() + mc.con = nil + } + + return +} + +func usePrecise(dur time.Duration) bool { + return dur < time.Second || dur%time.Second != 0 +} + +func formatMs(dur time.Duration) int64 { + if dur > 0 && dur < time.Millisecond { + return 1 + } + return int64(dur / time.Millisecond) +} + +func formatSec(dur time.Duration) int64 { + if dur > 0 && dur < time.Second { + return 1 + } + return int64(dur / time.Second) +}