zookeeper的补充
七、Watcher
在ZooKeeper中,接口类Watcher用于表示一个标准的事件处理器,其定义了事件通知相关的逻辑,包含KeeperState和EventType两个枚举类,分别代表了通知状态和事件类型,同时定义了事件的回调方法:process(WatchedEvent event)。
7.1什么是Watcher接口
同一个事件类型在不同的通知状态中代表的含义有所不同,表7-3列举了常见的通知状态和事件类型。
表7-3 Watcher通知状态与事件类型一览
|
KeeperState |
EventType |
触发条件 |
说明 |
|
None |
客户端与服务端成功建立连接 |
||
|
SyncConnected |
NodeCreated |
Watcher监听的对应数据节点被创建 |
|
|
NodeDeleted |
Watcher监听的对应数据节点被删除 |
此时客户端和服务器处于连接状态 |
|
|
NodeDataChanged |
Watcher监听的对应数据节点的数据内容发生变更 |
||
|
NodeChildChanged |
Wather监听的对应数据节点的子节点列表发生变更 |
||
|
Disconnected |
None |
客户端与ZooKeeper服务器断开连接 |
此时客户端和服务器处于断开连接状态 |
|
Expired |
Node |
会话超时 |
此时客户端会话失效,通常同时也会受到SessionExpiredException异常 |
|
AuthFailed |
None |
通常有两种情况,1:使用错误的schema进行权限检查 2:SASL权限检查失败 |
通常同时也会收到AuthFailedException异常 |
表7-3中列举了ZooKeeper中最常见的几个通知状态和事件类型。
回调方法process()
process方法是Watcher接口中的一个回调方法,当ZooKeeper向客户端发送一个Watcher事件通知时,客户端就会对相应的process方法进行回调,从而实现对事件的处理。process方法的定义如下:
abstract public void process(WatchedEvent event);
这个回调方法的定义非常简单,我们重点看下方法的参数定义:WatchedEvent。
WatchedEvent包含了每一个事件的三个基本属性:通知状态(keeperState),事件类型(EventType)和节点路径(path),其数据结构如图7-5所示。ZooKeeper使用WatchedEvent对象来封装服务端事件并传递给Watcher,从而方便回调方法process对服务端事件进行处理。
提到WatchedEvent,不得不讲下WatcherEvent实体。笼统地讲,两者表示的是同一个事物,都是对一个服务端事件的封装。不同的是,WatchedEvent是一个逻辑事件,用于服务端和客户端程序执行过程中所需的逻辑对象,而WatcherEvent因为实现了序列化接口,因此可以用于网络传输。
服务端在生成WatchedEvent事件之后,会调用getWrapper方法将自己包装成一个可序列化的WatcherEvent事件,以便通过网络传输到客户端。客户端在接收到服务端的这个事件对象后,首先会将WatcherEvent还原成一个WatchedEvent事件,并传递给process方法处理,回调方法process根据入参就能够解析出完整的服务端事件了。
需要注意的一点是,无论是WatchedEvent还是WatcherEvent,其对ZooKeeper服务端事件的封装都是机及其简单的。举个例子来说,当/zk-book这个节点的数据发生变更时,服务端会发送给客户端一个“ZNode数据内容变更”事件,客户端只能够接收到如下信
7.2代码
|
public class ZkClientWatcher implements Watcher { // 集群连接地址 private static final String CONNECT_ADDRES = "192.168.110.159:2181,192.168.110.160:2181,192.168.110.162:2181"; // 会话超时时间 private static final int SESSIONTIME = 2000; // 信号量,让zk在连接之前等待,连接成功后才能往下走. private static final CountDownLatch countDownLatch = new CountDownLatch(1); private static String LOG_MAIN = "【main】 "; private ZooKeeper zk;
public void createConnection(String connectAddres, int sessionTimeOut) { try { zk = new ZooKeeper(connectAddres, sessionTimeOut, this); System.out.println(LOG_MAIN + "zk 开始启动连接服务器...."); countDownLatch.await(); } catch (Exception e) { e.printStackTrace(); } }
public boolean createPath(String path, String data) { try { this.exists(path, true); this.zk.create(path, data.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); System.out.println(LOG_MAIN + "节点创建成功, Path:" + path + ",data:" + data); } catch (Exception e) { e.printStackTrace(); return false; } return true; }
/** * 判断指定节点是否存在 * * @param path * 节点路径 */ public Stat exists(String path, boolean needWatch) { try { return this.zk.exists(path, needWatch); } catch (Exception e) { e.printStackTrace(); return null; } }
public boolean updateNode(String path,String data) throws KeeperException, InterruptedException { exists(path, true); this.zk.setData(path, data.getBytes(), -1); return false; }
public void process(WatchedEvent watchedEvent) {
// 获取事件状态 KeeperState keeperState = watchedEvent.getState(); // 获取事件类型 EventType eventType = watchedEvent.getType(); // zk 路径 String path = watchedEvent.getPath(); System.out.println("进入到 process() keeperState:" + keeperState + ", eventType:" + eventType + ", path:" + path); // 判断是否建立连接 if (KeeperState.SyncConnected == keeperState) { if (EventType.None == eventType) { // 如果建立建立成功,让后程序往下走 System.out.println(LOG_MAIN + "zk 建立连接成功!"); countDownLatch.countDown(); } else if (EventType.NodeCreated == eventType) { System.out.println(LOG_MAIN + "事件通知,新增node节点" + path); } else if (EventType.NodeDataChanged == eventType) { System.out.println(LOG_MAIN + "事件通知,当前node节点" + path + "被修改...."); } else if (EventType.NodeDeleted == eventType) { System.out.println(LOG_MAIN + "事件通知,当前node节点" + path + "被删除...."); }
} System.out.println("--------------------------------------------------------"); }
public static void main(String[] args) throws KeeperException, InterruptedException { ZkClientWatcher zkClientWatcher = new ZkClientWatcher(); zkClientWatcher.createConnection(CONNECT_ADDRES, SESSIONTIME); //boolean createResult = zkClientWatcher.createPath("/p15", "pa-644064"); zkClientWatcher.updateNode("/pa2","7894561"); }
} |
八、Zookeeper实战分布式锁
8.1线程进程资源竞争
线程进程资源竞争
当有一个线程或进程在对资源进行操作时,其他线程或进程都不可以对这个资
原进行操作,直到该线程或进程完成操作,其他线程或进程才能对该资源进
行操作,而其他线程或进程又处于等待状态。
8.2线程进程同步的方式和机制
临界区
通过对多线程的串行化来访问公共资源或一段代码
synchronized 修饰的java方法
仅用于线程同步
互斥量
采用互斥对象机制。
只有拥有互斥对象的线程才有访问公共资
源竞争
的问题
源的权限
synchronized 修饰的代码块
java.util.concurrent.locks.Lock
分布式锁的主要实现机制
信号量
它允许多个任务在同一时刻访问同一资源,但是需要限制在同一
时刻访问此资源的最大线程数目;
解决执
行顺序
CountDownLatch,CyclicBarrier和Semaphore
的问题
事件
通过通知操作的方式来保持任务的同步,还可以方便实现对多个
任务的优先级比较的操作
8.3分布式锁实现的技术
基于数据实现分布式锁
性能较差,容易出现单点故障
锁没有失效事件,容易死锁。
非阻塞式
不可重入
基于缓存实现分布式锁
锁没有失效事件,容易死锁
非阻塞式
不可重入
基于Zookeeper实现分布式锁
实现相对简单
可靠性高
性能较好
8.4Zookeeper应用场景
数据发布订阅
负载均衡
命名服务
分布式协调
集群管理
配置管理
分布式队列
分布式锁
8.5Zookeeper实战分布式锁
场景描述
在线程高并发场景下,生成唯一的订单编号
如:2017-10-14-20-52-33-01
年 月 日 时 分 秒 序号
代码:
|
#####生成订单号###### import java.text.SimpleDateFormat; import java.util.Date;
//生成订单号 public class OrderNumGenerator { private static int count = 0; //生成订单号 public String getOrderNumber() { SimpleDateFormat smt = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); return smt.format(new Date()) + "-" + ++count; }
} #####订单业务逻辑###### public class OrderService implements Runnable { private OrderNumGenerator orderNumGenerator = new OrderNumGenerator(); private static Object oj = new Object(); private Lock lock = new ZookeeperDistrbuteLock();
public void run() { getNumber(); }
public void getNumber() { // synchronized (oj) { lock.getLock(); String orderNumber = orderNumGenerator.getOrderNumber(); System.out.println("获取订单号:" + orderNumber); lock.unLock(); // }
}
public static void main(String[] args) { for (int i = 0; i < 100; i++) { new Thread(new OrderService()).start(); } }
}
#####lock接口 ###### public interface Lock { // 获取锁 public void getLock(); // 释放锁 public void unLock(); }
#####ZookeeperAbstractLock抽象类接口 ###### public abstract class ZookeeperAbstractLock implements Lock { private static final String CONNECT_ADDRES = "192.168.110.159:2181,192.168.110.160:2181,192.168.110.162:2181";
protected ZkClient zkClient = new ZkClient(CONNECT_ADDRES); protected String PATH = "/lock";
public void getLock() { // 如果当前节点已经存在,则等待 if (tryLock()) { System.out.println("获取到锁 get"); } else { // 等待 waitLock(); // 重新获取锁 getLock(); } }
protected abstract void waitLock();
protected abstract boolean tryLock();
public void unLock() { if (zkClient != null) { zkClient.close(); } System.out.println("已经释放锁..."); } #####ZookeeperAbstractLock抽象类接口 ###### //实现锁 public class ZookeeperDistrbuteLock extends ZookeeperAbstractLock { private CountDownLatch countDownLatch = new CountDownLatch(1);
@Override protected boolean tryLock() { try { zkClient.createEphemeral(PATH); // 创建成功 return true; } catch (Exception e) { // 创建失败 return false; }
}
@Override protected void waitLock() { try { IZkDataListener iZkDataListener = new IZkDataListener() {
public void handleDataDeleted(String path) throws Exception { // 唤醒等待线程, 继续往下走. if (countDownLatch != null) { countDownLatch.countDown(); } }
public void handleDataChange(String path, Object data) throws Exception {
} }; // 注册到zk监听中 zkClient.subscribeDataChanges(PATH, iZkDataListener); if (zkClient.exists(PATH)) { countDownLatch = new CountDownLatch(1);
// 等待 countDownLatch.await();
} // 删除事件通知 zkClient.unsubscribeDataChanges(PATH, iZkDataListener); } catch (Exception e) { // TODO: handle exception } }
} |
分布式锁解决思路
分布式锁使用zk,在zk上创建一个临时节点(有效期) ,使用临时节点作为锁,因为节点不允许重复。
如果能创建节点成功,生成订单号,如果创建节点失败,等待。临时节点zk关闭,释放锁,其他节点就可以重新生成订单号。
zookeeper的补充的更多相关文章
- Dubbo+zookeeper面试题补充
什么是分布式?什么是集群?主要区别 分布式是将一个服务分个部分,然后通过远程调用方式进行.远程调用框架RPC框架,spring cloud,dubbo.集群是将同一个服务的多个副本部署在不同的集群上, ...
- 架构设计:远程调用服务架构设计及zookeeper技术详解(下篇)
一.下篇开头的废话 终于开写下篇了,这也是我写远程调用框架的第三篇文章,前两篇都被博客园作为[编辑推荐]的文章,很兴奋哦,嘿嘿~~~~,本人是个很臭美的人,一定得要截图为证: 今天是2014年的第一天 ...
- ZooKeeper安装与配置
一. 单机安装.配置: 1. 下载zookeeper二进制安装包下载地址:http://apache.dataguru.cn/zookeeper/zookeeper-3.4.3/zookeeper-3 ...
- ZooKeeper原理及使用
ZooKeeper是Hadoop Ecosystem中非常重要的组件,它的主要功能是为分布式系统提供一致性协调(Coordination)服务,与之对应的Google的类似服务叫Chubby.今天这篇 ...
- zookeeper原理及作用
ZooKeeper是Hadoop Ecosystem中非常重要的组件,它的主要功能是为分布式系统提供一致性协调(Coordination)服务,与之对应的Google的类似服务叫Chubby.今天这篇 ...
- zookeeper能做什么?
Zookeeper是Hadoop的一个子项目,虽然源自hadoop,但是我发现zookeeper脱离hadoop的范畴开发分布式框架的运用越来越多.今天我想谈谈zookeeper,本文不谈如何使用zo ...
- solrCloud+tomcat+zookeeper集群配置
solrcolud安装solrCloud+tomcat+zookeeper部署 转载请出自出处:http://eksliang.iteye.com/blog/2107002 http://eksli ...
- ZooKeeper学习第一期---Zookeeper简单介绍
一.分布式协调技术 在给大家介绍ZooKeeper之前先来给大家介绍一种技术——分布式协调技术.那么什么是分布式协调技术?那么我来告诉大家,其实分布式协调技术主要用来解决分布式环境当中多个进程之间的同 ...
- Mac OS Storm+Kafka+Zookeeper配置
先补充一个前两天mac配置的文档. 首先确定由jdk scala环境 JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/Cu ...
随机推荐
- 【LEETCODE】55、数组分类,适中级别,题目:79、611、950
第950题,这题我是真的没想到居然会说使用队列去做,大神的答案,拿过来瞻仰一下 package y2019.Algorithm.array; import java.util.HashMap; imp ...
- c# NPOI文件操作
public static Byte[] RenderDataToExcel<T>(List<T> SourceList, List<String> filter) ...
- 递归在JavaScript中的应用实例
递归 适用的必要条件:①过程的描述中包含它自身②有明确的结束递归的条件. 主要思路:在每一次调用自己时,使用相同的解决问题的方法,但调用的参数每次不同(有规律的变化),使用一个终止处理(结束递归)的条 ...
- c# 获取网页的爬虫程序
转载于:https://www.cnblogs.com/wzk153/p/9145684.html HtmlAgilityPack相关详解: https://www.cnblogs.com/asxin ...
- java之hibernate之helloworld
这篇文章,会一步一步的演示hibernate的使用. 目录结构如下: 1.新建java项目 2.增加一个lib文件夹,并把 hibernate必须的jar包 和 数据库驱动包 一起复制进去 然后把hi ...
- Base64图片编码的使用
一.base64编码介绍 Base64是网络上最常见的用于传输8Bit字节代码的编码方式之一,Base64编码可用于在HTTP环境下传递较长的标识信息.采用Base64编码具有不可读性,即所编码的数据 ...
- Layui学习笔记(一)—— 关于模块的扩展
在使用layui的时候,总有官方自带模块不够用想自己扩展的时候,这时候我们就需要扩展模块了. 模块扩展有两种: (一)普通地扩展 layui.define( function (exports) { ...
- android RecyclerView的Grid布局案例
1.先创建activity_grid.xml 和 activity_grid_item.xml <?xml version="1.0" encoding="utf- ...
- FreeRTOS计数型信号量
API函数 //创建 #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) #define xSemaphoreCreateCounting( uxMaxCount ...
- UCOSIII钩子函数
OSIdleTaskHook 空闲任务调用这个函数,可以用来让CPU进入低功耗模式 void OSIdleTaskHook (void) { #if OS_CFG_APP_HOOKS_EN > ...