zk理论知识点
一、特性
1、高效
适用于大型的分布式系统. 如果写多的话性能不高,因为它要做所有节点之间的数据同步。
2、可靠
支持集群,大部分可用即服务可用
3、顺序
所有写请求由leader生成递增zxid,写操作时,采用mvcc乐观锁机制进行写,保证所有写操作顺序。
4、简洁
对外提供的api非常实用、简洁。仅仅7个api
create - 在树形结构的位置中创建节点
delete - 删除一个节点
exists - 测试节点在指定位置上是否存在
get data - 从节点上读取数据
set data - 往节点写入输入
get chilren - 检索一个节点的子节点列表
sync - 等待传输数据
二、数据模型
1.临时节点
2.临时顺序节点
3.持久节点
4.持久顺序节点
三、zk事件通知
Zookeeper Watcher事件通知 当节点发生变化的时候 修改、删除、新增 都会监听到通知
ZkClient zkClient = new ZkClient(ADDRES, TIMEOUT); String path = "/mayikt-service"; zkClient.subscribeChildChanges(path, new IZkChildListener() { @Override public void handleChildChange(String s, List<String> list) throws Exception { System.out.println(s + "节点发生了变化,剩余一下节点"); list.forEach((t) -> System.out.println(t)); } }); zkClient.subscribeDataChanges(path+"/8080", new IZkDataListener() { @Override public void handleDataChange(String s, Object o) throws Exception { System.out.println(s + "节点内容发生了变化:" + o); } @Override public void handleDataDeleted(String s) throws Exception { System.out.println(s + "节点被移除~~~"); } });
四、zk实现分布式锁的原因
因为Zookeeper节点路径保持唯一,不允许重复 且有临时节点特性连接关闭后当前节点会自动消失,从而实现分布式锁。
实现原理:
1.多请求同时创建相同的节点(lockPath),只要谁能够创建成功 谁就能够获取到锁;
2.如果创建节点的时候,突然该节点已经被其他请求创建的话则直接等待;
3.只要能够创建节点成功,则开始进入到正常业务逻辑操作,其他没有获取锁进行等待;
4.正常业务逻辑流程执行完后,调用zk关闭连接方式释放锁,从而是其他的请求开始进入到获取锁的资源。
如果使用zk实现分布式锁,获取锁之后业务逻辑方法一直没有执行完毕,导致其他所有的请求等待的话如何解决?
设置Session连接超时时间,在规定的时间内获取锁后超时啦~自动回滚当前数据库业务逻辑。
public class OrderNumGenerator { // 全局id private static int count = 0; public String getNumber() { SimpleDateFormat simpt = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); try { Thread.sleep(30); } catch (Exception e) { } return simpt.format(new Date()) + "-" + ++count; } }
public interface Lock { /** * 获取锁 */ void getLock(); /** * 释放锁 */ void unLock(); }
public abstract class ZookeeperAbstractTemplateLock implements Lock { //参数1 连接地址 private static final String ADDRES = "192.168.212.147:2181"; // 参数2 zk超时时间 private static final int TIMEOUT = 5000; // 计数器 protected static CountDownLatch countDownLatch = null; // 创建zkClient连接 protected ZkClient zkClient = new ZkClient(ADDRES, TIMEOUT); protected String lockPaht = "/lockPath"; @Override public void getLock() { if (tryLock()) { System.out.println(Thread.currentThread().getName() + ">>>获取锁信息成功<<<<"); } else { // 等待 waitLock(); //重新获取锁 getLock(); } } // 尝试获取锁 protected abstract boolean tryLock(); // 当前线程等待 protected abstract boolean waitLock(); @Override public void unLock() { if (zkClient != null) { System.out.println(Thread.currentThread().getName() + ">>>关闭锁成功<<<<"); zkClient.close(); } } }
public class ZookeeperTemplateImplLock extends ZookeeperAbstractTemplateLock { @Override protected boolean tryLock() { try { String s = zkClient.create(lockPaht, lockPaht, CreateMode.EPHEMERAL); // 获取锁成功 return true; } catch (Exception e) { // // 获取锁失败 // e.printStackTrace(); return false; } } @Override protected boolean waitLock() { //采用时间监听节点是否被删除 IZkDataListener iZkDataListener = new IZkDataListener() { @Override public void handleDataChange(String s, Object o) throws Exception { } @Override public void handleDataDeleted(String s) throws Exception { if (countDownLatch != null) { countDownLatch.countDown();//重新进入获取锁 } } }; // 注册当前监听事件通知 zkClient.subscribeDataChanges(lockPaht, iZkDataListener); // 判断当前节点是否存在,如果存在就等待 if (zkClient.exists(lockPaht)) { if (countDownLatch == null) { countDownLatch = new CountDownLatch(1); } try { // 等待 countDownLatch.await(); } catch (Exception e) { } // 正常执行业务逻辑 重新获取锁 } // 移除事件通知 zkClient.unsubscribeDataChanges(lockPaht, iZkDataListener); return false; } }
public class OrderService implements Runnable { // 生产订单号码 private OrderNumGenerator orderNumGenerator = new OrderNumGenerator(); // private Lock lock = new ReentrantLock(); private Lock lock = new ZookeeperTemplateImplLock(); @Override public void run() { getNumber(); } private void getNumber() { try { lock.getLock(); String number = orderNumGenerator.getNumber(); System.out.println("number:" + number); } catch (Exception e) { } finally { lock.unLock(); } } }
一、特性1、高效适用于大型的分布式系统. 如果写多的话性能不高,因为它要做所有节点之间的数据同步。2、可靠支持集群,大部分可用即服务可用3、顺序所有写请求由leader生成递增zxid,写操作时,采用mvcc乐观锁机制进行写,保证所有写操作顺序。4、简洁对外提供的api非常实用、简洁。仅仅7个apicreate - 在树形结构的位置中创建节点delete - 删除一个节点exists - 测试节点在指定位置上是否存在get data - 从节点上读取数据set data - 往节点写入输入get chilren - 检索一个节点的子节点列表sync - 等待传输数据二、数据模型1.临时节点2.临时顺序节点3.持久节点4.持久顺序节点三、zk事件通知Zookeeper Watcher事件通知 当节点发生变化的时候 修改、删除、新增 都会监听到通知ZkClient zkClient = new ZkClient(ADDRES, TIMEOUT);String path = "/mayikt-service";
zkClient.subscribeChildChanges(path, new IZkChildListener() {@Overridepublic void handleChildChange(String s, List<String> list) throws Exception {System.out.println(s + "节点发生了变化,剩余一下节点");list.forEach((t) -> System.out.println(t));}});
zkClient.subscribeDataChanges(path+"/8080", new IZkDataListener() {@Overridepublic void handleDataChange(String s, Object o) throws Exception {System.out.println(s + "节点内容发生了变化:" + o);}
@Overridepublic void handleDataDeleted(String s) throws Exception {System.out.println(s + "节点被移除~~~");}});四、zk实现分布式锁的原因因为Zookeeper节点路径保持唯一,不允许重复 且有临时节点特性连接关闭后当前节点会自动消失,从而实现分布式锁。实现原理:1.多请求同时创建相同的节点(lockPath),只要谁能够创建成功 谁就能够获取到锁;2.如果创建节点的时候,突然该节点已经被其他请求创建的话则直接等待;3.只要能够创建节点成功,则开始进入到正常业务逻辑操作,其他没有获取锁进行等待;4.正常业务逻辑流程执行完后,调用zk关闭连接方式释放锁,从而是其他的请求开始进入到获取锁的资源。如果使用zk实现分布式锁,获取锁之后业务逻辑方法一直没有执行完毕,导致其他所有的请求等待的话如何解决?设置Session连接超时时间,在规定的时间内获取锁后超时啦~自动回滚当前数据库业务逻辑。
public class OrderNumGenerator { // 全局id private static int count = 0;
public String getNumber() { SimpleDateFormat simpt = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); try { Thread.sleep(30); } catch (Exception e) {
} return simpt.format(new Date()) + "-" + ++count; }}
public interface Lock { /** * 获取锁 */ void getLock();
/** * 释放锁 */ void unLock();}
public abstract class ZookeeperAbstractTemplateLock implements Lock { //参数1 连接地址 private static final String ADDRES = "192.168.212.147:2181"; // 参数2 zk超时时间 private static final int TIMEOUT = 5000; // 计数器 protected static CountDownLatch countDownLatch = null; // 创建zkClient连接 protected ZkClient zkClient = new ZkClient(ADDRES, TIMEOUT); protected String lockPaht = "/lockPath";
@Override public void getLock() { if (tryLock()) { System.out.println(Thread.currentThread().getName() + ">>>获取锁信息成功<<<<"); } else { // 等待 waitLock(); //重新获取锁 getLock(); }
}
// 尝试获取锁 protected abstract boolean tryLock();
// 当前线程等待 protected abstract boolean waitLock();
@Override public void unLock() { if (zkClient != null) { System.out.println(Thread.currentThread().getName() + ">>>关闭锁成功<<<<"); zkClient.close(); } }}
public class ZookeeperTemplateImplLock extends ZookeeperAbstractTemplateLock { @Override protected boolean tryLock() { try { String s = zkClient.create(lockPaht, lockPaht, CreateMode.EPHEMERAL); // 获取锁成功 return true; } catch (Exception e) {// // 获取锁失败// e.printStackTrace(); return false; } }
@Override protected boolean waitLock() { //采用时间监听节点是否被删除 IZkDataListener iZkDataListener = new IZkDataListener() {
@Override public void handleDataChange(String s, Object o) throws Exception {
}
@Override public void handleDataDeleted(String s) throws Exception { if (countDownLatch != null) { countDownLatch.countDown();//重新进入获取锁 } } }; // 注册当前监听事件通知 zkClient.subscribeDataChanges(lockPaht, iZkDataListener); // 判断当前节点是否存在,如果存在就等待 if (zkClient.exists(lockPaht)) { if (countDownLatch == null) { countDownLatch = new CountDownLatch(1); } try { // 等待 countDownLatch.await(); } catch (Exception e) {
} // 正常执行业务逻辑 重新获取锁 } // 移除事件通知 zkClient.unsubscribeDataChanges(lockPaht, iZkDataListener); return false; }}
public class OrderService implements Runnable { // 生产订单号码 private OrderNumGenerator orderNumGenerator = new OrderNumGenerator(); // private Lock lock = new ReentrantLock(); private Lock lock = new ZookeeperTemplateImplLock();
@Override public void run() { getNumber();
}
private void getNumber() { try { lock.getLock(); String number = orderNumGenerator.getNumber(); System.out.println("number:" + number); } catch (Exception e) {
} finally { lock.unLock(); } }}
浙公网安备 33010602011771号