用 zookeeper 来实现分布式锁
结合我们前面对 zookeeper 特性的分析和理解,我们可以 利用 zookeeper 节点的特性来实现独占锁,就是同级节点 的唯一性,多个进程往 zookeeper 的指定节点下创建一个 相同名称的节点,只有一个能成功,另外一个是创建失败; 创建失败的节点全部通过 zookeeper 的 watcher 机制来监听 zookeeper 这个子节点的变化,一旦监听到子节点的删 除事件,则再次触发所有进程去写锁;
这种实现方式很简单,但是会产生“惊群效应”,简单来说就 是如果存在许多的客户端在等待获取锁,当成功获取到锁 的进程释放该节点后,所有处于等待状态的客户端都会被 唤醒,这个时候 zookeeper 在短时间内发送大量子节点变 更事件给所有待获取锁的客户端,然后实际情况是只会有 一个客户端获得锁。如果在集群规模比较大的情况下,会 对 zookeeper 服务器的性能产生比较的影响。
利用有序节点来实现分布式锁
我们可以通过有序节点来实现分布式锁,每个客户端都往指定的节点下注册一个临时有序节点,越早创建的节点, 节点的顺序编号就越小,那么我们可以判断子节点中最小 的节点设置为获得锁。如果自己的节点不是所有子节点中 最小的,意味着还没有获得锁。这个的实现和前面单节点 实现的差异性在于,每个节点只需要监听比自己小的节点, 当比自己小的节点删除以后,客户端会收到 watcher 事件, 此时再次判断自己的节点是不是所有子节点中最小的,如 果是则获得锁,否则就不断重复这个过程,这样就不会导 致羊群效应,因为每个客户端只需要监控一个节点。
public static void main(String[] args) {
CuratorFramework curatorFramework=null;
curatorFramework=CuratorFrameworkFactory.builder().
connectString(ZkConfig.ZK_CONNECT_STR).sessionTimeoutMs(ZkConfig.ZK_SESSION_TIMEOUT).
retryPolicy(new ExponentialBackoffRetry(1000,10)).build();
curatorFramework.start();
final InterProcessMutex lock=new InterProcessMutex(curatorFramework ,"/locks");
for(int i=0;i<10;i++){ new Thread(()->{
System.out.println(Thread.currentThread().getName()+"->尝试获取锁");
try {
lock.acquire();
System.out.println(Thread.currentT
hread().getName()+"->获得锁成功");
} catch (Exception e){
e.printStackTrace();
}
try {
Thread.sleep(4000);
lock.release();
System.out.println(Thread.currentT hread().getName()+"->释放锁成功");
} catch (Exception e)
e.printStackTrace(); }
},"t"+i).start();
}
}
什么是分布式一致性问题呢?
简单来说,就是在一个分布式系统中,有多个节点,每个节点 都会提出一个请求,但是在所有节点中只能确定一个请求被通过。而这个通过是需要所有节 点达成一致的结果,所以所谓的一致性就是在提出的所有请求中能够选出最终一个确定请求。 并且这个请求选出来以后,所有的节点都要知道。这个就是典型的拜占庭将军问题
拜占庭将军问题说的是:拜占庭帝国军队的将军们必须通过投票达成一致来决定是否对某一 个国家发起进攻。但是这些将军在地里位置上是分开的,并且在将军中存在叛徒。叛徒可以 通过任意行动来达到自己的目标:
- 欺骗某些将军采取进攻行动
- 促使一个不是所有将军都统一的决定,比如将军们本意是不希望进攻,但是叛徒可以促成 进攻行动
- 迷惑将军使得他们无法做出决定 如果叛徒达到了任意一个目标,那么这次行动必然失败。只有完全达成一致那么这次进攻才 可能胜利 拜占庭问题的本质是,由于网络通信存在不可靠的问题,也就是可能存在消息丢失,或者网 络延迟。如何在这样的背景下对某一个请求达成一致。 为了解决这个问题,很多人提出了各种协议,比如大名鼎鼎的 Paxos; 也就是说在不可信的 网络环境中,按照 paxos 这个协议就能够针对某个提议达成一致。 所以:分布式一致性的本质就是在分布式系统中,多个节点就某一个提议如何达成一致
这个和 Google Chubby 有什么关系呢
在 Google 有一个 GFS(google file system),他们有一个需求就是要从多个 gfs server 中选出 一个 master server。这个就是典型的一致性问题,5 个分布在不同节点的 server,需要确定 一个 master server,而他们要达成的一致性目标是:确定某一个节点为 master,并且所有节 点要同意。
而 GFS 就是使用 chubby 来解决这个问题的。
实现原理是:所有的 server 通过 Chubby 提供的通信协议到 Chubby server 上创建同一个文 件,当然,最终只有一个 server 能够获准创建这个文件,这个 server 就成为了 master,它 会在这个文件中写入自己 的地址,这样其它的 server 通过读取这个文件就能知道被选出的 master 的地址