Gin 框架学习实录 · 第9篇:实现 Redis 缓存模块封装与应用

前言

在前面几篇文章中,我们已经完成了用户模块的增删改查接口、统一响应结构的封装、数据库和日志模块的配置化管理等核心功能。随着项目的逐步完善,我们的系统也开始处理越来越多的请求。在这个过程中,我们就需要用到缓存操作。

缓存 作为一种常用的性能优化手段,可以有效减少数据库查询压力,提升系统响应速度。而在常见的缓存方案中,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.测试接口

现在,接口已经完成并注册,我们可以使用 Postmancurl 来测试这个接口。

请求示例

发送一个 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
  }
}

总结:

  1. 问题:Go 的结构体字段默认是大写的,导致 JSON 序列化时字段名也变成大写。
  2. 解决方案 :通过在结构体字段上添加 json 标签,显式指定返回的 JSON 字段名为小写。
  3. 效果:返回的 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) 实现用户认证。我们会通过以下步骤逐步完成:

  1. 实现用户登录接口:用户通过提供用户名和密码进行登录。
  2. JWT Token 签发:登录成功后,我们会生成一个 JWT Token,并将其返回给用户。
  3. JWT 中间件验证:通过 JWT 中间件对用户请求进行验证,确保每个请求都携带有效的 Token。

通过这些步骤,我们能够实现一个简单的认证机制,为后续的 API 项目接口做准备。

本篇对应代码提交记录

commit: eb7f08564c75dd0cd567b20a26b0c8b71769eb06

👉 GitHub 源码地址:github.com/luokakale-k...

相关推荐
喵个咪18 小时前
Ent代码生成工具链
后端·go
洛卡卡了21 小时前
Gin 框架学习实录 · 第10篇:实现用户登录功能与 JWT Token 签发及中间件验证
go
CHSnake21 小时前
设计HTTP和gRPC错误码.md
后端·go
一个热爱生活的普通人21 小时前
GO 扩展库: semaphore 实现原理与使用
后端·go
CyberTM1 天前
终端党的福音!开源Git命令速查工具Git Cheat Sheet TUI
git·python·go
Vespeng1 天前
Go-SJSON 组件,JSON 动态修改新方案
go
Wo3Shi4七1 天前
双向列队
数据结构·go
addaduvyhup1 天前
从 Java 的 Spring Boot MVC 转向 Go 语言开发的差异变化
java·spring boot·go·mvc
<e^πi+1=0>1 天前
playwright-go实战:自动化登录测试
go·playwright