Watcher、ZK状态、事件类型 ,权限
zookeeper有watch事件,是一次性触发的,当watch监视的数据发生变化时,通知设置了该watch的client,即watcher。
同样,其watcher是监听数据发送了某些变化,那就一定会有对应的事件类型和状态类型。
事件类型:(znode节点相关的)
EventType.NodeCreated
EventType.NodeDataChanged
EventType.NodeChildrenChanged
EventType.NodeDeleted
状态类型:(跟客户端实例相关的)
keeperState.Disconnected
keeperState.SyncConnected
keeperState.AuthFailed
keeperState.Expired
watcher的特性:一次性、客户端串行执行、轻量
一次性:对于ZK的watcher,只需要记住一点:zookeeper有watch事件,是一次性触发的,当watch监视的数据发生变化时,通知设置了该watch的client,即watcher,由于zookeeper的监控都是一次性的,所以每次必须设置监控。
客户端串行执行:客户端Watcher回调的过程是一个串行同步的过程,这为我们保证了顺序,同事需要开发人员注意一点,千万不要因为一个Watcher的处理逻辑影响了整个客户端的Watcher回调。
轻量:WatchedEvent 是Zookeeper整个Watcher通知机制的最小通知单元,整个结构只包含三部分:通知状态、事件类型和节点路径。也就是说Watcher通知非常的简单,只会告诉客户端发生了事件,而不会告知其具体内容,需要客户端自己去进行获取,比如NodeDataChanged事件,Zookeeper只会通知客户端指定节点的数据发生了变更,而不会直接提供具体的数据内容。
- package bjsxt.zookeeper.watcher;
- import java.util.List;
- import java.util.concurrent.CountDownLatch;
- import java.util.concurrent.atomic.AtomicInteger;
- import org.apache.zookeeper.CreateMode;
- import org.apache.zookeeper.WatchedEvent;
- import org.apache.zookeeper.Watcher;
- import org.apache.zookeeper.Watcher.Event.EventType;
- import org.apache.zookeeper.Watcher.Event.KeeperState;
- import org.apache.zookeeper.ZooDefs.Ids;
- import org.apache.zookeeper.ZooKeeper;
- import org.apache.zookeeper.data.Stat;
- /**
- * Zookeeper Wathcher
- * 本类就是一个Watcher类(实现了org.apache.zookeeper.Watcher类)
- * @author(alienware)
- * @since 2015-6-14
- */
- public class ZooKeeperWatcher implements Watcher {
- /** 定义原子变量 */
- AtomicInteger seq = new AtomicInteger();
- /** 定义session失效时间 */
- private static final int SESSION_TIMEOUT = 10000;
- /** zookeeper服务器地址 */
- private static final String CONNECTION_ADDR = "192.168.80.88:2181";
- /** zk父路径设置 */
- private static final String PARENT_PATH = "/testWatch";
- /** zk子路径设置 */
- private static final String CHILDREN_PATH = "/testWatch/children";
- /** 进入标识 */
- private static final String LOG_PREFIX_OF_MAIN = "【Main】";
- /** zk变量 */
- private ZooKeeper zk = null;
- /** 信号量设置,用于等待zookeeper连接建立之后 通知阻塞程序继续向下执行 */
- private CountDownLatch connectedSemaphore = new CountDownLatch(1);
- /**
- * 创建ZK连接
- * @param connectAddr ZK服务器地址列表
- * @param sessionTimeout Session超时时间
- */
- public void createConnection(String connectAddr, int sessionTimeout) {
- this.releaseConnection();
- try {
- zk = new ZooKeeper(connectAddr, sessionTimeout, this);
- System.out.println(LOG_PREFIX_OF_MAIN + "开始连接ZK服务器");
- connectedSemaphore.await();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- /**
- * 关闭ZK连接
- */
- public void releaseConnection() {
- if (this.zk != null) {
- try {
- this.zk.close();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- /**
- * 创建节点
- * @param path 节点路径
- * @param data 数据内容
- * @return
- */
- public boolean createPath(String path, String data) {
- try {
- //设置监控(由于zookeeper的监控都是一次性的所以 每次必须设置监控)
- this.zk.exists(path, true);
- System.out.println(LOG_PREFIX_OF_MAIN + "节点创建成功, Path: " +
- this.zk.create( /**路径*/
- path,
- /**数据*/
- data.getBytes(),
- /**所有可见*/
- Ids.OPEN_ACL_UNSAFE,
- /**永久存储*/
- CreateMode.PERSISTENT ) +
- ", content: " + data);
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- return true;
- }
- /**
- * 读取指定节点数据内容
- * @param path 节点路径
- * @return
- */
- public String readData(String path, boolean needWatch) {
- try {
- return new String(this.zk.getData(path, needWatch, null));
- } catch (Exception e) {
- e.printStackTrace();
- return "";
- }
- }
- /**
- * 更新指定节点数据内容
- * @param path 节点路径
- * @param data 数据内容
- * @return
- */
- public boolean writeData(String path, String data) {
- try {
- System.out.println(LOG_PREFIX_OF_MAIN + "更新数据成功,path:" + path + ", stat: " +
- this.zk.setData(path, data.getBytes(), -1));
- } catch (Exception e) {
- e.printStackTrace();
- }
- return false;
- }
- /**
- * 删除指定节点
- *
- * @param path
- * 节点path
- */
- public void deleteNode(String path) {
- try {
- this.zk.delete(path, -1);
- System.out.println(LOG_PREFIX_OF_MAIN + "删除节点成功,path:" + path);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- /**
- * 判断指定节点是否存在
- * @param path 节点路径
- */
- public Stat exists(String path, boolean needWatch) {
- try {
- return this.zk.exists(path, needWatch);
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
- }
- /**
- * 获取子节点
- * @param path 节点路径
- */
- private List<String> getChildren(String path, boolean needWatch) {
- try {
- return this.zk.getChildren(path, needWatch);
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
- }
- /**
- * 删除所有节点
- */
- public void deleteAllTestPath() {
- if(this.exists(CHILDREN_PATH, false) != null){
- this.deleteNode(CHILDREN_PATH);
- }
- if(this.exists(PARENT_PATH, false) != null){
- this.deleteNode(PARENT_PATH);
- }
- }
- /**
- * 收到来自Server的Watcher通知后的处理。
- */
- @Override
- public void process(WatchedEvent event) {
- System.out.println("进入 process 。。。。。event = " + event);
- try {
- Thread.sleep(200);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- if (event == null) {
- return;
- }
- // 连接状态
- KeeperState keeperState = event.getState();
- // 事件类型
- EventType eventType = event.getType();
- // 受影响的path
- String path = event.getPath();
- String logPrefix = "【Watcher-" + this.seq.incrementAndGet() + "】";
- System.out.println(logPrefix + "收到Watcher通知");
- System.out.println(logPrefix + "连接状态:\t" + keeperState.toString());
- System.out.println(logPrefix + "事件类型:\t" + eventType.toString());
- if (KeeperState.SyncConnected == keeperState) {
- // 成功连接上ZK服务器
- if (EventType.None == eventType) {
- System.out.println(logPrefix + "成功连接上ZK服务器");
- connectedSemaphore.countDown();
- }
- //创建节点
- else if (EventType.NodeCreated == eventType) {
- System.out.println(logPrefix + "节点创建");
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- this.exists(path, true);
- }
- //更新节点
- else if (EventType.NodeDataChanged == eventType) {
- System.out.println(logPrefix + "节点数据更新");
- System.out.println("我看看走不走这里........");
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(logPrefix + "数据内容: " + this.readData(PARENT_PATH, true));
- }
- //更新子节点
- else if (EventType.NodeChildrenChanged == eventType) {
- System.out.println(logPrefix + "子节点变更");
- try {
- Thread.sleep(3000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(logPrefix + "子节点列表:" + this.getChildren(PARENT_PATH, true));
- }
- //删除节点
- else if (EventType.NodeDeleted == eventType) {
- System.out.println(logPrefix + "节点 " + path + " 被删除");
- }
- else ;
- }
- else if (KeeperState.Disconnected == keeperState) {
- System.out.println(logPrefix + "与ZK服务器断开连接");
- }
- else if (KeeperState.AuthFailed == keeperState) {
- System.out.println(logPrefix + "权限检查失败");
- }
- else if (KeeperState.Expired == keeperState) {
- System.out.println(logPrefix + "会话失效");
- }
- else ;
- System.out.println("--------------------------------------------");
- }
- /**
- * <B>方法名称:</B>测试zookeeper监控<BR>
- * <B>概要说明:</B>主要测试watch功能<BR>
- * @param args
- * @throws Exception
- */
- public static void main(String[] args) throws Exception {
- //建立watcher
- ZooKeeperWatcher zkWatch = new ZooKeeperWatcher();
- //创建连接
- zkWatch.createConnection(CONNECTION_ADDR, SESSION_TIMEOUT);
- //System.out.println(zkWatch.zk.toString());
- Thread.sleep(1000);
- // 清理节点
- zkWatch.deleteAllTestPath();
- if (zkWatch.createPath(PARENT_PATH, System.currentTimeMillis() + "")) {
- Thread.sleep(1000);
- // 读取数据
- System.out.println("---------------------- read parent ----------------------------");
- //zkWatch.readData(PARENT_PATH, true);
- // 读取子节点
- System.out.println("---------------------- read children path ----------------------------");
- zkWatch.getChildren(PARENT_PATH, true);
- // 更新数据
- zkWatch.writeData(PARENT_PATH, System.currentTimeMillis() + "");
- Thread.sleep(1000);
- // 创建子节点
- zkWatch.createPath(CHILDREN_PATH, System.currentTimeMillis() + "");
- Thread.sleep(1000);
- zkWatch.writeData(CHILDREN_PATH, System.currentTimeMillis() + "");
- }
- Thread.sleep(50000);
- // 清理节点
- zkWatch.deleteAllTestPath();
- Thread.sleep(1000);
- zkWatch.releaseConnection();
- }
- }
zookeeper的ACL (AUTH)
ACL(Access Control List),Zookeeper作为一个分布式协调框架,其内部存储的都是一些关乎分布式系统运行时状态的元数据,尤其是涉及到一些分布式锁、Master选举和协调等应用场景。我们需要有效地保障Zookeeper中的数据安全,Zookeeper提供一套完善的ACL权限控制机制来保障数据的安全。
ZK提供了三种模式。权限模式,授权对象,权限。
权限模式:Scheme,开发人员最多使用一下四种权限模式:
IP:ip模式通过ip地址粒度来进行控制权限,例如配置了:ip:192.168.1.109即表示权限控制都是针对这个ip地址的,同时也支持按网段分配,比如192.168.1.*
Digest:digest是最常用的权限控制模式,也更符合我们对权限控制的认识,其类似于“username:password” 形式的权限标识进行权限配置,ZK会对形成的权限标识先后进行两次编码处理,分别是SHA-1加密算法、BASE64编码。
World:World是一直最开放的全校性控制模式,这种模式可以看作为特殊的Digest,他仅仅是一个标识而已。
Super:超级用户模式,在超级用户模式下可以对ZK任意进行操作
权限对象:指的是权限赋予的用户或者一个指定的实体,例如ip地址或机器等,在不同的模式下,授权对象是不同的,这种模式和权限对象一一对应。
权限:权限就是指那些通过权限检测后可以被允许执行的操作,在ZK中,对数据的操作权限分为以下五大类:
CREATE,DELLETE,READ,WRITE,ADMIN
- package bjsxt.zookeeper.auth;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.concurrent.CountDownLatch;
- import java.util.concurrent.atomic.AtomicInteger;
- import org.apache.zookeeper.CreateMode;
- import org.apache.zookeeper.WatchedEvent;
- import org.apache.zookeeper.Watcher;
- import org.apache.zookeeper.Watcher.Event.EventType;
- import org.apache.zookeeper.Watcher.Event.KeeperState;
- import org.apache.zookeeper.ZooDefs.Ids;
- import org.apache.zookeeper.ZooKeeper;
- import org.apache.zookeeper.data.ACL;
- import org.apache.zookeeper.data.Stat;
- /**
- * Zookeeper 节点授权
- * @author(alienware)
- * @since 2015-6-14
- */
- public class ZookeeperAuth implements Watcher {
- /** 连接地址 */
- final static String CONNECT_ADDR = "192.168.80.88:2181";
- /** 测试路径 */
- final static String PATH = "/testAuth";
- final static String PATH_DEL = "/testAuth/delNode";
- /** 认证类型 */
- final static String authentication_type = "digest";
- /** 认证正确方法 */
- final static String correctAuthentication = "123456";
- /** 认证错误方法 */
- final static String badAuthentication = "654321";
- static ZooKeeper zk = null;
- /** 计时器 */
- AtomicInteger seq = new AtomicInteger();
- /** 标识 */
- private static final String LOG_PREFIX_OF_MAIN = "【Main】";
- private CountDownLatch connectedSemaphore = new CountDownLatch(1);
- @Override
- public void process(WatchedEvent event) {
- try {
- Thread.sleep(200);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- if (event==null) {
- return;
- }
- // 连接状态
- KeeperState keeperState = event.getState();
- // 事件类型
- EventType eventType = event.getType();
- // 受影响的path
- String path = event.getPath();
- String logPrefix = "【Watcher-" + this.seq.incrementAndGet() + "】";
- System.out.println(logPrefix + "收到Watcher通知");
- System.out.println(logPrefix + "连接状态:\t" + keeperState.toString());
- System.out.println(logPrefix + "事件类型:\t" + eventType.toString());
- if (KeeperState.SyncConnected == keeperState) {
- // 成功连接上ZK服务器
- if (EventType.None == eventType) {
- System.out.println(logPrefix + "成功连接上ZK服务器");
- connectedSemaphore.countDown();
- }
- } else if (KeeperState.Disconnected == keeperState) {
- System.out.println(logPrefix + "与ZK服务器断开连接");
- } else if (KeeperState.AuthFailed == keeperState) {
- System.out.println(logPrefix + "权限检查失败");
- } else if (KeeperState.Expired == keeperState) {
- System.out.println(logPrefix + "会话失效");
- }
- System.out.println("--------------------------------------------");
- }
- /**
- * 创建ZK连接
- *
- * @param connectString
- * ZK服务器地址列表
- * @param sessionTimeout
- * Session超时时间
- */
- public void createConnection(String connectString, int sessionTimeout) {
- this.releaseConnection();
- try {
- zk = new ZooKeeper(connectString, sessionTimeout, this);
- //添加节点授权
- zk.addAuthInfo(authentication_type,correctAuthentication.getBytes());
- System.out.println(LOG_PREFIX_OF_MAIN + "开始连接ZK服务器");
- //倒数等待
- connectedSemaphore.await();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- /**
- * 关闭ZK连接
- */
- public void releaseConnection() {
- if (this.zk!=null) {
- try {
- this.zk.close();
- } catch (InterruptedException e) {
- }
- }
- }
- /**
- *
- * <B>方法名称:</B>测试函数<BR>
- * <B>概要说明:</B>测试认证<BR>
- * @param args
- * @throws Exception
- */
- public static void main(String[] args) throws Exception {
- ZookeeperAuth testAuth = new ZookeeperAuth();
- testAuth.createConnection(CONNECT_ADDR,2000);
- List<ACL> acls = new ArrayList<ACL>(1);
- for (ACL ids_acl : Ids.CREATOR_ALL_ACL) {
- acls.add(ids_acl);
- }
- try {
- zk.create(PATH, "init content".getBytes(), acls, CreateMode.PERSISTENT);
- System.out.println("使用授权key:" + correctAuthentication + "创建节点:"+ PATH + ", 初始内容是: init content");
- } catch (Exception e) {
- e.printStackTrace();
- }
- try {
- zk.create(PATH_DEL, "will be deleted! ".getBytes(), acls, CreateMode.PERSISTENT);
- System.out.println("使用授权key:" + correctAuthentication + "创建节点:"+ PATH_DEL + ", 初始内容是: init content");
- } catch (Exception e) {
- e.printStackTrace();
- }
- // 获取数据
- getDataByNoAuthentication();
- getDataByBadAuthentication();
- getDataByCorrectAuthentication();
- // 更新数据
- updateDataByNoAuthentication();
- updateDataByBadAuthentication();
- updateDataByCorrectAuthentication();
- // 删除数据
- deleteNodeByBadAuthentication();
- deleteNodeByNoAuthentication();
- deleteNodeByCorrectAuthentication();
- //
- Thread.sleep(1000);
- deleteParent();
- //释放连接
- testAuth.releaseConnection();
- }
- /** 获取数据:采用错误的密码 */
- static void getDataByBadAuthentication() {
- String prefix = "[使用错误的授权信息]";
- try {
- ZooKeeper badzk = new ZooKeeper(CONNECT_ADDR, 2000, null);
- //授权
- badzk.addAuthInfo(authentication_type,badAuthentication.getBytes());
- Thread.sleep(2000);
- System.out.println(prefix + "获取数据:" + PATH);
- System.out.println(prefix + "成功获取数据:" + badzk.getData(PATH, false, null));
- } catch (Exception e) {
- System.err.println(prefix + "获取数据失败,原因:" + e.getMessage());
- }
- }
- /** 获取数据:不采用密码 */
- static void getDataByNoAuthentication() {
- String prefix = "[不使用任何授权信息]";
- try {
- System.out.println(prefix + "获取数据:" + PATH);
- ZooKeeper nozk = new ZooKeeper(CONNECT_ADDR, 2000, null);
- Thread.sleep(2000);
- System.out.println(prefix + "成功获取数据:" + nozk.getData(PATH, false, null));
- } catch (Exception e) {
- System.err.println(prefix + "获取数据失败,原因:" + e.getMessage());
- }
- }
- /** 采用正确的密码 */
- static void getDataByCorrectAuthentication() {
- String prefix = "[使用正确的授权信息]";
- try {
- System.out.println(prefix + "获取数据:" + PATH);
- System.out.println(prefix + "成功获取数据:" + zk.getData(PATH, false, null));
- } catch (Exception e) {
- System.out.println(prefix + "获取数据失败,原因:" + e.getMessage());
- }
- }
- /**
- * 更新数据:不采用密码
- */
- static void updateDataByNoAuthentication() {
- String prefix = "[不使用任何授权信息]";
- System.out.println(prefix + "更新数据: " + PATH);
- try {
- ZooKeeper nozk = new ZooKeeper(CONNECT_ADDR, 2000, null);
- Thread.sleep(2000);
- Stat stat = nozk.exists(PATH, false);
- if (stat!=null) {
- nozk.setData(PATH, prefix.getBytes(), -1);
- System.out.println(prefix + "更新成功");
- }
- } catch (Exception e) {
- System.err.println(prefix + "更新失败,原因是:" + e.getMessage());
- }
- }
- /**
- * 更新数据:采用错误的密码
- */
- static void updateDataByBadAuthentication() {
- String prefix = "[使用错误的授权信息]";
- System.out.println(prefix + "更新数据:" + PATH);
- try {
- ZooKeeper badzk = new ZooKeeper(CONNECT_ADDR, 2000, null);
- //授权
- badzk.addAuthInfo(authentication_type,badAuthentication.getBytes());
- Thread.sleep(2000);
- Stat stat = badzk.exists(PATH, false);
- if (stat!=null) {
- badzk.setData(PATH, prefix.getBytes(), -1);
- System.out.println(prefix + "更新成功");
- }
- } catch (Exception e) {
- System.err.println(prefix + "更新失败,原因是:" + e.getMessage());
- }
- }
- /**
- * 更新数据:采用正确的密码
- */
- static void updateDataByCorrectAuthentication() {
- String prefix = "[使用正确的授权信息]";
- System.out.println(prefix + "更新数据:" + PATH);
- try {
- Stat stat = zk.exists(PATH, false);
- if (stat!=null) {
- zk.setData(PATH, prefix.getBytes(), -1);
- System.out.println(prefix + "更新成功");
- }
- } catch (Exception e) {
- System.err.println(prefix + "更新失败,原因是:" + e.getMessage());
- }
- }
- /**
- * 不使用密码 删除节点
- */
- static void deleteNodeByNoAuthentication() throws Exception {
- String prefix = "[不使用任何授权信息]";
- try {
- System.out.println(prefix + "删除节点:" + PATH_DEL);
- ZooKeeper nozk = new ZooKeeper(CONNECT_ADDR, 2000, null);
- Thread.sleep(2000);
- Stat stat = nozk.exists(PATH_DEL, false);
- if (stat!=null) {
- nozk.delete(PATH_DEL,-1);
- System.out.println(prefix + "删除成功");
- }
- } catch (Exception e) {
- System.err.println(prefix + "删除失败,原因是:" + e.getMessage());
- }
- }
- /**
- * 采用错误的密码删除节点
- */
- static void deleteNodeByBadAuthentication() throws Exception {
- String prefix = "[使用错误的授权信息]";
- try {
- System.out.println(prefix + "删除节点:" + PATH_DEL);
- ZooKeeper badzk = new ZooKeeper(CONNECT_ADDR, 2000, null);
- //授权
- badzk.addAuthInfo(authentication_type,badAuthentication.getBytes());
- Thread.sleep(2000);
- Stat stat = badzk.exists(PATH_DEL, false);
- if (stat!=null) {
- badzk.delete(PATH_DEL, -1);
- System.out.println(prefix + "删除成功");
- }
- } catch (Exception e) {
- System.err.println(prefix + "删除失败,原因是:" + e.getMessage());
- }
- }
- /**
- * 使用正确的密码删除节点
- */
- static void deleteNodeByCorrectAuthentication() throws Exception {
- String prefix = "[使用正确的授权信息]";
- try {
- System.out.println(prefix + "删除节点:" + PATH_DEL);
- Stat stat = zk.exists(PATH_DEL, false);
- if (stat!=null) {
- zk.delete(PATH_DEL, -1);
- System.out.println(prefix + "删除成功");
- }
- } catch (Exception e) {
- System.out.println(prefix + "删除失败,原因是:" + e.getMessage());
- }
- }
- /**
- * 使用正确的密码删除节点
- */
- static void deleteParent() throws Exception {
- try {
- Stat stat = zk.exists(PATH_DEL, false);
- if (stat == null) {
- zk.delete(PATH, -1);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
Watcher、ZK状态、事件类型 ,权限的更多相关文章
- AS3事件类型
Event事件类型: ACTIVATE 激活事件 DEACTIVATE 对象休眠事件 ADDED 可视对象添加事件 ADDED_TO_STAGE 可视对象添加到舞台事件 REMO ...
- Zookeeper学习之Watcher事件类型和ZK状态
1.Zookeepe Watcherr的事件类型和ZK状态. zookeeper:Watcher.ZK状态,事件类型(一)zookeeper有watch事件,是一次性触发的,当watch监视的数据发 ...
- openerp学习笔记 视图样式(表格行颜色、按钮,字段只读、隐藏,按钮状态、类型、图标、权限,group边距,聚合[合计、平均],样式)
表格行颜色: <tree string="请假单列表" colors="red:state == 'refuse';blue:state = ...
- MySQL binlog中的事件类型
MySQL binlog记录的所有操作实际上都有对应的事件类型的,譬如STATEMENT格式中的DML操作对应的是QUERY_EVENT类型,ROW格式下的DML操作对应的是ROWS_EVENT类型. ...
- 深入理解DOM事件类型系列第二篇——键盘事件
× 目录 [1]类型 [2]顺序 [3]按键信息[4]应用 前面的话 鼠标和键盘是电脑端主要的输入设备,上篇介绍了鼠标事件,本文将详细介绍键盘事件 类型 键盘事件用来描述键盘行为,主要有keydown ...
- DOM事件类型详解
一.表单事件: input事件当<input>.<textarea>的值发生变化时触发.此外,打开contenteditable属性的元素,只要值发生变化,也会触发input事 ...
- JS中的事件类型和事件属性的基础知识
周末无聊, 这几天又复习了下JS搞基程序设计3, 想着好记性不如浪笔头哇, 要么把这些东西写下来, 这样基础才能更加扎实么么哒, 知道的同学也可以直接过一下,当做复习, 小姨子再也不用担心我的学习啦 ...
- 在 SharePoint Server 2013 中配置建议和使用率事件类型
http://technet.microsoft.com/zh-cn/library/jj715889.aspx 适用于: SharePoint Server 2013 利用使用事件,您可以跟踪用户与 ...
- WWF3事件类型活动<第三篇>
WWF将工作流分为两大类: 面向Human:在工作流运行时通过用户对外部应用程序的操作来影响工作流的业务流转. 面向System:应用程序控制流程. 工作流与应用程序都是可以单独存在的,因此它们之间的 ...
随机推荐
- java解析文件
遇到两个小坑: 1.使用String.split,部分分隔符需要转义:https://www.cnblogs.com/mingforyou/archive/2013/09/03/3299569.htm ...
- CentOS 7.0安装配置Vsftp服务器步骤详解
安装Vsftp讲过最多的就是在centos6.x版本中了,这里小编看到有朋友写了一篇非常不错的CentOS 7.0安装配置Vsftp服务器教程,下面整理分享给各位. 一.配置防火墙,开启FTP服务器需 ...
- IUSER 匿名帐户密码获取
如何获取IUSR帐号和密码呢?有两种方法 1.安装IIS Resources,打开IIS Resources中的Metabase Explorer->机器名->LM->W3SVC-& ...
- 机器学习进阶-案例实战-图像全景拼接-书籍SIFT特征点连接 1.cv2.drawMatches(对两个图像的关键点进行连线操作)
1.cv2.drawMatches(imageA, kpsA, imageB, kpsB, matches[:10], None, flags=2) # 对两个图像关键点进行连线操作 参数说明:im ...
- JAVA版开源微信管家—JeeWx捷微3.2版本发布,支持微信公众号,微信企业号,支付窗、小程序
JeeWx捷微3.2微信企业号升级版本发布^_^ JeeWx捷微V3.2——多触点管理平台(支持微信公众号,微信企业号,支付窗.小程序) JeeWx捷微V3.2.0版本引入了更多新特性,支持微信公 ...
- Vue 修改dist 目录.
前后端分析之后,前端 打包处理 2
- 一个Tparams小测试
var aParams: TParams; aPar: TParam; I:Integer; begin aParams := TParams.Create(nil); aPar := aParams ...
- 火狐的3d视图插件Tilt 3D
15年的时候,使用过此功能.后来没注意就发现没了.firefox在47之后就停止自带了. 换成插件了. https://addons.mozilla.org/en-US/firefox/addon/t ...
- UploadFtp
#!/bin/bash FILENAME=$ DSTDIR=$ FTPSRV=ip FTPUSER="user" FTPPWD="password" SRCDI ...
- DAX2012 R3安装
安装程序跟DAX2009大同小异,不过这验证需要的组件也太多了,简直是.NET Framework大阅兵啊,各种版本都需要安装,特别是VC++从2008一直装到2012,有点崩溃... DEMO数据的 ...