GO学习记录八——多文件封装功能+redis使用 - 教程

这篇打算学习redis使用,在学习过程中顺便梳理了下怎么多文件封装方法,多文件调用。
redis内容有点多,目前只接触了基础api,打算之后完善下再继续补充,先贴出来部分内容是为了防止之后懒得写这个系列了。
一、多文件封装功能函数
1.现在主目录下创建文件目录结构,以此项目为例,我创建了redis_model文件夹(最开始创建的是redis文件夹名称,发现会和官方的redis模块名称冲突提示编译错误),在redis_model文件夹下创建具体的go脚本文件:redis_client.go,redis_hash.go,redis_list.go,redis_set.go,redis_string.go,redis_zset.go。对应redis的客户端和不同的数据结构。
2.具体redis数据结构就不写介绍凑字数了,网上或者AI有一大堆的介绍。直接贴代码内容
redis_client.go

// redis_model/redis_client.go
package redis_model
import (
"context"
"time"
"github.com/go-redis/redis/v8"
)
var (
Client *redis.Client
Ctx = context.Background()
)
// InitRedis 初始化 Redis 客户端
func InitRedis(addr, password string, db int) error {
Client = redis.NewClient(&redis.Options{
Addr: addr, // e.g., "localhost:6379"
Password: password, // no password set
DB: db, // use default DB
PoolSize: 10,
DialTimeout: 5 * time.Second,
ReadTimeout: 3 * time.Second,
WriteTimeout: 3 * time.Second,
})
// 测试连接
_, err := Client.Ping(Ctx).Result()
return err
}
// Close 关闭 Redis 连接
func Close() {
_ = Client.Close()
}

redis_hash.go

// redis_model/redis_hash.go
package redis_model
// HashSet 设置 hash 字段
func HashSet(key, field, value string) error {
return Client.HSet(Ctx, key, field, value).Err()
}
// HashMSet 批量设置 hash
func HashMSet(key string, fields map[string]interface{
}) error {
return Client.HMSet(Ctx, key, fields).Err()
}
// HashGet 获取 hash 字段
func HashGet(key, field string) (string, error) {
return Client.HGet(Ctx, key, field).Result()
}
// HashGetAll 获取所有字段
func HashGetAll(key string) (map[string]string, error) {
return Client.HGetAll(Ctx, key).Result()
}
// HashDel 删除字段
func HashDel(key string, fields ...string) error {
return Client.HDel(Ctx, key, fields...).Err()
}
// HashExists 判断字段是否存在
func HashExists(key, field string) (bool, error) {
exists, err := Client.HExists(Ctx, key, field).Result()
return exists, err
}

redis_list.go

// redis_model/redis_list.go
package redis_model
// ListLPush 从左侧插入
func ListLPush(key string, values ...interface{
}) error {
return Client.LPush(Ctx, key, values...).Err()
}
// ListRPush 从右侧插入
func ListRPush(key string, values ...interface{
}) error {
return Client.RPush(Ctx, key, values...).Err()
}
// ListLPop 从左侧弹出
func ListLPop(key string) (string, error) {
return Client.LPop(Ctx, key).Result()
}
// ListRPop 从右侧弹出
func ListRPop(key string) (string, error) {
return Client.RPop(Ctx, key).Result()
}
// ListRange 获取范围 [start, stop]
func ListRange(key string, start, stop int64) ([]string, error) {
return Client.LRange(Ctx, key, start, stop).Result()
}
// ListLen 获取长度
func ListLen(key string) (int64, error) {
return Client.LLen(Ctx, key).Result()
}
// ListDel 删除整个 list
func ListDel(key string) error {
return Client.Del(Ctx, key).Err()
}

redis_set.go

// redis_model/redis_set.go
package redis_model
// SetAdd 添加元素
func SetAdd(key string, members ...interface{
}) error {
return Client.SAdd(Ctx, key, members...).Err()
}
// SetMembers 获取所有元素
func SetMembers(key string) ([]string, error) {
return Client.SMembers(Ctx, key).Result()
}
// SetIsMember 判断是否是成员
func SetIsMember(key string, member interface{
}) (bool, error) {
exists, err := Client.SIsMember(Ctx, key, member).Result()
return exists, err
}
// SetRem 删除元素
func SetRem(key string, members ...interface{
}) error {
return Client.SRem(Ctx, key, members...).Err()
}
// SetCard 获取集合大小
func SetCard(key string) (int64, error) {
return Client.SCard(Ctx, key).Result()
}
// SetPop 随机弹出一个元素
func SetPop(key string) (string, error) {
return Client.SPop(Ctx, key).Result()
}

redis_string.go

// redis_model/redis_string.go
package redis_model
import "time"
// StringSet 设置字符串
func StringSet(key, value string, expiration time.Duration) error {
return Client.Set(Ctx, key, value, expiration).Err()
}
// StringGet 获取字符串
func StringGet(key string) (string, error) {
return Client.Get(Ctx, key).Result()
}
// StringDel 删除字符串
func StringDel(key string) error {
return Client.Del(Ctx, key).Err()
}
// StringExpire 设置过期时间
func StringExpire(key string, expiration time.Duration) error {
return Client.Expire(Ctx, key, expiration).Err()
}
// StringExists 检查是否存在
func StringExists(key string) (bool, error) {
count, err := Client.Exists(Ctx, key).Result()
return count >
0, err
}

redis_zset.go

// redis_model/redis_zset.go
package redis_model
import "github.com/go-redis/redis/v8"
// ZAdd 添加元素(score, member)
func ZAdd(key string, members ...*redis.Z) error {
return Client.ZAdd(Ctx, key, members...).Err()
}
// ZRange 获取升序范围
func ZRange(key string, start, stop int64) ([]string, error) {
return Client.ZRange(Ctx, key, start, stop).Result()
}
// ZRevRange 获取降序范围
func ZRevRange(key string, start, stop int64) ([]string, error) {
return Client.ZRevRange(Ctx, key, start, stop).Result()
}
// ZRangeWithScores 获取带分数的元素
func ZRangeWithScores(key string, start, stop int64) ([]redis.Z, error) {
return Client.ZRangeWithScores(Ctx, key, start, stop).Result()
}
// ZScore 获取成员分数
func ZScore(key, member string) (float64, error) {
return Client.ZScore(Ctx, key, member).Result()
}
// ZRem 删除成员
func ZRem(key string, members ...string) error {
var interfaceMembers []interface{
}
for _, member := range members {
interfaceMembers = append(interfaceMembers, member)
}
return Client.ZRem(Ctx, key, interfaceMembers...).Err()
}
// ZCard 获取有序集合大小
func ZCard(key string) (int64, error) {
return Client.ZCard(Ctx, key).Result()
}
// ZRank 获取升序排名
func ZRank(key, member string) (int64, error) {
return Client.ZRank(Ctx, key, member).Result()
}
// ZRevRank 获取降序排名
func ZRevRank(key, member string) (int64, error) {
return Client.ZRevRank(Ctx, key, member).Result()
}

这里只贴了main方法中的代码,其他部分和之前的一样。

package main
import (
"context"
"crypto/rand"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"path/filepath"
"regexp"
"strings"
"sync"
"time"
"github.com/gin-gonic/gin"
"github.com/go-redis/redis/v8"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
// 注意:替换为你项目的实际路径
// _ "your_project/docs" // docs 包,由 swag 生成
// 如果 docs 包在根目录,且 main.go 也在根目录,可以这样导入
_ "HTTPServices/docs" // 假设 docs 目录在项目根目录下
"HTTPServices/redis_model"
_ "HTTPServices/redis_model"
)
const (
tokenLength = 32 // 令牌长度
tokenExpiry = 24 * time.Hour // 令牌有效期
)
var (
validTokens = make(map[string]time.Time) // 有效令牌
tokenMutex sync.Mutex // 令牌锁
)
type TokenResponse struct {
Token string `json:"token"`
Expires time.Time `json:"expires"`
}
var db *pgxpool.Pool
// 启动函数
func main() {
// 初始化 直接使用文件夹名称作为对象,调用里面的方法
if err := redis_model.InitRedis("localhost:6379", "", 0); err != nil {
LogError("Redis连接失败:%v", err)
} else {
LogSuccess("Redis连接成功")
}
defer redis_model.Close()
// String 操作
redis_model.StringSet("name", "Alice", 10*time.Second)
name, _ := redis_model.StringGet("name")
LogInfo("Name:", name)
// Hash 操作
redis_model.HashSet("user:1001", "name", "Bob")
redis_model.HashSet("user:1001", "age", "25")
user, _ := redis_model.HashGetAll("user:1001")
LogInfo("User:", user)
// List 操作
redis_model.ListRPush("tasks", "task1", "task2")
tasks, _ := redis_model.ListRange("tasks", 0, -1)
LogInfo("Tasks:", tasks)
// Set 操作
redis_model.SetAdd("tags", "go", "redis", "cache")
tags, _ := redis_model.SetMembers("tags")
LogInfo("Tags:", tags)
// ZSet 操作
redis_model.ZAdd("leaderboard", &redis.Z{Score: 100, Member: "alice"
}, &redis.Z{Score: 90, Member: "bob"
})
top, _ := redis_model.ZRevRange("leaderboard", 0, -1)
LogInfo("Top:", top)
// 初始化数据库连接
db = InitDB()
defer db.Close()
// 注册路由
RegisterRouter()
// 启动 HTTP 服务
go func() {
StartHTTPServer()
}()
// 启动 HTTP api测试服务
go func() {
StartDebugHTTPServer()
}()
// 阻塞主线程
select {
}
}

3.main()代码中可以看到如何调用的其他文件中的方法。
1)直接使用文件夹名称“redis_model”作为对象名,就可以直接点出里面的方法了。
2)作为封装,提供给外部调用的方法,go要求方法名首字母必须大写。
3)注意package包的引入内容,redis相关的功能脚本都放在了“redis_model”文件夹下,所以package对应redis_model,而main.go放在根目录下,所以package对应main。
4)在main的import中,添加redis_model的对应路径,“HTTPServices/redis_model”,我的项目工程叫“HTTPServices”。
二、redis学习理解
1.安装也不说了,网上和AI都能查到。
2.作为前端开发出身,了解redis后,我理解它就类似于是把内容存在了程序内存中。就好比做登录授权时,登录成功后,我把用户的功能权限存在了代码内存中,然后依据功能权限去配置前端页面的功能展示。这时保存用户功能权限数据的部分,就相当于redis在后端位置。
我查了下为什么不直接使用内存,而选用redis,给我的答案是redis的性能比直接用代码内存要高,这个我闲的没事的时候会去验证下。
3.redis也相当于一个应用程序,在进程管理器的后台列表也是可以看到redis的服务的。
redis提供了几种数据类型,方便对应不同的业务场景去使用。
4.既然类似直接使用代码内存,那使用代码内存会遇到的问题,redis也会遇到。比如使用数据的时候正好赶上了数据的更改,或者多个客户端同时访问,同时有删除/访问操作。只是这些问题在redis上有它自己的学名,锁/解锁等等,这些是之后我学完要继续补充的,目前代码只是最基本的操作。

posted @ 2025-08-27 12:32  yjbjingcha  阅读(15)  评论(0)    收藏  举报