最近在项目中用到ZooKeeper, 通过Java连接比较容易,.net项目就没那么容易,尤其对于不熟悉Linux的开发人员,这里写点搭建ZooKeeper测试环境的经验,供参考。
背景知识:
Zookeeper的优点和用途就不再赘述,但是关于ZooKeeper的特点和原理还是要清楚,可以参考官方文档:http://zookeeper.apache.org/doc/trunk/zookeeperOver.html
环境准备:
系统:对于ZooKeeper等等开源类的服务端软件,运行环境通常都推荐Linux,所以,建议使用虚拟机安装Linux系统,本文中使用VirtualBox 运行Ubuntu 14.04 ,并且需要安装和配置好JDK。编译ZooKeeperNet 需要在Windows下进行,使用VisualStudio。
虚机网络:通常使用NAT 连接外网,使用网桥进行主机与虚间进行交互。由于公司的网络有特殊限制,所以,我在笔记本上,使用了网桥桥接了笔记本的无线网卡,使用NAT连接有线网卡。由于主机上网也要通过公司的代理,所以也要在Ubuntu中设置相应的代理实现网络访问。
1. Ubuntu中安装ZooKeeper
1)下载
http://mirror.bit.edu.cn/apache/zookeeper/
下载3.4.8 得到文件:zookeeper-3.4.8.tar.gz
2)在Ubuntu中解压文件:
tar -zxvf zookeeper-3.4.8.tar.gz
3)初始化zookeeper 配置文件:
zookeeper 默认包含了配置文件,在 zookeeper-3.4.8/conf 目录中,文件名是zoo_sample.cfg, 我们需要的是zoo.cfg文件来运行zookeeper服务,所以,最简单的办法就是复制这个文件即可。
cp zoo_sample.cfg zoo.cfg
4)运行 zookeeper 服务:
#切换至zookeeper的bin目录 rqing@rqing-VirtualBox:~/shared/tools/zookeeper-3.4.8/bin$ ls README.txt zkCli.cmd zkEnv.cmd zkServer.cmd zookeeper.out zkCleanup.sh zkCli.sh zkEnv.sh zkServer.sh #运行 zkServer.sh 启动服务 rqing@rqing-VirtualBox:~/shared/tools/zookeeper-3.4.8/bin$ ./zkServer.sh start ZooKeeper JMX enabled by default Using config: /home/rqing/shared/tools/zookeeper-3.4.8/bin/../conf/zoo.cfg Starting zookeeper ... STARTED
5)运行zookeeper 客户端进行验证
#运行 zkCli.sh 启动客户端 rqing@rqing-VirtualBox:~/shared/tools/zookeeper-3.4.8/bin$ ./zkCli.sh Connecting to localhost:2181 2016-05-26 11:29:24,943 [myid:] - INFO [main:Environment@100] - Client environment:zookeeper.version=3.4.8--1, built on 02/06/2016 03:18 GMT 2016-05-26 11:29:24,962 [myid:] - INFO [main:Environment@100] - Client environment:host.name=rqing-VirtualBox 2016-05-26 11:29:24,962 [myid:] - INFO [main:Environment@100] - Client environment:java.version=1.8.0_91 2016-05-26 11:29:24,976 [myid:] - INFO [main:Environment@100] - Client environment:java.vendor=Oracle Corporation 2016-05-26 11:29:24,976 [myid:] - INFO [main:Environment@100] - Client environment:java.home=/media/sf_Shared/tools/jdk1.8.0_91/jre 2016-05-26 11:29:24,976 [myid:] - INFO [main:Environment@100] - Client environment:java.class.path=/home/rqing/shared/tools/zookeeper-3.4.8/bin/../build/classes:/home/rqing/shared/tools/zookeeper-3.4.8/bin/../build/lib/*.jar:/home/rqing/shared/tools/zookeeper-3.4.8/bin/../lib/slf4j-log4j12-1.6.1.jar:/home/rqing/shared/tools/zookeeper-3.4.8/bin/../lib/slf4j-api-1.6.1.jar:/home/rqing/shared/tools/zookeeper-3.4.8/bin/../lib/netty-3.7.0.Final.jar:/home/rqing/shared/tools/zookeeper-3.4.8/bin/../lib/log4j-1.2.16.jar:/home/rqing/shared/tools/zookeeper-3.4.8/bin/../lib/jline-0.9.94.jar:/home/rqing/shared/tools/zookeeper-3.4.8/bin/../zookeeper-3.4.8.jar:/home/rqing/shared/tools/zookeeper-3.4.8/bin/../src/java/lib/*.jar:/home/rqing/shared/tools/zookeeper-3.4.8/bin/../conf: 2016-05-26 11:29:24,976 [myid:] - INFO [main:Environment@100] - Client environment:java.library.path=/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib 2016-05-26 11:29:24,977 [myid:] - INFO [main:Environment@100] - Client environment:java.io.tmpdir=/tmp 2016-05-26 11:29:24,977 [myid:] - INFO [main:Environment@100] - Client environment:java.compiler=<NA> 2016-05-26 11:29:24,977 [myid:] - INFO [main:Environment@100] - Client environment:os.name=Linux 2016-05-26 11:29:24,977 [myid:] - INFO [main:Environment@100] - Client environment:os.arch=amd64 2016-05-26 11:29:24,977 [myid:] - INFO [main:Environment@100] - Client environment:os.version=3.13.0-85-generic 2016-05-26 11:29:24,977 [myid:] - INFO [main:Environment@100] - Client environment:user.name=rqing 2016-05-26 11:29:24,977 [myid:] - INFO [main:Environment@100] - Client environment:user.home=/home/rqing 2016-05-26 11:29:24,977 [myid:] - INFO [main:Environment@100] - Client environment:user.dir=/media/sf_Shared/tools/zookeeper-3.4.8/bin 2016-05-26 11:29:24,986 [myid:] - INFO [main:ZooKeeper@438] - Initiating client connection, connectString=localhost:2181 sessionTimeout=30000 watcher=org.apache.zookeeper.ZooKeeperMain$MyWatcher@506c589e Welcome to ZooKeeper! 2016-05-26 11:29:25,244 [myid:] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@1032] - Opening socket connection to server localhost/127.0.0.1:2181. Will not attempt to authenticate using SASL (unknown error) JLine support is enabled 2016-05-26 11:29:25,577 [myid:] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@876] - Socket connection established to localhost/127.0.0.1:2181, initiating session 2016-05-26 11:29:25,778 [myid:] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@1299] - Session establishment complete on server localhost/127.0.0.1:2181, sessionid = 0x154e80a09410000, negotiated timeout = 30000 WATCHER:: WatchedEvent state:SyncConnected type:None path:null #运行 ls 命令查看zookeeper 节点 [zk: localhost:2181(CONNECTED) 0] ls / [zookeeper]
到这里,zookeeper 的测试环境就已经搭建好了
更多操作,参见:http://zookeeper.apache.org/doc/trunk/zookeeperStarted.html
2. 编译ZooKeeperNet
1)下载源代码:
地址:https://github.com/ExactTargetDev/zookeeper/tree/et-develop
dotnet代码在解压后目录的 zookeeper-et-develop\src\dotnet 下
直接使用VisualStudio编译项目会报缺少依赖的错误。
建议把下载后的文件放到主机与虚机的共享目录中,便于后面使用ant下载依赖。
2)使用ant 下载依赖项, 在Ubuntu上操作
a. 下载并安装ant: 下载地址:apache-ant-1.9.7-bin.tar.gz
b. 配置环境变量,修改 ~/.bashrc
$ vim ~/.bashrc 添加如下环境变量: ANT_HOME=~/shared/tools/apache-ant-1.9.7 export $ANT_HOME PATH=$ANT_HOME/bin:$PATH
c. 切换到 zookeeper-et-develop 目录下,运行ant命令
$ ant
ant 命令会根据目录中build.xml的配置执行依赖项下载,build.xml文件在zookeeper-et-develop目录下。 但是,这时,构建会失败,但并不影响,只要依赖项正常下载就够了。
3)使用VisualStudio编译项目ZooKeeperNet 。这时编译即可成功。
3 编写测试程序
上面编译好了ZooKeeperNet,我们就可以使用了,建议引用项目而不是只引用编译好的dll,这样更便于调试。
建立测试项目,我的习惯是编写一个WinForm程序来做测试。
下面列出关键代码:
1) 建立Watcher类:
class zkWatcher : IWatcher { public delegate void NodeChangeCallBack(string path); public delegate void ConnStateChangeCallBack(KeeperState state); public NodeChangeCallBack cbNodeChange; public ConnStateChangeCallBack cbConnection; /// <summary> /// 自定义构造函数以传递委托 /// </summary> /// <param name="cbConn">连接状态回调</param> /// <param name="cbNode">节点变更回调</param> public zkWatcher(ConnStateChangeCallBack cbConn,NodeChangeCallBack cbNode) { this.cbConnection = cbConn; this.cbNodeChange = cbNode; } public void Process(WatchedEvent e) { if (e.Type == EventType.None) { this.cbConnection(e.State); } else if (e.Type == EventType.NodeDataChanged) { this.cbNodeChange(e.Path); } } }
2 窗体类
public partial class Form1 : Form { private ZooKeeper _zk; private string _defaultServer = "10.130.201.47:2181"; //默认服务地址,实为Ubuntu中运行的zookeeper服务的地址。 public Form1() { InitializeComponent(); toolStripStatusLabel1.Text = "Not Connected"; this.tbServerAddr.Text = _defaultServer; } /// <summary> /// 建立连接 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button1_Click(object sender, EventArgs e) { string zkConn = this.tbServerAddr.Text; try { zkWatcher w = new zkWatcher(ConnectionCallBack, NodeChangeCallBack); this._zk = new ZooKeeper(zkConn, new TimeSpan(0, 0, 0, 50000), w); //次操作为异步的,需要通过Watcher获取连接状态 } catch (Exception ex) { MessageBox.Show("建立连接失败!"); } } private void btnCreate_Click(object sender, EventArgs e) { //未实现 } /// <summary> /// 获取节点,前提为连接已经成功 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnGet_Click(object sender, EventArgs e) { try { StringBuilder sb = new StringBuilder(); string node = tbNode.Text; var stat = _zk.Exists(node, false); //是否存在 var data = _zk.GetData(node, false, stat); //获取数据,如果没有数据,会返回null //输出节点值 sb.AppendLine("Value:"); if (data != null) { string value = System.Text.Encoding.Default.GetString(data); sb.AppendLine(value); } //输出节点的子节点名 var childList = _zk.GetChildren(node, null); //获取子节点 sb.AppendLine("Children:"); foreach (string child in childList) { sb.AppendLine(child); } tbResult.Text = sb.ToString(); //输出节点值及自节点名 } catch (Exception ex) { //当连接状态不为已连接时,会触发异常。 } } private void NodeChangeCallBack(string path) { //未实现 } /// <summary> /// 处理连接状态变更 /// </summary> /// <param name="state"></param> private void ConnectionCallBack(KeeperState state) { this.toolStripStatusLabel1.Text = state.ToString(); } }
上面是节点查询的实现,实际效果如下
以上通过ZooKeeperNet,实现了.Net对ZooKeeper服务的调用。