From f1a0c9555b8317927a35e7d058503a44334fbd4a Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Thu, 7 Sep 2023 19:13:09 +0800 Subject: [PATCH] =?UTF-8?q?feat=E5=8A=A0=E5=85=A5redis=E5=B7=A5=E5=85=B7?= =?UTF-8?q?=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 1 + go.sum | 4 + lib/core/redis/redis.go | 237 ++++++++++++++++++++++++++++++++++++ restagent/etc/restconf.yaml | 7 ++ restagent/restagent.go | 5 + 5 files changed, 254 insertions(+) create mode 100644 lib/core/redis/redis.go diff --git a/go.mod b/go.mod index 4cdb3132..e05e85d4 100644 --- a/go.mod +++ b/go.mod @@ -30,6 +30,7 @@ require ( gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/yaml.v3 v3.0.1 xorm.io/xorm v1.3.2 + github.com/redis/go-redis/v9 v9.1.0 ) require ( diff --git a/go.sum b/go.sum index 28897b8c..5a079b72 100644 --- a/go.sum +++ b/go.sum @@ -67,6 +67,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bsm/ginkgo/v2 v2.9.5 h1:rtVBYPs3+TC5iLUVOis1B9tjLTup7Cj5IfzosKtvTJ0= +github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y= github.com/bsm/redislock v0.8.2 h1:W0aDRjt6FNmAZovbG2fPyjl1YZZdlqMkCKKCffJew1o= github.com/bsm/redislock v0.8.2/go.mod h1:tC0JZxZCdJN4DCB31cGxgjgf/ye1R4LLNJQd5ecjg08= github.com/bytedance/go-tagexpr/v2 v2.7.12 h1:qL2f0j11S8DHQsUWUA6aacLNBcbPTbNKuzVjaW4kF/M= @@ -571,6 +573,8 @@ github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/redis/go-redis/v9 v9.1.0 h1:137FnGdk+EQdCbye1FW+qOEcY5S+SpY9T0NiuqvtfMY= +github.com/redis/go-redis/v9 v9.1.0/go.mod h1:urWj3He21Dj5k4TK1y59xH8Uj6ATueP8AH1cY3lZl4c= github.com/reiver/go-oi v1.0.0 h1:nvECWD7LF+vOs8leNGV/ww+F2iZKf3EYjYZ527turzM= github.com/reiver/go-oi v1.0.0/go.mod h1:RrDBct90BAhoDTxB1fenZwfykqeGvhI6LsNfStJoEkI= github.com/reiver/go-telnet v0.0.0-20180421082511-9ff0b2ab096e h1:quuzZLi72kkJjl+f5AQ93FMcadG19WkS7MO6TXFOSas= diff --git a/lib/core/redis/redis.go b/lib/core/redis/redis.go new file mode 100644 index 00000000..26e89440 --- /dev/null +++ b/lib/core/redis/redis.go @@ -0,0 +1,237 @@ +package redis + +import ( + "context" + "fmt" + "strings" + "time" + + "ems.agt/lib/core/conf" + "ems.agt/lib/log" + "github.com/redis/go-redis/v9" +) + +// Redis连接实例 +var rdb *redis.Client + +// 声明定义限流脚本命令 +var rateLimitCommand = redis.NewScript(` +local key = KEYS[1] +local time = tonumber(ARGV[1]) +local count = tonumber(ARGV[2]) +local current = redis.call('get', key); +if current and tonumber(current) >= count then + return tonumber(current); +end +current = redis.call('incr', key) +if tonumber(current) == 1 then + redis.call('expire', key, time) +end +return tonumber(current);`) + +// 连接Redis实例 +func Connect() { + ctx := context.Background() + client := conf.Get("redis").(map[string]any) + address := fmt.Sprintf("%s:%d", client["host"], client["port"]) + // 创建连接 + rdb = redis.NewClient(&redis.Options{ + Addr: address, + Password: client["password"].(string), + DB: client["db"].(int), + }) + // 测试数据库连接 + pong, err := rdb.Ping(ctx).Result() + if err != nil { + log.Fatalf("failed error ping redis: %v", err) + } + log.Infof("redis %s connection is successful.", pong) +} + +// 关闭Redis实例 +func Close() { + if err := rdb.Close(); err != nil { + log.Fatalf("fatal error db close: %s", err) + } +} + +// Info 获取redis服务信息 +func Info() map[string]map[string]string { + ctx := context.Background() + info, err := rdb.Info(ctx).Result() + if err != nil { + return map[string]map[string]string{} + } + infoObj := make(map[string]map[string]string) + lines := strings.Split(info, "\r\n") + label := "" + for _, line := range lines { + if strings.Contains(line, "#") { + label = strings.Fields(line)[len(strings.Fields(line))-1] + label = strings.ToLower(label) + infoObj[label] = make(map[string]string) + continue + } + kvArr := strings.Split(line, ":") + if len(kvArr) >= 2 { + key := strings.TrimSpace(kvArr[0]) + value := strings.TrimSpace(kvArr[len(kvArr)-1]) + infoObj[label][key] = value + } + } + return infoObj +} + +// KeySize 获取redis当前连接可用键Key总数信息 +func KeySize() int64 { + ctx := context.Background() + size, err := rdb.DBSize(ctx).Result() + if err != nil { + return 0 + } + return size +} + +// CommandStats 获取redis命令状态信息 +func CommandStats() []map[string]string { + ctx := context.Background() + commandstats, err := rdb.Info(ctx, "commandstats").Result() + if err != nil { + return []map[string]string{} + } + statsObjArr := make([]map[string]string, 0) + lines := strings.Split(commandstats, "\r\n") + for _, line := range lines { + if !strings.HasPrefix(line, "cmdstat_") { + continue + } + kvArr := strings.Split(line, ":") + key := kvArr[0] + valueStr := kvArr[len(kvArr)-1] + statsObj := make(map[string]string) + statsObj["name"] = key[8:] + statsObj["value"] = valueStr[6:strings.Index(valueStr, ",usec=")] + statsObjArr = append(statsObjArr, statsObj) + } + return statsObjArr +} + +// 获取键的剩余有效时间(秒) +func GetExpire(key string) float64 { + ctx := context.Background() + ttl, err := rdb.TTL(ctx, key).Result() + if err != nil { + return 0 + } + return ttl.Seconds() +} + +// 获得缓存数据的key列表 +func GetKeys(pattern string) []string { + // 初始化变量 + var keys []string + var cursor uint64 = 0 + ctx := context.Background() + // 循环遍历获取匹配的键 + for { + // 使用 SCAN 命令获取匹配的键 + batchKeys, nextCursor, err := rdb.Scan(ctx, cursor, pattern, 100).Result() + if err != nil { + log.Errorf("Failed to scan keys: %v", err) + break + } + cursor = nextCursor + keys = append(keys, batchKeys...) + // 当 cursor 为 0,表示遍历完成 + if cursor == 0 { + break + } + } + return keys +} + +// 批量获得缓存数据 +func GetBatch(keys []string) []any { + if len(keys) == 0 { + return []any{} + } + // 获取缓存数据 + result, err := rdb.MGet(context.Background(), keys...).Result() + if err != nil { + log.Errorf("Failed to get batch data: %v", err) + return []any{} + } + return result +} + +// 获得缓存数据 +func Get(key string) string { + ctx := context.Background() + value, err := rdb.Get(ctx, key).Result() + if err == redis.Nil || err != nil { + return "" + } + return value +} + +// 获得缓存数据Hash +func GetHash(key string) map[string]string { + ctx := context.Background() + value, err := rdb.HGetAll(ctx, key).Result() + if err == redis.Nil || err != nil { + return map[string]string{} + } + return value +} + +// 判断是否存在 +func Has(keys ...string) bool { + ctx := context.Background() + exists, err := rdb.Exists(ctx, keys...).Result() + if err != nil { + return false + } + return exists >= 1 +} + +// 设置缓存数据 +func Set(key string, value any) bool { + ctx := context.Background() + err := rdb.Set(ctx, key, value, 0).Err() + return err == nil +} + +// 设置缓存数据与过期时间 +func SetByExpire(key string, value any, expiration time.Duration) bool { + ctx := context.Background() + err := rdb.Set(ctx, key, value, expiration).Err() + return err == nil +} + +// 删除单个 +func Del(key string) bool { + ctx := context.Background() + err := rdb.Del(ctx, key).Err() + return err == nil +} + +// 删除多个 +func DelKeys(keys []string) bool { + if len(keys) == 0 { + return false + } + ctx := context.Background() + err := rdb.Del(ctx, keys...).Err() + return err == nil +} + +// 限流查询并记录 +func RateLimit(limitKey string, time, count int64) int64 { + ctx := context.Background() + result, err := rateLimitCommand.Run(ctx, rdb, []string{limitKey}, time, count).Result() + if err != nil { + log.Errorf("redis lua script err %v", err) + return 0 + } + return result.(int64) +} diff --git a/restagent/etc/restconf.yaml b/restagent/etc/restconf.yaml index ef3da907..7963c0c5 100644 --- a/restagent/etc/restconf.yaml +++ b/restagent/etc/restconf.yaml @@ -43,6 +43,13 @@ database: name: omc_db backup: d:/local.git/ems.agt/restagent/database +# Redis 缓存数据 +redis: + port: 6379 + host: "192.168.4.228" + password: "" + db: 0 + mml: port: 4100 sleep: 200 diff --git a/restagent/restagent.go b/restagent/restagent.go index 16925e0f..336e172c 100644 --- a/restagent/restagent.go +++ b/restagent/restagent.go @@ -10,6 +10,7 @@ import ( "strconv" "strings" + "ems.agt/lib/core/redis" "ems.agt/lib/dborm" "ems.agt/lib/global" "ems.agt/lib/log" @@ -133,6 +134,7 @@ func main() { fmt.Printf("OMC restagent version: %s\n", global.Version) log.Infof("========================= OMC restagent startup =========================") log.Infof("OMC restagent version: %s %s %s", global.Version, global.BuildTime, global.GoVer) + err := dborm.InitDbClient(conf.Database.Type, conf.Database.User, conf.Database.Password, conf.Database.Host, conf.Database.Port, conf.Database.Name) if err != nil { @@ -164,6 +166,9 @@ func main() { os.Exit(4) } + // 连接redis + redis.Connect() + router := routes.NewRouter() // 开启监控采集