首先,分布式锁和我们平时讲的锁原理基本是一样的。目的是保证多个线程并发时,只有一个线程同时操作业务。或者方法,变量。在一个进程中,也就是一个jvm或者一个应用程序中,我们很容易去处理控制。在jdk的java.util并发包中,已经为我们提供了这几种加锁的方法,比如synchronized关键字或者Lock锁。处理。但是如果我们现在的应用只部署一台服务器的话,并发性是很差的。如果同时有上万个请求,很可能造成服务器压力过大而瘫痪。想想晚上10:00双十一、支付宝红包等业务场景。30日。自然需要多台服务器同时处理这些业务。那么可能有数百台服务器同时处理这些业务,但是请大家想一想,如果有100台服务器处理红包业务,现在假设有1000万人分发了1亿个红包,金额是多少?是随机的,那么在这个业务场景下,是否需要保证这1000万人最后发的红包的总和?等于一亿。如果处理不好,每人分到100万,那马云父亲估计大年初一就要宣告破产了。请求并发)变大,一个worker的处理能力有限,所以招更多的worker一起处理。假设1000万个请求平均分配到100台服务器,每台服务器接收10万个请求(这10万个请求不是同一秒来的,可能是1、2小时以内,你可以想到我们的三开红包十晚上,等到10.20,有的人马上打开,有的人12点才记起来~)这种情况下平均每秒请求数不到1000。这种压力,普通服务器还是可以承受的。第一个请求过来后,是不是要把这1亿的一部分给他,数量是随机的,假设第一个人拿到100,是不是要从1亿中减去100,剩下的99999900~第二个用户再次来分,金额是随机的,这次分了200元,所以要从剩下的99999900中减去200元,剩下的99999700。当第100000个用户来了,看到还有1000w,那么这1000w就是他的了。相当于每台服务器分1亿,也就是10万用户分1亿,最后一共100台服务器,分100亿。如果这样的话,虽然马云爸爸不会破产(最新统计,马云有2300亿),那分红的开发项目组和产品经理就可以GG了~简化结构图如下如下:2.如何获取分布式锁处理?所以为了解决这个问题,让1000万用户只分1亿,而不是100亿。这时候,分布式锁就派上用场了。分布式锁可以把整个集群当做一个应用,所以需要这个锁,而且必须独立于各个服务,不能在服务中。假设第一个服务器收到用户1的请求后,那么这个时候,他不能只判断自己的申请还剩多少钱可以分享,而是需要到外面去请求负责人管理一个亿的红包(Service),问他:哎,我这里要分100元,给我100元。管理红包的妹子(服务)看到还剩1亿。好吧,我给你100元,然后就剩下99999900元了。第二个请求过来后,被服务器2获取到,我继续问,管理红包的妹子,我想在这里分享10块钱,管理红包的妹子先查看了还有99999900,然后说:好的,我给你10元一块。然后剩下的99999890元等到1000w的请求到了,服务器100拿到请求,继续问,管理红包的妹子,你要100,妹子翻了个白眼,告诉你,只有1个元剩了,爱要了没有,那我这时候只能给你1元(1元也是钱,买辣条还是可以的)。这些请求编号1和2不代表执行顺序。正式场景下,应该有100台服务器,每台服务器都持有请求访问负责管理红包的妹子(服务)。收到100个请求后,这时候需要给负责红包(扔绣球花)的妹子加一把锁。谁从你100台服务器拿到锁(抢到绣球花)就进来和我说话,我给你分享,其他人等着吧。经过上面的分布式锁处理,马云爸爸终于松了口气,决定给红包队伍每人加一个鸡腿。简化结构图如下:3、分布式锁的实现有哪些?说到分布式锁的实现,还是有很多的,比如数据库方法,redis分布式锁,zookeeper分布式锁等等,如果我们采用分布式锁,redis可以换成上图中的redis作为“负责红包的女孩(服务)”。请你自己拿主意。3.1.为什么redis可以实现分布式锁?首先,redis是单线程的。这里的单线程是指网络请求模块使用一个线程(所以不需要考虑并发安全),即一个线程处理所有的网络请求,其他模块仍然使用多个线程。在实际操作中,流程大致是这样的:服务器1要访问发红包的妹子,也就是redis,那么他会通过“setnxkeyvalue”操作在redis中设置一个key,value并不重要。重要的是要有一个key,也就是一个标记,这个key你随便叫什么都可以,只要所有服务器都设置相同的key即可。假设我们设置一个,如下图,那么可以看到会返回一个1,代表成功。如果有另一个请求设置相同的key,如下图:此时会返回0,表示失败。那么我们可以通过这个操作来判断当前是否可以拿到锁,也可以访问“负责发红包的妹子”。如果它返回1,那么我就开始执行下面的逻辑。如果它返回0,那么说明已经有人占用了,那我就继续等。服务器1获得锁后,进行业务处理。完成之后需要释放锁,如下图所示:如果删除成功返回1,那么其他服务器可以继续重复上面的步骤设置key来达到锁的目的。当然,以上操作是直接在redis客户端进行的。如果是通过程序调用,一定不能这样写。比如java需要通过jedis来调用,但是整个处理逻辑基本是一样的。通过上面的方法,我们似乎解决了分布式锁的问题,但是想想有没有什么问题呢?是的,还是有问题,可能会出现死锁问题。比如服务器1在搭建好并获取到锁后,突然出现Downtime。那么后面的删除键操作就无法进行了。这个key会一直存在于redis中。其他服务器每次检查都会返回0,他们会认为有人在使用锁,我需要等待。为了解决这个死锁问题,我们需要给key设置有效期。有两种设置方法。1、第一种是在设置key后直接设置key的有效期“expirekeytimeout”,给key设置一个超时时间。单位是秒。过了这个时间,锁会自动释放,避免死锁。锁。这种方式相当于把锁的有效期交给redis来控制。如果时间到了,你还没有帮我删除key,那么redis会直接帮你删除,其他服务器可以继续去setnx获取锁。2、第二种方式是将密钥的删除权交给其他服务器。这时候就需要用到value值了。比如服务器1设置的值,即超时时间为当前时间+1秒。此时服务器2通过get,发现时间已经超过了系统当前时间,说明服务器1还没有释放锁,服务器1可能有问题,服务器2会开始删除关键操作,继续执行setnx操作。但是这样有个问题,就是不仅你的server2可能发现server1超时了,server3也可能发现,如果发生了,server2,setnx操作完成,server3会继续删除,难道server3也能setnx成功?这意味着服务器2和服务器3都获得了锁,这是一个大问题。这个时候怎么办?这时候我们就需要用到“GETSET键值”命令。这条命令的意思是获取当前key的值,并设置一个新的值。假设服务器2发现key已经过期,开始调用getset命令,然后用获取到的时间判断是否过期。如果获取到的时间仍然过期,说明已经获取到了锁。如果不是,说明在服务2执行getset之前,服务器3也可能发现锁已经过期,先于服务器2执行getset操作,并重新设置过期时间。那么server2需要放弃后续操作,继续等待server3释放锁或者监听key的有效期是否过期。其实这块有个小问题。服务器3修改了有效期。服务器2获取锁后,也修改了有效期,但是获取锁失败。不过有效期是在服务器3的基础上确定的。增加一些,但是这个影响其实很小,几乎可以忽略不计。3.2.为什么zookeeper可以实现分布式锁?百度百科是这样介绍的:ZooKeeper是一个分布式的、开源的分布式应用协调服务,是Google的Chubby的开源实现,是Hadoop和Hbase的重要组件。对于我们这些初次见面的人来说,可以理解为ZooKeeper就像是我们的电脑文件系统。我们可以在d盘创建文件夹a,继续在a文件夹创建文件夹a1和a2。我们的文件系统有什么特点?即同一目录下的文件名不能重复,ZooKeeper也是如此。ZooKeeper中所有的节点,也就是文件夹,都叫做Znode,这个Znode节点可以存储数据。我们可以使用“create/zkjjjnice”来创建一个节点。该命令的意思是在根目录下创建一个zkjjj节点,取值nice。同样,这里的值和我前面提到的redis中的值是一样的,没有任何意义,你随便给。另外,ZooKeeper可以创建4种类型的节点,分别是:持久节点、持久时序节点、临时节点、临时时序节点。首先说一下持久节点和临时节点的区别,无论你的ZooKeeper客户端是否断开连接,ZooKeeper服务端都会记录这个节点。临时节点正好相反。一旦你的ZooKeeper客户端断开连接,ZooKeeper服务器将不再保存这个节点。让我们谈谈顺序节点。顺序节点是指在创建节点时,ZooKeeper会自动给节点编号,比如0000001、0000002。最后,zookeeper有一个监控机制。客户端注册以监视它关心的目录节点。当目录节点发生变化(数据变化、被删除、子目录节点增加或删除)时,zookeeper会通知客户端。下面继续结合我们上面的bonuspackage场景来描述如何锁定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~以此类推。
