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.HBaseConfiguration; 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 private static final int DEFAULT_NUM_REGIONS = 1000000; 058 private static Option NUM_REGIONS_OPT = new Option("regions", true, 059 "Number of regions to consider by load balancer. Default: " + DEFAULT_NUM_REGIONS); 060 061 private static final int DEFAULT_NUM_SERVERS = 1000; 062 private static Option NUM_SERVERS_OPT = new Option("servers", true, 063 "Number of servers to consider by load balancer. Default: " + DEFAULT_NUM_SERVERS); 064 065 private static final String DEFAULT_LOAD_BALANCER = 066 "org.apache.hadoop.hbase.master.balancer.StochasticLoadBalancer"; 067 private static Option LOAD_BALANCER_OPT = new Option("load_balancer", true, 068 "Type of Load Balancer to use. Default: " + DEFAULT_LOAD_BALANCER); 069 070 private int numRegions; 071 private int numServers; 072 private String loadBalancerType; 073 private Class<?> loadBalancerClazz; 074 075 private LoadBalancer loadBalancer; 076 077 // data 078 private List<ServerName> servers; 079 private List<RegionInfo> regions; 080 private Map<RegionInfo, ServerName> regionServerMap; 081 private Map<TableName, Map<ServerName, List<RegionInfo>>> tableServerRegionMap; 082 083 // Non-default configurations. 084 private void setupConf() { 085 conf.setClass(HConstants.HBASE_MASTER_LOADBALANCER_CLASS, loadBalancerClazz, 086 LoadBalancer.class); 087 loadBalancer = LoadBalancerFactory.getLoadBalancer(conf); 088 } 089 090 private void generateRegionsAndServers() { 091 TableName tableName = TableName.valueOf("LoadBalancerPerfTable"); 092 // regions 093 regions = new ArrayList<>(numRegions); 094 regionServerMap = new HashMap<>(numRegions); 095 for (int i = 0; i < numRegions; ++i) { 096 byte[] start = new byte[16]; 097 byte[] end = new byte[16]; 098 099 Bytes.putInt(start, 0, i); 100 Bytes.putInt(end, 0, i + 1); 101 RegionInfo hri = RegionInfoBuilder.newBuilder(tableName).setStartKey(start).setEndKey(end) 102 .setSplit(false).setRegionId(i).build(); 103 regions.add(hri); 104 regionServerMap.put(hri, null); 105 } 106 107 // servers 108 servers = new ArrayList<>(numServers); 109 Map<ServerName, List<RegionInfo>> serverRegionMap = new HashMap<>(numServers); 110 for (int i = 0; i < numServers; ++i) { 111 ServerName sn = ServerName.valueOf("srv" + i, HConstants.DEFAULT_REGIONSERVER_PORT, i); 112 servers.add(sn); 113 serverRegionMap.put(sn, i == 0 ? regions : Collections.emptyList()); 114 } 115 tableServerRegionMap = Collections.singletonMap(tableName, serverRegionMap); 116 } 117 118 @Override 119 protected void addOptions() { 120 addOption(NUM_REGIONS_OPT); 121 addOption(NUM_SERVERS_OPT); 122 addOption(LOAD_BALANCER_OPT); 123 } 124 125 @Override 126 protected void processOptions(CommandLine cmd) { 127 numRegions = getOptionAsInt(cmd, NUM_REGIONS_OPT.getOpt(), DEFAULT_NUM_REGIONS); 128 Preconditions.checkArgument(numRegions > 0, "Invalid number of regions!"); 129 130 numServers = getOptionAsInt(cmd, NUM_SERVERS_OPT.getOpt(), DEFAULT_NUM_SERVERS); 131 Preconditions.checkArgument(numServers > 0, "Invalid number of servers!"); 132 133 loadBalancerType = cmd.getOptionValue(LOAD_BALANCER_OPT.getOpt(), DEFAULT_LOAD_BALANCER); 134 Preconditions.checkArgument(!loadBalancerType.isEmpty(), "Invalid load balancer type!"); 135 136 try { 137 loadBalancerClazz = Class.forName(loadBalancerType); 138 } catch (ClassNotFoundException e) { 139 System.err.println("Class '" + loadBalancerType + "' not found!"); 140 System.exit(1); 141 } 142 143 setupConf(); 144 } 145 146 private String formatResults(final String methodName, final long timeMillis) { 147 return String.format("Time for %-25s: %dms%n", methodName, timeMillis); 148 } 149 150 @Override 151 protected int doWork() throws Exception { 152 generateRegionsAndServers(); 153 154 String methodName = "roundRobinAssignment"; 155 LOG.info("Calling " + methodName); 156 Stopwatch watch = Stopwatch.createStarted(); 157 loadBalancer.roundRobinAssignment(regions, servers); 158 System.out.print(formatResults(methodName, watch.elapsed(TimeUnit.MILLISECONDS))); 159 160 methodName = "retainAssignment"; 161 LOG.info("Calling " + methodName); 162 watch.reset().start(); 163 loadBalancer.retainAssignment(regionServerMap, servers); 164 System.out.print(formatResults(methodName, watch.elapsed(TimeUnit.MILLISECONDS))); 165 166 methodName = "balanceCluster"; 167 LOG.info("Calling " + methodName); 168 watch.reset().start(); 169 170 loadBalancer.balanceCluster(tableServerRegionMap); 171 System.out.print(formatResults(methodName, watch.elapsed(TimeUnit.MILLISECONDS))); 172 173 return EXIT_SUCCESS; 174 } 175 176 public static void main(String[] args) throws IOException { 177 LoadBalancerPerformanceEvaluation tool = new LoadBalancerPerformanceEvaluation(); 178 tool.setConf(HBaseConfiguration.create()); 179 tool.run(args); 180 } 181}