1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.zookeeper;
20
21 import com.google.common.annotations.VisibleForTesting;
22
23 import java.io.BufferedReader;
24 import java.io.File;
25 import java.io.IOException;
26 import java.io.InputStreamReader;
27 import java.io.InterruptedIOException;
28 import java.io.OutputStream;
29 import java.io.Reader;
30 import java.net.BindException;
31 import java.net.InetSocketAddress;
32 import java.net.Socket;
33 import java.nio.charset.StandardCharsets;
34 import java.util.ArrayList;
35 import java.util.List;
36 import java.util.Random;
37
38 import org.apache.commons.logging.Log;
39 import org.apache.commons.logging.LogFactory;
40 import org.apache.hadoop.conf.Configuration;
41 import org.apache.hadoop.hbase.HConstants;
42 import org.apache.hadoop.hbase.classification.InterfaceAudience;
43 import org.apache.hadoop.hbase.classification.InterfaceStability;
44 import org.apache.hadoop.hbase.util.Bytes;
45 import org.apache.zookeeper.server.NIOServerCnxnFactory;
46 import org.apache.zookeeper.server.ZooKeeperServer;
47 import org.apache.zookeeper.server.persistence.FileTxnLog;
48
49
50
51
52
53
54 @InterfaceAudience.Public
55 @InterfaceStability.Evolving
56 public class MiniZooKeeperCluster {
57 private static final Log LOG = LogFactory.getLog(MiniZooKeeperCluster.class);
58
59 private static final int TICK_TIME = 2000;
60 private static final int DEFAULT_CONNECTION_TIMEOUT = 30000;
61 private int connectionTimeout;
62
63 private boolean started;
64
65
66 private int defaultClientPort = 0;
67
68 private List<NIOServerCnxnFactory> standaloneServerFactoryList;
69 private List<ZooKeeperServer> zooKeeperServers;
70 private List<Integer> clientPortList;
71
72 private int activeZKServerIndex;
73 private int tickTime = 0;
74
75 private Configuration configuration;
76
77 public MiniZooKeeperCluster() {
78 this(new Configuration());
79 }
80
81 public MiniZooKeeperCluster(Configuration configuration) {
82 this.started = false;
83 this.configuration = configuration;
84 activeZKServerIndex = -1;
85 zooKeeperServers = new ArrayList<ZooKeeperServer>();
86 clientPortList = new ArrayList<Integer>();
87 standaloneServerFactoryList = new ArrayList<NIOServerCnxnFactory>();
88 connectionTimeout = configuration.getInt(HConstants.ZK_SESSION_TIMEOUT + ".localHBaseCluster",
89 DEFAULT_CONNECTION_TIMEOUT);
90 }
91
92
93
94
95
96
97 public void addClientPort(int clientPort) {
98 clientPortList.add(clientPort);
99 }
100
101
102
103
104
105 @VisibleForTesting
106 public List<Integer> getClientPortList() {
107 return clientPortList;
108 }
109
110
111
112
113
114
115 private boolean hasValidClientPortInList(int index) {
116 return (clientPortList.size() > index && clientPortList.get(index) > 0);
117 }
118
119 public void setDefaultClientPort(int clientPort) {
120 if (clientPort <= 0) {
121 throw new IllegalArgumentException("Invalid default ZK client port: "
122 + clientPort);
123 }
124 this.defaultClientPort = clientPort;
125 }
126
127
128
129
130
131
132
133 private int selectClientPort(int seedPort) {
134 int i;
135 int returnClientPort = seedPort + 1;
136 if (returnClientPort == 0) {
137
138
139
140
141 if (defaultClientPort > 0) {
142 returnClientPort = defaultClientPort;
143 } else {
144 returnClientPort = 0xc000 + new Random().nextInt(0x3f00);
145 }
146 }
147
148 while (true) {
149 for (i = 0; i < clientPortList.size(); i++) {
150 if (returnClientPort == clientPortList.get(i)) {
151
152 returnClientPort++;
153 break;
154 }
155 }
156 if (i == clientPortList.size()) {
157 break;
158 }
159 }
160 return returnClientPort;
161 }
162
163 public void setTickTime(int tickTime) {
164 this.tickTime = tickTime;
165 }
166
167 public int getBackupZooKeeperServerNum() {
168 return zooKeeperServers.size()-1;
169 }
170
171 public int getZooKeeperServerNum() {
172 return zooKeeperServers.size();
173 }
174
175
176 private static void setupTestEnv() {
177
178
179
180
181 System.setProperty("zookeeper.preAllocSize", "100");
182 FileTxnLog.setPreallocSize(100 * 1024);
183
184 System.setProperty("zookeeper.4lw.commands.whitelist","*");
185 }
186
187 public int startup(File baseDir) throws IOException, InterruptedException {
188 int numZooKeeperServers = clientPortList.size();
189 if (numZooKeeperServers == 0) {
190 numZooKeeperServers = 1;
191 }
192 return startup(baseDir, numZooKeeperServers);
193 }
194
195
196
197
198
199
200
201
202
203 public int startup(File baseDir, int numZooKeeperServers) throws IOException,
204 InterruptedException {
205 if (numZooKeeperServers <= 0)
206 return -1;
207
208 setupTestEnv();
209 shutdown();
210
211 int tentativePort = -1;
212 int currentClientPort;
213
214
215 for (int i = 0; i < numZooKeeperServers; i++) {
216 File dir = new File(baseDir, "zookeeper_"+i).getAbsoluteFile();
217 createDir(dir);
218 int tickTimeToUse;
219 if (this.tickTime > 0) {
220 tickTimeToUse = this.tickTime;
221 } else {
222 tickTimeToUse = TICK_TIME;
223 }
224
225
226 if (hasValidClientPortInList(i)) {
227 currentClientPort = clientPortList.get(i);
228 } else {
229 tentativePort = selectClientPort(tentativePort);
230 currentClientPort = tentativePort;
231 }
232
233 ZooKeeperServer server = new ZooKeeperServer(dir, dir, tickTimeToUse);
234
235 server.setMinSessionTimeout(configuration.getInt("hbase.zookeeper.property.minSessionTimeout", -1));
236 server.setMaxSessionTimeout(configuration.getInt("hbase.zookeeper.property.maxSessionTimeout", -1));
237 NIOServerCnxnFactory standaloneServerFactory;
238 while (true) {
239 try {
240 standaloneServerFactory = new NIOServerCnxnFactory();
241 standaloneServerFactory.configure(
242 new InetSocketAddress(currentClientPort),
243 configuration.getInt(HConstants.ZOOKEEPER_MAX_CLIENT_CNXNS, 1000));
244 } catch (BindException e) {
245 LOG.debug("Failed binding ZK Server to client port: " +
246 currentClientPort, e);
247
248 if (hasValidClientPortInList(i)) {
249 return -1;
250 }
251
252 tentativePort = selectClientPort(tentativePort);
253 currentClientPort = tentativePort;
254 continue;
255 }
256 break;
257 }
258
259
260 standaloneServerFactory.startup(server);
261
262 if (!waitForServerUp(currentClientPort, connectionTimeout)) {
263 throw new IOException("Waiting for startup of standalone server");
264 }
265
266
267 if (clientPortList.size() <= i) {
268 clientPortList.add(currentClientPort);
269 }
270 else if (clientPortList.get(i) <= 0) {
271 clientPortList.remove(i);
272 clientPortList.add(i, currentClientPort);
273 }
274
275 standaloneServerFactoryList.add(standaloneServerFactory);
276 zooKeeperServers.add(server);
277 }
278
279
280 activeZKServerIndex = 0;
281 started = true;
282 int clientPort = clientPortList.get(activeZKServerIndex);
283 LOG.info("Started MiniZooKeeperCluster and ran successful 'stat' " +
284 "on client port=" + clientPort);
285 return clientPort;
286 }
287
288 private void createDir(File dir) throws IOException {
289 try {
290 if (!dir.exists()) {
291 dir.mkdirs();
292 }
293 } catch (SecurityException e) {
294 throw new IOException("creating dir: " + dir, e);
295 }
296 }
297
298
299
300
301 public void shutdown() throws IOException {
302
303 for (int i = 0; i < standaloneServerFactoryList.size(); i++) {
304 NIOServerCnxnFactory standaloneServerFactory =
305 standaloneServerFactoryList.get(i);
306 int clientPort = clientPortList.get(i);
307
308 standaloneServerFactory.shutdown();
309 if (!waitForServerDown(clientPort, connectionTimeout)) {
310 throw new IOException("Waiting for shutdown of standalone server");
311 }
312 }
313 standaloneServerFactoryList.clear();
314
315 for (ZooKeeperServer zkServer: zooKeeperServers) {
316
317 zkServer.getZKDatabase().close();
318 }
319 zooKeeperServers.clear();
320
321
322 if (started) {
323 started = false;
324 activeZKServerIndex = 0;
325 clientPortList.clear();
326 LOG.info("Shutdown MiniZK cluster with all ZK servers");
327 }
328 }
329
330
331
332
333
334
335 public int killCurrentActiveZooKeeperServer() throws IOException,
336 InterruptedException {
337 if (!started || activeZKServerIndex < 0) {
338 return -1;
339 }
340
341
342 NIOServerCnxnFactory standaloneServerFactory =
343 standaloneServerFactoryList.get(activeZKServerIndex);
344 int clientPort = clientPortList.get(activeZKServerIndex);
345
346 standaloneServerFactory.shutdown();
347 if (!waitForServerDown(clientPort, connectionTimeout)) {
348 throw new IOException("Waiting for shutdown of standalone server");
349 }
350
351 zooKeeperServers.get(activeZKServerIndex).getZKDatabase().close();
352
353
354 standaloneServerFactoryList.remove(activeZKServerIndex);
355 clientPortList.remove(activeZKServerIndex);
356 zooKeeperServers.remove(activeZKServerIndex);
357 LOG.info("Kill the current active ZK servers in the cluster " +
358 "on client port: " + clientPort);
359
360 if (standaloneServerFactoryList.size() == 0) {
361
362 return -1;
363 }
364 clientPort = clientPortList.get(activeZKServerIndex);
365 LOG.info("Activate a backup zk server in the cluster " +
366 "on client port: " + clientPort);
367
368 return clientPort;
369 }
370
371
372
373
374
375
376 public void killOneBackupZooKeeperServer() throws IOException,
377 InterruptedException {
378 if (!started || activeZKServerIndex < 0 ||
379 standaloneServerFactoryList.size() <= 1) {
380 return ;
381 }
382
383 int backupZKServerIndex = activeZKServerIndex+1;
384
385 NIOServerCnxnFactory standaloneServerFactory =
386 standaloneServerFactoryList.get(backupZKServerIndex);
387 int clientPort = clientPortList.get(backupZKServerIndex);
388
389 standaloneServerFactory.shutdown();
390 if (!waitForServerDown(clientPort, connectionTimeout)) {
391 throw new IOException("Waiting for shutdown of standalone server");
392 }
393
394 zooKeeperServers.get(backupZKServerIndex).getZKDatabase().close();
395
396
397 standaloneServerFactoryList.remove(backupZKServerIndex);
398 clientPortList.remove(backupZKServerIndex);
399 zooKeeperServers.remove(backupZKServerIndex);
400 LOG.info("Kill one backup ZK servers in the cluster " +
401 "on client port: " + clientPort);
402 }
403
404
405 private static boolean waitForServerDown(int port, long timeout) throws IOException {
406 long start = System.currentTimeMillis();
407 while (true) {
408 try {
409 Socket sock = new Socket("localhost", port);
410 try {
411 OutputStream outstream = sock.getOutputStream();
412 outstream.write(Bytes.toBytes("stat"));
413 outstream.flush();
414 } finally {
415 sock.close();
416 }
417 } catch (IOException e) {
418 return true;
419 }
420
421 if (System.currentTimeMillis() > start + timeout) {
422 break;
423 }
424 try {
425 Thread.sleep(250);
426 } catch (InterruptedException e) {
427 throw (InterruptedIOException)new InterruptedIOException().initCause(e);
428 }
429 }
430 return false;
431 }
432
433
434 private static boolean waitForServerUp(int port, long timeout) throws IOException {
435 long start = System.currentTimeMillis();
436 while (true) {
437 try {
438 Socket sock = new Socket("localhost", port);
439 BufferedReader reader = null;
440 try {
441 OutputStream outstream = sock.getOutputStream();
442 outstream.write(Bytes.toBytes("stat"));
443 outstream.flush();
444
445 Reader isr = new InputStreamReader(sock.getInputStream(), StandardCharsets.UTF_8);
446 reader = new BufferedReader(isr);
447 String line = reader.readLine();
448 if (line != null && line.startsWith("Zookeeper version:")) {
449 return true;
450 }
451 } finally {
452 sock.close();
453 if (reader != null) {
454 reader.close();
455 }
456 }
457 } catch (IOException e) {
458
459 LOG.info("server localhost:" + port + " not up " + e);
460 }
461
462 if (System.currentTimeMillis() > start + timeout) {
463 break;
464 }
465 try {
466 Thread.sleep(250);
467 } catch (InterruptedException e) {
468 throw (InterruptedIOException)new InterruptedIOException().initCause(e);
469 }
470 }
471 return false;
472 }
473
474 public int getClientPort() {
475 return activeZKServerIndex < 0 || activeZKServerIndex >= clientPortList.size() ? -1
476 : clientPortList.get(activeZKServerIndex);
477 }
478 }