使用gin搭建api后台系统之redis

redis 在web系统中有着非常重要的地位,可以充当缓存,消息队列,分步式锁等,本文介绍一下使用go-redis这个库来简单地操作redis。

环境准备

使用docker 本地启一个redis

docker run --name myredis -p 6379:6379 -d redis 启动一个无密码的redis服务器。

安装go-redis, go get github.com/go-redis/redis/v8

连接redis

在之前介绍MySQL的时候,在项目global中定义一个全局的MySQL对象,这里依然采用这种方式,在global/global.go中声明Redis变量

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package global

import (
	"database/sql"
	"github.com/go-redis/redis/v8"
)

var (
	Mysql *sql.DB
	Redis *redis.Client
)

有两种方式连接redis,一种是使用配置方式,一种是使用字符串的方式

使用配置方式,通过设置redis.Options 结构体,可以对redis的连接信息进行设置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package db

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/go-redis/redis/v8"
	_ "github.com/go-sql-driver/mysql"
	"time"
)



func InitDB() *sql.DB {
	dsn := fmt.Sprintf("%s:%s@(%s)/%s?charset=%s&parseTime=true&loc=Local",
		"root", "123456", "127.0.0.1:3306", "gintest", "utf8")

	if conn, err := sql.Open("mysql", dsn); err != nil {
		panic(err.Error())
	} else {
		conn.SetConnMaxLifetime(7 * time.Second) //设置空闲时间,这个是比mysql 主动断开的时候短
		conn.SetMaxOpenConns(10)
		conn.SetMaxIdleConns(10)
		return conn
	}
}

func InitRedis() *redis.Client  {
	rdb := redis.NewClient(&redis.Options{
		Addr:	  "localhost:6379",
		Password: "", // no password set
		DB:		  0,  // use default DB
	})
	result := rdb.Ping(context.Background())
	fmt.Println("redis ping:", result.Val())
	if result.Val()!="PONG"{
		// 连接有问题
		return nil
	}
	return rdb
}

还有一种是字符串的方式

1
2
3
4
5
opt, err := redis.ParseURL("redis://<user>:<pass>@localhost:6379/<db>")
if err != nil {
	panic(err)
}
rdb := redis.NewClient(opt)

个人还是比较喜欢用配置的方式,可以很直观的看出来各种参数。

如果连接没有问题,执行Ping命令,会返回PONG 字符串,如果有问题,如redis命令连接有问题,则返回空字符串,可以根据这个返回值判断是否正常连接上了redis服务器。

在main.go 中对Redis进行赋值

1
2
3
4
5
6
func main() {
	r := gin.Default()
	global.Mysql = db.InitDB()
	global.Redis = db.InitRedis()
  r.Run(":8080") 
}

连接池

为了减少频繁的数据库连接,推荐使用连接池,由go-redis来维护可用的连接,在初始化redis连接时,使用PoolSize 参数来设置连接池大小。

1
2
3
4
5
6
rdb := redis.NewClient(&redis.Options{
		Addr:	  "localhost:6379",
		Password: "", // no password set
		DB:		  0,  // use default DB
		PoolSize: 10,
})

操作redis

redis.Client 内置了很多操作方法,基本上可以满足一般的使用,如操作字符串的Get,Set, SetNX等,操作hash的HSet等,基本上和redis-cli差不多,下面以普通的字符串为例

redis的SET命令的返回值说明如下

从 Redis 2.6.12 版本开始, SET 命令只在设置操作成功完成时才返回 OK ; 如果命令使用了 NX 或者 XX 选项, 但是因为条件没达到而造成设置操作未执行, 那么命令将返回空批量回复(NULL Bulk Reply)。

参考: http://redisdoc.com/string/set.html

在handler文件中编写代码

1
2
3
4
5
6
7
8
func (UserV1) RedisSet(c *gin.Context)  {
	key := c.Query("key")
	val := c.Query("val")
	result, err := global.Redis.Set(c, key,val,0).Result()
	fmt.Println("err:", err)
	fmt.Println(result)
	c.JSON(200, gin.H{"result": result})
}

在V8版本的go-redis中,操作redis的命令都需要一个context参数,这里可以传*gin.Context,上面的函数如果没有错误,err为nil, result 为OK。

空值nil问题

当redis返回为空的时候,如要查询的key 不存在时,或者命令执行时返回的就是nil,

如果key不存在的时候,也可以判断Result()返回的err是否等于redisl.Nil

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
func (UserV1) RedisGet(c *gin.Context)  {
	key := c.Query("key")
	result := global.Redis.Get(c, key)
	res := make(map[string]string)
	val, err := result.Result()
	if err == redis.Nil{
		res["msg"] = fmt.Sprintf("没有key:%s ", key)
	}else if err!= nil{
		res["msg"] = err.Error()
	}else{
		res["msg"] = val
	}
	c.JSON(200, gin.H{"data": res})
}

命令的执行结果有多种,有的是String, 有的bool,还有Scan类型的,具体还得看源码, 如Get返回的是*StringCmd,它的Result() 方法返回 的是string, err,又如SetNX返回的就是*BoolCmd类型,它的Result() 方法返回的就是bool,err, 则这时就可以直接判断了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
func (UserV1) RedisSet(c *gin.Context)  {
	key := c.Query("key")
	val := c.Query("val")
	result, err := global.Redis.SetNX(c, key,val,0).Result()
	fmt.Println(result, err, 111)
	if err == redis.Nil{
		c.JSON(200, gin.H{"result": "key:"+key+" 已经存在"})
	}else if err!=nil{
		c.JSON(200, gin.H{"result": err})
	}else{
		if result{
			c.JSON(200, gin.H{"result": "设置成功"})
		}else{
			c.JSON(200, gin.H{"result": "设置失败"})
		}
	}
}

本文简单的介绍了使用go-redis连接redis并执行一些set,get,之后会再介绍更高级的使用场景,如消息队列,发布订阅,pipeline, 事务等。

参考文章

golang操作redis/go-redis库

go-redis文档