Gone框架介绍18 - redis 分布式缓存 和 分布式锁

gone是可以高效开发Web服务的Golang依赖注入框架

github地址:https://github.com/gone-io/gone

文档地址:https://goner.fun/zh/

请帮忙在github上点个 ⭐️吧,这对我很重要 ;万分感谢!!

文章目录

利用redis提供分布式锁和分布式缓存

在本文中,我们将分享在gone中如何使用分布式缓存和分布式锁,其中分布式锁中实现了一种较为自由的处理方式---------"智能锁",对一个处理函数进行上锁,函数执行中会周期性检测锁过期的剩余时间并自动给锁续期,函数执行完后会自动解锁。

第一步:将redis相关Goner埋葬到Cemetery

什么是 Goner?

什么是 埋葬?

什么是 Cemetery?

参考 Gone的核心概念

在Priest函数中增加_ = goner.RedisPriest(cemetery),如下:

go 复制代码
func priest(cemetery gone.Cemetery) error {

	//使用 goner.RedisPriest 函数,将 redis 相关的Goner 埋葬到 Cemetery 中
	_ = goner.RedisPriest(cemetery)

	cemetery.Bury(&redisUser{})
	return nil
}

第二步:在配置文件中增加redis相关配置

创建配置文件 config/default.properties,内容如下:

properties 复制代码
# redis服务地址,格式为 `host:port`
redis.server=localhost:6379

# redis服务密码,不配置默认为空
redis.password=

其中,redis服务地址需要改你能访问到的redis服务地址。

更多配置:

  • redis.max-idle:最大空闲链接数,不配置默认为2
  • redis.max-active:最大活跃链接数,不配置默认为10
  • redis.db:使用的db,不配置默认为0
  • redis.cache.prefix:key前缀,如果设置了,对redis的增删改查都会拼接该前缀,拼接方式${prefix}#${key};默认为空

关于配置文件,更多参考:通过内置Goners支持配置文件

第三步,使用redis

注入接口

在需要使用的结构体中注入 接口redis.redis.Cacheredis.Locker,他们的GonerId分别为:gone-redis-cachegone-redis-locker

go 复制代码
type redisUser struct {
	gone.Flag

	cache  redis.Cache  `gone:"gone-redis-cache"`
	locker redis.Locker `gone:"gone-redis-locker"`
}

使用分布是缓存

请看下面代码中的注释:

go 复制代码
func (r *redisUser) UseCache() {
	key := "gone-address"
	value := "https://github.com/gone-io/gone"

	//设置缓存
	err := r.cache.Put(
		key,            //第一个参数为 缓存的key,类型为 `string`
		value,          // 第二参数为 需要缓存的值,类型为any,可以是任意类型;传入的值会被编码为 `[]byte` 发往redis
		10*time.Second, // 第三个参数为 过期时间,类型为 `time.Duration`;省略,表示不设置过期时间
	)

	if err != nil {
		fmt.Printf("err:%v", err)
		return
	}

	//获取缓存
	var getValue string
	err = r.cache.Get(
		key,       //第一个参数为 缓存的key,类型为 `string`
		&getValue, //第二参数为指针,接收获取缓存的值,类型为any,可以是任意类型;从redis获取的值会被解码为传入的指针类型
	)
	if err != nil {
		fmt.Printf("err:%v", err)
		return
	}
	fmt.Printf("getValue:%v", getValue)
}

接口上的其他方法:

  • Remove(key string) (err error):用于删除redis某个key,支持通配符
  • Keys(key string) ([]string, error):使用前缀或者通配符查询存在哪些key,⚠️该方法慎用
  • Prefix() string:获取当前缓存配置的key前缀

使用分布时锁

  1. 锁定一段时间
go 复制代码
func (r *redisUser) UseLock() {
	lockKey := "gone-lock-key"

	//尝试获取锁 并 锁定一段时间
	//返回的第一个参数为一个解锁的函数
	unlock, err := r.locker.TryLock(
		lockKey,        //第一个参数为 锁的key,类型为 `string`
		10*time.Second, //第二参数为 锁的过期时间,类型为 `time.Duration`
	)
	if err != nil {
		fmt.Printf("err:%v", err)
		return
	}
	//操作完后,需要解锁
	defer unlock()

	//获取锁成功后,可以进行业务操作
	//todo
}

这种方式,使用锁需要保证在锁定的时间内能够执行完所有操作,否则由于锁过期可能会导致问题。

  1. 锁定一个操作,操作没结束会自动给锁续期,操作结束自动解锁
go 复制代码
func (r *redisUser) LockFunc() {
	lockKey := "gone-lock-key"
	err := r.locker.LockAndDo(
		lockKey, //第一个参数为 锁的key,类型为 `string`
		func() { //第二个参数为 需要执行的函数,类型为 `func()`,代表一个操作
			//获取锁成功后,可以进行业务操作
			//todo
			println("do some options")
		},
		100*time.Second, //第三个参数为 锁的过期时间,类型为 `time.Duration`;第一次加锁和后续锁续期都将使用该值
		5*time.Second,   //第四个参数为 锁续期的间隔时间,类型为 `time.Duration`;周期性检查所是否将过期,如果在下个周期内会过期则对锁续期
	)
	if err != nil {
		fmt.Printf("err:%v", err)
	}
}

这种方式比较智能,姑且将其称为"智能锁"吧!

推荐使用这种方式,可以无脑使用,降低使用的心智负担。

上面例子完整代码

例子的源代码可以在这里找到

go 复制代码
package main

import (
	"fmt"
	"github.com/gone-io/gone"
	"github.com/gone-io/gone/goner"
	"github.com/gone-io/gone/goner/redis"
	"time"
)

func priest(cemetery gone.Cemetery) error {

	//使用 goner.RedisPriest 函数,将 redis 相关的Goner 埋葬到 Cemetery 中
	_ = goner.RedisPriest(cemetery)

	cemetery.Bury(&redisUser{})
	return nil
}

type redisUser struct {
	gone.Flag

	cache  redis.Cache  `gone:"gone-redis-cache"`
	locker redis.Locker `gone:"gone-redis-locker"`
}

func (r *redisUser) UseCache() {
	key := "gone-address"
	value := "https://github.com/gone-io/gone"

	//设置缓存
	err := r.cache.Put(
		key,            //第一个参数为 缓存的key,类型为 `string`
		value,          // 第二参数为 需要缓存的值,类型为any,可以是任意类型;传入的值会被编码为 `[]byte` 发往redis
		10*time.Second, // 第三个参数为 过期时间,类型为 `time.Duration`;省略,表示不设置过期时间
	)

	if err != nil {
		fmt.Printf("err:%v", err)
		return
	}

	//获取缓存
	var getValue string
	err = r.cache.Get(
		key,       //第一个参数为 缓存的key,类型为 `string`
		&getValue, //第二参数为指针,接收获取缓存的值,类型为any,可以是任意类型;从redis获取的值会被解码为传入的指针类型
	)
	if err != nil {
		fmt.Printf("err:%v", err)
		return
	}
	fmt.Printf("getValue:%v", getValue)
}

func (r *redisUser) LockTime() {
	lockKey := "gone-lock-key"

	//尝试获取锁 并 锁定一段时间
	//返回的第一个参数为一个解锁的函数
	unlock, err := r.locker.TryLock(
		lockKey,        //第一个参数为 锁的key,类型为 `string`
		10*time.Second, //第二参数为 锁的过期时间,类型为 `time.Duration`
	)
	if err != nil {
		fmt.Printf("err:%v", err)
		return
	}
	//操作完后,需要解锁
	defer unlock()

	//获取锁成功后,可以进行业务操作
	//todo
}

func (r *redisUser) LockFunc() {
	lockKey := "gone-lock-key"
	err := r.locker.LockAndDo(
		lockKey, //第一个参数为 锁的key,类型为 `string`
		func() { //第二个参数为 需要执行的函数,类型为 `func()`,代表一个操作
			//获取锁成功后,可以进行业务操作
			//todo
			println("do some options")
		},
		100*time.Second, //第三个参数为 锁的过期时间,类型为 `time.Duration`;第一次加锁和后续锁续期都将使用该值
		5*time.Second,   //第四个参数为 锁续期的间隔时间,类型为 `time.Duration`;周期性检查所是否将过期,如果在下个周期内会过期则对锁续期
	)
	if err != nil {
		fmt.Printf("err:%v", err)
	}
}

func main() {
	gone.Prepare(priest).AfterStart(func(in struct {
		r *redisUser `gone:"*"`
	}) {
		in.r.UseCache()
		in.r.LockTime()
	}).Run()
}

上一篇:Gone框架介绍17 - 创建一个可运行在生产环境的Web项目

下一篇:Gone框架介绍19 -如何进行单元测试?

相关推荐
Cloud Traveler3 分钟前
第3天-Jenkins详解-3
运维·分布式·jenkins
zhz521429 分钟前
Spring Boot + Redis 缓存性能优化实战:从5秒到毫秒级的性能提升
java·spring boot·redis·缓存·vue
在未来等你35 分钟前
Elasticsearch面试精讲 Day 16:索引性能优化策略
大数据·分布式·elasticsearch·搜索引擎·面试
恣艺40 分钟前
Redis环境搭建指南:Windows/Linux/Docker多场景安装与配置
linux·windows·redis
北极光SD-WAN组网1 小时前
某光伏电力监控系统网络安全监测项目:智能组网技术优化方案实践
大数据·网络·分布式
EndingCoder2 小时前
Electron 新特性:2025 版本更新解读
前端·javascript·缓存·electron·前端框架·node.js·桌面端
a587692 小时前
Spring Cloud Gateway:下一代API网关的深度解析与实战指南
java·分布式·网关
潘达斯奈基~2 小时前
kafka:【2】工作原理
大数据·分布式·kafka
励志成为糕手2 小时前
Kafka架构:构建高吞吐量分布式消息系统的艺术
分布式·架构·kafka·消息中间件·数据流处理
小句2 小时前
RabbitMQ对接MQTT消息发布指南
分布式·rabbitmq·ruby