当前位置:网站首页>Golang+redis distributed mutex
Golang+redis distributed mutex
2022-06-24 15:47:00 【lestat】
introduction
Suppose one of our businesses involves data updating , At the same time, there is a large amount of concurrency in the actual scenario . technological process : Read -> modify -> preservation , Based on DB In the case of concurrent processing of layer , This scenario may cause unexpected execution results for some data , At this point, you can consider using distributed locks to solve this problem
Problems to be solved
- False release of lock
- Business execution timeout causes concurrency
- Retry mechanism
GETandDELNon atomicity
Code
Directory structure :
│ main.go
│
└─demo
lock.golock.go:
package demo
import (
"context"
"fmt"
"github.com/go-redis/redis/v8"
"math/rand"
"time"
)
// Retry count
var retryTimes = 5
// Retry frequency
var retryInterval = time.Millisecond * 50
var rdb = redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
// The default expiration time of the lock
var expiration time.Duration
// Simulate the locking scenario of distributed services
func MockTest(tag string) {
var ctx, cancel = context.WithCancel(context.Background())
defer func() {
// stop it goroutine
cancel()
}()
// Random value
lockV := getRandValue()
lockK := "EXAMPLE_LOCK"
// Default expiration time
expiration = time.Millisecond * 200
fmt.Println(tag + " Try to lock ")
set, err := rdb.SetNX(ctx, lockK, lockV, expiration).Result()
if err != nil {
panic(err.Error())
}
// Locking failed , retry
if set == false && retry(ctx, rdb, lockK, lockV, expiration, tag) == false {
fmt.Println(tag + " server unavailable, try again later")
return
}
fmt.Println(tag + " Lock successfully ")
// Locking success , Add a new daemon thread
go watchDog(ctx, rdb, lockK, expiration, tag)
// Deal with business ( Through random time delay simulation )
fmt.Println(tag + " Wait for the business processing to complete ...")
time.Sleep(getRandDuration())
// Business processing completed
// Release the lock
val := delByKeyWhenValueEquals(ctx, rdb, lockK, lockV)
fmt.Println(tag+" Release results :", val)
}
// Release the lock
func delByKeyWhenValueEquals(ctx context.Context, rdb *redis.Client, key string, value interface{}) bool {
lua := `
-- If the current value is consistent with the lock value , Delete key
if redis.call('GET', KEYS[1]) == ARGV[1] then
return redis.call('DEL', KEYS[1])
else
return 0
end
`
scriptKeys := []string{key}
val, err := rdb.Eval(ctx, lua, scriptKeys, value).Result()
if err != nil {
panic(err.Error())
}
return val == int64(1)
}
// Generate random time
func getRandDuration() time.Duration {
rand.Seed(time.Now().UnixNano())
min := 50
max := 100
return time.Duration(rand.Intn(max-min)+min) * time.Millisecond
}
// Generate random values
func getRandValue() int {
rand.Seed(time.Now().UnixNano())
return rand.Int()
}
// The guardian thread
func watchDog(ctx context.Context, rdb *redis.Client, key string, expiration time.Duration, tag string) {
for {
select {
// Business done
case <-ctx.Done():
fmt.Printf("%s Task to complete , close %s Automatic renewal of \n", tag, key)
return
// Business not completed
default:
// Automatic renewal
rdb.PExpire(ctx, key, expiration)
// Continue to wait for
time.Sleep(expiration / 2)
}
}
}
// retry
func retry(ctx context.Context, rdb *redis.Client, key string, value interface{}, expiration time.Duration, tag string) bool {
i := 1
for i <= retryTimes {
fmt.Printf(tag+" The first %d Attempts to lock ...\n", i)
set, err := rdb.SetNX(ctx, key, value, expiration).Result()
if err != nil {
panic(err.Error())
}
if set == true {
return true
}
time.Sleep(retryInterval)
i++
}
return false
}Process description
hypothesis
MockTestMethods are business processing methods
- initialization
contextUsed to control the exit of the daemon thread - Set random value to try to lock ( Random value can avoid false release when releasing lock )
- If locking fails , Try again , The retry mechanism depends on the business , Retry failure processing depends on the business
- Open a daemon thread after locking successfully (
watchDog), The expiration time used to continuously refresh the lock , Ensure that the lock will not expire during business execution - Simulation of random time-consuming business processing
- Release the lock after business processing (
luaProcessing ensures atomicity , And comparevalueAvoid accidental release ) - adopt
cancelClose the daemon thread (watchDog), Avoid deadlock
Respond to the scene
- The thread terminates abnormally after obtaining the lock , The lock will be there
expireAutomatically release after expiration - Thread execution time exceeds the default value of the lock
expire, adoptwatchDogAutomatic renewal , Avoid this
test
main.go:
package main
import (
"play/demo"
"time"
)
func main() {
go demo.MockTest("A")
go demo.MockTest("B")
go demo.MockTest("C")
go demo.MockTest("D")
go demo.MockTest("E")
// Used for testing goroutine Received ctx.Done() Printing after signal
time.Sleep(time.Second * 2)
}result :
$ go run main.go A Try to lock D Try to lock E Try to lock B Try to lock C Try to lock D Lock successfully D Wait for the business processing to complete ... B The first 1 Attempts to lock ... E The first 1 Attempts to lock ... A The first 1 Attempts to lock ... C The first 1 Attempts to lock ... B The first 2 Attempts to lock ... D Release results : true B Lock successfully E The first 2 Attempts to lock ... B Wait for the business processing to complete ... C The first 2 Attempts to lock ... A The first 2 Attempts to lock ... D Task to complete , close EXAMPLE_LOCK Automatic renewal of A The first 3 Attempts to lock ... C The first 3 Attempts to lock ... E The first 3 Attempts to lock ... B Release results : true A Lock successfully A Wait for the business processing to complete ... B Task to complete , close EXAMPLE_LOCK Automatic renewal of E The first 4 Attempts to lock ... C The first 4 Attempts to lock ... A Release results : true A Task to complete , close EXAMPLE_LOCK Automatic renewal of C The first 5 Attempts to lock ... E The first 5 Attempts to lock ... C Lock successfully C Wait for the business processing to complete ... E server unavailable, try again later C Release results : true C Task to complete , close EXAMPLE_LOCK Automatic renewal of
If you are lazy, you don't write unit tests
边栏推荐
- Recommend several super practical data analysis tools
- 如何在Thymeleaf3标签中使用嵌套标签
- How to achieve long-term development of voice social source code?
- 在Gradle 中对Junit5 测试框架引用
- MySQL toolset: the official performance testing tool mysqlslap
- Efficient tools commonly used by individuals
- How to implement SQLSERVER database migration in container
- Attacked! Cloud development monitoring alarm practice
- How to optimize performance
- Istio FAQ: 431 request header fields too large
猜你喜欢

刚刚阿里面软件测试回来,3+1面任职阿里P7,年薪28*15薪

I just came back from the Ali software test. I worked for Alibaba P7 in 3+1, with an annual salary of 28*15

Database tools in intelij can connect but cannot display schema, tables

推荐几款超级实用的数据分析利器

运营商5G用户渗透远远比4G慢,5G的普及还得看中国广电

Linux记录-4.22 MySQL5.37安装(补充)

nifi从入门到实战(保姆级教程)——环境篇

How to generate assembly code using clang in Intel syntax- How to generate assembly code with clang in Intel syntax?

日志记录真没你想的那么简单

使用阿里云RDS for SQL Server性能洞察优化数据库负载-初识性能洞察
随机推荐
I just came back from the Ali software test. I worked for Alibaba P7 in 3+1, with an annual salary of 28*15
Design of CAN bus controller based on FPGA (Part 2)
The future of robots -- deep space exploration
The equipment is connected to the easycvr platform through the national standard gb28181. How to solve the problem of disconnection?
Security Analysis on mining trend of dogecoin, a public cloud
国产最长寿的热销手机,苹果也不是对手,总算让国产手机找回面子
Vim编辑器的最常用的用法
如何在Thymeleaf3标签中使用嵌套标签
为什么企业实施WMS仓储管理系统很容易失败
How to allow easydss online classroom system to upload an on-demand file with a space in the file name?
Ethical considerations
A full set of tutorials for interviewers from Android manufacturers teach you: prepare for the interview and get the offer smoothly!
Here comes Wi Fi 7. How strong is it?
Jenkins 镜像无法更新插件中心的3种解决方法
如何实现容器内的SqlServer的数据库迁移
Xingxinghai, it is said that the new generation can fight better?
不忘初心
MySQL replication series 6- tables related to replication information
Istio practical tips: Customize Max_ body_ size
10 hands-free idea plug-ins. These codes do not need to be written (the second bullet)