2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 package org
.apache
.hadoop
.hbase
;
20 import static org
.junit
.Assert
.assertTrue
;
22 import java
.io
.IOException
;
23 import java
.util
.HashSet
;
24 import java
.util
.Hashtable
;
25 import java
.util
.Iterator
;
26 import java
.util
.List
;
28 import java
.util
.Random
;
30 import javax
.management
.MBeanAttributeInfo
;
31 import javax
.management
.MBeanInfo
;
32 import javax
.management
.MBeanServerConnection
;
33 import javax
.management
.ObjectInstance
;
34 import javax
.management
.ObjectName
;
35 import javax
.management
.remote
.JMXConnector
;
36 import javax
.management
.remote
.JMXConnectorFactory
;
37 import org
.apache
.hadoop
.conf
.Configuration
;
38 import org
.apache
.hadoop
.hbase
.client
.RegionInfo
;
39 import org
.apache
.hadoop
.hbase
.coprocessor
.CoprocessorHost
;
40 import org
.apache
.hadoop
.hbase
.master
.balancer
.BalancerTestBase
;
41 import org
.apache
.hadoop
.hbase
.master
.balancer
.StochasticLoadBalancer
;
42 import org
.apache
.hadoop
.hbase
.testclassification
.MediumTests
;
43 import org
.apache
.hadoop
.hbase
.testclassification
.MiscTests
;
44 import org
.apache
.hadoop
.hbase
.util
.Threads
;
45 import org
.apache
.hadoop
.net
.DNSToSwitchMapping
;
46 import org
.junit
.AfterClass
;
47 import org
.junit
.BeforeClass
;
48 import org
.junit
.ClassRule
;
49 import org
.junit
.FixMethodOrder
;
50 import org
.junit
.Ignore
;
51 import org
.junit
.Test
;
52 import org
.junit
.experimental
.categories
.Category
;
53 import org
.junit
.runners
.MethodSorters
;
54 import org
.slf4j
.Logger
;
55 import org
.slf4j
.LoggerFactory
;
57 @Category({ MiscTests
.class, MediumTests
.class })
58 @FixMethodOrder(MethodSorters
.NAME_ASCENDING
)
60 public class TestStochasticBalancerJmxMetrics
extends BalancerTestBase
{
63 public static final HBaseClassTestRule CLASS_RULE
=
64 HBaseClassTestRule
.forClass(TestStochasticBalancerJmxMetrics
.class);
66 private static final Logger LOG
= LoggerFactory
.getLogger(TestStochasticBalancerJmxMetrics
.class);
67 private static HBaseTestingUtility UTIL
= new HBaseTestingUtility();
68 private static int connectorPort
= 61120;
69 private static StochasticLoadBalancer loadBalancer
;
71 * a simple cluster for testing JMX.
73 private static int[] mockCluster_ensemble
= new int[] { 0, 1, 2, 3 };
74 private static int[] mockCluster_pertable_1
= new int[] { 0, 1, 2 };
75 private static int[] mockCluster_pertable_2
= new int[] { 3, 1, 1 };
76 private static int[] mockCluster_pertable_namespace
= new int[] { 1, 3, 1 };
78 private static final String TABLE_NAME_1
= "Table1";
79 private static final String TABLE_NAME_2
= "Table2";
80 private static final String TABLE_NAME_NAMESPACE
= "hbase:namespace";
82 private static Configuration conf
= null;
85 * Setup the environment for the test.
88 public static void setupBeforeClass() throws Exception
{
90 conf
= UTIL
.getConfiguration();
92 conf
.setClass("hbase.util.ip.to.rack.determiner", MockMapping
.class, DNSToSwitchMapping
.class);
93 conf
.setFloat("hbase.master.balancer.stochastic.maxMovePercent", 0.75f
);
94 conf
.setFloat("hbase.regions.slop", 0.0f
);
95 conf
.set(CoprocessorHost
.REGIONSERVER_COPROCESSOR_CONF_KEY
, JMXListener
.class.getName());
96 Random rand
= new Random();
97 for (int i
= 0; i
< 10; i
++) {
99 int sign
= i
% 2 == 0 ?
1 : -1;
100 connectorPort
+= sign
* rand
.nextInt(100);
101 } while (!HBaseTestingUtility
.available(connectorPort
));
103 conf
.setInt("regionserver.rmi.registry.port", connectorPort
);
105 UTIL
.startMiniCluster();
107 } catch (Exception e
) {
108 LOG
.debug("Encountered exception when starting cluster. Trying port " + connectorPort
, e
);
110 // this is to avoid "IllegalStateException: A mini-cluster is already running"
111 UTIL
.shutdownMiniCluster();
112 } catch (Exception ex
) {
113 LOG
.debug("Encountered exception shutting down cluster", ex
);
120 public static void tearDownAfterClass() throws Exception
{
121 UTIL
.shutdownMiniCluster();
125 * In Ensemble mode, there should be only one ensemble table
128 public void testJmxMetrics_EnsembleMode() throws Exception
{
129 loadBalancer
= new StochasticLoadBalancer();
131 conf
.setBoolean(HConstants
.HBASE_MASTER_LOADBALANCE_BYTABLE
, false);
132 loadBalancer
.setConf(conf
);
134 TableName tableName
= HConstants
.ENSEMBLE_TABLE_NAME
;
135 Map
<ServerName
, List
<RegionInfo
>> clusterState
= mockClusterServers(mockCluster_ensemble
);
136 loadBalancer
.balanceTable(tableName
, clusterState
);
138 String
[] tableNames
= new String
[] { tableName
.getNameAsString() };
139 String
[] functionNames
= loadBalancer
.getCostFunctionNames();
140 Set
<String
> jmxMetrics
= readJmxMetricsWithRetry();
141 Set
<String
> expectedMetrics
= getExpectedJmxMetrics(tableNames
, functionNames
);
143 // printMetrics(jmxMetrics, "existing metrics in ensemble mode");
144 // printMetrics(expectedMetrics, "expected metrics in ensemble mode");
146 // assert that every expected is in the JMX
147 for (String expected
: expectedMetrics
) {
148 assertTrue("Metric " + expected
+ " can not be found in JMX in ensemble mode.",
149 jmxMetrics
.contains(expected
));
154 * In per-table mode, each table has a set of metrics
157 public void testJmxMetrics_PerTableMode() throws Exception
{
158 loadBalancer
= new StochasticLoadBalancer();
160 conf
.setBoolean(HConstants
.HBASE_MASTER_LOADBALANCE_BYTABLE
, true);
161 loadBalancer
.setConf(conf
);
163 // NOTE the size is normally set in setClusterMetrics, for test purpose, we set it manually
164 // Tables: hbase:namespace, table1, table2
165 // Functions: costFunctions, overall
166 String
[] functionNames
= loadBalancer
.getCostFunctionNames();
167 loadBalancer
.updateMetricsSize(3 * (functionNames
.length
+ 1));
170 TableName tableName
= TableName
.valueOf(TABLE_NAME_1
);
171 Map
<ServerName
, List
<RegionInfo
>> clusterState
= mockClusterServers(mockCluster_pertable_1
);
172 loadBalancer
.balanceTable(tableName
, clusterState
);
175 tableName
= TableName
.valueOf(TABLE_NAME_2
);
176 clusterState
= mockClusterServers(mockCluster_pertable_2
);
177 loadBalancer
.balanceTable(tableName
, clusterState
);
179 // table hbase:namespace
180 tableName
= TableName
.valueOf(TABLE_NAME_NAMESPACE
);
181 clusterState
= mockClusterServers(mockCluster_pertable_namespace
);
182 loadBalancer
.balanceTable(tableName
, clusterState
);
184 String
[] tableNames
= new String
[] { TABLE_NAME_1
, TABLE_NAME_2
, TABLE_NAME_NAMESPACE
};
185 Set
<String
> jmxMetrics
= readJmxMetricsWithRetry();
186 Set
<String
> expectedMetrics
= getExpectedJmxMetrics(tableNames
, functionNames
);
188 // printMetrics(jmxMetrics, "existing metrics in per-table mode");
189 // printMetrics(expectedMetrics, "expected metrics in per-table mode");
191 // assert that every expected is in the JMX
192 for (String expected
: expectedMetrics
) {
193 assertTrue("Metric " + expected
+ " can not be found in JMX in per-table mode.",
194 jmxMetrics
.contains(expected
));
198 private Set
<String
> readJmxMetricsWithRetry() throws IOException
{
200 for (int i
= 0; i
< 10; i
++) {
201 Set
<String
> metrics
= readJmxMetrics();
202 if (metrics
!= null) return metrics
;
203 LOG
.warn("Failed to get jmxmetrics... sleeping, retrying; " + i
+ " of " + count
+ " times");
210 * Read the attributes from Hadoop->HBase->Master->Balancer in JMX
211 * @throws IOException
213 private Set
<String
> readJmxMetrics() throws IOException
{
214 JMXConnector connector
= null;
215 ObjectName target
= null;
216 MBeanServerConnection mb
= null;
219 JMXConnectorFactory
.connect(JMXListener
.buildJMXServiceURL(connectorPort
, connectorPort
));
220 mb
= connector
.getMBeanServerConnection();
222 Hashtable
<String
, String
> pairs
= new Hashtable
<>();
223 pairs
.put("service", "HBase");
224 pairs
.put("name", "Master");
225 pairs
.put("sub", "Balancer");
226 target
= new ObjectName("Hadoop", pairs
);
227 MBeanInfo beanInfo
= mb
.getMBeanInfo(target
);
229 Set
<String
> existingAttrs
= new HashSet
<>();
230 for (MBeanAttributeInfo attrInfo
: beanInfo
.getAttributes()) {
231 existingAttrs
.add(attrInfo
.getName());
233 return existingAttrs
;
234 } catch (Exception e
) {
235 LOG
.warn("Failed to get bean!!! " + target
, e
);
237 Set
<ObjectInstance
> instances
= mb
.queryMBeans(null, null);
238 Iterator
<ObjectInstance
> iterator
= instances
.iterator();
239 System
.out
.println("MBean Found:");
240 while (iterator
.hasNext()) {
241 ObjectInstance instance
= iterator
.next();
242 System
.out
.println("Class Name: " + instance
.getClassName());
243 System
.out
.println("Object Name: " + instance
.getObjectName());
247 if (connector
!= null) {
250 } catch (Exception e
) {
259 * Given the tables and functions, return metrics names that should exist in JMX
261 private Set
<String
> getExpectedJmxMetrics(String
[] tableNames
, String
[] functionNames
) {
262 Set
<String
> ret
= new HashSet
<>();
264 for (String tableName
: tableNames
) {
265 ret
.add(StochasticLoadBalancer
.composeAttributeName(tableName
, "Overall"));
266 for (String functionName
: functionNames
) {
267 String metricsName
= StochasticLoadBalancer
.composeAttributeName(tableName
, functionName
);
268 ret
.add(metricsName
);
275 private static void printMetrics(Set
<String
> metrics
, String info
) {
276 if (null != info
) LOG
.info("++++ ------ " + info
+ " ------");
278 LOG
.info("++++ metrics count = " + metrics
.size());
279 for (String str
: metrics
) {
280 LOG
.info(" ++++ " + str
);