zookeeper 分布式锁_关于redis分布式锁,zookeeper分布式锁原理的一些学习与思考

编辑:业余草来源:https://www.xttblog.com/?p=4946

首先分布式锁和我们平常讲到的锁原理基本一样,目的就是确保,在多个线程并发时,只有一个线程在同一刻操作这个业务或者说方法、变量。

在一个进程中,也就是一个jvm 或者说应用中,我们很容易去处理控制,在jdk java.util 并发包中已经为我们提供了这些方法去加锁, 比如synchronized 关键字 或者Lock 锁,都可以处理。

但是我们现在的应用程序如果只部署一台服务器,那并发量是很差的,如果同时有上万的请求那么很有可能造成服务器压力过大,而瘫痪。

想想双十一 和 三十晚上十点分支付宝红包等业务场景,自然需要用到多台服务器去同时处理这些业务,那么这些服务可能会有上百台同时处理,

但是请我们大家想一想,如果有100台服务器 要处理分红包的业务,现在假设有1亿的红包,1千万个人分,金额随机,那么这个业务场景下是不是必须确保这1千万个人最后分的红包金额总和等于1亿。

如果处理不好每人分到100万,那马云爸爸估计大年初一,就得宣布破产了

1,常规锁会造成什么情况?

首先说一下我们为什么要搞集群,简单理解就是,需求量(请求并发量)变大了,一个工人处理能力有限,那就多招一些工人来一起处理。

假设1千万个请求平均分配到100台服务器上,每个服务器 接收10w的请求(这10w个请求并不是在同一秒中来的,可能是在1,2个小时内,可以联想下我们三十晚上开红包,等到10.20开始,有的人立马开了,有的人是不是等到12点了才想起来~)

那这样的话,平均到每一秒上的请求也就不到1千个,这种压力一般的服务器还是可以承受的。

第一个请求到来后,是不是需要在1亿里面给他分一部分钱,金额随机,假设第一个人分到了100,那是不是要在这1亿中减去100块,剩下99999900 块~

第二个用户再来分,金额随机,这次分200块,那是不是就需要在剩下的99999900块中再减去200块,剩下99999700 块。

等到第10w个用户来,一看还有1000w,那这1000w全成他的了。

等于是在每个服务器中去分1亿,也就是10w个用户分了一个亿,最后总计有100个服务器,要分100亿。

如果真这样了,虽说马云爸爸不会破产(据最新统计马云有2300亿人民币),那分红包的开发项目组,以及产品经理,可以GG了~

简化结构图如下:

d5c82813fccbfcf4b0679bc378da8e5b.png

2,分布式锁怎么去处理?

那么为了解决这个问题,让1000万用户只分1亿,而不是100亿,这个时候分布式锁就派上用处了。

分布式锁可以把整个集群就当作是一个应用一样去处理,那么也就需要这个锁,要独立于每一个服务之外,而不是在服务里面。

假设第一个服务器接收到用户1的请求后,那么这个时候,他就不能只在自己的应用中去判断还有多少钱可以分了,而需要去外部请求专门负责管理这1亿红包的人(服务),问他:哎,我这里要分100块,给我100。

管理红包的妹子(服务)一看,还有1个亿,那好,给你100块,然后剩下99999900块。

第二个请求到来后,被服务器2获取,继续去询问,管理红包的妹子,我这边要分10块,管理红包的妹子先查了下还有99999900,那就说:好,给你10块。那就剩下99999890块

等到第1000w个请求到来后,服务器100拿到请求,继续去询问,管理红包的妹子,你要100,妹子翻了翻白眼,对你说,就剩1块了,爱要不要,那这个时候就只能给你1块了(1块也是钱啊,买根辣条还是可以的)。

这些请求编号1,2不代表执行的先后顺序,正式的场景下,应该是 100台服务器每个服务器持有一个请求去访问负责管理红包的妹子(服务),那在管红包的妹子那里同时会接收到100个请求,这个时候就需要在负责红包的妹子那里加个锁就可以了(抛绣球),你们100个服务器谁拿到锁(抢到绣球),谁就进来和我谈,我给你分,其他人就等着去吧

经过上面的分布式锁的处理后,马云爸爸终于放心了,决定给红包团队每人加一个鸡腿。

简化的结构图如下:

52e5ced7a75ba42ccefd4f8d96b0d4db.png

3,分布式锁的实现有哪些?

说到分布式锁的实现,还是有很多的,有数据库方式的,有redis分布式锁,有zookeeper分布式锁等等

我们如果采用redis作为分布式锁,那么上图中负“责红包的妹子(服务)”,就可以替换成redis,请自行脑补。

3.1,为什么redis可以实现分布式锁?

首先redis是单线程的,这里的单线程指的是网络请求模块使用了一个线程(所以不需考虑并发安全性),即一个线程处理所有网络请求,其他模块仍用了多个线程。

在实际的操作中过程大致是这样子的:

服务器1要去访问发红包的妹子,也就是redis,那么他会在redis中通过"setnx key value" 操作设置一个key 进去,value是啥不重要,重要的是要有一个key,也就是一个标记,而且这个key你爱叫啥叫啥,只要所有的服务器设置的key相同就可以。

假设我们设置一个,如下图

afc2b019c7c2ec6f0af0baaad65c3b02.png

那么我们可以看到会返回一个1,那就代表了成功。

如果再来一个请求去设置同样的key,如下图:

fdf0a2489232afa42a443dc4caee76c4.png

这个时候会返回0,那就代表失败了。

那么我们就可以通过这个操作去判断是不是当前可以拿到锁,或者说可以去访问“负责发红包的妹子”,如果返回1,那我就开始去执行后面的逻辑,如果返回0,那就说明已经被人占用了,我就要继续等待。

当服务器1拿到锁之后,进行了业务处理,完成后,还需要释放锁,如下图所示:

d956874bfdf472c6d3cd13d3eb00f835.png

删除成功返回1,那么其他的服务器就可以继续重复上面的步骤去设置这个key,以达到获取锁的目的。

当然以上的操作是在redis客户端直接进行的,通过程序调用的话,肯定就不能这么写,比如java 就需要通过jedis 去调用,但是整个处理逻辑基本都是一样的

通过上面的方式,我们好像是解决了分布式锁的问题,但是想想还有没有什么问题呢??

对,问题还是有的,可能会有死锁的问题发生,比如服务器1设置完之后,获取了锁之后,忽然发生了宕机。

那后续的删除key操作就没法执行,这个key会一直在redis中存在,其他服务器每次去检查,都会返回0,他们都会认为有人在使用锁,我需要等。

为了解决这个死锁的问题,我们就就需要给key 设置有效期了。

设置的方式有2种

1,第一种就是在set完key之后,直接设置key的有效期 "expire key timeout" ,为key设置一个超时时间,单位为second,超过这个时间锁会自动释放,避免死锁。

这种方式相当于,把锁持有的有效期,交给了redis去控制。如果时间到了,你还没有给我删除key,那redis就直接给你删了,其他服务器就可以继续去setnx获取锁。

2,第二种方式,就是把删除key权利交给其他的服务器,那这个时候就需要用到value值了,

比如服务器1,设置了value 也就是 timeout 为 当前时间+1 秒 ,这个时候服务器2 通过get 发现时间已经超过系统当前时间了,那就说明服务器1没有释放锁,服务器1可能出问题了,

服务器2就开始执行删除key操作,并且继续执行setnx 操作。

但是这块有一个问题,也就是,不光你服务器2可能会发现服务器1超时了,服务器3也可能会发现,如果刚好,服务器2,setnx操作完成,服务器3就接着删除,是不是服务器3也可以setnx成功了?

那就等于是服务器2和服务器3都拿到锁了,那就问题大了。这个时候怎么办呢?

这个时候需要用到  “GETSET  key value” 命令了。这个命令的意思就是获取当前key的值,并且设置新的值。

假设服务器2发现key过期了,开始调用 getset 命令,然后用获取的时间判断是否过期,如果获取的时间仍然是过期的,那就说明拿到锁了。

如果没有,则说明在服务2执行getset之前,服务器3可能也发现锁过期了,并且在服务器2之前执行了getset操作,重新设置了过期时间。

那么服务器2就需要放弃后续的操作,继续等待服务器3释放锁或者去监测key的有效期是否过期。

这块其实有一个小问题是,服务器3已经修改了有效期,拿到锁之后,服务器2,也修改了有效期,但是没能拿到锁,但是这个有效期的时间已经被在服务器3的基础上有增加一些,但是这种影响其实还是很小的,几乎可以忽略不计。

3.2,为什么zookeeper可以实现分布式锁?

百度百科是这么介绍的:ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。

那对于我们初次认识的人,可以理解成ZooKeeper就像是我们的电脑文件系统,我们可以在d盘中创建文件夹a,并且可以继续在文件夹a中创建 文件夹a1,a2。

那我们的文件系统有什么特点??那就是同一个目录下文件名称不能重复,同样ZooKeeper也是这样的。

在ZooKeeper所有的节点,也就是文件夹称作 Znode,而且这个Znode节点是可以存储数据的。

我们可以通过“ create /zkjjj nice” 来创建一个节点,这个命令就表示,在跟目录下创建一个zkjjj的节点,值是nice。同样这里的值,和我在前面说的redis中的一样,没什么意义,你随便给。

另外ZooKeeper可以创建4种类型的节点,分别是:

1,持久性节点

2,持久性顺序节点

3,临时性节点

4,临时性顺序节点

首先说下持久性节点和临时性节点的区别,持久性节点表示只要你创建了这个节点,那不管你ZooKeeper的客户端是否断开连接,ZooKeeper的服务端都会记录这个节点。

临时性节点刚好相反,一旦你ZooKeeper客户端断开了连接,那ZooKeeper服务端就不再保存这个节点。

再说下顺序性节点,顺序性节点是指,在创建节点的时候,ZooKeeper会自动给节点编号比如0000001 ,0000002 这种的。

最后说下,zookeeper有一个监听机制,客户端注册监听它关心的目录节点,当目录节点发生变化(数据改变、被删除、子目录节点增加删除)等,zookeeper会通知客户端。

下面我们继续结合我们上面的分红包场景,描述下在zookeeper中如何加锁。

假设服务器1,创建了一个节点 /zkjjj ,成功了,那服务器1就获取了锁,服务器2再去创建相同的锁,那么他就会失败,这个时候他就就只能监听这个节点的变化。

等到服务器1,处理完业务,删除了节点后,他就会得到通知,然后去创建同样的节点,获取锁处理业务,再删除节点,后续的100台服务器与之类似

注意这里的100台服务器并不是挨个去执行上面的创建节点的操作,而是并发的,当服务器1创建成功,那么剩下的99个就都会注册监听这个节点,等通知,以此类推。

但是大家有没有注意到,这里还是有问题的,还是会有死锁的情况存在,对不对?

当服务器1创建了节点后挂了,没能删除,那其他99台服务器就会一直等通知,那就完蛋了。。。

这个时候呢,就需要用到临时性节点了,我们前面说过了,临时性节点的特点是客户端一旦断开,就会丢失,也就是当服务器1创建了节点后,如果挂了。

那这个节点会自动被删除,这样后续的其他服务器,就可以继续去创建节点,获取锁了。

但是我们可能还需要注意到一点,就是惊群效应:举一个很简单的例子,当你往一群鸽子中间扔一块食物,虽然最终只有一个鸽子抢到食物,但所有鸽子都会被惊动来争夺,没有抢到..

就是当服务器1节点有变化,会通知其余的99个服务器,但是最终只有1个服务器会创建成功,这样98还是需要等待监听,那么为了处理这种情况,就需要用到临时顺序性节点

大致意思就是,之前是所有99个服务器都监听一个节点,现在就是每一个服务器监听自己前面的一个节点。

假设100个服务器同时发来请求,这个时候会在 /zkjjj 节点下创建 100 个临时顺序性节点 /zkjjj/000000001,  /zkjjj/000000002,一直到  /zkjjj/000000100,这个编号就等于是已经给他们设置了获取锁的先后顺序了。

当001节点处理完毕,删除节点后,002收到通知,去获取锁,开始执行,执行完毕,删除节点,通知003~以此类推。

29c298ea6915c110ce278c49b16fa1c1.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/533943.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java线程怎么发送消息_Java客户端Socket如何能在阻塞线程下收到服务端发送来的消息?...

最近在写Socket客户端的时候遇到点问题客户端在创建时创建了2个线程一个监听键盘输入事件,使用的是buffered,当检测到输入完成时写入流发送给服务端。String content "";while (!(content bufferedReader.readLine()).equals("exit&quo…

python函数参数传递机制_Python 学习笔记(一) 理解Python的函数传参机制

对于刚接触Python不久的新手,Python的函数传参机制往往会让人迷惑。学过C的同学都知道函数参数可以传值或者传地址。比如下面这段代码点击(此处)折叠或打开void func(int input) {input 100;}int a 0;func(a);printf("%d", a);结果应该是打印0&#xff…

网页无法调用java9_JAVA 9 (内部类,异常,包)

内部类:将一个类定义在另一个类里面,称里面那个类为内部类。举例:class Zx{public static void main(String []arr){Outer o new Outer();o.method();}}class Outer{class Inner //内部类,可以定义多个内部类{void function(){S…

python renamer模块_Python - 批量文件重命名

两个目标两个:1. 输入一组文件名,进行批量重命名;2. 输入一组目录名,批量重命名各个目录下的文件。附加功能:1. 可根据文件的创建日期对文件重新排序;2. 重命名方式为递增数列,可带前后缀&#…

spark算子_Spark篇之持久化算子

一、前述Spark中控制算子也是懒执行的,需要Action算子触发才能执行,主要是为了对数据进行缓存。控制算子有三种,cache,persist,checkpoint,以上算子都可以将RDD持久化,持久化的单位是partition。cache和persist都是懒执…

java g1 gc ref proc_深入理解垃圾收集器的G1及日志分析

尽管Hotspot 最新的垃圾回收器G1是在2006年推出的。但是G1从推行至今的市场反响来看,但现在足以证明这款垃圾收集器是经得起考验的,从java9开始,就默认为G1垃圾收集器。G1是一款面向服务端应用的垃圾收集器。HotSpot开发团队赋予它的使命是(在…

python爬取哔哩哔哩视频_Python实现视频爬取下载

注:源内容来自公众号【python学习开发】一般情况下我们使用爬虫更多的是对数据或者图片进行爬取,今天在这里和大家分享一下关于使用爬虫技术来进行视频下载的方法,不仅可以方便的下载一些体积小的视频,针对大容量的视频下载同样试用。接下来我们来介绍此次爬取视频过…

java约瑟夫环pta上_cdoj525-猴子选大王 (约瑟夫环)

约瑟夫斯问题问题有时候也被描述成猴子选大王问题,题目如下.(最后会贴上约瑟夫问题的来历) 一群猴子排成一圈,按1,2,…,n依次编号. 然后从第1只开始数,数到第m只,把它踢出圈,从它后面再开始数,再数到第m只,在把它踢出去…,如此不停的进行下去,直到最后只剩下一只猴子为止,那只猴…

java++记录+运行_记录java+testng运行selenium(三)---xml、ini、excel、日志等配置

一: ini文件ini目前只用处存储浏览类型及需要打开的url,ini文件放在configs文件夹下面。读取ini代码如下:1 packagetoolskit.documents;23 import java.io.*;4 import java.util.*;56 /**7 * ClassName: ReadIni8 * Author: DingDong9 * D…

python字符串前面加f什么意思_Python 字符串前面加u,r,b,f的含义

1、字符串前加 u例:u"我是含有中文字符组成的字符串。"作用:后面字符串以 Unicode 格式 进行编码,一般用在中文字符串前面,防止因为源码储存格式问题,导致再次使用时出现乱码。2、字符串前加 r例&#xff1a…

mysql居左查询abcd_MySql速查手册

索引定义索引用来快速地寻找那些具有特定值的记录,所有MySQL索引都以B树的形式保存。就像是数据的目录。索引类型唯一索引主键索引B-Tree普通索引R-Tree联合索引Hash全文索引FullText在mysql中fulltext索引只针对myisam生效。符合索引对于创建的多列索引(复合索引)&…

和python哪个容易胖_为什么有些人特别容易胖?

7种人,最容易被肥胖盯上,其中有你吗?胖,总是来得猝不及防,肥肉也总是不知不觉地长出来……许多胖友对自己的身材老摸不着脑,其实,长胖都是有迹可循的!正如下面这7类人,就…

loadrunner 错误: 无法找到 java.exe_LoadRunner错误及解决方法总结

1. error:missing newline in d:\loadrunner\name.dat场景执行时报error:missing newline in d:\loadrunner\name.dat第二次执行不报两个解决办法:第一:如果参数不是很多的话,不要打开记事本去编辑参数,就直接在LR提供的参数的表格…

gif透明背景动画_【超实用干货! 】iPad上的动画App大推荐

作者/立夏编辑/彼方大家好,我是立夏。大概在两年前吧我为大家写过几款动画APP的评测,这一次我也想给大家推荐一些我的新宠,供大家参考。我在这里就不过多提及如Animation Desk、Procreate或是Callipeg之类知名度相对更高一些的动画App了&…

python框架是干什么的_django框架是干什么的

django(Python Web 框架)Django是一个开放源代码的Web应用框架,由Python写成。采用了MTV的框架模式,即模型M,视图V和模版T。它最初是被开发来用于管理劳伦斯出版集团旗下的一些以新闻内容为主的网站的,即是CMS(内容管理系统)软件。…

iphone如何信任软件_【手机软件】千禾影院:全新观影神器,支持安卓+iOS,最新、最全、高清、免费!...

Hello,大家好,我是春哥!每天记得打卡哦!感谢每一位小伙伴们的关注和支持!免责声明大部分资源来源于网络,仅供学习和交流使用,如有侵权请联系我们删除。每期文章末尾都会有关键词,在公众号发消息…

Java小魔女芭芭拉_沉迷蘑菇不可自拔,黏土人《小魔女学园》苏西·曼芭芭拉 图赏...

GOOD SMILE出品的黏土人系列手办新作——《小魔女学园》苏西曼芭芭拉,已经开始接受预定了。这款黏土人的原型师是来自中国上海的陈天,售价4167日元,预计2018年4月发售。苏西是主人公亚可的室友,她是从东南亚来的身份不明的魔女。热…

java int相除向上取整_java实战项目常用类,Date、Calendar、BigDecimal、Math、UUID

Java开发中经常用到的类和方法,以下主要就日期是时间处理、金融数字处理、数学计算、随机数、MD5加密等。java.util.Date类java.util 包提供了 Date 类来封装当前的日期和时间。 Date 类提供两个构造函数来实例化 Date 对象。日期时间的本质是一个long,它…

python四级中考有用的_一位中考生家长的后悔药:考前30多天,千万别做这7件傻事...

中考即将来临,考后必定是几家欢乐几家愁,有庆幸的、有后悔的。中考前的几个月到底应该怎么过?考前考中需要注意什么?家长做哪些事情是画蛇添足的。今天给大家推荐一位“中考失败”学子父亲的自白,其中有对优秀儿子的心疼&#xf…

R语言中dim函数_R语言--向量化计算(apply族函数)

R语言最优秀的是它的向量化编程,这其中apply族函数扮演了非常重要的角色。apply族函数是由apply、sapply、lapply、mapply、tapply等函数组成的。熟练使用apply族函数,能够简化程序,提高代码的运算速度。软件&环境win10 64bitR 3.6.1appl…