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.Collections; 023import java.util.HashMap; 024import java.util.List; 025import java.util.Map; 026import java.util.concurrent.TimeUnit; 027import org.apache.hadoop.hbase.HBaseCommonTestingUtility; 028import org.apache.hadoop.hbase.HBaseInterfaceAudience; 029import org.apache.hadoop.hbase.HConstants; 030import org.apache.hadoop.hbase.ServerName; 031import org.apache.hadoop.hbase.TableName; 032import org.apache.hadoop.hbase.client.RegionInfo; 033import org.apache.hadoop.hbase.client.RegionInfoBuilder; 034import org.apache.hadoop.hbase.master.LoadBalancer; 035import org.apache.hadoop.hbase.util.AbstractHBaseTool; 036import org.apache.hadoop.hbase.util.Bytes; 037import org.apache.yetus.audience.InterfaceAudience; 038import org.slf4j.Logger; 039import org.slf4j.LoggerFactory; 040 041import org.apache.hbase.thirdparty.com.google.common.base.Preconditions; 042import org.apache.hbase.thirdparty.com.google.common.base.Stopwatch; 043import org.apache.hbase.thirdparty.org.apache.commons.cli.CommandLine; 044import org.apache.hbase.thirdparty.org.apache.commons.cli.Option; 045 046/** 047 * Tool to test performance of different {@link org.apache.hadoop.hbase.master.LoadBalancer} 048 * implementations. Example command: $ bin/hbase 049 * org.apache.hadoop.hbase.master.balancer.LoadBalancerPerformanceEvaluation -regions 1000 -servers 050 * 100 -load_balancer org.apache.hadoop.hbase.master.balancer.SimpleLoadBalancer 051 */ 052@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.TOOLS) 053public class LoadBalancerPerformanceEvaluation extends AbstractHBaseTool { 054 private static final Logger LOG = 055 LoggerFactory.getLogger(LoadBalancerPerformanceEvaluation.class.getName()); 056 057 protected static final HBaseCommonTestingUtility UTIL = new HBaseCommonTestingUtility(); 058 059 private static final int DEFAULT_NUM_REGIONS = 1000000; 060 private static Option NUM_REGIONS_OPT = new Option("regions", true, 061 "Number of regions to consider by load balancer. Default: " + DEFAULT_NUM_REGIONS); 062 063 private static final int DEFAULT_NUM_SERVERS = 1000; 064 private static Option NUM_SERVERS_OPT = new Option("servers", true, 065 "Number of servers to consider by load balancer. Default: " + DEFAULT_NUM_SERVERS); 066 067 private static final String DEFAULT_LOAD_BALANCER = 068 "org.apache.hadoop.hbase.master.balancer.StochasticLoadBalancer"; 069 private static Option LOAD_BALANCER_OPT = new Option("load_balancer", true, 070 "Type of Load Balancer to use. Default: " + DEFAULT_LOAD_BALANCER); 071 072 private int numRegions; 073 private int numServers; 074 private String loadBalancerType; 075 private Class<?> loadBalancerClazz; 076 077 private LoadBalancer loadBalancer; 078 079 // data 080 private List<ServerName> servers; 081 private List<RegionInfo> regions; 082 private Map<RegionInfo, ServerName> regionServerMap; 083 private Map<TableName, Map<ServerName, List<RegionInfo>>> tableServerRegionMap; 084 085 // Non-default configurations. 086 private void setupConf() { 087 conf.setClass(HConstants.HBASE_MASTER_LOADBALANCER_CLASS, loadBalancerClazz, 088 LoadBalancer.class); 089 loadBalancer = LoadBalancerFactory.getLoadBalancer(conf); 090 } 091 092 private void generateRegionsAndServers() { 093 TableName tableName = TableName.valueOf("LoadBalancerPerfTable"); 094 // regions 095 regions = new ArrayList<>(numRegions); 096 regionServerMap = new HashMap<>(numRegions); 097 for (int i = 0; i < numRegions; ++i) { 098 byte[] start = new byte[16]; 099 byte[] end = new byte[16]; 100 101 Bytes.putInt(start, 0, i); 102 Bytes.putInt(end, 0, i + 1); 103 RegionInfo hri = RegionInfoBuilder.newBuilder(tableName).setStartKey(start).setEndKey(end) 104 .setSplit(false).setRegionId(i).build(); 105 regions.add(hri); 106 regionServerMap.put(hri, null); 107 } 108 109 // servers 110 servers = new ArrayList<>(numServers); 111 Map<ServerName, List<RegionInfo>> serverRegionMap = new HashMap<>(numServers); 112 for (int i = 0; i < numServers; ++i) { 113 ServerName sn = ServerName.valueOf("srv" + i, HConstants.DEFAULT_REGIONSERVER_PORT, i); 114 servers.add(sn); 115 serverRegionMap.put(sn, i == 0 ? regions : Collections.emptyList()); 116 } 117 tableServerRegionMap = Collections.singletonMap(tableName, serverRegionMap); 118 } 119 120 @Override 121 protected void addOptions() { 122 addOption(NUM_REGIONS_OPT); 123 addOption(NUM_SERVERS_OPT); 124 addOption(LOAD_BALANCER_OPT); 125 } 126 127 @Override 128 protected void processOptions(CommandLine cmd) { 129 numRegions = getOptionAsInt(cmd, NUM_REGIONS_OPT.getOpt(), DEFAULT_NUM_REGIONS); 130 Preconditions.checkArgument(numRegions > 0, "Invalid number of regions!"); 131 132 numServers = getOptionAsInt(cmd, NUM_SERVERS_OPT.getOpt(), DEFAULT_NUM_SERVERS); 133 Preconditions.checkArgument(numServers > 0, "Invalid number of servers!"); 134 135 loadBalancerType = cmd.getOptionValue(LOAD_BALANCER_OPT.getOpt(), DEFAULT_LOAD_BALANCER); 136 Preconditions.checkArgument(!loadBalancerType.isEmpty(), "Invalid load balancer type!"); 137 138 try { 139 loadBalancerClazz = Class.forName(loadBalancerType); 140 } catch (ClassNotFoundException e) { 141 System.err.println("Class '" + loadBalancerType + "' not found!"); 142 System.exit(1); 143 } 144 145 setupConf(); 146 } 147 148 private String formatResults(final String methodName, final long timeMillis) { 149 return String.format("Time for %-25s: %dms%n", methodName, timeMillis); 150 } 151 152 @Override 153 protected int doWork() throws Exception { 154 generateRegionsAndServers(); 155 156 String methodName = "roundRobinAssignment"; 157 LOG.info("Calling " + methodName); 158 Stopwatch watch = Stopwatch.createStarted(); 159 loadBalancer.roundRobinAssignment(regions, servers); 160 System.out.print(formatResults(methodName, watch.elapsed(TimeUnit.MILLISECONDS))); 161 162 methodName = "retainAssignment"; 163 LOG.info("Calling " + methodName); 164 watch.reset().start(); 165 loadBalancer.retainAssignment(regionServerMap, servers); 166 System.out.print(formatResults(methodName, watch.elapsed(TimeUnit.MILLISECONDS))); 167 168 methodName = "balanceCluster"; 169 LOG.info("Calling " + methodName); 170 watch.reset().start(); 171 172 loadBalancer.balanceCluster(tableServerRegionMap); 173 System.out.print(formatResults(methodName, watch.elapsed(TimeUnit.MILLISECONDS))); 174 175 return EXIT_SUCCESS; 176 } 177 178 public static void main(String[] args) throws IOException { 179 LoadBalancerPerformanceEvaluation tool = new LoadBalancerPerformanceEvaluation(); 180 tool.setConf(UTIL.getConfiguration()); 181 tool.run(args); 182 } 183}