分布式锁的实现

分布式服务需要一个全局锁来实现同一个模块多个服务之间的同步。就需要用到分布式锁。

实现方式:数据库主键、redis、zookeeper

1、数据库

利用数据库主键唯一规则,在争抢锁的时候向DB中写一条记录,这条记录主要包含锁的id、当前占用锁的线程名、重入的次数和创建时间等,如果插入成功表示当前线程获取到了锁,如果插入失败那么证明锁被其他人占用,等待一会儿继续争抢,直到争抢到或者超时为止。

优点:实现简单

缺点:没有超时保护机制、没有线程唤醒机制。

2、redis(推荐)

基于SetNX实现:
setNX是Redis提供的一个原子操作,如果指定key存在,那么setNX失败,如果不存在会进行Set操作并返回成功。我们可以利用这个来实现一个分布式的锁,主要思路就是,set成功表示获取锁,set失败表示获取失败,失败后需要重试。

优点:实现简单,吞吐量十分可观,对于高并发情况应付自如,自带超时保护,对于网络抖动的情况也可以利用超时删除策略保证不会阻塞所有流程。

缺点:没有线程唤醒机制。

对key设置失效时间,这个超时时间需要把控好,过大那么系统吞吐量低。如果过小那么会有并发安全问题。

3、Zookeeper

方式一:Zookeeper的写入都是顺序的,在一个节点创建之后,其他请求再次创建便会失败,同时可以对这个节点进行Watch,如果节点删除会通知其他节点抢占锁。

由于Zookeeper本身需要维护自己的一致性,所以性能上较Redis还是有一定差距的。

方式二:创建一个 EPHEMERAL_SEQUENTIAL (临时自动编号)目录节点,然后调用 getChildren方法获取当前的目录节点列表中最小的目录节点是不是就是自己创建的目录节点,如果正是自己创建的,那么它就获得了这个锁,如果不是那么它就调用 exists(String path, boolean watch) 方法并监控 Zookeeper 上目录节点列表的变化,一直到自己创建的节点是列表中最小编号的目录节点,从而获得锁,释放锁很简单,只要删除前面它自己所创建的目录节点就行了。

 
项目中使用:
服务中有一个负责文件上传的模块(部署多路),客户端文件会分包上传,服务端通过redis的SetNX实现分布式锁,避免多个线程或服务同时操作一个文件。


链接:https://www.jianshu.com/p/c2b4aa7a12f1

原文地址:https://www.cnblogs.com/xyfaneast/p/11106595.html