001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.master.balancer; 019 020import java.io.IOException; 021import java.util.ArrayList; 022import java.util.Collection; 023import java.util.Collections; 024import java.util.HashMap; 025import java.util.List; 026import java.util.Map; 027import java.util.concurrent.TimeUnit; 028import java.util.function.Predicate; 029import java.util.function.Supplier; 030import org.apache.hadoop.conf.Configuration; 031import org.apache.hadoop.hbase.HBaseConfiguration; 032import org.apache.hadoop.hbase.HBaseInterfaceAudience; 033import org.apache.hadoop.hbase.HConstants; 034import org.apache.hadoop.hbase.HDFSBlocksDistribution; 035import org.apache.hadoop.hbase.ServerMetrics; 036import org.apache.hadoop.hbase.ServerName; 037import org.apache.hadoop.hbase.TableName; 038import org.apache.hadoop.hbase.client.BalancerDecision; 039import org.apache.hadoop.hbase.client.BalancerRejection; 040import org.apache.hadoop.hbase.client.Connection; 041import org.apache.hadoop.hbase.client.RegionInfo; 042import org.apache.hadoop.hbase.client.RegionInfoBuilder; 043import org.apache.hadoop.hbase.client.TableDescriptor; 044import org.apache.hadoop.hbase.master.LoadBalancer; 045import org.apache.hadoop.hbase.util.AbstractHBaseTool; 046import org.apache.hadoop.hbase.util.Bytes; 047import org.apache.yetus.audience.InterfaceAudience; 048import org.slf4j.Logger; 049import org.slf4j.LoggerFactory; 050 051import org.apache.hbase.thirdparty.com.google.common.base.Preconditions; 052import org.apache.hbase.thirdparty.com.google.common.base.Stopwatch; 053import org.apache.hbase.thirdparty.org.apache.commons.cli.CommandLine; 054import org.apache.hbase.thirdparty.org.apache.commons.cli.Option; 055 056/** 057 * Tool to test performance of different {@link org.apache.hadoop.hbase.master.LoadBalancer} 058 * implementations. Example command: $ bin/hbase 059 * org.apache.hadoop.hbase.master.balancer.LoadBalancerPerformanceEvaluation -regions 1000 -servers 060 * 100 -load_balancer org.apache.hadoop.hbase.master.balancer.SimpleLoadBalancer 061 */ 062@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.TOOLS) 063public class LoadBalancerPerformanceEvaluation extends AbstractHBaseTool { 064 private static final Logger LOG = 065 LoggerFactory.getLogger(LoadBalancerPerformanceEvaluation.class.getName()); 066 067 private static final int DEFAULT_NUM_REGIONS = 1000000; 068 private static final Option NUM_REGIONS_OPT = new Option("regions", true, 069 "Number of regions to consider by load balancer. Default: " + DEFAULT_NUM_REGIONS); 070 071 private static final int DEFAULT_NUM_SERVERS = 1000; 072 private static final Option NUM_SERVERS_OPT = new Option("servers", true, 073 "Number of servers to consider by load balancer. Default: " + DEFAULT_NUM_SERVERS); 074 075 private static final String DEFAULT_LOAD_BALANCER = 076 "org.apache.hadoop.hbase.master.balancer.StochasticLoadBalancer"; 077 private static final Option LOAD_BALANCER_OPT = new Option("load_balancer", true, 078 "Type of Load Balancer to use. Default: " + DEFAULT_LOAD_BALANCER); 079 080 private int numRegions; 081 private int numServers; 082 private String loadBalancerType; 083 private Class<?> loadBalancerClazz; 084 085 private LoadBalancer loadBalancer; 086 087 // data 088 private List<ServerName> servers; 089 private List<RegionInfo> regions; 090 private Map<RegionInfo, ServerName> regionServerMap; 091 private Map<TableName, Map<ServerName, List<RegionInfo>>> tableServerRegionMap; 092 093 // Non-default configurations. 094 private void setupConf() { 095 conf.setClass(HConstants.HBASE_MASTER_LOADBALANCER_CLASS, loadBalancerClazz, 096 LoadBalancer.class); 097 loadBalancer = LoadBalancerFactory.getLoadBalancer(conf); 098 loadBalancer.setClusterInfoProvider(new DummyClusterInfoProvider(conf)); 099 try { 100 loadBalancer.initialize(); 101 } catch (IOException e) { 102 LOG.error("Failed to initialize load balancer", e); 103 throw new RuntimeException("Failed to initialize load balancer", e); 104 } 105 } 106 107 private void generateRegionsAndServers() { 108 TableName tableName = TableName.valueOf("LoadBalancerPerfTable"); 109 // regions 110 regions = new ArrayList<>(numRegions); 111 regionServerMap = new HashMap<>(numRegions); 112 for (int i = 0; i < numRegions; ++i) { 113 byte[] start = new byte[16]; 114 byte[] end = new byte[16]; 115 116 Bytes.putInt(start, 0, i); 117 Bytes.putInt(end, 0, i + 1); 118 RegionInfo hri = RegionInfoBuilder.newBuilder(tableName).setStartKey(start).setEndKey(end) 119 .setSplit(false).setRegionId(i).build(); 120 regions.add(hri); 121 regionServerMap.put(hri, null); 122 } 123 124 // servers 125 servers = new ArrayList<>(numServers); 126 Map<ServerName, List<RegionInfo>> serverRegionMap = new HashMap<>(numServers); 127 for (int i = 0; i < numServers; ++i) { 128 ServerName sn = ServerName.valueOf("srv" + i, HConstants.DEFAULT_REGIONSERVER_PORT, i); 129 servers.add(sn); 130 serverRegionMap.put(sn, i == 0 ? regions : Collections.emptyList()); 131 } 132 tableServerRegionMap = Collections.singletonMap(tableName, serverRegionMap); 133 } 134 135 @Override 136 protected void addOptions() { 137 addOption(NUM_REGIONS_OPT); 138 addOption(NUM_SERVERS_OPT); 139 addOption(LOAD_BALANCER_OPT); 140 } 141 142 @Override 143 protected void processOptions(CommandLine cmd) { 144 numRegions = getOptionAsInt(cmd, NUM_REGIONS_OPT.getOpt(), DEFAULT_NUM_REGIONS); 145 Preconditions.checkArgument(numRegions > 0, "Invalid number of regions!"); 146 147 numServers = getOptionAsInt(cmd, NUM_SERVERS_OPT.getOpt(), DEFAULT_NUM_SERVERS); 148 Preconditions.checkArgument(numServers > 0, "Invalid number of servers!"); 149 150 loadBalancerType = cmd.getOptionValue(LOAD_BALANCER_OPT.getOpt(), DEFAULT_LOAD_BALANCER); 151 Preconditions.checkArgument(!loadBalancerType.isEmpty(), "Invalid load balancer type!"); 152 153 try { 154 loadBalancerClazz = Class.forName(loadBalancerType); 155 } catch (ClassNotFoundException e) { 156 System.err.println("Class '" + loadBalancerType + "' not found!"); 157 System.exit(1); 158 } 159 160 setupConf(); 161 } 162 163 private String formatResults(final String methodName, final long timeMillis) { 164 return String.format("Time for %-25s: %dms%n", methodName, timeMillis); 165 } 166 167 @Override 168 protected int doWork() throws Exception { 169 generateRegionsAndServers(); 170 171 String methodName = "roundRobinAssignment"; 172 LOG.info("Calling {}", methodName); 173 Stopwatch watch = Stopwatch.createStarted(); 174 loadBalancer.roundRobinAssignment(regions, servers); 175 System.out.print(formatResults(methodName, watch.elapsed(TimeUnit.MILLISECONDS))); 176 177 methodName = "retainAssignment"; 178 LOG.info("Calling {}", methodName); 179 watch.reset().start(); 180 loadBalancer.retainAssignment(regionServerMap, servers); 181 System.out.print(formatResults(methodName, watch.elapsed(TimeUnit.MILLISECONDS))); 182 183 methodName = "balanceCluster"; 184 LOG.info("Calling {}", methodName); 185 watch.reset().start(); 186 187 loadBalancer.balanceCluster(tableServerRegionMap); 188 System.out.print(formatResults(methodName, watch.elapsed(TimeUnit.MILLISECONDS))); 189 190 return EXIT_SUCCESS; 191 } 192 193 public static void main(String[] args) throws IOException { 194 LoadBalancerPerformanceEvaluation tool = new LoadBalancerPerformanceEvaluation(); 195 tool.setConf(HBaseConfiguration.create()); 196 tool.run(args); 197 } 198 199 private static class DummyClusterInfoProvider implements ClusterInfoProvider { 200 201 private volatile Configuration conf; 202 203 public DummyClusterInfoProvider(Configuration conf) { 204 this.conf = conf; 205 } 206 207 @Override 208 public Configuration getConfiguration() { 209 return conf; 210 } 211 212 @Override 213 public Connection getConnection() { 214 return null; 215 } 216 217 @Override 218 public List<RegionInfo> getAssignedRegions() { 219 return Collections.emptyList(); 220 } 221 222 @Override 223 public void unassign(RegionInfo regionInfo) throws IOException { 224 225 } 226 227 @Override 228 public TableDescriptor getTableDescriptor(TableName tableName) throws IOException { 229 return null; 230 } 231 232 @Override 233 public int getNumberOfTables() throws IOException { 234 return 0; 235 } 236 237 @Override 238 public HDFSBlocksDistribution computeHDFSBlocksDistribution(Configuration conf, 239 TableDescriptor tableDescriptor, RegionInfo regionInfo) throws IOException { 240 return new HDFSBlocksDistribution(); 241 } 242 243 @Override 244 public boolean hasRegionReplica(Collection<RegionInfo> regions) throws IOException { 245 return false; 246 } 247 248 @Override 249 public List<ServerName> getOnlineServersList() { 250 return Collections.emptyList(); 251 } 252 253 @Override 254 public List<ServerName> getOnlineServersListWithPredicator(List<ServerName> servers, 255 Predicate<ServerMetrics> filter) { 256 return Collections.emptyList(); 257 } 258 259 @Override 260 public Map<ServerName, List<RegionInfo>> 261 getSnapShotOfAssignment(Collection<RegionInfo> regions) { 262 return Collections.emptyMap(); 263 } 264 265 @Override 266 public boolean isOffPeakHour() { 267 return false; 268 } 269 270 @Override 271 public void recordBalancerDecision(Supplier<BalancerDecision> decision) { 272 273 } 274 275 @Override 276 public void recordBalancerRejection(Supplier<BalancerRejection> rejection) { 277 278 } 279 280 @Override 281 public ServerMetrics getLoad(ServerName serverName) { 282 return null; 283 } 284 285 @Override 286 public void onConfigurationChange(Configuration conf) { 287 this.conf = conf; 288 } 289 } 290}