使用ZooKeeper可以实现命名服务、集群管理、配置管理、分布式锁功能。
配置管理
对于配置的修改在项目中是非常常见的,如果是一两台机器,可以直接手动进行修改。但是对于集群环境,如果要修改这些相同的配置项,那么就必须同时修改每台运行机器,像这样的配置信息完全可以交给Zookeeper来管理。
ZooKeeper实现配置管理
对于需要修改的配置信息,可以保存在Zookeeper的某个目录节点中,然后将所有需要修改的应用机器监控配置信息的状态,一旦配置信息发生变化,每台应用机器就会收到Zookeeper的通知,然后从Zookeeper获取新的配置信息应用到系统中。如图:
代码如下:
1 | package com.zookeeper.config.manager; |
如上代码,定义一个Config
配置类,Config
类中host
、username
、passwd
分别对应于Zookeeper中/config/host
、/config/username
、/config/passwd
节点中数据,当这几个节点中数据发生变化时,如果客户端对这些节点设置了watch,则会收到相应的通知。
1 | package com.zookeeper.config.manager; |
ZookeeperConfig
负责创建Config
配置类中的节点,并为节点设置相应的默认值。
运行ZookeeperConfig
,可以看到Zookeeper服务器创建了上述节点,并设置了相应的默认值:
[zk: 127.0.0.1:2181(CONNECTED) 7] ls /config
[host, passwd, username]
[zk: 127.0.0.1:2181(CONNECTED) 8] get /config/host
192.168.1.128:3306
cZxid = 0x697
ctime = Wed Jun 27 21:00:32 CST 2018
mZxid = 0x697
mtime = Wed Jun 27 21:00:32 CST 2018
pZxid = 0x697
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 18
numChildren = 0
1 | package com.zookeeper.config.manager; |
客户端Client设置了对节点的Watcher
,当节点的数据发生变化时,则会更新配置项。
运行Client程序,输出如下:
test:123456@192.168.1.128:3306
修改host
节点的值:
[zk: 127.0.0.1:2181(CONNECTED) 12] set /config/host 127.0.0.1:2379
cZxid = 0x697
ctime = Wed Jun 27 21:00:32 CST 2018
mZxid = 0x69e
mtime = Wed Jun 27 21:09:36 CST 2018
pZxid = 0x697
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 14
numChildren = 0
程序输出如下:
test:123456@127.0.0.1:2379
修改username
节点的值:
[zk: 127.0.0.1:2181(CONNECTED) 13] set /config/username heql
程序输出如下:
heql:123456@127.0.0.1:2379
分布式锁
在分布式系统中的多个进程竞争同一资源,为了保证分布式系统中多个进程能够有序的访问该临界资源,这就要使用分布式锁,互斥锁在同一个进程中很容易实现,但是在跨进程或者在不同Server之间就不好实现。但是使用Zookeeper却很容易实现这个功能。
ZooKeeper实现分布式锁
ZooKeeper分布式锁的流程:
- 在zookeeper指定节点(lock)下创建临时顺序节点(EPHEMERAL_SEQUENTIAL)
- 获取locks下所有子节点children
- 对子节点按节点自增序号从小到大排序
- 判断本节点是不是第一个子节点,若是,则获取锁;若不是,则监听比该节点小的那个节点的删除事件
- 若监听事件生效,则回到第二步重新进行判断,直到获取到锁
如图:
代码如下:
1 | package com.zookeeper.distribute.lock; |
Lock
是一个接口,lock()
表示使用阻塞式获得锁、lock(long time, TimeUnit unit)
表示在指定时间内未获得锁,则直接返回、unLock
用于释放锁。
1 | package com.zookeeper.distribute.lock; |
ZkDistributeLock
实现了Lock
接口,在调用attemptLock
获得锁时,实现了上述流程。假设有3个客户端分别创建了/lock/lock-0000000000
、/lock/lock-0000000001
、/lock/lock-0000000002
三个节点。那么当前能获得锁是/lock/lock-0000000000
对应的客户端、当/lock/lock-0000000000
被删除时,/lock/lock-0000000001
节点将会得到通知。
测试程序如下:
1 | package com.zookeeper.distribute.lock; |
上面的测试程序,开启了10个线程,进行测试。
运行程序,输出如下:
INFO - children:[lock-0000000030, lock-0000000031, lock-0000000032, lock-0000000033, lock-0000000034, lock-0000000035, lock-0000000036, lock-0000000037, lock-0000000038, lock-0000000039]
INFO - children:[lock-0000000030, lock-0000000031, lock-0000000032, lock-0000000033, lock-0000000034, lock-0000000035, lock-0000000036, lock-0000000037, lock-0000000038, lock-0000000039]
INFO - get the lock, currentLockPath:/lock/lock-0000000030
INFO - end for waitForLock , hasLock = true
INFO - Thread-6: 正在处理逻辑......
INFO - children:[lock-0000000030, lock-0000000031, lock-0000000032, lock-0000000033, lock-0000000034, lock-0000000035, lock-0000000036, lock-0000000037, lock-0000000038, lock-0000000039]
INFO - children:[lock-0000000030, lock-0000000031, lock-0000000032, lock-0000000033, lock-0000000034, lock-0000000035, lock-0000000036, lock-0000000037, lock-0000000038, lock-0000000039]
INFO - children:[lock-0000000030, lock-0000000031, lock-0000000032, lock-0000000033, lock-0000000034, lock-0000000035, lock-0000000036, lock-0000000037, lock-0000000038, lock-0000000039]
INFO - children:[lock-0000000030, lock-0000000031, lock-0000000032, lock-0000000033, lock-0000000034, lock-0000000035, lock-0000000036, lock-0000000037, lock-0000000038, lock-0000000039]
INFO - children:[lock-0000000030, lock-0000000031, lock-0000000032, lock-0000000033, lock-0000000034, lock-0000000035, lock-0000000036, lock-0000000037, lock-0000000038, lock-0000000039]
INFO - children:[lock-0000000030, lock-0000000031, lock-0000000032, lock-0000000033, lock-0000000034, lock-0000000035, lock-0000000036, lock-0000000037, lock-0000000038, lock-0000000039]
INFO - children:[lock-0000000030, lock-0000000031, lock-0000000032, lock-0000000033, lock-0000000034, lock-0000000035, lock-0000000036, lock-0000000037, lock-0000000038, lock-0000000039]
INFO - children:[lock-0000000030, lock-0000000031, lock-0000000032, lock-0000000033, lock-0000000034, lock-0000000035, lock-0000000036, lock-0000000037, lock-0000000038, lock-0000000039]
INFO - children:[lock-0000000031, lock-0000000032, lock-0000000033, lock-0000000034, lock-0000000035, lock-0000000036, lock-0000000037, lock-0000000038, lock-0000000039]
INFO - get the lock, currentLockPath:/lock/lock-0000000031
INFO - end for waitForLock , hasLock = true
INFO - Thread-0: 正在处理逻辑......
INFO - children:[lock-0000000032, lock-0000000033, lock-0000000034, lock-0000000035, lock-0000000036, lock-0000000037, lock-0000000038, lock-0000000039]
INFO - get the lock, currentLockPath:/lock/lock-0000000032
INFO - end for waitForLock , hasLock = true
INFO - Thread-8: 正在处理逻辑......
INFO - children:[lock-0000000033, lock-0000000034, lock-0000000035, lock-0000000036, lock-0000000037, lock-0000000038, lock-0000000039]
INFO - get the lock, currentLockPath:/lock/lock-0000000033
INFO - end for waitForLock , hasLock = true
INFO - Thread-4: 正在处理逻辑......
INFO - children:[lock-0000000034, lock-0000000035, lock-0000000036, lock-0000000037, lock-0000000038, lock-0000000039]
INFO - get the lock, currentLockPath:/lock/lock-0000000034
INFO - end for waitForLock , hasLock = true
INFO - Thread-7: 正在处理逻辑......
INFO - children:[lock-0000000035, lock-0000000036, lock-0000000037, lock-0000000038, lock-0000000039]
INFO - get the lock, currentLockPath:/lock/lock-0000000035
INFO - end for waitForLock , hasLock = true
INFO - Thread-3: 正在处理逻辑......
INFO - children:[lock-0000000036, lock-0000000037, lock-0000000038, lock-0000000039]
INFO - get the lock, currentLockPath:/lock/lock-0000000036
INFO - end for waitForLock , hasLock = true
INFO - Thread-1: 正在处理逻辑......
INFO - children:[lock-0000000037, lock-0000000038, lock-0000000039]
INFO - get the lock, currentLockPath:/lock/lock-0000000037
INFO - end for waitForLock , hasLock = true
INFO - Thread-2: 正在处理逻辑......
INFO - children:[lock-0000000038, lock-0000000039]
INFO - get the lock, currentLockPath:/lock/lock-0000000038
INFO - end for waitForLock , hasLock = true
INFO - Thread-5: 正在处理逻辑......
INFO - children:[lock-0000000039]
INFO - get the lock, currentLockPath:/lock/lock-0000000039
INFO - end for waitForLock , hasLock = true
INFO - Thread-9: 正在处理逻辑......