zk客户端连接、ping server端时采用了轮询算法作为负载均衡算法。每次向服务端发送ping请求时,都会调用next方法获取一个server地址,然后发送ping请求。
1 | public InetSocketAddress next(long spinDelay) { |
zk客户端连接、ping server端时采用了轮询算法作为负载均衡算法。每次向服务端发送ping请求时,都会调用next方法获取一个server地址,然后发送ping请求。
1 | public InetSocketAddress next(long spinDelay) { |
原创技术文章,转载请注明:转自http://newliferen.github.io/
ZooKeeper客户端代码相对简单,核心的类就是ZooKeeper、HostProvider、ClientCnxn、ClientCnxnSocketNIO这几个核心类。其中ZooKeeper是客户端接口类,为客户端提供各种操作访问zk的接口方法,HostProvider持有服务器连接端口等信息,应用程序通过创建ZooKeeper对象建立与zk server端的socket连接,ClientCnxnSocketNIO则向socket连接读写数据。
创建ZooKeeper对象时发生了什么
1 | public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher, |
ClientCnxn构造器
1 | public ClientCnxn(String chrootPath, HostProvider hostProvider, int sessionTimeout, ZooKeeper zooKeeper, |
揭开ZooKeeper请求的神秘面纱
以创建节点为例,对zk客户端内部实现展开学习,在学习之前,一直在猜想zk server是如何处理客户端的watcher事件,以往的经验是发送消息,或者是在响应结果中进行回调,但是看了zk源码之后,发现zk采用了很聪明的方式处理watcher事件。每一次请求,只要是注册了watcher或者回调事件,在组织request请求对象时,都不会让watcher事件和request产生关联,并且watcher事件也不会随着request发送到zk server端,只是创建了Packet对象,将watcher事件的实现保存在Packet对象内部,在向zk server端发送请求后,将Packet对象保存到pendingQueue队列中。eventThread线程从pendingQueue队列中取出Packet对象后,封装成WatcheEvent进行回调,这样客户端的回调事件得到执行。
在整个请求过程中,可以看出ZooKeeper的实现一直在尽一切可能减少网络传输时占用带宽,提高请求响应效率,整个过程中回调事件对zk server端是透明的,回调对server端不产生任何压力,在高并发场景中server高可用性得到进一步保障。
1 | public void create(final String path, byte data[], List<ACL> acl, |
1 | Packet queuePacket(RequestHeader h, ReplyHeader r, Record request, |
1 | clientCnxnSocket.doTransport(to, pendingQueue, outgoingQueue, ClientCnxn.this); |
真正要发送到zk server端的数据,就在createBB()方法中组织的。
1 | synchronized(outgoingQueue) { |
1 | public void createBB() { |
原创技术文章,转载请注明:转自http://newliferen.github.io/
MyCommandOptions类为命令处理器类,该类作为用户控制台和ZooKeeper核心类的一个适配接口,解析客户端输入命令,将用户输入内容解析为command和args。针对不同的command调用ZooKeeper类的对应方法,将args中path作为参数传给ZooKeeper类,由ZooKeeper进行请求对象封装,并向ZK server发起请求和处理响应结果。
解析命令
1 | public boolean parseCommand( String cmdstring ) { |
处理命令
1 | if (cmd.equals("ls") && args.length >= 2) { |
原创技术文章,转载请注明:转自http://newliferen.github.io/
客户端启动过程可以通过debug org.apache.zookeeper.ZooKeeperMain类即可了解启动时zk客户端都做了些什么以及核心处理类的功能,这里不再赘述,接下来记录一下如何debug客户端命令。
由于zk客户端启动后,ZooKeeperMain类内部通过jline监听客户端输入命令,然后执行,所以需要设置vm arguments,即可实现通过IDE控制台输入命令debug客户端代码,简单方便。1
-Djline.terminal=jline.UnsupportedTerminal
本文记录重点不是spring拦截器的配置方式,而是使用spring aop切面注解方式代替传统的在spring mvc配置文件中配置mvc:interceptor方法。这种传统方式配置起来比较灵活,修改配置后重启系统即可,不需要重新发布到线上,缺点是配置较臃肿,如果请求路径无法使用通配符,那么每个路径都需要在拦截器的配置中写一遍,但是使用切面配置的方式,代码相对优雅很多。
今天在已有项目中开发一个spring面向切面的功能,发现切面在Controller中不生效,经查,是由于把aop:aspectj-autoproxy配置在了applicationContext.xml配置文件中了。如果需要切面在Controller中生效,需要在mvc.xml配置文件中增加aop:aspectj-autoproxy proxy-target-class=”true”。
1 | import java.lang.annotation.*; |
1 | import org.aspectj.lang.JoinPoint; |
spring mvc.xml配置文件中增加:
1 | <aop:aspectj-autoproxy proxy-target-class="true"/> |
问题描述:给定一个整型数组,写一个算法,找出其中所有的奇数。
Java实现:数组的每个元素和1进行&运算,结果是1表示该元素为奇数,结果是0表示该元素是偶数。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17/**
* @auth zhengzhi.ren
* @date 2015/8/6.
*/
public class FindOdd {
public static void main(String[] args) {
// 找出数组中所有奇数
int[] arr = {21,23,12,33,1,5,9};
int len = arr.length;
for (int i=0;i<len;i++) {
if ((arr[i] & 1) == 1) {
System.out.println("第" + i + "个数字是奇数, 值:" + arr[i]);
}
}
}
}
该算法时间复杂度是n,n为数组的长度。
问题描述:有一个整形数组,数组中只有一个元素出现一次,其他元素都出现两次,写一个算法,找出这个只出现一次的元素。
Java实现:每个元素与数组中的其他元素(元素自身除外)进行^(异或)运算,结果是0,表示有相同元素,否则表示不存在相同元素。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24/**
* @auth zhengzhi.ren
* @date 2015/8/6.
*/
public class Bachelor {
public static void main(String[] args) {
int[] arr = {1,3,4,6,7,1,4,5,6,7};
int len = arr.length;
int tmp;
for (int i=0;i<len;i++) {
tmp = arr[i];
for (int j=0;j<len;j ++) {
if ((tmp^arr[j]) == 0 && i != j){
System.out.println("元素" + tmp + "存在相同元素");
break;
}
if (j == len -1) {
System.out.println("元素" + tmp + "不存在相同元素");
}
}
}
}
}
这种实现方式算法的时间复杂度为n^2,期待有更好的实现方式,降低时间复杂度。
工作几年之后,大学学过的数字电路与、或、非、异或等位运算忘的差不多了,这里使用位运算模拟linux操作系统权限,复习一下位运算。
实现思路
1 | /** |