View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase;
20  
21  import java.io.IOException;
22  import java.security.PrivilegedExceptionAction;
23  import java.util.ArrayList;
24  import java.util.Collections;
25  import java.util.List;
26  
27  import com.google.common.base.Joiner;
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  import org.apache.hadoop.hbase.classification.InterfaceAudience;
31  import org.apache.hadoop.hbase.classification.InterfaceStability;
32  import org.apache.hadoop.conf.Configuration;
33  import org.apache.hadoop.hbase.client.Admin;
34  import org.apache.hadoop.hbase.client.HBaseAdmin;
35  import org.apache.hadoop.hbase.regionserver.HRegionServer;
36  import org.apache.hadoop.hbase.security.User;
37  import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread;
38  import org.apache.hadoop.hbase.util.Threads;
39  
40  import java.util.concurrent.CopyOnWriteArrayList;
41  import org.apache.hadoop.hbase.master.HMaster;
42  import org.apache.hadoop.hbase.util.JVMClusterUtil;
43  
44  /**
45   * This class creates a single process HBase cluster. One thread is created for
46   * a master and one per region server.
47   *
48   * Call {@link #startup()} to start the cluster running and {@link #shutdown()}
49   * to close it all down. {@link #join} the cluster is you want to wait on
50   * shutdown completion.
51   *
52   * <p>Runs master on port 16000 by default.  Because we can't just kill the
53   * process -- not till HADOOP-1700 gets fixed and even then.... -- we need to
54   * be able to find the master with a remote client to run shutdown.  To use a
55   * port other than 16000, set the hbase.master to a value of 'local:PORT':
56   * that is 'local', not 'localhost', and the port number the master should use
57   * instead of 16000.
58   *
59   */
60  @InterfaceAudience.Public
61  @InterfaceStability.Evolving
62  public class LocalHBaseCluster {
63    private static final Log LOG = LogFactory.getLog(LocalHBaseCluster.class);
64    private final List<JVMClusterUtil.MasterThread> masterThreads =
65      new CopyOnWriteArrayList<JVMClusterUtil.MasterThread>();
66    private final List<JVMClusterUtil.RegionServerThread> regionThreads =
67      new CopyOnWriteArrayList<JVMClusterUtil.RegionServerThread>();
68    private final static int DEFAULT_NO = 1;
69    /** local mode */
70    public static final String LOCAL = "local";
71    /** 'local:' */
72    public static final String LOCAL_COLON = LOCAL + ":";
73    public static final String ASSIGN_RANDOM_PORTS = "hbase.localcluster.assign.random.ports";
74  
75    private final Configuration conf;
76    private final Class<? extends HMaster> masterClass;
77    private final Class<? extends HRegionServer> regionServerClass;
78  
79    /**
80     * Constructor.
81     * @param conf
82     * @throws IOException
83     */
84    public LocalHBaseCluster(final Configuration conf)
85    throws IOException {
86      this(conf, DEFAULT_NO);
87    }
88  
89    /**
90     * Constructor.
91     * @param conf Configuration to use.  Post construction has the master's
92     * address.
93     * @param noRegionServers Count of regionservers to start.
94     * @throws IOException
95     */
96    public LocalHBaseCluster(final Configuration conf, final int noRegionServers)
97    throws IOException {
98      this(conf, 1, noRegionServers, getMasterImplementation(conf),
99          getRegionServerImplementation(conf));
100   }
101 
102   /**
103    * Constructor.
104    * @param conf Configuration to use.  Post construction has the active master
105    * address.
106    * @param noMasters Count of masters to start.
107    * @param noRegionServers Count of regionservers to start.
108    * @throws IOException
109    */
110   public LocalHBaseCluster(final Configuration conf, final int noMasters,
111       final int noRegionServers)
112   throws IOException {
113     this(conf, noMasters, noRegionServers, getMasterImplementation(conf),
114         getRegionServerImplementation(conf));
115   }
116 
117   @SuppressWarnings("unchecked")
118   private static Class<? extends HRegionServer> getRegionServerImplementation(final Configuration conf) {
119     return (Class<? extends HRegionServer>)conf.getClass(HConstants.REGION_SERVER_IMPL,
120        HRegionServer.class);
121   }
122 
123   @SuppressWarnings("unchecked")
124   private static Class<? extends HMaster> getMasterImplementation(final Configuration conf) {
125     return (Class<? extends HMaster>)conf.getClass(HConstants.MASTER_IMPL,
126        HMaster.class);
127   }
128 
129   /**
130    * Constructor.
131    * @param conf Configuration to use.  Post construction has the master's
132    * address.
133    * @param noMasters Count of masters to start.
134    * @param noRegionServers Count of regionservers to start.
135    * @param masterClass
136    * @param regionServerClass
137    * @throws IOException
138    */
139   @SuppressWarnings("unchecked")
140   public LocalHBaseCluster(final Configuration conf, final int noMasters,
141     final int noRegionServers, final Class<? extends HMaster> masterClass,
142     final Class<? extends HRegionServer> regionServerClass)
143   throws IOException {
144     this.conf = conf;
145 
146     // Always have masters and regionservers come up on port '0' so we don't
147     // clash over default ports.
148     if (conf.getBoolean(ASSIGN_RANDOM_PORTS, true)) {
149       conf.set(HConstants.MASTER_PORT, "0");
150       conf.set(HConstants.REGIONSERVER_PORT, "0");
151       if (conf.getInt(HConstants.REGIONSERVER_INFO_PORT, 0) != -1) {
152         conf.set(HConstants.REGIONSERVER_INFO_PORT, "0");
153       }
154       if (conf.getInt(HConstants.MASTER_INFO_PORT, 0) != -1) {
155         conf.set(HConstants.MASTER_INFO_PORT, "0");
156       }
157     }
158 
159     this.masterClass = (Class<? extends HMaster>)
160       conf.getClass(HConstants.MASTER_IMPL, masterClass);
161     // Start the HMasters.
162     for (int i = 0; i < noMasters; i++) {
163       addMaster(new Configuration(conf), i);
164     }
165 
166     // Populate the master address host ports in the config. This is needed if a master based
167     // registry is configured for client metadata services (HBASE-18095)
168     List<String> masterHostPorts = new ArrayList<>();
169     for (JVMClusterUtil.MasterThread masterThread: getMasters()) {
170       masterHostPorts.add(masterThread.getMaster().getServerName().getAddress().toString());
171     }
172     conf.set(HConstants.MASTER_ADDRS_KEY, Joiner.on(",").join(masterHostPorts));
173 
174     // Start the HRegionServers.
175     this.regionServerClass =
176       (Class<? extends HRegionServer>)conf.getClass(HConstants.REGION_SERVER_IMPL,
177        regionServerClass);
178 
179     for (int i = 0; i < noRegionServers; i++) {
180       addRegionServer(new Configuration(conf), i);
181     }
182   }
183 
184   public JVMClusterUtil.RegionServerThread addRegionServer()
185       throws IOException {
186     return addRegionServer(new Configuration(conf), this.regionThreads.size());
187   }
188 
189   @SuppressWarnings("unchecked")
190   public JVMClusterUtil.RegionServerThread addRegionServer(
191       Configuration config, final int index)
192   throws IOException {
193     // Create each regionserver with its own Configuration instance so each has
194     // its HConnection instance rather than share (see HBASE_INSTANCES down in
195     // the guts of HConnectionManager.
196 
197     // Also, create separate CoordinatedStateManager instance per Server.
198     // This is special case when we have to have more than 1 CoordinatedStateManager
199     // within 1 process.
200     CoordinatedStateManager cp = CoordinatedStateManagerFactory.getCoordinatedStateManager(conf);
201 
202     JVMClusterUtil.RegionServerThread rst =
203         JVMClusterUtil.createRegionServerThread(config, cp, (Class<? extends HRegionServer>) conf
204             .getClass(HConstants.REGION_SERVER_IMPL, this.regionServerClass), index);
205 
206     this.regionThreads.add(rst);
207     return rst;
208   }
209 
210   public JVMClusterUtil.RegionServerThread addRegionServer(
211       final Configuration config, final int index, User user)
212   throws IOException, InterruptedException {
213     return user.runAs(
214         new PrivilegedExceptionAction<JVMClusterUtil.RegionServerThread>() {
215           @Override
216           public JVMClusterUtil.RegionServerThread run() throws Exception {
217             return addRegionServer(config, index);
218           }
219         });
220   }
221 
222   public JVMClusterUtil.MasterThread addMaster() throws IOException {
223     return addMaster(new Configuration(conf), this.masterThreads.size());
224   }
225 
226   public JVMClusterUtil.MasterThread addMaster(Configuration c, final int index)
227       throws IOException {
228     // Create each master with its own Configuration instance so each has
229     // its HConnection instance rather than share (see HBASE_INSTANCES down in
230     // the guts of HConnectionManager.
231 
232     // Also, create separate CoordinatedStateManager instance per Server.
233     // This is special case when we have to have more than 1 CoordinatedStateManager
234     // within 1 process.
235     CoordinatedStateManager cp = CoordinatedStateManagerFactory.getCoordinatedStateManager(conf);
236 
237     JVMClusterUtil.MasterThread mt = JVMClusterUtil.createMasterThread(c, cp,
238         (Class<? extends HMaster>) conf.getClass(HConstants.MASTER_IMPL, this.masterClass), index);
239     this.masterThreads.add(mt);
240     return mt;
241   }
242 
243   public JVMClusterUtil.MasterThread addMaster(
244       final Configuration c, final int index, User user)
245   throws IOException, InterruptedException {
246     return user.runAs(
247         new PrivilegedExceptionAction<JVMClusterUtil.MasterThread>() {
248           @Override
249           public JVMClusterUtil.MasterThread run() throws Exception {
250             return addMaster(c, index);
251           }
252         });
253   }
254 
255   /**
256    * @param serverNumber
257    * @return region server
258    */
259   public HRegionServer getRegionServer(int serverNumber) {
260     return regionThreads.get(serverNumber).getRegionServer();
261   }
262 
263   /**
264    * @return Read-only list of region server threads.
265    */
266   public List<JVMClusterUtil.RegionServerThread> getRegionServers() {
267     return Collections.unmodifiableList(this.regionThreads);
268   }
269 
270   /**
271    * @return List of running servers (Some servers may have been killed or
272    * aborted during lifetime of cluster; these servers are not included in this
273    * list).
274    */
275   public List<JVMClusterUtil.RegionServerThread> getLiveRegionServers() {
276     List<JVMClusterUtil.RegionServerThread> liveServers =
277       new ArrayList<JVMClusterUtil.RegionServerThread>();
278     List<RegionServerThread> list = getRegionServers();
279     for (JVMClusterUtil.RegionServerThread rst: list) {
280       if (rst.isAlive()) liveServers.add(rst);
281       else LOG.info("Not alive " + rst.getName());
282     }
283     return liveServers;
284   }
285 
286   /**
287    * @return the Configuration used by this LocalHBaseCluster
288    */
289   public Configuration getConfiguration() {
290     return this.conf;
291   }
292 
293   /**
294    * Wait for the specified region server to stop. Removes this thread from list of running threads.
295    * @return Name of region server that just went down.
296    */
297   public String waitOnRegionServer(int serverNumber) {
298     JVMClusterUtil.RegionServerThread regionServerThread = this.regionThreads.get(serverNumber);
299     return waitOnRegionServer(regionServerThread);
300   }
301 
302   /**
303    * Wait for the specified region server to stop. Removes this thread from list of running threads.
304    * @return Name of region server that just went down.
305    */
306   public String waitOnRegionServer(JVMClusterUtil.RegionServerThread rst) {
307     while (rst.isAlive()) {
308       try {
309         LOG.info("Waiting on " + rst.getRegionServer().toString());
310         rst.join();
311       } catch (InterruptedException e) {
312         e.printStackTrace();
313       }
314     }
315     regionThreads.remove(rst);
316     return rst.getName();
317   }
318 
319   /**
320    * @return the HMaster thread
321    */
322   public HMaster getMaster(int serverNumber) {
323     return masterThreads.get(serverNumber).getMaster();
324   }
325 
326   /**
327    * Gets the current active master, if available.  If no active master, returns
328    * null.
329    * @return the HMaster for the active master
330    */
331   public HMaster getActiveMaster() {
332     for (JVMClusterUtil.MasterThread mt : masterThreads) {
333       // Ensure that the current active master is not stopped.
334       // We don't want to return a stopping master as an active master.
335       if (mt.getMaster().isActiveMaster() && !mt.getMaster().isStopped()) {
336         return mt.getMaster();
337       }
338     }
339     return null;
340   }
341 
342   /**
343    * @return Read-only list of master threads.
344    */
345   public List<JVMClusterUtil.MasterThread> getMasters() {
346     return Collections.unmodifiableList(this.masterThreads);
347   }
348 
349   /**
350    * @return List of running master servers (Some servers may have been killed
351    * or aborted during lifetime of cluster; these servers are not included in
352    * this list).
353    */
354   public List<JVMClusterUtil.MasterThread> getLiveMasters() {
355     List<JVMClusterUtil.MasterThread> liveServers = new ArrayList<>();
356     List<JVMClusterUtil.MasterThread> list = getMasters();
357     for (JVMClusterUtil.MasterThread mt: list) {
358       if (mt.isAlive()) {
359         liveServers.add(mt);
360       }
361     }
362     return liveServers;
363   }
364 
365   /**
366    * Wait for the specified master to stop. Removes this thread from list of running threads.
367    * @return Name of master that just went down.
368    */
369   public String waitOnMaster(int serverNumber) {
370     JVMClusterUtil.MasterThread masterThread = this.masterThreads.get(serverNumber);
371     return waitOnMaster(masterThread);
372   }
373 
374   /**
375    * Wait for the specified master to stop. Removes this thread from list of running threads.
376    * @return Name of master that just went down.
377    */
378   public String waitOnMaster(JVMClusterUtil.MasterThread masterThread) {
379     while (masterThread.isAlive()) {
380       try {
381         LOG.info("Waiting on " + masterThread.getMaster().getServerName().toString());
382         masterThread.join();
383       } catch (InterruptedException e) {
384         e.printStackTrace();
385       }
386     }
387     masterThreads.remove(masterThread);
388     return masterThread.getName();
389   }
390 
391   /**
392    * Wait for Mini HBase Cluster to shut down.
393    * Presumes you've already called {@link #shutdown()}.
394    */
395   public void join() {
396     if (this.regionThreads != null) {
397       for(Thread t: this.regionThreads) {
398         if (t.isAlive()) {
399           try {
400             Threads.threadDumpingIsAlive(t);
401           } catch (InterruptedException e) {
402             LOG.debug("Interrupted", e);
403           }
404         }
405       }
406     }
407     if (this.masterThreads != null) {
408       for (Thread t : this.masterThreads) {
409         if (t.isAlive()) {
410           try {
411             Threads.threadDumpingIsAlive(t);
412           } catch (InterruptedException e) {
413             LOG.debug("Interrupted", e);
414           }
415         }
416       }
417     }
418   }
419 
420   /**
421    * Start the cluster.
422    */
423   public void startup() throws IOException {
424     JVMClusterUtil.startup(this.masterThreads, this.regionThreads);
425   }
426 
427   /**
428    * Shut down the mini HBase cluster
429    */
430   public void shutdown() {
431     JVMClusterUtil.shutdown(this.masterThreads, this.regionThreads);
432   }
433 
434   /**
435    * @param c Configuration to check.
436    * @return True if a 'local' address in hbase.master value.
437    */
438   public static boolean isLocal(final Configuration c) {
439     boolean mode = c.getBoolean(HConstants.CLUSTER_DISTRIBUTED, HConstants.DEFAULT_CLUSTER_DISTRIBUTED);
440     return(mode == HConstants.CLUSTER_IS_LOCAL);
441   }
442 
443   /**
444    * Test things basically work.
445    * @param args
446    * @throws IOException
447    */
448   public static void main(String[] args) throws IOException {
449     Configuration conf = HBaseConfiguration.create();
450     LocalHBaseCluster cluster = new LocalHBaseCluster(conf);
451     cluster.startup();
452     Admin admin = new HBaseAdmin(conf);
453     try {
454       HTableDescriptor htd =
455         new HTableDescriptor(TableName.valueOf(cluster.getClass().getName()));
456       admin.createTable(htd);
457     } finally {
458       admin.close();
459     }
460     cluster.shutdown();
461   }
462 }