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状态、事件类型 ,权限的更多相关文章

  1. AS3事件类型

    Event事件类型: ACTIVATE       激活事件 DEACTIVATE 对象休眠事件 ADDED      可视对象添加事件 ADDED_TO_STAGE 可视对象添加到舞台事件 REMO ...

  2. Zookeeper学习之Watcher事件类型和ZK状态

    1.Zookeepe  Watcherr的事件类型和ZK状态. zookeeper:Watcher.ZK状态,事件类型(一)zookeeper有watch事件,是一次性触发的,当watch监视的数据发 ...

  3. openerp学习笔记 视图样式(表格行颜色、按钮,字段只读、隐藏,按钮状态、类型、图标、权限,group边距,聚合[合计、平均],样式)

    表格行颜色:             <tree string="请假单列表" colors="red:state == 'refuse';blue:state = ...

  4. MySQL binlog中的事件类型

    MySQL binlog记录的所有操作实际上都有对应的事件类型的,譬如STATEMENT格式中的DML操作对应的是QUERY_EVENT类型,ROW格式下的DML操作对应的是ROWS_EVENT类型. ...

  5. 深入理解DOM事件类型系列第二篇——键盘事件

    × 目录 [1]类型 [2]顺序 [3]按键信息[4]应用 前面的话 鼠标和键盘是电脑端主要的输入设备,上篇介绍了鼠标事件,本文将详细介绍键盘事件 类型 键盘事件用来描述键盘行为,主要有keydown ...

  6. DOM事件类型详解

    一.表单事件: input事件当<input>.<textarea>的值发生变化时触发.此外,打开contenteditable属性的元素,只要值发生变化,也会触发input事 ...

  7. JS中的事件类型和事件属性的基础知识

    周末无聊, 这几天又复习了下JS搞基程序设计3, 想着好记性不如浪笔头哇, 要么把这些东西写下来, 这样基础才能更加扎实么么哒, 知道的同学也可以直接过一下,当做复习,  小姨子再也不用担心我的学习啦 ...

  8. 在 SharePoint Server 2013 中配置建议和使用率事件类型

    http://technet.microsoft.com/zh-cn/library/jj715889.aspx 适用于: SharePoint Server 2013 利用使用事件,您可以跟踪用户与 ...

  9. WWF3事件类型活动<第三篇>

    WWF将工作流分为两大类: 面向Human:在工作流运行时通过用户对外部应用程序的操作来影响工作流的业务流转. 面向System:应用程序控制流程. 工作流与应用程序都是可以单独存在的,因此它们之间的 ...

随机推荐

  1. delphi多语言

    LoadLangFromStrings http://docwiki.embarcadero.com/Libraries/Berlin/en/FMX.Types.TLang http://blog.c ...

  2. 缓存框架有使用过哪些?memcache和redis有什么区别?项目中,怎么去选择?

    缓存有:ehcache,memcache和redis等 区别: 1. Redis和Memcache都是将数据存放在内存中,都是内存数据库.不过memcache还可用于缓存其他东西,例如图片.视频等等. ...

  3. elasticsearch-java异常

    1. Unsupported major.minor version 52.0 java的jdk版本过低导致,需要更换为jdk1.8+ 2. elasticsearch 的version在pom中提示 ...

  4. 文件操作 freopen函数

    转自http://blog.csdn.net/zhuyi2654715/article/details/6963673 当我们求解acm题目时,通常在设计好算法和程序后,要在调试环境(例如VC等)中运 ...

  5. 利用docker-machine安装swarm

    转自:https://www.cnblogs.com/jsonhc/p/7832642.html 安装之前的环境: 两个节点,节点1:192.168.101.14,用来创建manager1 machi ...

  6. UNITY2018 真机开启deepprofiling的操作

    手机上运行游戏并开启deepprofiling的命令如下 命令一:adb shell am start -n com.szyh.YHP1.kaopu/com.szyh.YHP1.kaopu.MainA ...

  7. MOBA项目问题记录

    1,动态变化的文本,使用较多时,耗时大,原因:只要字符串发生改变就会重绘,原理:每个字符的宽度不一样 项目中使用了网上写的一个文本控件,大概类型UGUI的东西,实现了字符缓存,绘制过的字符就不会再重建 ...

  8. 在服务器上同时启动多个tomcat

    我所用Tomcat服务器都为zip版,非安装版.以两个为例:安装第二个Tomcat完成后,到安装目录下的conf子目录中打开server.xml文件,查找以下三处:(1) 修改http访问端口(默认为 ...

  9. NTKO OFFICE文档控件

    目录 前言 什么是ntko 准备工作 实战演练 总结 一.前言 Web开发中经常需要用到在线处理office文档的功能,现在市面上有一些常用的Web页面调用显示Office的控件技术,用起来很方便. ...

  10. CSS----学习2

    CSS2属性 文本 1 水平对齐方式 text-align:left/right/center 也可以让img.input等有水平方向的对齐方式 2 垂直对齐方式 vertical-align:top ...