基于ZooKeeper实现——分布式锁与实现

引言

ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。

ZooKeeper的架构通过冗余服务实现高可用性。因此,如果第一次无应答,客户端就可以询问另一台ZooKeeper主机。ZooKeeper节点将它们的数据存储于一个分层的命名空间,非常类似于一个文件系统或一个前缀树结构。客户端可以在节点读写,从而以这种方式拥有一个共享的配置服务。更新是全序的。

基于ZooKeeper分布式锁的流程

  • 在zookeeper指定节点(locks)下创建临时顺序节点node_n
  • 获取locks下所有子节点children
  • 对子节点按节点自增序号从小到大排序
  • 判断本节点是不是第一个子节点,若是,则获取锁;若不是,则监听比该节点小的那个节点的删除事件
  • 若监听事件生效,则回到第二步重新进行判断,直到获取到锁

具体实现

下面就具体使用java和zookeeper实现分布式锁,操作zookeeper使用的是apache提供的zookeeper的包。

  • 通过实现Watch接口,实现process(WatchedEvent event)方法来实施监控,使CountDownLatch来完成监控,在等待锁的时候使用CountDownLatch来计数,等到后进行countDown,停止等待,继续运行。
  • 以下整体流程基本与上述描述流程一致,只是在监听的时候使用的是CountDownLatch来监听前一个节点。

分布式锁

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

import org.apache.zookeeper.*;

import org.apache.zookeeper.data.Stat;

 

import java.io.IOException;

import java.util.ArrayList;

import java.util.Collections;

import java.util.List;

import java.util.concurrent.CountDownLatch;

import java.util.concurrent.TimeUnit;

import java.util.concurrent.locks.Condition;

import java.util.concurrent.locks.Lock;

 

/**

 * Created by liuyang on 2017/4/20.

 */

public class DistributedLock implements Lock, Watcher {

    private ZooKeeper zk = null;

    // 根节点

    private String ROOT_LOCK = "/locks";

    // 竞争的资源

    private String lockName;

    // 等待的前一个锁

    private String WAIT_LOCK;

    // 当前锁

    private String CURRENT_LOCK;

    // 计数器

    private CountDownLatch countDownLatch;

    private int sessionTimeout = 30000;

    private List<Exception> exceptionList = new ArrayList<Exception>();

 

    /**

     * 配置分布式锁

     * @param config 连接的url

     * @param lockName 竞争资源

     */

    public DistributedLock(String config, String lockName) {

        this.lockName = lockName;

        try {

            // 连接zookeeper

            zk = new ZooKeeper(config, sessionTimeout, this);

            Stat stat = zk.exists(ROOT_LOCK, false);

            if (stat == null) {

                // 如果根节点不存在,则创建根节点

                zk.create(ROOT_LOCK, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

            }

        catch (IOException e) {

            e.printStackTrace();

        catch (InterruptedException e) {

            e.printStackTrace();

        catch (KeeperException e) {

            e.printStackTrace();

        }

    }

 

    // 节点监视器

    public void process(WatchedEvent event) {

        if (this.countDownLatch != null) {

            this.countDownLatch.countDown();

        }

    }

 

    public void lock() {

        if (exceptionList.size() > 0) {

            throw new LockException(exceptionList.get(0));

        }

        try {

            if (this.tryLock()) {

                System.out.println(Thread.currentThread().getName() + " " + lockName + "获得了锁");

                return;

            else {

                // 等待锁

                waitForLock(WAIT_LOCK, sessionTimeout);

            }

        catch (InterruptedException e) {

            e.printStackTrace();

        catch (KeeperException e) {

            e.printStackTrace();

        }

    }

 

    public boolean tryLock() {

        try {

            String splitStr = "_lock_";

            if (lockName.contains(splitStr)) {

                throw new LockException("锁名有误");

            }

            // 创建临时有序节点

            CURRENT_LOCK = zk.create(ROOT_LOCK + "/" + lockName + splitStr, new byte[0],

                    ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);

            System.out.println(CURRENT_LOCK + " 已经创建");

            // 取所有子节点

            List<String> subNodes = zk.getChildren(ROOT_LOCK, false);

            // 取出所有lockName的锁

            List<String> lockObjects = new ArrayList<String>();

            for (String node : subNodes) {

                String _node = node.split(splitStr)[0];

                if (_node.equals(lockName)) {

                    lockObjects.add(node);

                }

            }

            Collections.sort(lockObjects);

            System.out.println(Thread.currentThread().getName() + " 的锁是 " + CURRENT_LOCK);

            // 若当前节点为最小节点,则获取锁成功

            if (CURRENT_LOCK.equals(ROOT_LOCK + "/" + lockObjects.get(0))) {

                return true;

            }

 

            // 若不是最小节点,则找到自己的前一个节点

            String prevNode = CURRENT_LOCK.substring(CURRENT_LOCK.lastIndexOf("/") + 1);

            WAIT_LOCK = lockObjects.get(Collections.binarySearch(lockObjects, prevNode) - 1);

        catch (InterruptedException e) {

            e.printStackTrace();

        catch (KeeperException e) {

            e.printStackTrace();

        }

        return false;

    }

 

    public boolean tryLock(long timeout, TimeUnit unit) {

        try {

            if (this.tryLock()) {

                return true;

            }

            return waitForLock(WAIT_LOCK, timeout);

        catch (Exception e) {

            e.printStackTrace();

        }

        return false;

    }

 

    // 等待锁

    private boolean waitForLock(String prev, long waitTime) throws KeeperException, InterruptedException {

        Stat stat = zk.exists(ROOT_LOCK + "/" + prev, true);

 

        if (stat != null) {

            System.out.println(Thread.currentThread().getName() + "等待锁 " + ROOT_LOCK + "/" + prev);

            this.countDownLatch = new CountDownLatch(1);

            // 计数等待,若等到前一个节点消失,则precess中进行countDown,停止等待,获取锁

            this.countDownLatch.await(waitTime, TimeUnit.MILLISECONDS);

            this.countDownLatch = null;

            System.out.println(Thread.currentThread().getName() + " 等到了锁");

        }

        return true;

    }

 

    public void unlock() {

        try {

            System.out.println("释放锁 " + CURRENT_LOCK);

            zk.delete(CURRENT_LOCK, -1);

            CURRENT_LOCK = null;

            zk.close();

        catch (InterruptedException e) {

            e.printStackTrace();

        catch (KeeperException e) {

            e.printStackTrace();

        }

    }

 

    public Condition newCondition() {

        return null;

    }

 

    public void lockInterruptibly() throws InterruptedException {

        this.lock();

    }

 

 

    public class LockException extends RuntimeException {

        private static final long serialVersionUID = 1L;

        public LockException(String e){

            super(e);

        }

        public LockException(Exception e){

            super(e);

        }

    }

}

  测试代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

public class Test {

    static int n = 500;

 

    public static void secskill() {

        System.out.println(--n);

    }

 

    public static void main(String[] args) {

         

        Runnable runnable = new Runnable() {

            public void run() {

                DistributedLock lock null;

                try {

                    lock new DistributedLock("127.0.0.1:2181""test1");

                    lock.lock();

                    secskill();

                    System.out.println(Thread.currentThread().getName() + "正在运行");

                finally {

                    if (lock != null) {

                        lock.unlock();

                    }

                }

            }

        };

 

        for (int i = 0; i < 10; i++) {

            Thread t = new Thread(runnable);

            t.start();

        }

    }

}

 转载:http://www.cnblogs.com/liuyang0/p/6800538.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值