Repository: zookeeper Updated Branches: refs/heads/website cf70f7b5e -> 86349e3b2
http://git-wip-us.apache.org/repos/asf/zookeeper/blob/86349e3b/_released_docs/r3.4.13/zookeeperStarted.html ---------------------------------------------------------------------- diff --git a/_released_docs/r3.4.13/zookeeperStarted.html b/_released_docs/r3.4.13/zookeeperStarted.html new file mode 100644 index 0000000..361b334 --- /dev/null +++ b/_released_docs/r3.4.13/zookeeperStarted.html @@ -0,0 +1,629 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<html> +<head> +<META http-equiv="Content-Type" content="text/html; charset=UTF-8"> +<meta content="Apache Forrest" name="Generator"> +<meta name="Forrest-version" content="0.9"> +<meta name="Forrest-skin-name" content="pelt"> +<title>ZooKeeper Getting Started Guide</title> +<link type="text/css" href="skin/basic.css" rel="stylesheet"> +<link media="screen" type="text/css" href="skin/screen.css" rel="stylesheet"> +<link media="print" type="text/css" href="skin/print.css" rel="stylesheet"> +<link type="text/css" href="skin/profile.css" rel="stylesheet"> +<script src="skin/getBlank.js" language="javascript" type="text/javascript"></script><script src="skin/getMenu.js" language="javascript" type="text/javascript"></script><script src="skin/fontsize.js" language="javascript" type="text/javascript"></script> +<link rel="shortcut icon" href="images/favicon.ico"> +</head> +<body onload="init()"> +<script type="text/javascript">ndeSetTextSize();</script> +<div id="top"> +<!--+ + |breadtrail + +--> +<div class="breadtrail"> +<a href="http://www.apache.org/">Apache</a> > <a href="http://zookeeper.apache.org/">ZooKeeper</a> > <a href="http://zookeeper.apache.org/">ZooKeeper</a><script src="skin/breadcrumbs.js" language="JavaScript" type="text/javascript"></script> +</div> +<!--+ + |header + +--> +<div class="header"> +<!--+ + |start group logo + +--> +<div class="grouplogo"> +<a href="http://hadoop.apache.org/"><img class="logoImage" alt="Hadoop" src="images/hadoop-logo.jpg" title="Apache Hadoop"></a> +</div> +<!--+ + |end group logo + +--> +<!--+ + |start Project Logo + +--> +<div class="projectlogo"> +<a href="http://zookeeper.apache.org/"><img class="logoImage" alt="ZooKeeper" src="images/zookeeper_small.gif" title="ZooKeeper: distributed coordination"></a> +</div> +<!--+ + |end Project Logo + +--> +<!--+ + |start Search + +--> +<div class="searchbox"> +<form action="http://www.google.com/search" method="get" class="roundtopsmall"> +<input value="zookeeper.apache.org" name="sitesearch" type="hidden"><input onFocus="getBlank (this, 'Search the site with google');" size="25" name="q" id="query" type="text" value="Search the site with google"> + <input name="Search" value="Search" type="submit"> +</form> +</div> +<!--+ + |end search + +--> +<!--+ + |start Tabs + +--> +<ul id="tabs"> +<li> +<a class="unselected" href="http://zookeeper.apache.org/">Project</a> +</li> +<li> +<a class="unselected" href="https://cwiki.apache.org/confluence/display/ZOOKEEPER/">Wiki</a> +</li> +<li class="current"> +<a class="selected" href="index.html">ZooKeeper 3.4 Documentation</a> +</li> +</ul> +<!--+ + |end Tabs + +--> +</div> +</div> +<div id="main"> +<div id="publishedStrip"> +<!--+ + |start Subtabs + +--> +<div id="level2tabs"></div> +<!--+ + |end Endtabs + +--> +<script type="text/javascript"><!-- +document.write("Last Published: " + document.lastModified); +// --></script> +</div> +<!--+ + |breadtrail + +--> +<div class="breadtrail"> + + + </div> +<!--+ + |start Menu, mainarea + +--> +<!--+ + |start Menu + +--> +<div id="menu"> +<div onclick="SwitchMenu('menu_selected_1.1', 'skin/')" id="menu_selected_1.1Title" class="menutitle" style="background-image: url('skin/images/chapter_open.gif');">Overview</div> +<div id="menu_selected_1.1" class="selectedmenuitemgroup" style="display: block;"> +<div class="menuitem"> +<a href="index.html">Welcome</a> +</div> +<div class="menuitem"> +<a href="zookeeperOver.html">Overview</a> +</div> +<div class="menupage"> +<div class="menupagetitle">Getting Started</div> +</div> +<div class="menuitem"> +<a href="releasenotes.html">Release Notes</a> +</div> +</div> +<div onclick="SwitchMenu('menu_1.2', 'skin/')" id="menu_1.2Title" class="menutitle">Developer</div> +<div id="menu_1.2" class="menuitemgroup"> +<div class="menuitem"> +<a href="api/index.html">API Docs</a> +</div> +<div class="menuitem"> +<a href="zookeeperProgrammers.html">Programmer's Guide</a> +</div> +<div class="menuitem"> +<a href="javaExample.html">Java Example</a> +</div> +<div class="menuitem"> +<a href="zookeeperTutorial.html">Barrier and Queue Tutorial</a> +</div> +<div class="menuitem"> +<a href="recipes.html">Recipes</a> +</div> +</div> +<div onclick="SwitchMenu('menu_1.3', 'skin/')" id="menu_1.3Title" class="menutitle">BookKeeper</div> +<div id="menu_1.3" class="menuitemgroup"> +<div class="menuitem"> +<a href="bookkeeperStarted.html">Getting started</a> +</div> +<div class="menuitem"> +<a href="bookkeeperOverview.html">Overview</a> +</div> +<div class="menuitem"> +<a href="bookkeeperConfig.html">Setup guide</a> +</div> +<div class="menuitem"> +<a href="bookkeeperProgrammer.html">Programmer's guide</a> +</div> +</div> +<div onclick="SwitchMenu('menu_1.4', 'skin/')" id="menu_1.4Title" class="menutitle">Admin & Ops</div> +<div id="menu_1.4" class="menuitemgroup"> +<div class="menuitem"> +<a href="zookeeperAdmin.html">Administrator's Guide</a> +</div> +<div class="menuitem"> +<a href="zookeeperQuotas.html">Quota Guide</a> +</div> +<div class="menuitem"> +<a href="zookeeperJMX.html">JMX</a> +</div> +<div class="menuitem"> +<a href="zookeeperObservers.html">Observers Guide</a> +</div> +</div> +<div onclick="SwitchMenu('menu_1.5', 'skin/')" id="menu_1.5Title" class="menutitle">Contributor</div> +<div id="menu_1.5" class="menuitemgroup"> +<div class="menuitem"> +<a href="zookeeperInternals.html">ZooKeeper Internals</a> +</div> +</div> +<div onclick="SwitchMenu('menu_1.6', 'skin/')" id="menu_1.6Title" class="menutitle">Miscellaneous</div> +<div id="menu_1.6" class="menuitemgroup"> +<div class="menuitem"> +<a href="https://cwiki.apache.org/confluence/display/ZOOKEEPER">Wiki</a> +</div> +<div class="menuitem"> +<a href="https://cwiki.apache.org/confluence/display/ZOOKEEPER/FAQ">FAQ</a> +</div> +<div class="menuitem"> +<a href="http://zookeeper.apache.org/mailing_lists.html">Mailing Lists</a> +</div> +</div> +<div id="credit"></div> +<div id="roundbottom"> +<img style="display: none" class="corner" height="15" width="15" alt="" src="skin/images/rc-b-l-15-1body-2menu-3menu.png"></div> +<!--+ + |alternative credits + +--> +<div id="credit2"></div> +</div> +<!--+ + |end Menu + +--> +<!--+ + |start content + +--> +<div id="content"> +<div title="Portable Document Format" class="pdflink"> +<a class="dida" href="zookeeperStarted.pdf"><img alt="PDF -icon" src="skin/images/pdfdoc.gif" class="skin"><br> + PDF</a> +</div> +<h1>ZooKeeper Getting Started Guide</h1> +<div id="front-matter"> +<div id="minitoc-area"> +<ul class="minitoc"> +<li> +<a href="#ch_GettingStarted">Getting Started: Coordinating Distributed Applications with + ZooKeeper</a> +<ul class="minitoc"> +<li> +<a href="#sc_Prerequisites">Pre-requisites</a> +</li> +<li> +<a href="#sc_Download">Download</a> +</li> +<li> +<a href="#sc_InstallingSingleMode">Standalone Operation</a> +</li> +<li> +<a href="#sc_FileManagement">Managing ZooKeeper Storage</a> +</li> +<li> +<a href="#sc_ConnectingToZooKeeper">Connecting to ZooKeeper</a> +</li> +<li> +<a href="#sc_ProgrammingToZooKeeper">Programming to ZooKeeper</a> +</li> +<li> +<a href="#sc_RunningReplicatedZooKeeper">Running Replicated ZooKeeper</a> +</li> +<li> +<a href="#Other+Optimizations">Other Optimizations</a> +</li> +</ul> +</li> +</ul> +</div> +</div> + + + + + +<a name="ch_GettingStarted"></a> +<h2 class="h3">Getting Started: Coordinating Distributed Applications with + ZooKeeper</h2> +<div class="section"> +<p>This document contains information to get you started quickly with + ZooKeeper. It is aimed primarily at developers hoping to try it out, and + contains simple installation instructions for a single ZooKeeper server, a + few commands to verify that it is running, and a simple programming + example. Finally, as a convenience, there are a few sections regarding + more complicated installations, for example running replicated + deployments, and optimizing the transaction log. However for the complete + instructions for commercial deployments, please refer to the <a href="zookeeperAdmin.html">ZooKeeper + Administrator's Guide</a>.</p> +<a name="sc_Prerequisites"></a> +<h3 class="h4">Pre-requisites</h3> +<p>See <a href="zookeeperAdmin.html#sc_systemReq"> + System Requirements</a> in the Admin guide.</p> +<a name="sc_Download"></a> +<h3 class="h4">Download</h3> +<p>To get a ZooKeeper distribution, download a recent + <a href="http://zookeeper.apache.org/releases.html"> + stable</a> release from one of the Apache Download + Mirrors.</p> +<a name="sc_InstallingSingleMode"></a> +<h3 class="h4">Standalone Operation</h3> +<p>Setting up a ZooKeeper server in standalone mode is + straightforward. The server is contained in a single JAR file, + so installation consists of creating a configuration.</p> +<p>Once you've downloaded a stable ZooKeeper release unpack + it and cd to the root</p> +<p>To start ZooKeeper you need a configuration file. Here is a sample, + create it in <strong>conf/zoo.cfg</strong>:</p> +<pre class="code"> +tickTime=2000 +dataDir=/var/lib/zookeeper +clientPort=2181 +</pre> +<p>This file can be called anything, but for the sake of this + discussion call + it <strong>conf/zoo.cfg</strong>. Change the + value of <strong>dataDir</strong> to specify an + existing (empty to start with) directory. Here are the meanings + for each of the fields:</p> +<dl> + +<dt> +<term> +<strong>tickTime</strong> +</term> +</dt> +<dd> +<p>the basic time unit in milliseconds used by ZooKeeper. It is + used to do heartbeats and the minimum session timeout will be + twice the tickTime.</p> +</dd> + +</dl> +<dl> + +<dt> +<term> +<strong>dataDir</strong> +</term> +</dt> +<dd> +<p>the location to store the in-memory database snapshots and, + unless specified otherwise, the transaction log of updates to the + database.</p> +</dd> + + +<dt> +<term> +<strong>clientPort</strong> +</term> +</dt> +<dd> +<p>the port to listen for client connections</p> +</dd> + +</dl> +<p>Now that you created the configuration file, you can start + ZooKeeper:</p> +<pre class="code">bin/zkServer.sh start</pre> +<p>ZooKeeper logs messages using log4j -- more detail + available in the + <a href="zookeeperProgrammers.html#Logging">Logging</a> + section of the Programmer's Guide. You will see log messages + coming to the console (default) and/or a log file depending on + the log4j configuration.</p> +<p>The steps outlined here run ZooKeeper in standalone mode. There is + no replication, so if ZooKeeper process fails, the service will go down. + This is fine for most development situations, but to run ZooKeeper in + replicated mode, please see <a href="#sc_RunningReplicatedZooKeeper">Running Replicated + ZooKeeper</a>.</p> +<a name="sc_FileManagement"></a> +<h3 class="h4">Managing ZooKeeper Storage</h3> +<p>For long running production systems ZooKeeper storage must + be managed externally (dataDir and logs). See the section on + <a href="zookeeperAdmin.html#sc_maintenance">maintenance</a> for + more details.</p> +<a name="sc_ConnectingToZooKeeper"></a> +<h3 class="h4">Connecting to ZooKeeper</h3> +<pre class="code">$ bin/zkCli.sh -server 127.0.0.1:2181</pre> +<p>This lets you perform simple, file-like operations.</p> +<p>Once you have connected, you should see something like: + </p> +<pre class="code"> + +Connecting to localhost:2181 +log4j:WARN No appenders could be found for logger (org.apache.zookeeper.ZooKeeper). +log4j:WARN Please initialize the log4j system properly. +Welcome to ZooKeeper! +JLine support is enabled +[zkshell: 0] + </pre> +<p> + From the shell, type <span class="codefrag command">help</span> to get a listing of commands that can be executed from the client, as in: + </p> +<pre class="code"> + +[zkshell: 0] help +ZooKeeper host:port cmd args + get path [watch] + ls path [watch] + set path data [version] + delquota [-n|-b] path + quit + printwatches on|off + createpath data acl + stat path [watch] + listquota path + history + setAcl path acl + getAcl path + sync path + redo cmdno + addauth scheme auth + delete path [version] + setquota -n|-b val path + + </pre> +<p>From here, you can try a few simple commands to get a feel for this simple command line interface. First, start by issuing the list command, as + in <span class="codefrag command">ls</span>, yielding: + </p> +<pre class="code"> + +[zkshell: 8] ls / +[zookeeper] + </pre> +<p>Next, create a new znode by running <span class="codefrag command">create /zk_test my_data</span>. This creates a new znode and associates the string "my_data" with the node. + You should see:</p> +<pre class="code"> + +[zkshell: 9] create /zk_test my_data +Created /zk_test + </pre> +<p> Issue another <span class="codefrag command">ls /</span> command to see what the directory looks like: + </p> +<pre class="code"> + +[zkshell: 11] ls / +[zookeeper, zk_test] + + </pre> +<p> + Notice that the zk_test directory has now been created. + </p> +<p>Next, verify that the data was associated with the znode by running the <span class="codefrag command">get</span> command, as in: + </p> +<pre class="code"> + +[zkshell: 12] get /zk_test +my_data +cZxid = 5 +ctime = Fri Jun 05 13:57:06 PDT 2009 +mZxid = 5 +mtime = Fri Jun 05 13:57:06 PDT 2009 +pZxid = 5 +cversion = 0 +dataVersion = 0 +aclVersion = 0 +ephemeralOwner = 0 +dataLength = 7 +numChildren = 0 + </pre> +<p>We can change the data associated with zk_test by issuing the <span class="codefrag command">set</span> command, as in: + </p> +<pre class="code"> + +[zkshell: 14] set /zk_test junk +cZxid = 5 +ctime = Fri Jun 05 13:57:06 PDT 2009 +mZxid = 6 +mtime = Fri Jun 05 14:01:52 PDT 2009 +pZxid = 5 +cversion = 0 +dataVersion = 1 +aclVersion = 0 +ephemeralOwner = 0 +dataLength = 4 +numChildren = 0 +[zkshell: 15] get /zk_test +junk +cZxid = 5 +ctime = Fri Jun 05 13:57:06 PDT 2009 +mZxid = 6 +mtime = Fri Jun 05 14:01:52 PDT 2009 +pZxid = 5 +cversion = 0 +dataVersion = 1 +aclVersion = 0 +ephemeralOwner = 0 +dataLength = 4 +numChildren = 0 + </pre> +<p> + (Notice we did a <span class="codefrag command">get</span> after setting the data and it did, indeed, change.</p> +<p>Finally, let's <span class="codefrag command">delete</span> the node by issuing: + </p> +<pre class="code"> + +[zkshell: 16] delete /zk_test +[zkshell: 17] ls / +[zookeeper] +[zkshell: 18] +</pre> +<p>That's it for now. To explore more, continue with the rest of this document and see the <a href="zookeeperProgrammers.html">Programmer's Guide</a>. </p> +<a name="sc_ProgrammingToZooKeeper"></a> +<h3 class="h4">Programming to ZooKeeper</h3> +<p>ZooKeeper has a Java bindings and C bindings. They are + functionally equivalent. The C bindings exist in two variants: single + threaded and multi-threaded. These differ only in how the messaging loop + is done. For more information, see the <a href="zookeeperProgrammers.html#ch_programStructureWithExample">Programming + Examples in the ZooKeeper Programmer's Guide</a> for + sample code using of the different APIs.</p> +<a name="sc_RunningReplicatedZooKeeper"></a> +<h3 class="h4">Running Replicated ZooKeeper</h3> +<p>Running ZooKeeper in standalone mode is convenient for evaluation, + some development, and testing. But in production, you should run + ZooKeeper in replicated mode. A replicated group of servers in the same + application is called a <em>quorum</em>, and in replicated + mode, all servers in the quorum have copies of the same configuration + file.</p> +<div class="note"> +<div class="label">Note</div> +<div class="content"> + +<p> + For replicated mode, a minimum of three servers are required, + and it is strongly recommended that you have an odd number of + servers. If you only have two servers, then you are in a + situation where if one of them fails, there are not enough + machines to form a majority quorum. Two servers is inherently + <strong>less</strong> + stable than a single server, because there are two single + points of failure. + </p> + +</div> +</div> +<p> + The required + <strong>conf/zoo.cfg</strong> + file for replicated mode is similar to the one used in standalone + mode, but with a few differences. Here is an example: + </p> +<pre class="code"> +tickTime=2000 +dataDir=/var/lib/zookeeper +clientPort=2181 +initLimit=5 +syncLimit=2 +server.1=zoo1:2888:3888 +server.2=zoo2:2888:3888 +server.3=zoo3:2888:3888 +</pre> +<p>The new entry, <strong>initLimit</strong> is + timeouts ZooKeeper uses to limit the length of time the ZooKeeper + servers in quorum have to connect to a leader. The entry <strong>syncLimit</strong> limits how far out of date a server can + be from a leader.</p> +<p>With both of these timeouts, you specify the unit of time using + <strong>tickTime</strong>. In this example, the timeout + for initLimit is 5 ticks at 2000 milleseconds a tick, or 10 + seconds.</p> +<p>The entries of the form <em>server.X</em> list the + servers that make up the ZooKeeper service. When the server starts up, + it knows which server it is by looking for the file + <em>myid</em> in the data directory. That file has the + contains the server number, in ASCII.</p> +<p>Finally, note the two port numbers after each server + name: " 2888" and "3888". Peers use the former port to connect + to other peers. Such a connection is necessary so that peers + can communicate, for example, to agree upon the order of + updates. More specifically, a ZooKeeper server uses this port + to connect followers to the leader. When a new leader arises, a + follower opens a TCP connection to the leader using this + port. Because the default leader election also uses TCP, we + currently require another port for leader election. This is the + second port in the server entry. + </p> +<div class="note"> +<div class="label">Note</div> +<div class="content"> + +<p>If you want to test multiple servers on a single + machine, specify the servername + as <em>localhost</em> with unique quorum & + leader election ports (i.e. 2888:3888, 2889:3889, 2890:3890 in + the example above) for each server.X in that server's config + file. Of course separate <em>dataDir</em>s and + distinct <em>clientPort</em>s are also necessary + (in the above replicated example, running on a + single <em>localhost</em>, you would still have + three config files).</p> + +<p>Please be aware that setting up multiple servers on a single + machine will not create any redundancy. If something were to + happen which caused the machine to die, all of the zookeeper + servers would be offline. Full redundancy requires that each + server have its own machine. It must be a completely separate + physical server. Multiple virtual machines on the same physical + host are still vulnerable to the complete failure of that host.</p> + +</div> +</div> +<a name="Other+Optimizations"></a> +<h3 class="h4">Other Optimizations</h3> +<p>There are a couple of other configuration parameters that can + greatly increase performance:</p> +<ul> + +<li> + +<p>To get low latencies on updates it is important to + have a dedicated transaction log directory. By default + transaction logs are put in the same directory as the data + snapshots and <em>myid</em> file. The dataLogDir + parameters indicates a different directory to use for the + transaction logs.</p> + +</li> + + +<li> + +<p> +<em>[tbd: what is the other config param?]</em> +</p> + +</li> + +</ul> +</div> + +<p align="right"> +<font size="-2"></font> +</p> +</div> +<!--+ + |end content + +--> +<div class="clearboth"> </div> +</div> +<div id="footer"> +<!--+ + |start bottomstrip + +--> +<div class="lastmodified"> +<script type="text/javascript"><!-- +document.write("Last Published: " + document.lastModified); +// --></script> +</div> +<div class="copyright"> + Copyright © + <a href="http://www.apache.org/licenses/">The Apache Software Foundation.</a> +</div> +<!--+ + |end bottomstrip + +--> +</div> +</body> +</html> http://git-wip-us.apache.org/repos/asf/zookeeper/blob/86349e3b/_released_docs/r3.4.13/zookeeperStarted.pdf ---------------------------------------------------------------------- diff --git a/_released_docs/r3.4.13/zookeeperStarted.pdf b/_released_docs/r3.4.13/zookeeperStarted.pdf new file mode 100644 index 0000000..95d7fcf Binary files /dev/null and b/_released_docs/r3.4.13/zookeeperStarted.pdf differ http://git-wip-us.apache.org/repos/asf/zookeeper/blob/86349e3b/_released_docs/r3.4.13/zookeeperTutorial.html ---------------------------------------------------------------------- diff --git a/_released_docs/r3.4.13/zookeeperTutorial.html b/_released_docs/r3.4.13/zookeeperTutorial.html new file mode 100644 index 0000000..e9c0646 --- /dev/null +++ b/_released_docs/r3.4.13/zookeeperTutorial.html @@ -0,0 +1,937 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<html> +<head> +<META http-equiv="Content-Type" content="text/html; charset=UTF-8"> +<meta content="Apache Forrest" name="Generator"> +<meta name="Forrest-version" content="0.9"> +<meta name="Forrest-skin-name" content="pelt"> +<title>Programming with ZooKeeper - A basic tutorial</title> +<link type="text/css" href="skin/basic.css" rel="stylesheet"> +<link media="screen" type="text/css" href="skin/screen.css" rel="stylesheet"> +<link media="print" type="text/css" href="skin/print.css" rel="stylesheet"> +<link type="text/css" href="skin/profile.css" rel="stylesheet"> +<script src="skin/getBlank.js" language="javascript" type="text/javascript"></script><script src="skin/getMenu.js" language="javascript" type="text/javascript"></script><script src="skin/fontsize.js" language="javascript" type="text/javascript"></script> +<link rel="shortcut icon" href="images/favicon.ico"> +</head> +<body onload="init()"> +<script type="text/javascript">ndeSetTextSize();</script> +<div id="top"> +<!--+ + |breadtrail + +--> +<div class="breadtrail"> +<a href="http://www.apache.org/">Apache</a> > <a href="http://zookeeper.apache.org/">ZooKeeper</a> > <a href="http://zookeeper.apache.org/">ZooKeeper</a><script src="skin/breadcrumbs.js" language="JavaScript" type="text/javascript"></script> +</div> +<!--+ + |header + +--> +<div class="header"> +<!--+ + |start group logo + +--> +<div class="grouplogo"> +<a href="http://hadoop.apache.org/"><img class="logoImage" alt="Hadoop" src="images/hadoop-logo.jpg" title="Apache Hadoop"></a> +</div> +<!--+ + |end group logo + +--> +<!--+ + |start Project Logo + +--> +<div class="projectlogo"> +<a href="http://zookeeper.apache.org/"><img class="logoImage" alt="ZooKeeper" src="images/zookeeper_small.gif" title="ZooKeeper: distributed coordination"></a> +</div> +<!--+ + |end Project Logo + +--> +<!--+ + |start Search + +--> +<div class="searchbox"> +<form action="http://www.google.com/search" method="get" class="roundtopsmall"> +<input value="zookeeper.apache.org" name="sitesearch" type="hidden"><input onFocus="getBlank (this, 'Search the site with google');" size="25" name="q" id="query" type="text" value="Search the site with google"> + <input name="Search" value="Search" type="submit"> +</form> +</div> +<!--+ + |end search + +--> +<!--+ + |start Tabs + +--> +<ul id="tabs"> +<li> +<a class="unselected" href="http://zookeeper.apache.org/">Project</a> +</li> +<li> +<a class="unselected" href="https://cwiki.apache.org/confluence/display/ZOOKEEPER/">Wiki</a> +</li> +<li class="current"> +<a class="selected" href="index.html">ZooKeeper 3.4 Documentation</a> +</li> +</ul> +<!--+ + |end Tabs + +--> +</div> +</div> +<div id="main"> +<div id="publishedStrip"> +<!--+ + |start Subtabs + +--> +<div id="level2tabs"></div> +<!--+ + |end Endtabs + +--> +<script type="text/javascript"><!-- +document.write("Last Published: " + document.lastModified); +// --></script> +</div> +<!--+ + |breadtrail + +--> +<div class="breadtrail"> + + + </div> +<!--+ + |start Menu, mainarea + +--> +<!--+ + |start Menu + +--> +<div id="menu"> +<div onclick="SwitchMenu('menu_1.1', 'skin/')" id="menu_1.1Title" class="menutitle">Overview</div> +<div id="menu_1.1" class="menuitemgroup"> +<div class="menuitem"> +<a href="index.html">Welcome</a> +</div> +<div class="menuitem"> +<a href="zookeeperOver.html">Overview</a> +</div> +<div class="menuitem"> +<a href="zookeeperStarted.html">Getting Started</a> +</div> +<div class="menuitem"> +<a href="releasenotes.html">Release Notes</a> +</div> +</div> +<div onclick="SwitchMenu('menu_selected_1.2', 'skin/')" id="menu_selected_1.2Title" class="menutitle" style="background-image: url('skin/images/chapter_open.gif');">Developer</div> +<div id="menu_selected_1.2" class="selectedmenuitemgroup" style="display: block;"> +<div class="menuitem"> +<a href="api/index.html">API Docs</a> +</div> +<div class="menuitem"> +<a href="zookeeperProgrammers.html">Programmer's Guide</a> +</div> +<div class="menuitem"> +<a href="javaExample.html">Java Example</a> +</div> +<div class="menupage"> +<div class="menupagetitle">Barrier and Queue Tutorial</div> +</div> +<div class="menuitem"> +<a href="recipes.html">Recipes</a> +</div> +</div> +<div onclick="SwitchMenu('menu_1.3', 'skin/')" id="menu_1.3Title" class="menutitle">BookKeeper</div> +<div id="menu_1.3" class="menuitemgroup"> +<div class="menuitem"> +<a href="bookkeeperStarted.html">Getting started</a> +</div> +<div class="menuitem"> +<a href="bookkeeperOverview.html">Overview</a> +</div> +<div class="menuitem"> +<a href="bookkeeperConfig.html">Setup guide</a> +</div> +<div class="menuitem"> +<a href="bookkeeperProgrammer.html">Programmer's guide</a> +</div> +</div> +<div onclick="SwitchMenu('menu_1.4', 'skin/')" id="menu_1.4Title" class="menutitle">Admin & Ops</div> +<div id="menu_1.4" class="menuitemgroup"> +<div class="menuitem"> +<a href="zookeeperAdmin.html">Administrator's Guide</a> +</div> +<div class="menuitem"> +<a href="zookeeperQuotas.html">Quota Guide</a> +</div> +<div class="menuitem"> +<a href="zookeeperJMX.html">JMX</a> +</div> +<div class="menuitem"> +<a href="zookeeperObservers.html">Observers Guide</a> +</div> +</div> +<div onclick="SwitchMenu('menu_1.5', 'skin/')" id="menu_1.5Title" class="menutitle">Contributor</div> +<div id="menu_1.5" class="menuitemgroup"> +<div class="menuitem"> +<a href="zookeeperInternals.html">ZooKeeper Internals</a> +</div> +</div> +<div onclick="SwitchMenu('menu_1.6', 'skin/')" id="menu_1.6Title" class="menutitle">Miscellaneous</div> +<div id="menu_1.6" class="menuitemgroup"> +<div class="menuitem"> +<a href="https://cwiki.apache.org/confluence/display/ZOOKEEPER">Wiki</a> +</div> +<div class="menuitem"> +<a href="https://cwiki.apache.org/confluence/display/ZOOKEEPER/FAQ">FAQ</a> +</div> +<div class="menuitem"> +<a href="http://zookeeper.apache.org/mailing_lists.html">Mailing Lists</a> +</div> +</div> +<div id="credit"></div> +<div id="roundbottom"> +<img style="display: none" class="corner" height="15" width="15" alt="" src="skin/images/rc-b-l-15-1body-2menu-3menu.png"></div> +<!--+ + |alternative credits + +--> +<div id="credit2"></div> +</div> +<!--+ + |end Menu + +--> +<!--+ + |start content + +--> +<div id="content"> +<div title="Portable Document Format" class="pdflink"> +<a class="dida" href="zookeeperTutorial.pdf"><img alt="PDF -icon" src="skin/images/pdfdoc.gif" class="skin"><br> + PDF</a> +</div> +<h1>Programming with ZooKeeper - A basic tutorial</h1> +<div id="front-matter"> +<div id="minitoc-area"> +<ul class="minitoc"> +<li> +<a href="#ch_Introduction">Introduction</a> +</li> +<li> +<a href="#sc_barriers">Barriers</a> +</li> +<li> +<a href="#sc_producerConsumerQueues">Producer-Consumer Queues</a> +</li> +<li> +<a href="#Complete+example">Complete example</a> +<ul class="minitoc"> +<li> +<a href="#Queue+test">Queue test</a> +</li> +<li> +<a href="#Barrier+test">Barrier test</a> +</li> +<li> +<a href="#sc_sourceListing">Source Listing</a> +</li> +</ul> +</li> +</ul> +</div> +</div> + + + + + +<a name="ch_Introduction"></a> +<h2 class="h3">Introduction</h2> +<div class="section"> +<p>In this tutorial, we show simple implementations of barriers and + producer-consumer queues using ZooKeeper. We call the respective classes Barrier and Queue. + These examples assume that you have at least one ZooKeeper server running.</p> +<p>Both primitives use the following common excerpt of code:</p> +<pre class="code"> + static ZooKeeper zk = null; + static Integer mutex; + + String root; + + SyncPrimitive(String address) { + if(zk == null){ + try { + System.out.println("Starting ZK:"); + zk = new ZooKeeper(address, 3000, this); + mutex = new Integer(-1); + System.out.println("Finished starting ZK: " + zk); + } catch (IOException e) { + System.out.println(e.toString()); + zk = null; + } + } + } + + synchronized public void process(WatchedEvent event) { + synchronized (mutex) { + mutex.notify(); + } + } +</pre> +<p>Both classes extend SyncPrimitive. In this way, we execute steps that are +common to all primitives in the constructor of SyncPrimitive. To keep the examples +simple, we create a ZooKeeper object the first time we instantiate either a barrier +object or a queue object, and we declare a static variable that is a reference +to this object. The subsequent instances of Barrier and Queue check whether a +ZooKeeper object exists. Alternatively, we could have the application creating a +ZooKeeper object and passing it to the constructor of Barrier and Queue.</p> +<p> +We use the process() method to process notifications triggered due to watches. +In the following discussion, we present code that sets watches. A watch is internal +structure that enables ZooKeeper to notify a client of a change to a node. For example, +if a client is waiting for other clients to leave a barrier, then it can set a watch and +wait for modifications to a particular node, which can indicate that it is the end of the wait. +This point becomes clear once we go over the examples. +</p> +</div> + + +<a name="sc_barriers"></a> +<h2 class="h3">Barriers</h2> +<div class="section"> +<p> + A barrier is a primitive that enables a group of processes to synchronize the + beginning and the end of a computation. The general idea of this implementation + is to have a barrier node that serves the purpose of being a parent for individual + process nodes. Suppose that we call the barrier node "/b1". Each process "p" then + creates a node "/b1/p". Once enough processes have created their corresponding + nodes, joined processes can start the computation. + </p> +<p>In this example, each process instantiates a Barrier object, and its constructor takes as parameters:</p> +<ul> +<li> +<p>the address of a ZooKeeper server (e.g., "zoo1.foo.com:2181")</p> +</li> + +<li> +<p>the path of the barrier node on ZooKeeper (e.g., "/b1")</p> +</li> + +<li> +<p>the size of the group of processes</p> +</li> + +</ul> +<p>The constructor of Barrier passes the address of the Zookeeper server to the +constructor of the parent class. The parent class creates a ZooKeeper instance if +one does not exist. The constructor of Barrier then creates a +barrier node on ZooKeeper, which is the parent node of all process nodes, and +we call root (<strong>Note:</strong> This is not the ZooKeeper root "/").</p> +<pre class="code"> + /** + * Barrier constructor + * + * @param address + * @param root + * @param size + */ + Barrier(String address, String root, int size) { + super(address); + this.root = root; + this.size = size; + + // Create barrier node + if (zk != null) { + try { + Stat s = zk.exists(root, false); + if (s == null) { + zk.create(root, new byte[0], Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + } + } catch (KeeperException e) { + System.out + .println("Keeper exception when instantiating queue: " + + e.toString()); + } catch (InterruptedException e) { + System.out.println("Interrupted exception"); + } + } + + // My node name + try { + name = new String(InetAddress.getLocalHost().getCanonicalHostName().toString()); + } catch (UnknownHostException e) { + System.out.println(e.toString()); + } + + } +</pre> +<p> +To enter the barrier, a process calls enter(). The process creates a node under +the root to represent it, using its host name to form the node name. It then wait +until enough processes have entered the barrier. A process does it by checking +the number of children the root node has with "getChildren()", and waiting for +notifications in the case it does not have enough. To receive a notification when +there is a change to the root node, a process has to set a watch, and does it +through the call to "getChildren()". In the code, we have that "getChildren()" +has two parameters. The first one states the node to read from, and the second is +a boolean flag that enables the process to set a watch. In the code the flag is true. +</p> +<pre class="code"> + /** + * Join barrier + * + * @return + * @throws KeeperException + * @throws InterruptedException + */ + + boolean enter() throws KeeperException, InterruptedException{ + zk.create(root + "/" + name, new byte[0], Ids.OPEN_ACL_UNSAFE, + CreateMode.EPHEMERAL_SEQUENTIAL); + while (true) { + synchronized (mutex) { + List<String> list = zk.getChildren(root, true); + + if (list.size() < size) { + mutex.wait(); + } else { + return true; + } + } + } + } +</pre> +<p> +Note that enter() throws both KeeperException and InterruptedException, so it is +the reponsability of the application to catch and handle such exceptions.</p> +<p> +Once the computation is finished, a process calls leave() to leave the barrier. +First it deletes its corresponding node, and then it gets the children of the root +node. If there is at least one child, then it waits for a notification (obs: note +that the second parameter of the call to getChildren() is true, meaning that +ZooKeeper has to set a watch on the the root node). Upon reception of a notification, +it checks once more whether the root node has any child.</p> +<pre class="code"> + /** + * Wait until all reach barrier + * + * @return + * @throws KeeperException + * @throws InterruptedException + */ + + boolean leave() throws KeeperException, InterruptedException{ + zk.delete(root + "/" + name, 0); + while (true) { + synchronized (mutex) { + List<String> list = zk.getChildren(root, true); + if (list.size() > 0) { + mutex.wait(); + } else { + return true; + } + } + } + } + } +</pre> +</div> + +<a name="sc_producerConsumerQueues"></a> +<h2 class="h3">Producer-Consumer Queues</h2> +<div class="section"> +<p> +A producer-consumer queue is a distributed data estructure thata group of processes +use to generate and consume items. Producer processes create new elements and add +them to the queue. Consumer processes remove elements from the list, and process them. +In this implementation, the elements are simple integers. The queue is represented +by a root node, and to add an element to the queue, a producer process creates a new node, +a child of the root node. +</p> +<p> +The following excerpt of code corresponds to the constructor of the object. As +with Barrier objects, it first calls the constructor of the parent class, SyncPrimitive, +that creates a ZooKeeper object if one doesn't exist. It then verifies if the root +node of the queue exists, and creates if it doesn't. +</p> +<pre class="code"> + /** + * Constructor of producer-consumer queue + * + * @param address + * @param name + */ + Queue(String address, String name) { + super(address); + this.root = name; + // Create ZK node name + if (zk != null) { + try { + Stat s = zk.exists(root, false); + if (s == null) { + zk.create(root, new byte[0], Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + } + } catch (KeeperException e) { + System.out + .println("Keeper exception when instantiating queue: " + + e.toString()); + } catch (InterruptedException e) { + System.out.println("Interrupted exception"); + } + } + } +</pre> +<p> +A producer process calls "produce()" to add an element to the queue, and passes +an integer as an argument. To add an element to the queue, the method creates a +new node using "create()", and uses the SEQUENCE flag to instruct ZooKeeper to +append the value of the sequencer counter associated to the root node. In this way, +we impose a total order on the elements of the queue, thus guaranteeing that the +oldest element of the queue is the next one consumed. +</p> +<pre class="code"> + /** + * Add element to the queue. + * + * @param i + * @return + */ + + boolean produce(int i) throws KeeperException, InterruptedException{ + ByteBuffer b = ByteBuffer.allocate(4); + byte[] value; + + // Add child with value i + b.putInt(i); + value = b.array(); + zk.create(root + "/element", value, Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT_SEQUENTIAL); + + return true; + } +</pre> +<p> +To consume an element, a consumer process obtains the children of the root node, +reads the node with smallest counter value, and returns the element. Note that +if there is a conflict, then one of the two contending processes won't be able to +delete the node and the delete operation will throw an exception.</p> +<p> +A call to getChildren() returns the list of children in lexicographic order. +As lexicographic order does not necessary follow the numerical order of the counter +values, we need to decide which element is the smallest. To decide which one has +the smallest counter value, we traverse the list, and remove the prefix "element" +from each one.</p> +<pre class="code"> + /** + * Remove first element from the queue. + * + * @return + * @throws KeeperException + * @throws InterruptedException + */ + int consume() throws KeeperException, InterruptedException{ + int retvalue = -1; + Stat stat = null; + + // Get the first element available + while (true) { + synchronized (mutex) { + List<String> list = zk.getChildren(root, true); + if (list.size() == 0) { + System.out.println("Going to wait"); + mutex.wait(); + } else { + Integer min = new Integer(list.get(0).substring(7)); + for(String s : list){ + Integer tempValue = new Integer(s.substring(7)); + //System.out.println("Temporary value: " + tempValue); + if(tempValue < min) min = tempValue; + } + System.out.println("Temporary value: " + root + "/element" + min); + byte[] b = zk.getData(root + "/element" + min, + false, stat); + zk.delete(root + "/element" + min, 0); + ByteBuffer buffer = ByteBuffer.wrap(b); + retvalue = buffer.getInt(); + + return retvalue; + } + } + } + } + } +</pre> +</div> + + +<a name="Complete+example"></a> +<h2 class="h3">Complete example</h2> +<div class="section"> +<p> +In the following section you can find a complete command line application to demonstrate the above mentioned +recipes. Use the following command to run it. +</p> +<pre class="code"> +ZOOBINDIR="[path_to_distro]/bin" +. "$ZOOBINDIR"/zkEnv.sh +java SyncPrimitive [Test Type] [ZK server] [No of elements] [Client type] +</pre> +<a name="Queue+test"></a> +<h3 class="h4">Queue test</h3> +<p>Start a producer to create 100 elements</p> +<pre class="code"> +java SyncPrimitive qTest localhost 100 p +</pre> +<p>Start a consumer to consume 100 elements</p> +<pre class="code"> +java SyncPrimitive qTest localhost 100 c +</pre> +<a name="Barrier+test"></a> +<h3 class="h4">Barrier test</h3> +<p>Start a barrier with 2 participants (start as many times as many participants you'd like to enter)</p> +<pre class="code"> +java SyncPrimitive bTest localhost 2 +</pre> +<a name="sc_sourceListing"></a> +<h3 class="h4">Source Listing</h3> +<div class="note example"> +<div class="label">SyncPrimitive.Java</div> +<div class="content"> + +<title>SyncPrimitive.Java</title> + +<pre class="code"> +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.util.List; +import java.util.Random; + +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.WatchedEvent; +import org.apache.zookeeper.Watcher; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.ZooDefs.Ids; +import org.apache.zookeeper.data.Stat; + +public class SyncPrimitive implements Watcher { + + static ZooKeeper zk = null; + static Integer mutex; + + String root; + + SyncPrimitive(String address) { + if(zk == null){ + try { + System.out.println("Starting ZK:"); + zk = new ZooKeeper(address, 3000, this); + mutex = new Integer(-1); + System.out.println("Finished starting ZK: " + zk); + } catch (IOException e) { + System.out.println(e.toString()); + zk = null; + } + } + //else mutex = new Integer(-1); + } + + synchronized public void process(WatchedEvent event) { + synchronized (mutex) { + //System.out.println("Process: " + event.getType()); + mutex.notify(); + } + } + + /** + * Barrier + */ + static public class Barrier extends SyncPrimitive { + int size; + String name; + + /** + * Barrier constructor + * + * @param address + * @param root + * @param size + */ + Barrier(String address, String root, int size) { + super(address); + this.root = root; + this.size = size; + + // Create barrier node + if (zk != null) { + try { + Stat s = zk.exists(root, false); + if (s == null) { + zk.create(root, new byte[0], Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + } + } catch (KeeperException e) { + System.out + .println("Keeper exception when instantiating queue: " + + e.toString()); + } catch (InterruptedException e) { + System.out.println("Interrupted exception"); + } + } + + // My node name + try { + name = new String(InetAddress.getLocalHost().getCanonicalHostName().toString()); + } catch (UnknownHostException e) { + System.out.println(e.toString()); + } + + } + + /** + * Join barrier + * + * @return + * @throws KeeperException + * @throws InterruptedException + */ + + boolean enter() throws KeeperException, InterruptedException{ + zk.create(root + "/" + name, new byte[0], Ids.OPEN_ACL_UNSAFE, + CreateMode.EPHEMERAL_SEQUENTIAL); + while (true) { + synchronized (mutex) { + List<String> list = zk.getChildren(root, true); + + if (list.size() < size) { + mutex.wait(); + } else { + return true; + } + } + } + } + + /** + * Wait until all reach barrier + * + * @return + * @throws KeeperException + * @throws InterruptedException + */ + + boolean leave() throws KeeperException, InterruptedException{ + zk.delete(root + "/" + name, 0); + while (true) { + synchronized (mutex) { + List<String> list = zk.getChildren(root, true); + if (list.size() > 0) { + mutex.wait(); + } else { + return true; + } + } + } + } + } + + /** + * Producer-Consumer queue + */ + static public class Queue extends SyncPrimitive { + + /** + * Constructor of producer-consumer queue + * + * @param address + * @param name + */ + Queue(String address, String name) { + super(address); + this.root = name; + // Create ZK node name + if (zk != null) { + try { + Stat s = zk.exists(root, false); + if (s == null) { + zk.create(root, new byte[0], Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + } + } catch (KeeperException e) { + System.out + .println("Keeper exception when instantiating queue: " + + e.toString()); + } catch (InterruptedException e) { + System.out.println("Interrupted exception"); + } + } + } + + /** + * Add element to the queue. + * + * @param i + * @return + */ + + boolean produce(int i) throws KeeperException, InterruptedException{ + ByteBuffer b = ByteBuffer.allocate(4); + byte[] value; + + // Add child with value i + b.putInt(i); + value = b.array(); + zk.create(root + "/element", value, Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT_SEQUENTIAL); + + return true; + } + + + /** + * Remove first element from the queue. + * + * @return + * @throws KeeperException + * @throws InterruptedException + */ + int consume() throws KeeperException, InterruptedException{ + int retvalue = -1; + Stat stat = null; + + // Get the first element available + while (true) { + synchronized (mutex) { + List<String> list = zk.getChildren(root, true); + if (list.size() == 0) { + System.out.println("Going to wait"); + mutex.wait(); + } else { + Integer min = new Integer(list.get(0).substring(7)); + String minNode = list.get(0); + for(String s : list){ + Integer tempValue = new Integer(s.substring(7)); + //System.out.println("Temporary value: " + tempValue); + if(tempValue < min) { + min = tempValue; + minNode = s; + } + } + System.out.println("Temporary value: " + root + "/" + minNode); + byte[] b = zk.getData(root + "/" + minNode, + false, stat); + zk.delete(root + "/" + minNode, 0); + ByteBuffer buffer = ByteBuffer.wrap(b); + retvalue = buffer.getInt(); + + return retvalue; + } + } + } + } + } + + public static void main(String args[]) { + if (args[0].equals("qTest")) + queueTest(args); + else + barrierTest(args); + + } + + public static void queueTest(String args[]) { + Queue q = new Queue(args[1], "/app1"); + + System.out.println("Input: " + args[1]); + int i; + Integer max = new Integer(args[2]); + + if (args[3].equals("p")) { + System.out.println("Producer"); + for (i = 0; i < max; i++) + try{ + q.produce(10 + i); + } catch (KeeperException e){ + + } catch (InterruptedException e){ + + } + } else { + System.out.println("Consumer"); + + for (i = 0; i < max; i++) { + try{ + int r = q.consume(); + System.out.println("Item: " + r); + } catch (KeeperException e){ + i--; + } catch (InterruptedException e){ + + } + } + } + } + + public static void barrierTest(String args[]) { + Barrier b = new Barrier(args[1], "/b1", new Integer(args[2])); + try{ + boolean flag = b.enter(); + System.out.println("Entered barrier: " + args[2]); + if(!flag) System.out.println("Error when entering the barrier"); + } catch (KeeperException e){ + + } catch (InterruptedException e){ + + } + + // Generate random integer + Random rand = new Random(); + int r = rand.nextInt(100); + // Loop for rand iterations + for (int i = 0; i < r; i++) { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + + } + } + try{ + b.leave(); + } catch (KeeperException e){ + + } catch (InterruptedException e){ + + } + System.out.println("Left barrier"); + } +} +</pre> +</div> +</div> +</div> + + +<p align="right"> +<font size="-2"></font> +</p> +</div> +<!--+ + |end content + +--> +<div class="clearboth"> </div> +</div> +<div id="footer"> +<!--+ + |start bottomstrip + +--> +<div class="lastmodified"> +<script type="text/javascript"><!-- +document.write("Last Published: " + document.lastModified); +// --></script> +</div> +<div class="copyright"> + Copyright © + <a href="http://www.apache.org/licenses/">The Apache Software Foundation.</a> +</div> +<!--+ + |end bottomstrip + +--> +</div> +</body> +</html> http://git-wip-us.apache.org/repos/asf/zookeeper/blob/86349e3b/_released_docs/r3.4.13/zookeeperTutorial.pdf ---------------------------------------------------------------------- diff --git a/_released_docs/r3.4.13/zookeeperTutorial.pdf b/_released_docs/r3.4.13/zookeeperTutorial.pdf new file mode 100644 index 0000000..5b3faf6 Binary files /dev/null and b/_released_docs/r3.4.13/zookeeperTutorial.pdf differ http://git-wip-us.apache.org/repos/asf/zookeeper/blob/86349e3b/documentation.md ---------------------------------------------------------------------- diff --git a/documentation.md b/documentation.md index 2c8e2e7..24d15bd 100644 --- a/documentation.md +++ b/documentation.md @@ -11,6 +11,7 @@ Setup instructions, programming guides, and other documentation are available fo * [ZooKeeper 3.5.2-alpha](doc/r3.5.2-alpha/index.html) * [ZooKeeper 3.5.1-alpha](doc/r3.5.1-alpha/index.html) * [ZooKeeper 3.5.0-alpha](doc/r3.5.0-alpha/index.html) +* [ZooKeeper 3.4.13](doc/r3.4.13/index.html) * [ZooKeeper 3.4.12](doc/r3.4.12/index.html) * [ZooKeeper 3.4.11](doc/r3.4.11/index.html) * [ZooKeeper 3.4.10](doc/r3.4.10/index.html) http://git-wip-us.apache.org/repos/asf/zookeeper/blob/86349e3b/releases.md ---------------------------------------------------------------------- diff --git a/releases.md b/releases.md index 47d9a0e..ae4c5d4 100644 --- a/releases.md +++ b/releases.md @@ -26,6 +26,10 @@ Release notes for Apache Zookeeper releases are available in Jira: [Browse relea ## News {#news} +### 15 July, 2018: release 3.4.13 available + +This is a bugfix release. It fixes 17 issues, including issues such as ZOOKEEPER-2959 that could cause data loss when observer is used, and ZOOKEEPER-2184 that prevents ZooKeeper Java clients working in dynamic IP (container / cloud) environment. See [ZooKeeper 3.4.13 Release Notes](https://zookeeper.apache.org/doc/r3.4.13/releasenotes.html) for details. + ### 17 May, 2018: release 3.5.4-beta available 3.5.4-beta is the second beta in the planned 3.5 release line leading up to a stable 3.5 release. It comprises 113 bug fixes and improvements.