blob: 9297043627dea4d43a9532c123fd4e0ec2f2f26e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.hbase.master.http;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.ClusterMetrics;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.RegionMetrics;
import org.apache.hadoop.hbase.ServerMetrics;
import org.apache.hadoop.hbase.ServerName;
import org.apache.hadoop.hbase.Size;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Admin;
import org.apache.hadoop.hbase.client.AsyncAdmin;
import org.apache.hadoop.hbase.client.AsyncConnection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import org.apache.hadoop.hbase.client.RegionInfo;
import org.apache.hadoop.hbase.util.AbstractHBaseTool;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.yetus.audience.InterfaceAudience;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.hbase.thirdparty.com.google.gson.FieldNamingPolicy;
import org.apache.hbase.thirdparty.com.google.gson.Gson;
import org.apache.hbase.thirdparty.com.google.gson.GsonBuilder;
import org.apache.hbase.thirdparty.com.google.gson.JsonElement;
import org.apache.hbase.thirdparty.com.google.gson.JsonObject;
import org.apache.hbase.thirdparty.com.google.gson.JsonPrimitive;
import org.apache.hbase.thirdparty.com.google.gson.JsonSerializationContext;
import org.apache.hbase.thirdparty.com.google.gson.JsonSerializer;
import org.apache.hbase.thirdparty.org.apache.commons.cli.CommandLine;
/**
* Support class for the "Region Visualizer" rendered out of
* {@code src/main/jamon/org/apache/hadoop/hbase/tmpl/master/RegionVisualizerTmpl.jamon}
*/
@InterfaceAudience.Private
public class RegionVisualizer extends AbstractHBaseTool {
private static final Logger LOG = LoggerFactory.getLogger(RegionVisualizer.class);
private final Admin admin;
private final AsyncAdmin asyncAdmin;
private final Gson gson;
public RegionVisualizer() {
admin = null;
asyncAdmin = null;
gson = null;
}
public RegionVisualizer(final Admin admin) {
this.admin = admin;
this.asyncAdmin = null;
this.gson = buildGson();
}
public RegionVisualizer(final AsyncAdmin asyncAdmin) {
this.admin = null;
this.asyncAdmin = asyncAdmin;
this.gson = buildGson();
}
public String renderRegionDetails() {
return gson.toJson(clusterStatusToRegionDetails());
}
public static void main(final String[] argv) {
new RegionVisualizer().doStaticMain(argv);
}
static Gson buildGson() {
return new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.enableComplexMapKeySerialization()
.registerTypeAdapter(byte[].class, new ByteArraySerializer())
.registerTypeAdapter(Size.class, new SizeAsBytesSerializer())
.registerTypeAdapter(RegionDetails.class, new RegionDetailsSerializer())
.create();
}
private ClusterMetrics getClusterMetrics()
throws ExecutionException, InterruptedException, IOException {
if (admin != null) {
return admin.getClusterMetrics();
}
if (asyncAdmin != null) {
return asyncAdmin.getClusterMetrics().get();
}
throw new RuntimeException("should not happen");
}
private List<RegionDetails> clusterStatusToRegionDetails() {
final ClusterMetrics clusterMetrics;
try {
clusterMetrics = getClusterMetrics();
} catch (Exception e) {
LOG.warn("Failed to retrieve cluster metrics.", e);
return Collections.emptyList();
}
return clusterMetrics.getLiveServerMetrics().entrySet().stream()
.flatMap(serverEntry -> {
final ServerName serverName = serverEntry.getKey();
final ServerMetrics serverMetrics = serverEntry.getValue();
return serverMetrics.getRegionMetrics().values().stream()
.map(regionMetrics -> {
final TableName tableName = RegionInfo.getTable(regionMetrics.getRegionName());
return new RegionDetails(serverName, tableName, regionMetrics);
});
})
.collect(Collectors.toList());
}
@Override protected void addOptions() {
}
@Override protected void processOptions(CommandLine cmd) {
}
@Override protected int doWork() throws Exception {
final Configuration conf = HBaseConfiguration.create(getConf());
final AsyncConnection conn = ConnectionFactory.createAsyncConnection(conf).get();
final RegionVisualizer viz = new RegionVisualizer(conn.getAdmin());
System.out.println(viz.renderRegionDetails());
return 0;
}
private static final class ByteArraySerializer implements JsonSerializer<byte[]> {
@Override
public JsonElement serialize(byte[] src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(Bytes.toString(src));
}
}
/**
* Simplify representation of a {@link Size} instance by converting to bytes.
*/
private static final class SizeAsBytesSerializer implements JsonSerializer<Size> {
@Override
public JsonElement serialize(Size src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src.get(Size.Unit.BYTE));
}
}
/**
* "Flatten" the serialized representation of a {@link RegionDetails}.
*/
private static final class RegionDetailsSerializer implements JsonSerializer<RegionDetails> {
@Override
public JsonElement serialize(RegionDetails src, Type typeOfSrc,
JsonSerializationContext context) {
final JsonObject jobj = (JsonObject) context.serialize(src.getRegionMetrics());
jobj.addProperty("server_name", src.getServerName().toShortString());
jobj.addProperty("table_name", src.getTableName().getNameAsString());
return jobj;
}
}
/**
* POJO carrying detailed information about a region for use in visualizations. Intended to be
* serialized to JSON and consumed from JavaScript.
*/
public static final class RegionDetails {
private final ServerName serverName;
private final TableName tableName;
private final RegionMetrics regionMetrics;
RegionDetails(
final ServerName serverName,
final TableName tableName,
final RegionMetrics regionMetrics
) {
this.serverName = serverName;
this.tableName = tableName;
this.regionMetrics = regionMetrics;
}
public ServerName getServerName() {
return serverName;
}
public TableName getTableName() {
return tableName;
}
public RegionMetrics getRegionMetrics() {
return regionMetrics;
}
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
.append("serverName", serverName)
.append("tableName", tableName)
.append("regionMetrics", regionMetrics)
.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
RegionDetails that = (RegionDetails) o;
return new EqualsBuilder()
.append(serverName, that.serverName)
.append(tableName, that.tableName)
.append(regionMetrics, that.regionMetrics)
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37)
.append(serverName)
.append(tableName)
.append(regionMetrics)
.toHashCode();
}
}
}