分享
  1. 首页
  2. 文章

Golang使用redigo实现redis的分布式锁

rfyiamcool · · 7628 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

有些分布式场景会有分布式锁的需求,可以为了原子操作,也可能为了性能的原因,不管是分布式锁市面是有不少解决方法的,比如etcd、consul、zookeeper... 初次之外redis这样的nosql也是可以实现分布式锁的。 python党喜欢用redis、etcd、consul来搞。 java这帮人更喜欢用zookeeper来实现分布式锁,zookeeper做分布式锁有临时节点(Ephemeral Node)的效果,也就是说当客户端出问题时,watch在zookeeper的服务会监听到的, 另外由这个客户端建立的键值也会被干掉...


该文章写的有些乱,欢迎来喷 ! 另外文章后续不断更新中,请到原文地址查看更新http://xiaorui.cc/?p=3028

如果我们用redis来实现分布式锁,是怎么实现? (真想直接贴代码,不废话...)

首先借助于redis的setnx命令来操作,setnx本身针对key赋值的时候会判断redis中是否存在这个key,如果有返回-1, 如果没有的化,他会直接set键值。那他跟直接set键值有啥区别? setnx是原子操作,而set不能保证原子性。


为了防止锁被长久锁定,或者防止客户端崩掉了没有删掉锁,可以用expire加入过期时间。 但这过期时间也不解决那种客户端异常退出,又没删除锁的情况。 我在使用etcd做服务发现注册时候,用了一个笨办法,把过期时间调的很细,可以开一个线程不停的去设置锁及过期时间. 这样能缓解一般的过期情况.

代码是基于https://github.com/everalbum/redislock/blob/master/redislock.go 修改的,这老外写的代码太高调了。 另外我在这基础上加入了增加过期时间的方法,及自定义prefix key和token,超时时间。

代码我已经整理到 https://github.com/rfyiamcool/go_redis_lock ,暂不能构建一个可直接用来使用包,没想过要构建一个库包. 下面是实现的代码及test实例...

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#http://xiaorui.cc
package main
import(
"fmt"
"log"
"time"
"github.com/garyburd/redigo/redis"
)
typeLockstruct{
resourcestring
tokenstring
conn redis.Conn
timeout int
}
func(lock*Lock)tryLock()(ok bool,err error){
_,err=redis.String(lock.conn.Do("SET",lock.key(),lock.token,"EX",int(lock.timeout),"NX"))
iferr==redis.ErrNil{
//The lock was notsuccessful,it already exists.
returnfalse,nil
}
iferr!=nil{
returnfalse,err
}
returntrue,nil
}
func(lock*Lock)Unlock()(err error){
_,err=lock.conn.Do("del",lock.key())
return
}
func(lock*Lock)key()string{
returnfmt.Sprintf("redislock:%s",lock.resource)
}
func(lock*Lock)AddTimeout(ex_time int64)(ok bool,err error){
ttl_time,err:=redis.Int64(lock.conn.Do("TTL",lock.key()))
fmt.Println(ttl_time)
iferr!=nil{
log.Fatal("redis get failed:",err)
}
ifttl_time>0{
fmt.Println(11)
_,err:=redis.String(lock.conn.Do("SET",lock.key(),lock.token,"EX",int(ttl_time+ex_time)))
iferr==redis.ErrNil{
returnfalse,nil
}
iferr!=nil{
returnfalse,err
}
}
returnfalse,nil
}
func TryLock(conn redis.Conn,resourcestring,tokenstring,DefaulTimeout int)(lock*Lock,ok bool,err error){
returnTryLockWithTimeout(conn,resource,token,DefaulTimeout)
}
func TryLockWithTimeout(conn redis.Conn,resourcestring,tokenstring,timeout int)(lock*Lock,ok bool,err error){
lock=&Lock{resource,token,conn,timeout}
ok,err=lock.tryLock()
if!ok||err!=nil{
lock=nil
}
return
}
func main(){
fmt.Println("start")
DefaultTimeout:=10
conn,err:=redis.Dial("tcp","localhost:6379")
lock,ok,err:=TryLock(conn,"xiaoru.cc","token",int(DefaultTimeout))
iferr!=nil{
log.Fatal("Error while attempting lock")
}
if!ok{
log.Fatal("Lock")
}
lock.AddTimeout(100)
time.Sleep(time.Duration(DefaultTimeout)*time.Second)
fmt.Println("end")
defer lock.Unlock()
}


这段golang代码运行后的正常结果是:

Python
1
2
3
4
5
6
$go run lock.go
start
10
11
end


如果同时起多个进程去测试,会遇到这么一个结果:

Python
1
2
3
4
5
$go run lock.go
start
2016/03/2301:23:22Lock
exit status1

有疑问加站长微信联系(非本文作者)

本文来自:峰云就她了

感谢作者:rfyiamcool

查看原文:Golang使用redigo实现redis的分布式锁

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
7628 次点击
被以下专栏收入,发现更多相似内容
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏