『实战』使用Golang实现Redis经典应用案例

使用 Golang+Redis 实现一些经典业务案例,如签到功能、分布式锁、限流器、消息队列、计数器、排行榜、订阅发布等
00-预先准备
项目文件架构:
|
|
下载 Go 第三方包依赖
|
|
初始化 Redis 连接
确保有一个 Redis 环境,若本地没有,请自行使用搜索引擎进行安装
编写以下代码,初始化 Redis 连接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
// example/redis_client.go package example import ( "context" "github.com/redis/go-redis/v9" ) var RedisCli *redis.Client func init() { RedisCli = redis.NewClient(&redis.Options{ Addr: "127.0.0.1:6379", // Your Redis Address Password: "123456", // Your Redis Password }) if err := RedisCli.Ping(context.Background()).Err(); err != nil { panic(err) } }
编写测试代码,测试连接是否成功
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// example/redis_client_test.go package example import ( "context" "testing" "github.com/stretchr/testify/assert" ) func TestNewRedis(t *testing.T) { ast := assert.New(t) ast.NotNil(RedisCli) ast.Nil(RedisCli.Ping(context.Background()).Err()) }
执行命令:
go test ./example -run="^TestNewRedis"
得到如下结果表示测试通过,Redis 连接初始化成功:
1 2
(base) ➜ go-redis-example git:(main) ✗ go test ./example -run="^TestNewRedis" ok go-redis-example/example 0.287s
通用方法
在 common
文件夹下新建两个文件,分别是 concurrent_event_log.go
和 concurrent_routine.go
common/concurrent_event_log.go
: 日志收集及打印工具
|
|
common/concurrent_routine.go
: 并发执行器
|
|
main 方法
作为程序的入口,我们为了更好的运行实现好的案例,需要对输入的参数进行解析。例如要运行的 Example,运行需要的参数信息等。
main.go
代码如下:
|
|
暂未实现的 example 注释即可,等实现完成之后,记得取消注释,否则无法运行。
到此为止,我们预先的准备工作都已经就绪,现在开始实战案例。
01-基于Incr
的签到功能
业务分析
对于每日签到功能,我们需要记录连续签到天数,并且如果用户在当天没有签到的话,清空计数。
可以发现使用 Redis 的 String 类型搭配设置过期时间就可以很好的解决这个问题。
对于过期时间的设置,如果我们在当天签到成功,则第二天不签到就会清空计数。所以把过期时间设置在第三天的凌晨 0 点即可。
代码实现
|
|
测试功能:go run main.go Ex01 1165894833417101
|
|
02-基于SETNX
的分布式锁
并发场景下,要求同时只有一个进程执行。即分布式情况下的逻辑、资源保护。
使用 redis 的 setnx
实现:
- 单线程,且可以保证原子性
- 只有不存在 key 时才可以执行成功
只是体验 SetNX 的特性,不是高可用的分布式锁实现
该实现存在的问题:
- 业务超时解锁,导致并发问题。业务执行时间超过锁超时时间
- redis 主备切换临界点问题。主备切换后,A 持有的锁还未同步到新的主节点时,B 可在新主节点获取锁,导致并发问题。
- redis 集群脑裂,导致出现多个主节点
代码实现
|
|
测试功能:go run main.go Ex02
|
|
03-基于Incr
和Decr
的简单限流器
要求 1s 内放行的请求为 N,超过 N 的请求次数则禁止访问。
通过 redis 的 string 类型,对 key 进行 Incr
和 Decr
操作。value 与 N 比对,判断是否放行。
代码实现
|
|
测试功能:go run main.go Ex03
|
|
04-基于List
的消息队列
- 消息队列是一种先进先出的队列型数据结构。消息被顺序插入队列中,其中发送进程将消息添加到队列末尾,接受进程从队列头读取消息。
- 多个进程可同时向一个消息队列发送消息,也可以同时从一个消息队列中接收消息。发送进程把消息发送到队列尾部,接受进程从消息队列头部读取消息,消息一旦被读出就从队列中删除。
使用 redis 的 List 的 lpush
和 rpop
可以实现一个简易的消息队列。
代码实现
|
|
测试功能:go run main.go Ex04
|
|
05-基于Hash
的计数器
对于一个用户有多个计数需求,例如点赞数量、粉丝数量、文章收藏数量、关注数量等,可以使用 Hash 的数据结构进行存储。
代码实现
|
|
测试功能
测试脚本:
|
|
执行结果:
|
|
06-基于Zset
的排行榜
积分榜变化时,排名要实时改变。通过 Zset 可以很好的实现功能
代码实现
|
|
测试功能
测试脚本:
|
|
执行结果:
|
|
07-基于PubSub
的消息订阅
对于文章的发布与订阅,也可以使用 PubSub 实现
代码实现
|
|
测试结果:go run main.go Ex07
|
|
总结
通过上述的 Redis 实战案例,管中窥豹地了解了 Redis 的一些经典用法。实际上 Redis 的功能并不止于此,在日后的学习工作中,Redis 也将会继续发挥更大的作用。