文章目录
- 前情提要
- 配置
- 缓存前缀 常量
- 缓存中` key `值问题
- `Redis` 工具包
- redis 使用
首先需要自己安装
redis
并提前了解相关知识
前情提要
学习项目github地址,有需要可以从这里查看源码
上一部分学习笔记
配置
在 api.ini
中新增 redis
相关配置
...
[redis]
Host = 127.0.0.1:6379
Password =
MaxIdle = 30
MaxActive = 30
IdleTimeout = 200
缓存前缀 常量
打开 pkg/e
目录,新建 cache.go
,写入内容:
package econst (CACHE_ARTICLE = "ARTICLE"CACHE_TAG = "TAG"
)
建立常量保存,方便后续管理
缓存中key
值问题
新建 service/cathe_service
目录,新建 article.go
:
// Package cache_service 缓存相关服务,单个或多个文章键名
package cache_serviceimport ("github.com/kingsill/gin-example/pkg/e""strconv""strings"
)// Article 定义缓存中可能的文章键名包含的要素
type Article struct {ID intTagID intState intPageNum intPageSize int
}// GetArticleKey 单个文章键名
func (a *Article) GetArticleKey() string {//ARTICLE_1return e.CACHE_ARTICLE + "_" + strconv.Itoa(a.ID)
}// GetArticlesKey 多个文章键名? 一个文章信息更多的键名
func (a *Article) GetArticlesKey() string {keys := []string{e.CACHE_ARTICLE,"LIST",}if a.ID > 0 {keys = append(keys, strconv.Itoa(a.ID))}if a.TagID > 0 {keys = append(keys, strconv.Itoa(a.TagID))}if a.State >= 0 {keys = append(keys, strconv.Itoa(a.State))}if a.PageNum > 0 {keys = append(keys, strconv.Itoa(a.PageNum))}if a.PageSize > 0 {keys = append(keys, strconv.Itoa(a.PageSize))}//ARTICLE_LIST_...return strings.Join(keys, "_")
}
同目录新建 tag.go
// Package cache_service 缓存相关服务,单个或多个tag键名
package cache_serviceimport ("github.com/kingsill/gin-example/pkg/e""strconv""strings"
)// Tag 定义缓存中可能的Tag键名包含的要素
type Tag struct {ID intName stringState intPageNum intPageSize int
}// GetTagsKey 获取全面的Tag键名
func (t *Tag) GetTagsKey() string {keys := []string{e.CACHE_TAG,"LIST",}if t.Name != "" {keys = append(keys, t.Name)}if t.State >= 0 {keys = append(keys, strconv.Itoa(t.State))}if t.PageNum > 0 {keys = append(keys, strconv.Itoa(t.PageNum))}if t.PageSize > 0 {keys = append(keys, strconv.Itoa(t.PageSize))}return strings.Join(keys, "_")
}
Redis
工具包
- 下载
redigo
包,帮助使用redis
go get github.com/gomodule/redigo@latest
- 打开
pkg
目录,新建gredis/redis.go
,写入内容:
package gredisimport ("encoding/json""github.com/gomodule/redigo/redis""github.com/kingsill/gin-example/pkg/setting""time"
)// RedisConn 定义全局变量RedisConn为redis连接池
var RedisConn *redis.Pool// Setup 初始化redis服务
func Setup() error {//实例化RedisConn,从setting中加载定义的配置信息RedisConn = &redis.Pool{MaxIdle: setting.RedisSetting.MaxIdle,MaxActive: setting.RedisSetting.MaxActive,IdleTimeout: setting.RedisSetting.IdleTimeout,Dial: func() (redis.Conn, error) { //创建并配置一个连接c, err := redis.Dial("tcp", setting.RedisSetting.Host)if err != nil {return nil, err}if setting.RedisSetting.Password != "" {if _, err := c.Do("AUTH", setting.RedisSetting.Password); err != nil {c.Close()return nil, err}}return c, err},TestOnBorrow: func(c redis.Conn, t time.Time) error { //测试连接_, err := c.Do("PING")return err},}return nil
}// Set redis存储的key-value,添加数据
func Set(key string, data interface{}, time int) error {conn := RedisConn.Get()defer conn.Close()//将data以json编码为valuevalue, err := json.Marshal(data)if err != nil {return err}_, err = conn.Do("SET", key, value)if err != nil {return err}//设置过期时间,单位为s_, err = conn.Do("EXPIRE", key, time)if err != nil {return err}return nil
}// Exists 检查当前redis中是否有当前key
func Exists(key string) bool {conn := RedisConn.Get()defer conn.Close()exists, err := redis.Bool(conn.Do("EXISTS", key))if err != nil {return false}return exists
}// Get 获取要查询的key对应的value
func Get(key string) ([]byte, error) {conn := RedisConn.Get()defer conn.Close()reply, err := redis.Bytes(conn.Do("GET", key))if err != nil {return nil, err}return reply, nil
}// Delete 删除redis中传入的键值对
func Delete(key string) (bool, error) {conn := RedisConn.Get()defer conn.Close()return redis.Bool(conn.Do("DEL", key))
}// LikeDeletes 删除所有包含指定键名的键值对
func LikeDeletes(key string) error {conn := RedisConn.Get()defer conn.Close()keys, err := redis.Strings(conn.Do("KEYS", "*"+key+"*"))if err != nil {return err}for _, key := range keys {_, err = Delete(key)if err != nil {return err}}return nil
}
redis 使用
在原来的逻辑中,以检索文件为例,我们直接在 mysql
中检索数据,现在我们在之前新增在 redis
中搜索,如果没有,再跳转到 mysql
并且我们将检索封装成一个函数,代码阅读更加直观
打开 service
目录,新建 article_service/article.go
package article_serviceimport ("encoding/json""github.com/kingsill/gin-example/models""github.com/kingsill/gin-example/pkg/gredis""github.com/kingsill/gin-example/pkg/logging""github.com/kingsill/gin-example/service/cache_service"
)// Article 建立文章结构体,方便信息存储及与gorm的互动
type Article struct {ID intTagID intTitle stringDesc stringContent stringCoverImageUrl stringState intCreatedBy stringModifiedBy stringPageNum intPageSize int
}// Add 新建文章服务
func (a *Article) Add() error {//创建article实例article := map[string]interface{}{"tag_id": a.TagID,"title": a.Title,"desc": a.Desc,"content": a.Content,"created_by": a.CreatedBy,"cover_image_url": a.CoverImageUrl,"state": a.State,}if err := models.AddArticle(article); err != nil {return err}return nil
}// Edit 编辑文章服务
func (a *Article) Edit() error {return models.EditArticle(a.ID, map[string]interface{}{"tag_id": a.TagID,"title": a.Title,"desc": a.Desc,"content": a.Content,"cover_image_url": a.CoverImageUrl,"state": a.State,"modified_by": a.ModifiedBy,})
}// Get 查询文章服务
func (a *Article) Get() (*models.Article, error) {var cacheArticle *models.Article//推算文章键名cache := cache_service.Article{ID: a.ID}key := cache.GetArticleKey()//根据文章键名在redis中查询if gredis.Exists(key) {data, err := gredis.Get(key)if err != nil {logging.Info(err)} else {json.Unmarshal(data, &cacheArticle)return cacheArticle, nil}}//redis中没有查询到则在mysql中查询article, err := models.GetArticle(a.ID)if err != nil {return nil, err}//在redis中存储该组键值对,并设置过期时间gredis.Set(key, article, 3600)return article, nil
}// GetAll 类比上面 get 进行理解
func (a *Article) GetAll() ([]*models.Article, error) {var (articles, cacheArticles []*models.Article)cache := cache_service.Article{TagID: a.TagID,State: a.State,PageNum: a.PageNum,PageSize: a.PageSize,}key := cache.GetArticlesKey()if gredis.Exists(key) {data, err := gredis.Get(key)if err != nil {logging.Info(err)} else {json.Unmarshal(data, &cacheArticles)return cacheArticles, nil}}articles, err := models.GetArticles(a.PageNum, a.PageSize, a.getMaps())if err != nil {return nil, err}gredis.Set(key, articles, 3600)return articles, nil
}// Delete 简单对之前函数的包装调用
func (a *Article) Delete() error {return models.DeleteArticle(a.ID)
}func (a *Article) ExistByID() (bool, error) {return models.ExistArticleByID(a.ID)
}func (a *Article) Count() (int, error) {return models.GetArticleTotal(a.getMaps())
}// 构建适用于多个文章查询等的条件映射
func (a *Article) getMaps() map[string]interface{} {maps := make(map[string]interface{})maps["deleted_on"] = 0if a.State != -1 {maps["state"] = a.State}if a.TagID != -1 {maps["tag_id"] = a.TagID}return maps
}
过程中可能会对之前的函数进行一定小小的修改,根据报错进行修改即可
同样的,大家可以自行建立tag_service/tag.go