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.http; 019 020import java.io.IOException; 021import java.lang.reflect.Type; 022import java.util.Collections; 023import java.util.List; 024import java.util.concurrent.ExecutionException; 025import java.util.stream.Collectors; 026import org.apache.commons.lang3.builder.EqualsBuilder; 027import org.apache.commons.lang3.builder.HashCodeBuilder; 028import org.apache.commons.lang3.builder.ToStringBuilder; 029import org.apache.commons.lang3.builder.ToStringStyle; 030import org.apache.hadoop.conf.Configuration; 031import org.apache.hadoop.hbase.ClusterMetrics; 032import org.apache.hadoop.hbase.HBaseConfiguration; 033import org.apache.hadoop.hbase.RegionMetrics; 034import org.apache.hadoop.hbase.ServerMetrics; 035import org.apache.hadoop.hbase.ServerName; 036import org.apache.hadoop.hbase.Size; 037import org.apache.hadoop.hbase.TableName; 038import org.apache.hadoop.hbase.client.Admin; 039import org.apache.hadoop.hbase.client.AsyncAdmin; 040import org.apache.hadoop.hbase.client.AsyncConnection; 041import org.apache.hadoop.hbase.client.ConnectionFactory; 042import org.apache.hadoop.hbase.client.RegionInfo; 043import org.apache.hadoop.hbase.util.AbstractHBaseTool; 044import org.apache.hadoop.hbase.util.Bytes; 045import org.apache.yetus.audience.InterfaceAudience; 046import org.slf4j.Logger; 047import org.slf4j.LoggerFactory; 048 049import org.apache.hbase.thirdparty.com.google.gson.FieldNamingPolicy; 050import org.apache.hbase.thirdparty.com.google.gson.Gson; 051import org.apache.hbase.thirdparty.com.google.gson.GsonBuilder; 052import org.apache.hbase.thirdparty.com.google.gson.JsonElement; 053import org.apache.hbase.thirdparty.com.google.gson.JsonObject; 054import org.apache.hbase.thirdparty.com.google.gson.JsonPrimitive; 055import org.apache.hbase.thirdparty.com.google.gson.JsonSerializationContext; 056import org.apache.hbase.thirdparty.com.google.gson.JsonSerializer; 057import org.apache.hbase.thirdparty.org.apache.commons.cli.CommandLine; 058 059/** 060 * Support class for the "Region Visualizer" rendered out of 061 * {@code src/main/jamon/org/apache/hadoop/hbase/tmpl/master/RegionVisualizerTmpl.jamon} 062 */ 063@InterfaceAudience.Private 064public class RegionVisualizer extends AbstractHBaseTool { 065 private static final Logger LOG = LoggerFactory.getLogger(RegionVisualizer.class); 066 067 private final Admin admin; 068 private final AsyncAdmin asyncAdmin; 069 private final Gson gson; 070 071 public RegionVisualizer() { 072 admin = null; 073 asyncAdmin = null; 074 gson = null; 075 } 076 077 public RegionVisualizer(final Admin admin) { 078 this.admin = admin; 079 this.asyncAdmin = null; 080 this.gson = buildGson(); 081 } 082 083 public RegionVisualizer(final AsyncAdmin asyncAdmin) { 084 this.admin = null; 085 this.asyncAdmin = asyncAdmin; 086 this.gson = buildGson(); 087 } 088 089 public String renderRegionDetails() { 090 return gson.toJson(clusterStatusToRegionDetails()); 091 } 092 093 public static void main(final String[] argv) { 094 new RegionVisualizer().doStaticMain(argv); 095 } 096 097 static Gson buildGson() { 098 return new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) 099 .enableComplexMapKeySerialization() 100 .registerTypeAdapter(byte[].class, new ByteArraySerializer()) 101 .registerTypeAdapter(Size.class, new SizeAsBytesSerializer()) 102 .registerTypeAdapter(RegionDetails.class, new RegionDetailsSerializer()).create(); 103 } 104 105 private ClusterMetrics getClusterMetrics() 106 throws ExecutionException, InterruptedException, IOException { 107 if (admin != null) { 108 return admin.getClusterMetrics(); 109 } 110 if (asyncAdmin != null) { 111 return asyncAdmin.getClusterMetrics().get(); 112 } 113 throw new RuntimeException("should not happen"); 114 } 115 116 private List<RegionDetails> clusterStatusToRegionDetails() { 117 final ClusterMetrics clusterMetrics; 118 try { 119 clusterMetrics = getClusterMetrics(); 120 } catch (Exception e) { 121 LOG.warn("Failed to retrieve cluster metrics.", e); 122 return Collections.emptyList(); 123 } 124 125 return clusterMetrics.getLiveServerMetrics().entrySet().stream().flatMap(serverEntry -> { 126 final ServerName serverName = serverEntry.getKey(); 127 final ServerMetrics serverMetrics = serverEntry.getValue(); 128 return serverMetrics.getRegionMetrics().values().stream().map(regionMetrics -> { 129 final TableName tableName = RegionInfo.getTable(regionMetrics.getRegionName()); 130 return new RegionDetails(serverName, tableName, regionMetrics); 131 }); 132 }).collect(Collectors.toList()); 133 } 134 135 @Override 136 protected void addOptions() { 137 138 } 139 140 @Override 141 protected void processOptions(CommandLine cmd) { 142 143 } 144 145 @Override 146 protected int doWork() throws Exception { 147 final Configuration conf = HBaseConfiguration.create(getConf()); 148 final AsyncConnection conn = ConnectionFactory.createAsyncConnection(conf).get(); 149 final RegionVisualizer viz = new RegionVisualizer(conn.getAdmin()); 150 System.out.println(viz.renderRegionDetails()); 151 return 0; 152 } 153 154 private static final class ByteArraySerializer implements JsonSerializer<byte[]> { 155 156 @Override 157 public JsonElement serialize(byte[] src, Type typeOfSrc, JsonSerializationContext context) { 158 return new JsonPrimitive(Bytes.toString(src)); 159 } 160 } 161 162 /** 163 * Simplify representation of a {@link Size} instance by converting to bytes. 164 */ 165 private static final class SizeAsBytesSerializer implements JsonSerializer<Size> { 166 167 @Override 168 public JsonElement serialize(Size src, Type typeOfSrc, JsonSerializationContext context) { 169 return new JsonPrimitive(src.get(Size.Unit.BYTE)); 170 } 171 } 172 173 /** 174 * "Flatten" the serialized representation of a {@link RegionDetails}. 175 */ 176 private static final class RegionDetailsSerializer implements JsonSerializer<RegionDetails> { 177 @Override 178 public JsonElement serialize(RegionDetails src, Type typeOfSrc, 179 JsonSerializationContext context) { 180 final JsonObject jobj = (JsonObject) context.serialize(src.getRegionMetrics()); 181 jobj.addProperty("server_name", src.getServerName().toShortString()); 182 jobj.addProperty("table_name", src.getTableName().getNameAsString()); 183 return jobj; 184 } 185 } 186 187 /** 188 * POJO carrying detailed information about a region for use in visualizations. Intended to be 189 * serialized to JSON and consumed from JavaScript. 190 */ 191 public static final class RegionDetails { 192 private final ServerName serverName; 193 private final TableName tableName; 194 private final RegionMetrics regionMetrics; 195 196 RegionDetails(final ServerName serverName, final TableName tableName, 197 final RegionMetrics regionMetrics) { 198 this.serverName = serverName; 199 this.tableName = tableName; 200 this.regionMetrics = regionMetrics; 201 } 202 203 public ServerName getServerName() { 204 return serverName; 205 } 206 207 public TableName getTableName() { 208 return tableName; 209 } 210 211 public RegionMetrics getRegionMetrics() { 212 return regionMetrics; 213 } 214 215 @Override 216 public String toString() { 217 return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) 218 .append("serverName", serverName).append("tableName", tableName) 219 .append("regionMetrics", regionMetrics).toString(); 220 } 221 222 @Override 223 public boolean equals(Object o) { 224 if (this == o) { 225 return true; 226 } 227 228 if (o == null || getClass() != o.getClass()) { 229 return false; 230 } 231 232 RegionDetails that = (RegionDetails) o; 233 234 return new EqualsBuilder().append(serverName, that.serverName) 235 .append(tableName, that.tableName).append(regionMetrics, that.regionMetrics).isEquals(); 236 } 237 238 @Override 239 public int hashCode() { 240 return new HashCodeBuilder(17, 37).append(serverName).append(tableName).append(regionMetrics) 241 .toHashCode(); 242 } 243 } 244}