Distributed lock principle

Distributed locks and lock principle we usually talked about essentially the same, the purpose is to ensure that when multiple threads concurrently, only one thread at the same moment the operation or business methods, variables.

In one process, which is a JVM or application, it is easy to process control, and in jdk java.util contract has been provided to us to lock these methods, such as Synchronized Lock key or lock, you can deal with.

But we are now deploying the application if only one server, that concurrency is poor, if there are thousands of simultaneous requests, the server is likely to cause excessive pressure and paralysis.

Think about two-eleven and ten at night New Year's Eve, divide Alipay red envelopes and other business scenarios, natural need to use multiple servers to simultaneously handle these services, these services may be hundreds of simultaneous processing.

But we think that if there are 100 servers to handle business bonus package now assume 100 million envelopes to 10 million individual points, the amount of random, then this business scenario, it is not necessary to ensure that 10 million individuals last minute amount equal to the sum of 100 million red envelopes?

 

If not handled properly ~~ each assigned to one million, it is estimated that New Year's Day Ma Dad, you have to declare bankruptcy ~~

Conventional lock will cause what?

First talk about why we engage in the cluster. Simple to understand, the demand (request concurrency) becomes larger, and a limited processing power of workers, it would recruit more workers to deal with a number.

10 million assuming requests are evenly distributed across servers 100, each server receives the request and 10w.

This is not to 10w request in the same second, probably within 1-2 hours, and we can think of opening envelopes thirty at night, until the 10:20 start, some people flew open, and some people wait until 12:00 think of it.

If that happens, on average every second request also less than a thousand, this pressure is generally server or can afford:

  • The first users to points, after the request arrives, it is necessary to give him points in which 100 million part of the money, the amount of random, assuming that the first person assigned to the 100, it would have to subtract 100 in this 100 million, the remaining 99,999,900 Piece.
  • The second user coming hours, the amount of random, the sub-200, it would need to subtract 200 99,999,900 remaining in the remaining 99,999,700.
  • 10w to wait for the first user, as well as a look 1000w, 1000w and that this whole into his.

Etc. So in each server to divide 100 million, which is divided 10w 100 million users, the final total of 100 servers, to 10 billion minutes.

If you really like this, and although Ma Dad will not go bankrupt (Ma According to the latest statistics there are 230 billion yuan), and that the bonus package development team, and product managers, can the GG ~

Simplified structure is as follows:

 

Distributed Lock how to deal with?

So in order to solve this problem, so only 10 million users 100 million minutes, rather than 10 billion, this time distributed lock to put to use.

Distributed Lock can put the entire cluster will be deemed as an application to handle, then you need this lock independent of each service, rather than in the service inside.

After assume that the first server receives a user request for 1, not only in their application to determine how much money can be divided, and the need to manage external requests responsible for 100 million red envelopes of people (service), and asked him : hey, I'm here to points 100, 100 to me.

Management red sister (service) a look, there are a million Well, to give you 100, then the remaining 99,999,900.

After the second request arrives, the server 2 acquires, continue to ask, manage red sister, my side to 10 points, manage red sister first checked under there 99.9999 million, it said: Well, to give you 10 block, then the remaining 99,999,890.

After the first arrival until 1000w request, server request 100 to get, continue to ask, manage red sister, I want 100, sister rolled his eyes and said to you, we only have one, and love to do that this time I can only give you an up (also a money, buy a hot strip or can root).

These requests do not represent the sequence of numbers 1, 2, executed, under official scene, it should be 100 servers per server holds a request to visit the sister in charge of managing a red envelope (services).

That's where the sister red envelope tube will also received 100 requests, this time need in the sister in charge of the red envelopes there plus a lock on it (throwing Hydrangea), you get a 100 server who lock (grab Hydrangea), who will come in and talk to me, I'll give you points, others waiting to go.

After treatment of the above distributed lock, Ma father was relieved, the team decided to give red envelopes per person plus a chicken leg.

The simplified structure is as follows:

 

 

顺便在此给大家推荐一个Java架构方面的交流学习群:468897908,里面有:Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系,进群免费领取学习资源!

分布式锁的实现有哪些?

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

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

①为什么 Redis 可以实现分布式锁?

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

在实际的操作中过程大致是这样子的:服务器 1 要去访问发红包的妹子,也就是 Redis,那么它会在 Redis 中通过"setnx key value" 操作设置一个 Key 进去,Value 是啥不重要,重要的是要有一个 Key,也就是一个标记。

而且这个 Key 你爱叫啥叫啥,只要所有的服务器设置的 Key 相同就可以。

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

 

 

 

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

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

 

 

 

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

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

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

 

 

 

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

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

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

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

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

为了解决这个死锁的问题,我们就需要给 Key 设置有效期了。设置的方式有 2 种:

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

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

第二种方式,就是把删除 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 的基础上又增加一些,但是这种影响其实还是很小的,几乎可以忽略不计。

②为什么 Zookeeper 可实现分布式锁?

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

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

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

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

我们可以通过“ create /zkjjj nice”来创建一个节点,这个命令就表示,在根目录下创建一个 zkjjj 的节点,值是 nice。

同样这里的值,和我在前面说的 Redis 中的一样,没什么意义,你随便给。

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

  • 持久性节点
  • 持久性顺序节点
  • 临时性节点
  • 临时性顺序节点

首先说下持久性节点和临时性节点的区别:

  • 持久性节点表示只要你创建了这个节点,那不管你 ZooKeeper 的客户端是否断开连接,ZooKeeper 的服务端都会记录这个节点。
  • 临时性节点刚好相反,一旦你 ZooKeeper 客户端断开了连接,那 ZooKeeper 服务端就不再保存这个节点。
  • 顺便也说下顺序性节点,顺序性节点是指,在创建节点的时候,ZooKeeper 会自动给节点编号比如 0000001,0000002 这种的。

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

加q群:424447547 可获取一份Java架构进阶学习资源(高并发+Spring源码+JVM原理解析+分布式架构+微服务架构+多线程并发原理等...这些成为架构师必备的内容)以及Java进阶学习路线图。

在 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~以此类推。

 

 

 

转载至:https://www.douban.com/note/721979662/

Guess you like

Origin www.cnblogs.com/zhaozhitong/p/12347397.html