客户端与服务端之间是怎么交互的
我的源码链接
github,我已经添加上了很多注释,拉下来就可以直接看。
简单描述
先总述一下,zk的客户端与服务端交互的大概思路。
客户端 一个线程 while循环从队列(LinkedList)中取数据发送给服务端或者接受服务端的数据
服务端 同理。
源码入口
客户端:
我们知道在linux中如果想链接zk则要通过zkCli.sh脚本。(这个不知道的请先去熟悉一下zk)
#!/usr/bin/env bash
# use POSTIX interface, symlink is followed automatically
ZOOBIN="${BASH_SOURCE-$0}"
ZOOBIN="$(dirname "${ZOOBIN}")"
ZOOBINDIR="$(cd "${ZOOBIN}"; pwd)"
if [ -e "$ZOOBIN/../libexec/zkEnv.sh" ]; then
. "$ZOOBINDIR"/../libexec/zkEnv.sh
else
. "$ZOOBINDIR"/zkEnv.sh
fi
"$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \
-cp "$CLASSPATH" $CLIENT_JVMFLAGS $JVMFLAGS \
org.apache.zookeeper.ZooKeeperMain "$@"
通过上面的脚本可以知道最终zkCli.sh 就是执行了org.apache.zookeeper.ZooKeeperMain的main方法。
服务端:
同理服务端也是通过zkServer.sh 脚本来启动(由于该脚本有点长。找到核心代码)
ZOOMAIN="org.apache.zookeeper.server.quorum.QuorumPeerMain"
=====================
start)
echo -n "Starting zookeeper ... "
if [ -f "$ZOOPIDFILE" ]; then
if kill -0 `cat "$ZOOPIDFILE"` > /dev/null 2>&1; then
echo $command already running as process `cat "$ZOOPIDFILE"`.
exit 0
fi
fi
nohup "$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \
-cp "$CLASSPATH" $JVMFLAGS $ZOOMAIN "$ZOOCFG" > "$_ZOO_DAEMON_OUT" 2>&1 < /dev/null &
if [ $? -eq 0 ]
then
case "$OSTYPE" in
*solaris*)
/bin/echo "${!}\\c" > "$ZOOPIDFILE"
;;
*)
/bin/echo -n $! > "$ZOOPIDFILE"
;;
esac
if [ $? -eq 0 ];
then
sleep 1
echo STARTED
else
echo FAILED TO WRITE PID
exit 1
fi
else
echo SERVER DID NOT START
exit 1
fi
;;
可以看到最终启动的时候也就是执行了QuorumPeerMain的main方法
客户端启动流程
ZookeeperMain.java
public static void main(String args[])
throws KeeperException, IOException, InterruptedException
{
// 初始化
// 去建立与服务端的sokcet
// 开启后台线程,循环从sokcet读、写数据
ZooKeeperMain main = new ZooKeeperMain(args);
//监听 后续命令行 命令并处理
//就是你执行完zkCli.sh后控制台与你交互的命令行,就是在这里执行的。
main.run();
}
重点看下 ZooKeeperMain main = new ZooKeeperMain(args)
public ZooKeeperMain(String args[]) throws IOException, InterruptedException {
// 命令行 参数解析
cl.parseOptions(args);
System.out.println("Connecting to " + cl.getOption("server"));
// 连接服务器,并且初始化zk
connectToZK(cl.getOption("server"));
//zk = new ZooKeeper(cl.getOption("server"),
// Integer.parseInt(cl.getOption("timeout")), new MyWatcher());
}
protected void connectToZK(String newHost) throws InterruptedException, IOException {
if (zk != null && zk.getState().isAlive()) {
zk.close();
}
host = newHost;
boolean readOnly = cl.getOption("readonly") != null;
// 初始化 ZooKeeper 类
/**
* 1、getClientCnxnSocket() 准备好一个 Socket连接
* 2、sendThread = new SendThread(clientCnxnSocket); 发送线程(包括连接、发送数据)
* 3、eventThread = new EventThread(); 事件线程
* 调用 start方法
*/
zk = new ZooKeeper(host,
Integer.parseInt(cl.getOption("timeout")),
new MyWatcher(), readOnly);
}
public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher,
boolean canBeReadOnly)
throws IOException
{
LOG.info("Initiating client connection, connectString=" + connectString
+ " sessionTimeout=" + sessionTimeout + " watcher&