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...

相关推荐
研究司马懿6 小时前
【云原生】Gateway API高级功能
云原生·go·gateway·k8s·gateway api
梦想很大很大20 小时前
使用 Go + Gin + Fx 构建工程化后端服务模板(gin-app 实践)
前端·后端·go
lekami_兰1 天前
MySQL 长事务:藏在业务里的性能 “隐形杀手”
数据库·mysql·go·长事务
却尘1 天前
一篇小白也能看懂的 Go 字符串拼接 & Builder & cap 全家桶
后端·go
ん贤1 天前
一次批量删除引发的死锁,最终我选择不加锁
数据库·安全·go·死锁
mtngt112 天前
AI DDD重构实践
go
Grassto3 天前
12 go.sum 是如何保证依赖安全的?校验机制源码解析
安全·golang·go·哈希算法·go module
Grassto5 天前
11 Go Module 缓存机制详解
开发语言·缓存·golang·go·go module
程序设计实验室6 天前
2025年的最后一天,分享我使用go语言开发的电子书转换工具网站
go
我的golang之路果然有问题6 天前
使用 Hugo + GitHub Pages + PaperMod 主题 + Obsidian 搭建开发博客
golang·go·github·博客·个人开发·个人博客·hugo