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()
    //测试redis数据结构
	testRedis()

	// 初始化数据库连接
	db = InitDB()
	defer db.Close()
	// 注册路由
	RegisterRouter()
	// 启动 HTTP 服务
	go func() {
		StartHTTPServer()
	}()

	// 启动 HTTP api测试服务
	go func() {
		StartDebugHTTPServer()
	}()

	// 阻塞主线程
	select {}
}

// 测试redis操作代码
func testRedis() {
	LogInfo("===============测试redis================")
	// String 操作
	// 像封装好的字典类,如果key存在,下一个加入的值会直接替换value
	//设置了数据只存在10s
	redis_model.StringSet("name", "Alice", 10*time.Second)
	redis_model.StringSet("name", "S", 10*time.Second)
	redis_model.StringSet("age", "20", 10*time.Second)
	name, _ := redis_model.StringGet("name")
	age, _ := redis_model.StringGet("age")
	LogInfo("Name:%s, Age:%s", name, age)

	// Hash 操作
	//可以当作类或者结构体理解,第一个值=类名,之后的是字段名+值
	//多次设置同一个字段会覆盖之前的值
	//设置过期时间 10s  如果不设置就作为持久化数据存储了
	redis_model.Client.Expire(context.Background(), "user:1001", 10*time.Second)
	redis_model.HashSet("user:1001", "name", "Bob")
	redis_model.HashSet("user:1001", "age", "25")
	redis_model.HashSet("user:1001", "age", "30")
	user, _ := redis_model.HashGetAll("user:1001")
	name, _ = redis_model.HashGet("user:1001", "name")
	age, _ = redis_model.HashGet("user:1001", "age")
	LogInfo("User:%v name=%v age=%v", user, name, age)

	// List 操作
	//clear tasks
	redis_model.Client.Del(context.Background(), "tasks")
	//设置过期时间
	redis_model.Client.Expire(context.Background(), "tasks", 10*time.Second)
	//添加数据
	//类似list,如果不设置过期时间或者不执行clear,数据会只增不减
	redis_model.ListRPush("tasks", "A", "B")
	redis_model.ListRPush("tasks", "task1", "task2", "A", "B")
	tasks, _ := redis_model.ListRange("tasks", 0, -1)
	LogInfo("Tasks:%v", tasks)

	// Set 操作
	//无序不重复,类似字典+list
	//清空数据
	redis_model.Client.Del(context.Background(), "tags")
	//设置过期时间
	redis_model.Client.Expire(context.Background(), "tags", 10*time.Second)
	redis_model.SetAdd("tags", "go", "redis", "cache")
	redis_model.SetAdd("tags", "go")
	redis_model.SetAdd("tags", "test")
	tags, _ := redis_model.SetMembers("tags")
	LogInfo("Tags:%v", tags)

	// ZSet 操作
	//有序集合,Score=进行排列的数据,Member=数据本身
	//清空数据
	redis_model.Client.Del(context.Background(), "leaderboard")
	//设置过期时间
	redis_model.Client.Expire(context.Background(), "leaderboard", 10*time.Second)
	//添加数据
	redis_model.ZAdd("leaderboard", &redis.Z{Score: 100, Member: "alice"}, &redis.Z{Score: 90, Member: "bob"})
	redis_model.ZAdd("leaderboard", &redis.Z{Score: 80, Member: 99}, &redis.Z{Score: 70, Member: "charlie"})
	//降序结果
	top, _ := redis_model.ZRevRange("leaderboard", 0, -1)
	//升序结果
	top2, _ := redis_model.ZRange("leaderboard", 0, -1)
	LogInfo("Top1:%v \nTop2:%v", top, top2)
	LogInfo("===============测试redis END================")
}

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上有它自己的学名,锁/解锁等等,这些是之后我学完要继续补充的,目前代码只是最基本的操作。
5.redis存储的数据默认是持久化存储的,这点和代码内存不同,如果没有设置过期时间或主动调用clear方法,在程序关闭后,redis中的数据在下次程序启动时,依旧可以获取访问。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值