/src/java/com/rapleaf/hank/zookeeper/ZooKeeperConnection.java

https://github.com/bryanduxbury/hank · Java · 223 lines · 95 code · 17 blank · 111 comment · 7 complexity · c3a3614ea2b6a91489337c33d2514d6c MD5 · raw file

  1. /**
  2. * Copyright 2011 Rapleaf
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.rapleaf.hank.zookeeper;
  17. import java.io.IOException;
  18. import java.util.concurrent.CountDownLatch;
  19. import org.apache.log4j.Logger;
  20. import org.apache.zookeeper.WatchedEvent;
  21. import org.apache.zookeeper.Watcher;
  22. import org.apache.zookeeper.Watcher.Event.KeeperState;
  23. /**
  24. * Base class that should be used by any class intending to connect to the
  25. * ZooKeeper service. This class automatically handles connecting,
  26. * disconnecting, and session expiry, and provides a clean interface for any
  27. * subclasses to take action upon these three notable events.
  28. */
  29. public class ZooKeeperConnection implements Watcher {
  30. private static final Logger LOG = Logger.getLogger(ZooKeeperConnection.class);
  31. public static final int DEFAULT_SESSION_TIMEOUT = 30000;
  32. public static final int DEFAULT_MAX_ATTEMPTS = 5;
  33. public static final int CONNECT_DELAY = 100; // ms
  34. public static final int MAX_CONNECT_DELAY = 7500; // ms
  35. protected ZooKeeperPlus zk;
  36. /**
  37. * Used to block while disconnected. Use {@link #waitForConnection()} in
  38. * subclasses to block while disconnected.
  39. */
  40. private CountDownLatch connectedSignal = new CountDownLatch(1);
  41. private String connectString;
  42. private int sessionTimeout;
  43. private int maxConnectAttempts;
  44. /**
  45. * Creates a new connection to the ZooKeeper service. Blocks until we are
  46. * connected to the service. Uses the default session timeout of 30 seconds.
  47. *
  48. * @param connectString
  49. * comma separated host:port pairs, each corresponding to a zk
  50. * server. e.g. "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002"
  51. * @throws InterruptedException
  52. */
  53. public ZooKeeperConnection(String connectString) throws InterruptedException {
  54. this(connectString, DEFAULT_SESSION_TIMEOUT);
  55. }
  56. /**
  57. * Creates a new connection to the ZooKeeper service. Blocks until we are
  58. * connected to the service.
  59. *
  60. * @param connectString
  61. * comma separated host:port pairs, each corresponding to a zk
  62. * server. e.g. "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002"
  63. * @param sessionTimeout
  64. * session timeout in milliseconds
  65. * @throws InterruptedException
  66. */
  67. public ZooKeeperConnection(String connectString, int sessionTimeout) throws InterruptedException {
  68. this(connectString, sessionTimeout, DEFAULT_MAX_ATTEMPTS);
  69. }
  70. /**
  71. * Creates a new connection to the ZooKeeper service. Blocks until we are
  72. * connected to the service.
  73. *
  74. * @param connectString
  75. * comma separated host:port pairs, each corresponding to a zk
  76. * server. e.g. "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002"
  77. * @param sessionTimeout
  78. * session timeout in milliseconds
  79. * @param maxConnectAttempts
  80. * how many times we should try to connect to the ZooKeeper ensemble
  81. * before dying
  82. * @throws InterruptedException
  83. */
  84. public ZooKeeperConnection(String connectString, int sessionTimeout, int maxConnectAttempts) throws InterruptedException {
  85. this.connectString = connectString;
  86. this.sessionTimeout = sessionTimeout;
  87. this.maxConnectAttempts = maxConnectAttempts;
  88. try {
  89. connect(maxConnectAttempts);
  90. } catch (IOException e) {
  91. // If we can't connect, then die so that someone can reconfigure.
  92. LOG.fatal("Failed to connect to the ZooKeeper service", e);
  93. throw new RuntimeException(e);
  94. }
  95. connectedSignal.await();
  96. }
  97. /**
  98. * Discards the current connection (if there is one), and tries to set up a
  99. * new connection to the ZooKeeper service.
  100. *
  101. * @param maxConnectAttempts
  102. * the maximum number of times we want to connect to the ZooKeeper
  103. * ensemble. One attempt means trying all the servers once. A value
  104. * of zero means to attempt to connect forever.
  105. * @throws IOException
  106. * if all of our connection attempts failed
  107. */
  108. private void connect(int maxConnectAttempts) throws IOException {
  109. int attempts = 0;
  110. int delay = CONNECT_DELAY;
  111. while (true) {
  112. try {
  113. zk = new ZooKeeperPlus(connectString, sessionTimeout, this);
  114. // We return as soon as the assignment has succeeded.
  115. return;
  116. } catch (IOException e) {
  117. //this means that we tried to connect to all the hosts, but they all failed
  118. attempts ++;
  119. // if maxConnectAttempts == 0, then try forever
  120. if (maxConnectAttempts != 0 && attempts >= maxConnectAttempts) {
  121. throw e;
  122. }
  123. delay *= 2; //use exponential backoff
  124. delay = Math.min(delay, MAX_CONNECT_DELAY);
  125. }
  126. try {
  127. Thread.sleep(delay);
  128. } catch (InterruptedException e) {
  129. // Someone wants us to stop connecting
  130. return;
  131. }
  132. }
  133. }
  134. /**
  135. * Listens for notifications from the ZooKeeper service telling that we have
  136. * been connected, disconnected, or our session has expired.
  137. *
  138. * Upon connection, we first make a call to {@link #onConnect()}, and then we
  139. * release all threads that are blocking on {@link #waitForConnection()}.
  140. *
  141. * Upon disconnection, we call {@link #onDisconnect()}, and then we reset the
  142. * latch to block any threads that call {@link #waitForConnection()}.
  143. *
  144. * On session expiry, we call {@link #onSessionExpire()}, reset the latch, and
  145. * then manually try to reconnect to the ZooKeeper service.
  146. *
  147. * @param event
  148. */
  149. @Override
  150. public void process(WatchedEvent event) {
  151. if (event.getType() == Event.EventType.None) {
  152. KeeperState state = event.getState();
  153. switch (state) {
  154. case SyncConnected:
  155. onConnect();
  156. connectedSignal.countDown();
  157. break;
  158. case Disconnected:
  159. onDisconnect();
  160. connectedSignal = new CountDownLatch(1);
  161. break;
  162. case Expired:
  163. onSessionExpire();
  164. connectedSignal = new CountDownLatch(1);
  165. try {
  166. connect(maxConnectAttempts);
  167. } catch (IOException e) {
  168. LOG.fatal("Failed to connect to the ZooKeeper service", e);
  169. throw new RuntimeException("Couldn't connect to the ZooKeeper service", e);
  170. }
  171. break;
  172. }
  173. // Return because we are done processing this event; do not let subclasses
  174. // process.
  175. return;
  176. }
  177. }
  178. /**
  179. * Allows for subclasses to block until we are connected to the ZooKeeper
  180. * service. Returns immediately if we are already connected.
  181. *
  182. * @throws InterruptedException
  183. */
  184. protected void waitForConnection() throws InterruptedException {
  185. connectedSignal.await();
  186. }
  187. /**
  188. * Called when a connection to the ZooKeeper service has been established.
  189. * Meant to be used by subclasses
  190. */
  191. protected void onConnect() {}
  192. /**
  193. * Called when the connection to the ZooKeeper service has been broken. Note
  194. * that a disconnect does not mean our session has expired. If the connection
  195. * can be reestablished before the session timeout, we will keep the same
  196. * session (which means that ephemeral nodes will stay alive).
  197. */
  198. protected void onDisconnect() {}
  199. /**
  200. * Called when our session with the ZooKeeper service has expired.
  201. */
  202. protected void onSessionExpire() {}
  203. public String getConnectString() {
  204. return connectString;
  205. }
  206. }