Skip to content

Commit

Permalink
feat(redis): tendisplus离线数据导入 TencentBlueKing#6596
Browse files Browse the repository at this point in the history
  • Loading branch information
lukemakeit committed Sep 14, 2024
1 parent 507943a commit c0cd3d9
Show file tree
Hide file tree
Showing 48 changed files with 3,544 additions and 85 deletions.
5 changes: 5 additions & 0 deletions dbm-services/redis/db-tools/dbactuator/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ require (

require (
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/clbanning/mxj v1.8.4 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/glebarez/go-sqlite v1.21.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
Expand All @@ -38,11 +40,14 @@ require (
github.com/leodido/go-urn v1.2.3 // indirect
github.com/lufia/plan9stats v0.0.0-20230110061619-bbe2e5e100de // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mozillazg/go-httpheader v0.4.0 // indirect
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rogpeppe/go-internal v1.8.0 // indirect
github.com/smartystreets/assertions v1.2.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/tencentyun/cos-go-sdk-v5 v0.7.54 // indirect
github.com/tklauser/go-sysconf v0.3.11 // indirect
github.com/tklauser/numcpus v0.6.0 // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
Expand Down
21 changes: 21 additions & 0 deletions dbm-services/redis/db-tools/dbactuator/go.sum
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I=
github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
Expand All @@ -31,15 +35,21 @@ github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/grafov/m3u8 v0.12.0/go.mod h1:nqzOkfBiZJENr52zTVd/Dcl03yzphIMbJqkXGu+u080=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
Expand All @@ -62,6 +72,12 @@ github.com/lufia/plan9stats v0.0.0-20230110061619-bbe2e5e100de h1:V53FWzU6KAZVi1
github.com/lufia/plan9stats v0.0.0-20230110061619-bbe2e5e100de/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60=
github.com/mozillazg/go-httpheader v0.4.0 h1:aBn6aRXtFzyDLZ4VIRLsZbbJloagQfMnCiYgOq6hK4w=
github.com/mozillazg/go-httpheader v0.4.0/go.mod h1:PuT8h0pw6efvp8ZeUec1Rs7dwjK08bt6gKSReGMqtdA=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
Expand Down Expand Up @@ -100,12 +116,17 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.563/go.mod h1:uom4Nvi9W+Qkom0exYiJ9VWJjXwyxtPYTkKkaLMlfE0=
github.com/tencentyun/cos-go-sdk-v5 v0.7.54 h1:FRamEhNBbSeggyYfWfzFejTLftgbICocSYFk4PKTSV4=
github.com/tencentyun/cos-go-sdk-v5 v0.7.54/go.mod h1:UN+VdbCl1hg+kKi5RXqZgaP+Boqfmk+D04GRc4XFk70=
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
Expand Down
57 changes: 57 additions & 0 deletions dbm-services/redis/db-tools/dbactuator/models/myredis/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2176,3 +2176,60 @@ func (db *RedisClient) TailRedisLogFile(tailNLine int) (data string, err error)
}
return string(dataBytes), nil
}

// IsReshapeRunning 判断tendisplus/tendisssd是否正在执行reshape
func (db *RedisClient) IsReshapeRunning() (ret bool, err error) {
compactInfo, err := db.Info("Compaction")
if err != nil {
return false, err
}
running := compactInfo["current-compaction-status"]
if running == "running" {
return true, nil
}
return false, nil
}

// WaitTendisReshapeDone 等待tendisplus/tendisssd reshape完成
func (db *RedisClient) WaitTendisReshapeDone() (err error) {
var msg string
count := 0
for {
isReshaping, err := db.IsReshapeRunning()
if err != nil {
return err
}
if !isReshaping {
msg = fmt.Sprintf("redis:%s reshape done", db.Addr)
mylog.Logger.Info(msg)
return nil
}
count++
if (count % 12) == 0 {
msg = fmt.Sprintf("redis:%s reshape is running", db.Addr)
mylog.Logger.Info(msg)
}
time.Sleep(5 * time.Second)
}
}

// TendisReshapeAndWaitDone tendisplus/tendisssd reshape并等待reshape完成
func (db *RedisClient) TendisReshapeAndWaitDone() (err error) {
if db.InstanceClient == nil {
err := fmt.Errorf("reshape redis:%s must create a standalone client", db.Addr)
mylog.Logger.Error(err.Error())
return err
}
isReshaping, err := db.IsReshapeRunning()
if err != nil {
return err
}
if isReshaping {
// 如果正在reshape,则等待reshape完成,不重复执行reshape
return db.WaitTendisReshapeDone()
}
cmd := []interface{}{"reshape"}
// reshape 是阻塞操作,可能会超时,所以不捕获错误
db.InstanceClient.Do(context.TODO(), cmd...).Result()
return db.WaitTendisReshapeDone()
}
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,9 @@ func (job *RedisConfigSet) allInstsAbleToConnect() (err error) {
}
job.AddrMapConfigFile[addr] = confFile
// 获取密码
if job.params.Role == consts.MetaRolePredixy || job.params.Role == consts.MetaRoleTwemproxy {
if job.params.Role == consts.MetaRolePredixy {
password, err = myredis.GetPredixyAdminPasswdFromConfFlie(port)
} else if job.params.Role == consts.MetaRoleTwemproxy {
password, err = myredis.GetProxyPasswdFromConfFlie(port, job.params.Role)
} else {
password, err = myredis.GetRedisPasswdFromConfFile(port)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
package atomredis

import (
"encoding/json"
"fmt"
"sync"
"time"

"github.com/go-playground/validator/v10"

"dbm-services/redis/db-tools/dbactuator/models/myredis"
"dbm-services/redis/db-tools/dbactuator/pkg/consts"
"dbm-services/redis/db-tools/dbactuator/pkg/jobruntime"
)

// RedisReshapeParam redis reshape param
type RedisReshapeParam struct {
RedisPassword string `json:"redis_password" validate:"required"`
Instances []instItem `json:"instances" validate:"required"`
}

// reshapeTaskItem reshape task item,便于做并发
type reshapeTaskItem struct {
instItem
Password string `json:"password"`
redisConn *myredis.RedisClient
Err error
}

// RedisReshape redis shape
type RedisReshape struct {
runtime *jobruntime.JobGenericRuntime
params RedisReshapeParam
ReshapeTasks []*reshapeTaskItem
}

// 无实际作用,仅确保实现了 jobruntime.JobRunner 接口
var _ jobruntime.JobRunner = (*RedisReshape)(nil)

// NewRedisReshape new
func NewRedisReshape() jobruntime.JobRunner {
return &RedisReshape{}
}

// Init 初始化
func (job *RedisReshape) Init(m *jobruntime.JobGenericRuntime) error {
job.runtime = m
err := json.Unmarshal([]byte(job.runtime.PayloadDecoded), &job.params)
if err != nil {
job.runtime.Logger.Error(fmt.Sprintf("json.Unmarshal failed,err:%+v", err))
return err
}
// 参数有效性检查
validate := validator.New()
err = validate.Struct(job.params)
if err != nil {
if _, ok := err.(*validator.InvalidValidationError); ok {
job.runtime.Logger.Error("RedisReshape Init params validate failed,err:%v,params:%+v",
err, job.params)
return err
}
for _, err := range err.(validator.ValidationErrors) {
job.runtime.Logger.Error("RedisReshape Init params validate failed,err:%v,params:%+v",
err, job.params)
return err
}
}
return nil
}

// Name 原子任务名
func (job *RedisReshape) Name() string {
return "redis_reshape"
}

// Run Command Run
func (job *RedisReshape) Run() (err error) {
job.ReshapeTasks = make([]*reshapeTaskItem, 0, len(job.params.Instances))
for _, instItem := range job.params.Instances {
reshapeTask := &reshapeTaskItem{
instItem: instItem,
Password: job.params.RedisPassword,
}
job.ReshapeTasks = append(job.ReshapeTasks, reshapeTask)
}
err = job.allInstCconnect()
if err != nil {
return err
}
defer job.allInstDisconnect()

err = job.ReshapeAndWaitDone()
if err != nil {
return err
}
return nil
}

func (job *RedisReshape) allInstCconnect() (err error) {
wg := sync.WaitGroup{}
// 并发建立连接
for _, tmp := range job.ReshapeTasks {
task := tmp
wg.Add(1)
go func(task *reshapeTaskItem) {
defer wg.Done()
task.redisConn, task.Err = myredis.NewRedisClientWithTimeout(task.Addr(), task.Password, 0,
consts.TendisTypeRedisInstance, 10*time.Hour)
}(task)
}
wg.Wait()
for _, tmp := range job.ReshapeTasks {
task := tmp
if task.Err != nil {
return task.Err
}
}
return nil
}

// allInstDisconnect 所有实例断开连接
func (job *RedisReshape) allInstDisconnect() {
for _, tmp := range job.ReshapeTasks {
task := tmp
if task.redisConn != nil {
task.redisConn.Close()
task.redisConn = nil
}
}
}

// ReshapeAndWaitDone 多实例并发执行reshape
func (job *RedisReshape) ReshapeAndWaitDone() error {
// 根据salveIP做分组
tasksMapSlice := make(map[string][]*reshapeTaskItem)
maxCount := 0
for _, tmp := range job.ReshapeTasks {
task := tmp
tasksMapSlice[task.IP] = append(tasksMapSlice[task.IP], task)
if len(tasksMapSlice[task.IP]) > maxCount {
maxCount = len(tasksMapSlice[task.IP])
}
}
// 同IP实例间串行,多IP实例间并行
for idx := 0; idx < maxCount; idx++ {
groupTasks := []*reshapeTaskItem{}
for ip := range tasksMapSlice {
if len(tasksMapSlice[ip]) > idx {
groupTasks = append(groupTasks, tasksMapSlice[ip][idx])
}
}
wg := sync.WaitGroup{}
for _, taskItem := range groupTasks {
task01 := taskItem
wg.Add(1)
go func(task02 *reshapeTaskItem) {
defer wg.Done()
job.runtime.Logger.Info("tendisplus %s start reshape", task02.Addr())
task02.Err = task02.redisConn.TendisReshapeAndWaitDone()
}(task01)
}
wg.Wait()
for _, taskItem := range groupTasks {
task01 := taskItem
if task01.Err != nil {
return task01.Err
}
}
}
return nil
}

// Retry times
func (job *RedisReshape) Retry() uint {
return 2
}

// Rollback rollback
func (job *RedisReshape) Rollback() error {
return nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ func (m *JobGenericManager) atomjobsMapperLoading() {
m.atomJobMapper[atomsys.NewChangePassword().Name()] = atomsys.NewChangePassword
m.atomJobMapper[atomredis.NewRedisLoadModules().Name()] = atomredis.NewRedisLoadModules
m.atomJobMapper[atomproxy.NewPredixyAddModulesCmds().Name()] = atomproxy.NewPredixyAddModulesCmds
m.atomJobMapper[atomredis.NewRedisReshape().Name()] = atomredis.NewRedisReshape
// 老备份系统
// m.atomJobMapper[atomredis.NewRedisDataRecover().Name()] = atomredis.NewRedisDataRecover
m.atomJobMapper[atomredis.NewRedisDataStructure().Name()] = atomredis.NewRedisDataStructure
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,10 @@ func (job *Job) SetEventSender() {
job.Conf.BeatPath,
job.Conf.AgentAddress,
)
if job.Err != nil {
mylog.Logger.Error(fmt.Sprintf("set event sender fail,err:%v", job.Err))
return
}
if len(job.Conf.Servers) == 0 {
return
}
Expand Down
3 changes: 2 additions & 1 deletion dbm-services/redis/redis-dts/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ func main() {
constvar.GetZoneName(), tclog.Logger, wg))
jobers = append(jobers, dtsJob.NewRedisCacheDtsJob(constvar.GetBkCloudID(), localIP,
constvar.GetZoneName(), tclog.Logger, wg))

jobers = append(jobers, dtsJob.NewTendisplusLightningJob(constvar.GetBkCloudID(), localIP,
constvar.GetZoneName(), tclog.Logger, wg))
for _, jober := range jobers {
jober.StartBgWorkers()
}
Expand Down
12 changes: 12 additions & 0 deletions dbm-services/redis/redis-dts/models/myredis/myredis.go
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,18 @@ func (db *RedisWorker) GetMasterAddrAndPasswd() (masterAddr, masterAuth string,
return
}

// Loadexternalfiles 加载外部sst文件
func (db *RedisWorker) Loadexternalfiles(sstDir, slosStr, loadMode string) (err error) {
cmd := []interface{}{"loadexternalfiles", sstDir, slosStr, loadMode}
_, err = db.Client.Do(context.TODO(), cmd...).Result()
if err != nil {
err = fmt.Errorf("redis:%s %+v,err:%v", db.Addr, cmd, err)
db.logger.Error(err.Error())
return
}
return
}

// Close :关闭client
func (db *RedisWorker) Close() {
if db.Client == nil {
Expand Down
Loading

0 comments on commit c0cd3d9

Please sign in to comment.