前言
在前面几篇文章中,我们已经完成了用户模块的增删改查接口、统一响应结构的封装、数据库和日志模块的配置化管理等核心功能。随着项目的逐步完善,我们的系统也开始处理越来越多的请求。在这个过程中,我们就需要用到缓存操作。
缓存 作为一种常用的性能优化手段,可以有效减少数据库查询压力,提升系统响应速度。而在常见的缓存方案中,Redis 因其高效、稳定、支持丰富的数据类型和操作而成为了首选的缓存技术之一。
本篇文章中,我们将介绍如何在 Gin 项目中集成 Redis 缓存模块,封装 Redis 操作,实现在数据库查询时利用 Redis 缓存数据,减少重复查询,从而优化系统性能。
1.安装 Redis 客户端库
使用官方推荐的 go-redis
bash
go get github.com/redis/go-redis/v9
2.配置 redis 信息(config.yaml)
yaml
redis:
addr: 127.0.0.1:6379
password: ""
db: 0
3.初始化 Redis 客户端
我们新建 config/redis.go
go
package config
import (
"context"
"github.com/redis/go-redis/v9"
)
var RedisClient *redis.Client
var Ctx = context.Background()
func InitRedis() {
RedisClient = redis.NewClient(&redis.Options{
Addr: Conf.Redis.Addr,
Password: Conf.Redis.Password,
DB: Conf.Redis.DB,
})
_, err := RedisClient.Ping(Ctx).Result()
if err != nil {
panic("Redis连接失败: " + err.Error())
}
}
别忘了在 config.go
的配置结构体中添加:
go
type Redis struct {
Addr string
Password string
DB int
}
// Config 总配置结构体(对应 config.yaml)
type Config struct {
App App `mapstructure:"app"`
MySQL MySQLInfo `mapstructure:"mysql"`
Logger Logger `mapstructure:"logger"`
Redis Redis `mapstructure:"redis"`
}
并在 main.go
中初始化:
go
config.InitRedis()
4.封装通用 Redis 方法
新建 utils/redis.go
go
package utils
import (
"gin-learn-notes/config"
"time"
)
func RedisSet(key string, value interface{}, expiration time.Duration) error {
return config.RedisClient.Set(config.Ctx, key, value, expiration).Err()
}
func RedisGet(key string) (string, error) {
return config.RedisClient.Get(config.Ctx, key).Result()
}
func RedisDel(key string) error {
return config.RedisClient.Del(config.Ctx, key).Err()
}
5. 应用到业务中
在前面,我们成功地配置并封装了 Redis 缓存操作。接下来,我们将缓存机制应用到具体的业务逻辑中,通过 Redis 缓存减少数据库访问的次数,从而提升系统的性能。
步骤:使用缓存获取用户信息
为了实现缓存,我们可以在 service/user_service.go
中创建一个方法,用于获取用户信息。如果缓存中存在用户数据,则直接从缓存中读取,否则从数据库中查询并将结果写入缓存。 以下是具体的实现:
go
package service
import (
"encoding/json"
"fmt"
"gin-learn-notes/model"
"gin-learn-notes/utils"
"time"
)
// 使用缓存获取用户信息
func GetUserProfileWithCache(userID uint) (*model.User, error) {
cacheKey := fmt.Sprintf("user:info:%d", userID)
// 先查询缓存
cacheStr, err := utils.RedisGet(cacheKey)
if err == nil && cacheStr != "" {
var user model.User
// 缓存命中,返回缓存中的数据
json.Unmarshal([]byte(cacheStr), &user)
return &user, nil
}
// 缓存未命中,查询数据库
user, err := GetUserByID(userID)
if err != nil {
return nil, err
}
// 将查询结果写入缓存,缓存时间设置为10分钟
userStr, _ := json.Marshal(user)
utils.RedisSet(cacheKey, string(userStr), 10*time.Minute)
return user, nil
}
6. 在控制器中应用 Redis 缓存
在上一部分,我们已经封装了使用 Redis 缓存获取用户信息的业务逻辑。接下来,我们需要在控制器中创建相应的接口,以便通过缓存机制提供用户信息。
步骤:在控制器中实现获取用户信息接口
我们在 controller/user.go
中新建一个函数,用于获取用户的个人信息。首先,它会通过缓存读取用户数据,如果缓存未命中则会查询数据库,并将结果存入缓存。
以下是实现代码:
go
package controller
import (
"gin-learn-notes/service"
"gin-learn-notes/request"
"gin-learn-notes/response"
"github.com/gin-gonic/gin"
)
// 获取用户个人信息(优先缓存)
func GetUserProfile(c *gin.Context) {
var req request.GetUserInfoRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Fail(c, response.ParamError, "参数错误")
return
}
// 调用服务层的 Redis 缓存方法
user, err := service.GetUserProfileWithCache(req.ID)
if err != nil {
// 如果用户不存在
response.Fail(c, response.NotFound, "用户不存在")
return
}
// 返回成功响应,返回用户信息
response.Success(c, user)
}
7.新增路由
我们将 GetUserProfile
接口通过 POST 请求注册到路由中。
在 router/router.go
文件中,添加以下内容:
go
package router
import (
"github.com/gin-gonic/gin"
"gin-learn-notes/controller"
)
func InitRouter() *gin.Engine {
r := gin.Default()
// 其他路由...
// 新增用户信息获取接口
r.POST("/profile", controller.GetUserProfile)
return r
}
8.测试接口
现在,接口已经完成并注册,我们可以使用 Postman 或 curl 来测试这个接口。
请求示例:
发送一个 POST
请求到 /profile
,请求体格式如下:
json
{
"id": 1
}
期望响应:
如果缓存命中,返回缓存的用户信息;如果缓存未命中,查询数据库并缓存结果。
成功响应的格式如下:
json
{
"code": 0,
"msg": "success",
"data": {
"ID": 1,
"Name": "Alice",
"Age": 25
}
}
如果用户未找到,返回:
json
{
"code": 10002,
"msg": "用户不存在"
}
错误示例:
如果请求参数错误,返回:
json
{
"code": 10001,
"msg": "参数错误"
}
9.验证缓存数据
在成功测试接口后,我们可以通过 Redis 客户端验证缓存是否已经成功存储了该用户 ID 对应的数据。通过 其他 Redis 客户端工具,我们可以查看 Redis 中缓存的数据。
10.优化返回字段为小写
在我们测试接口时,可能会发现返回的 JSON 数据中的字段名是大写的。例如:
json
{
"code": 0,
"msg": "success",
"data": {
"ID": 1,
"Name": "Alice",
"Age": 25
}
}
为什么返回字段是大写的?
在 Go 中,结构体的字段默认需要首字母大写,才能被外部包访问。这个特性也适用于 JSON 序列化,Go 默认会将结构体中导出的字段(即首字母大写的字段)转换为 JSON 中的大写字段。
例如,Go 语言中的结构体是这样定义的:
go
type User struct {
ID uint
Name string
Age int
}
JSON 序列化时,Go 会将这些大写字段名映射到 JSON 中,导致返回结果中的字段名是大写的。
如何修改为小写?
为了符合前端常用的小写字段规范,我们可以通过 json
标签来指定字段名。在结构体中,为每个字段添加 json
标签并指定小写字段名。
修改后的模型如下:
go
package model
type User struct {
ID uint `gorm:"primaryKey" json:"id"` // 小写字段名
Name string `gorm:"size:50" json:"name"` // 小写字段名
Age int `gorm:"default:18" json:"age"` // 小写字段名
}
通过为每个字段添加 json
标签,我们确保了返回的数据中的字段名会是小写的:
json
{
"code": 0,
"msg": "success",
"data": {
"id": 1,
"name": "Alice",
"age": 25
}
}
总结:
- 问题:Go 的结构体字段默认是大写的,导致 JSON 序列化时字段名也变成大写。
- 解决方案 :通过在结构体字段上添加
json
标签,显式指定返回的 JSON 字段名为小写。 - 效果:返回的 JSON 数据符合前端惯用的小写字段规范,避免了大小写不一致的问题。
通过这种方式,我们可以确保返回的数据格式更符合前端的要求,避免了因大小写不一致而可能出现的兼容性问题。
项目结构继续演进(Redis 缓存集成)
go
gin-learn-notes/
├── config/
│ ├── config.go // ✅ 配置读取模块(使用 viper)
│ ├── database.go // ✅ 使用配置初始化数据库
│ └── redis.go // ✅ 使用配置初始化 Redis
│
├── controller/
│ └── user.go // ✅ 新增 GetUserProfile 控制器,应用 Redis 缓存
│
├── core/
│ └── response/
│ ├── code.go // ✅ 错误码常量
│ ├── page.go // ✅ 分页响应结构
│ └── response.go // ✅ 通用响应封装
│
├── logger/
│ └── logger.go // ✅ zap 日志封装(支持配置 + 文件切割)
│
├── model/
│ └── user.go // ✅ 用户数据模型
│
├── request/
│ └── user_request.go // ✅ 用户请求参数定义
│
├── router/
│ └── router.go // ✅ 注册 POST 路由 /profile
│
├── service/
│ └── user_service.go // ✅ 使用 Redis 缓存优化用户查询
│
├── utils/
│ ├── paginate.go // ✅ 通用分页逻辑(泛型)
│ ├── response.go // ✅ Success / Fail 响应封装
│ ├── redis.go // ✅ Redis 操作封装(Set / Get / Del)
│ └── validator.go // ✅ 错误校验翻译工具
│
├── config.yaml // ✅ 全局配置文件(端口、数据库、日志、Redis)
├── go.mod
├── LICENSE
├── main.go // ✅ 启动入口(按配置加载服务)
├── README.md
本篇小结
在本篇中,我们成功地将 Redis 缓存 集成到了项目中,并优化了用户信息查询接口:
- 配置 Redis 客户端,并从
config.yaml
中读取配置信息; - 在
service
层封装了 Redis 缓存的操作方法,减少数据库查询; - 在
controller
层应用 Redis 缓存机制,提升了用户信息查询接口的性能; - 对返回字段进行了优化,确保 JSON 返回结果符合前端习惯(小写字段)。
在之前的几篇文章中,我们已经完成了 Gin 框架的基本入门和一些常见功能模块的实现,包括用户模块的增删改查、Redis 缓存的使用、通用的响应结构封装等。这些知识点帮助我们建立了一个基础的 Gin 项目框架。
接下来,我们将尝试开发一个 登录接口 ,并使用 JWT (JSON Web Token) 实现用户认证。我们会通过以下步骤逐步完成:
- 实现用户登录接口:用户通过提供用户名和密码进行登录。
- JWT Token 签发:登录成功后,我们会生成一个 JWT Token,并将其返回给用户。
- JWT 中间件验证:通过 JWT 中间件对用户请求进行验证,确保每个请求都携带有效的 Token。
通过这些步骤,我们能够实现一个简单的认证机制,为后续的 API 项目接口做准备。
本篇对应代码提交记录
commit: eb7f08564c75dd0cd567b20a26b0c8b71769eb06
👉 GitHub 源码地址:github.com/luokakale-k...